fix(og-banner): cache pre-generated JPEG in DB for instant og:image serving
The root cause of the Bluesky link card image not appearing was response
latency. The og-banner route was fetching the full-resolution blob from the
PDS and running ImageScript decode+resize+encode on every request. The first
(uncached) hit could take 3–5 s; Cardyb's timeout is shorter than that, so it
returned "Unable to serve image" and cached the failure.
Fix: generate and store the 1200×630 JPEG in a new `og_jpeg` BLOB column at
profile-save time. The og-banner route now returns the cached bytes in < 10 ms
(a single DB SELECT), identical in latency profile to serving a static file.
- lib/db.ts: add `og_jpeg BLOB` column + additive migration
- lib/registry.ts: add `ogJpeg` to UpsertProfileInput; new storeOgJpeg /
getOgJpeg helpers; ON CONFLICT preserves existing og_jpeg when not replaced
- routes/api/registry/profile.ts: run generateOgJpeg() on banner upload bytes
and pass result to upsertProfile
- routes/api/registry/og-banner/[did].ts: fast path returns DB-cached JPEG;
slow path (pre-feature profiles) fetches PDS blob, resizes, stores to DB,
returns result so next request is fast
- routes/api/admin/backfill-og-jpegs.ts: POST endpoint to backfill og_jpeg
for existing profiles that pre-date this feature
Made-with: Cursor