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:
@@ -343,13 +343,31 @@ async fn resolve_profile(
|
|||||||
// Order by `indexed_at DESC` so we get the most recent DID for
|
// 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
|
// this handle (a single user can re-use a handle if account
|
||||||
// history allows, but the latest is the active one).
|
// history allows, but the latest is the active one).
|
||||||
|
//
|
||||||
|
// 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)?;
|
||||||
|
if row.is_some() {
|
||||||
|
row
|
||||||
|
} else {
|
||||||
sqlx::query_scalar::<_, String>(
|
sqlx::query_scalar::<_, String>(
|
||||||
"SELECT did FROM posts WHERE handle = $1 ORDER BY indexed_at DESC LIMIT 1",
|
"SELECT did FROM posts WHERE handle = $1 \
|
||||||
|
ORDER BY indexed_at DESC LIMIT 1",
|
||||||
)
|
)
|
||||||
.bind(h)
|
.bind(h)
|
||||||
.fetch_optional(&state.db)
|
.fetch_optional(&state.db)
|
||||||
.await
|
.await
|
||||||
.map_err(db_err)?
|
.map_err(db_err)?
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ CREATE TABLE profiles (
|
|||||||
following_count BIGINT NOT NULL DEFAULT 0,
|
following_count BIGINT NOT NULL DEFAULT 0,
|
||||||
indexed_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
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);
|
CREATE INDEX profiles_indexed_at_idx ON profiles (indexed_at DESC);
|
||||||
|
|
||||||
-- Backfill: seed a profile row for every handle we've already
|
-- Backfill: seed a profile row for every handle we've already
|
||||||
|
|||||||
Reference in New Issue
Block a user