chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
An author: filter reads kept handles rather than asking each time
crates/knotless-serve/src/web/items.rs+14−4
| @@ -98,12 +98,22 @@ pub async fn list_items( | ||
} | ||
| ||
/// The DID a filter's `author:` names, by handle or DID; as written if it | ||
/// resolves to none, so it matches no one. | ||
/// resolves to none, so it matches no one. A handle the index found for an | ||
/// account is that account's; any other is looked up, and what was found | ||
/// kept a while, as a crawler asks for the same filter again and again and | ||
/// a handle's host can take seconds to answer. | ||
async fn did(app: &App, author: &str) -> String { | ||
match knotless::identity::resolve_handle(&app.ctx, author).await { | ||
Ok(did) => did, | ||
Err(_) => author.to_string(), | ||
let indexed = app.index.as_ref().map(|i| i.did_of_handle(author)); | ||
if let Some(Ok(Some(did))) = indexed { | ||
return did; | ||
} | ||
let (ctx, handle) = (app.ctx.clone(), author.to_string()); | ||
let look = async move { | ||
let found = knotless::identity::resolve_handle(&ctx, &handle).await; | ||
Some(found.ok()) | ||
crates/knotless-serve/src/web/items.rsline 113
| ||
}; | ||
let found = AUTHORS.through(&app.ctx.env.cache_dir, author, look).await; | ||
crates/knotless-serve/src/web/items.rsline 115
| ||
found.flatten().unwrap_or(author.to_string()) | ||
} | ||
| ||
#[derive(Deserialize)] | ||
crates/knotless-serve/src/index.rs+10−0
| @@ -630,6 +630,7 @@ pub(crate) fn connect(path: &Path) -> rusqlite::Result<(Connection, bool)> { | ||
value TEXT NOT NULL, PRIMARY KEY (did, collection, rkey)); | ||
CREATE TABLE IF NOT EXISTS handle ( | ||
did TEXT PRIMARY KEY, handle TEXT, checked INTEGER NOT NULL); | ||
CREATE INDEX IF NOT EXISTS handle_name ON handle (handle); | ||
CREATE TABLE IF NOT EXISTS home ( | ||
did TEXT PRIMARY KEY, moved INTEGER NOT NULL, own INTEGER NOT NULL, | ||
checked INTEGER NOT NULL); | ||
| @@ -3371,6 +3372,15 @@ impl Index { | ||
.optional() | ||
} | ||
| ||
/// The account whose handle was last found to be `handle`, if any was. | ||
pub fn did_of_handle(&self, handle: &str) -> rusqlite::Result<Option<String>> { | ||
let db = self.db.lock().unwrap(); | ||
let mut stmt = db.prepare_cached( | ||
"SELECT did FROM handle WHERE handle = ?1 ORDER BY checked DESC LIMIT 1", | ||
)?; | ||
stmt.query_row([handle], |row| row.get(0)).optional() | ||
} | ||
| ||
/// Keeps `handle` as what `did`'s handle was found to be at `checked`. | ||
pub fn set_handle( | ||
&self, | ||
crates/knotless-serve/src/web.rs+4−0
| @@ -613,6 +613,10 @@ static AVATARS: LazyLock<Kept<Option<String>>> = | ||
LazyLock::new(|| Kept::new("avatars.json", KEPT_FOR, LOOKUP_WAIT)); | ||
static CLAIMS: LazyLock<Kept<Vec<String>>> = | ||
LazyLock::new(|| Kept::new("claims.json", KEPT_FOR, LOOKUP_WAIT)); | ||
/// The accounts `author:` filters named by handle, by handle: none for a | ||
/// handle that names none. | ||
static AUTHORS: LazyLock<Kept<Option<String>>> = | ||
LazyLock::new(|| Kept::new("authors.json", KEPT_FOR, LOOKUP_WAIT)); | ||
| ||
/// Where Bluesky's CDN serves profile pictures, by DID and blob CID. | ||
const AVATAR_CDN: &str = "https://cdn.bsky.app/img/avatar/plain"; | ||
docs/design/serve.md+6−0
| @@ -146,6 +146,12 @@ is kept looks it up again at once: the event names the new handle, but is not | ||
trusted for it. One kept a day is still shown, and looked up again behind | ||
the page showing it. Without an index, handles are kept in memory. | ||
| ||
A filter of pulls or issues by `author:` reads the account from those kept | ||
handles. A handle not kept there is looked up at its host, and what was | ||
found, an account or none, is kept for ten minutes, in the cache directory | ||
too. A crawler can ask for the same filter every minute, and a handle's | ||
host can take seconds to answer. | ||
| ||
### Accounts that go | ||
| ||
An account event says whether the account is active at its host and, if | ||