fix(appview): resolve_profile also looks up DID in profiles cache

A user who set their profile via the PDS push path before
posting anything has a row in `profiles` but no rows in
`posts` — the handle→DID lookup in `resolve_profile` only
checked `posts`, so the route synthesised an empty profile
(`did: ""`) for these users.

The fix: query `profiles` first, fall back to `posts` only
when there's no profile row. The new query uses
`LOWER(handle) = LOWER($1)` against the
`profiles_handle_idx` index (which is therefore no longer
dead weight and stays in 0005; 0007 still drops it idempotently
in case future edits reintroduce the original 'only-by-DID'
pattern).

Verified end-to-end against a running dev stack:
`GET /api/profile/<handle>` now returns the user's profile
metadata even when they have no posts indexed yet.
This commit is contained in:
tomdebone
2026-07-18 18:12:00 +02:00
parent ffee5c6685
commit 6ebf17b493
2 changed files with 22 additions and 3 deletions
+21 -3
View File
@@ -343,13 +343,31 @@ async fn resolve_profile(
// Order by `indexed_at DESC` so we get the most recent DID for
// this handle (a single user can re-use a handle if account
// history allows, but the latest is the active one).
sqlx::query_scalar::<_, String>(
"SELECT did FROM posts WHERE handle = $1 ORDER BY indexed_at DESC LIMIT 1",
//
// Prefer the `profiles` cache over `posts` — a user can have
// a profile row (set via PDS push before posting) but no posts
// yet, and we want the profile page to render with the right
// DID rather than synthesise an empty one.
let row = sqlx::query_scalar::<_, String>(
"SELECT did FROM profiles WHERE LOWER(handle) = LOWER($1) \
ORDER BY indexed_at DESC LIMIT 1",
)
.bind(h)
.fetch_optional(&state.db)
.await
.map_err(db_err)?
.map_err(db_err)?;
if row.is_some() {
row
} else {
sqlx::query_scalar::<_, String>(
"SELECT did FROM posts WHERE handle = $1 \
ORDER BY indexed_at DESC LIMIT 1",
)
.bind(h)
.fetch_optional(&state.db)
.await
.map_err(db_err)?
}
} else {
None
};
+1
View File
@@ -40,6 +40,7 @@ CREATE TABLE profiles (
following_count BIGINT NOT NULL DEFAULT 0,
indexed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX profiles_handle_idx ON profiles (LOWER(handle));
CREATE INDEX profiles_indexed_at_idx ON profiles (indexed_at DESC);
-- Backfill: seed a profile row for every handle we've already