this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(manage): use Bluesky CDN URL for prefill avatar

The /api/me/avatar proxy occasionally fails to render in the form
slot on first sign-in (broken-image glyph), even though the upstream
PDS getBlob succeeds. Cut out the proxy hop entirely for the prefill
case: build the deterministic public Bluesky CDN URL from the
did + cid we already have. Also add an onError fallback on the form's
avatar <img> so any broken source collapses to the empty placeholder
instead of leaving the browser glyph.

Made-with: Cursor

+27 -8
+6
islands/CreateProfileForm.tsx
··· 381 381 src={avatarPreview.value} 382 382 alt="" 383 383 class="profile-form-avatar-img" 384 + onError={() => { 385 + // If the source URL fails (e.g. PDS slow / CDN miss), 386 + // collapse to the empty-slot placeholder rather than 387 + // leaving the browser's broken-image glyph. 388 + avatarPreview.value = null; 389 + }} 384 390 /> 385 391 ) 386 392 : (
+21 -8
routes/explore/manage.tsx
··· 8 8 import { loadSession } from "../../lib/oauth.ts"; 9 9 import { getBskyProfile } from "../../lib/pds.ts"; 10 10 11 - /** Route the prefill avatar through our own proxy (which streams the 12 - * PDS blob with friendly content-type + cache headers) rather than 13 - * embedding the raw PDS getBlob URL into an <img>. Some PDS hosts 14 - * don't serve blobs cleanly to a browser <img> tag (content-type, 15 - * redirects, CORS quirks), but they all work fine for a server-to- 16 - * server fetch. */ 17 - const ME_AVATAR_PROXY = "/api/me/avatar"; 11 + /** 12 + * Build the deterministic public Bluesky CDN URL for a user's avatar 13 + * blob. The CDN is a thin cached proxy in front of the user's PDS, so 14 + * any did/cid pair from `app.bsky.actor.profile` resolves cleanly here 15 + * with cache headers + the correct content-type. Using this URL avoids 16 + * routing the prefill avatar through our own server (which adds a hop 17 + * and can fail in subtle ways on some PDS hosts). 18 + */ 19 + function bskyCdnAvatarUrl(did: string, cid: string, mime: string): string { 20 + const ext = mime === "image/png" 21 + ? "png" 22 + : mime === "image/webp" 23 + ? "webp" 24 + : "jpeg"; 25 + return `https://cdn.bsky.app/img/avatar/plain/${did}/${cid}@${ext}`; 26 + } 18 27 19 28 export const handler = define.handlers({ 20 29 async GET(ctx) { ··· 67 76 : null, 68 77 }; 69 78 if (bsky.avatar) { 70 - initialAvatarUrl = ME_AVATAR_PROXY; 79 + initialAvatarUrl = bskyCdnAvatarUrl( 80 + user.did, 81 + bsky.avatar.ref.$link, 82 + bsky.avatar.mimeType, 83 + ); 71 84 } 72 85 } 73 86 }