this repo has no description
10
fork

Configure Feed

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

fix: set Content-Length on og-banner JPEG response

og-hero.png (static file, works) returns Content-Length: 317687.
og-banner (dynamic route, broken) had no Content-Length — Deno Deploy
does not set it for Blob bodies on dynamic routes. Bluesky's image upload
pipeline requires Content-Length to process external card thumbnails;
without it the image is silently rejected and the card shows text-only.

Use ArrayBuffer body + explicit content-length header so the og-banner
response matches the header profile of a static image file.

Made-with: Cursor

+16 -14
+16 -14
routes/api/registry/og-banner/[did].ts
··· 36 36 try { 37 37 const img = await Image.decode(buf); 38 38 const cov = img.cover(OG_W, OG_H); 39 - const jpeg = await cov.encodeJPEG(JPEG_QUALITY); 40 - const headers = new Headers({ 41 - "content-type": "image/jpeg", 42 - "cache-control": 43 - "public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400", 44 - "etag": `${profile.bannerCid}-og`, 45 - "content-disposition": 'inline; filename="og-banner.jpg"', 46 - "access-control-allow-origin": "*", 47 - "cross-origin-resource-policy": "cross-origin", 39 + const jpeg = new Uint8Array(await cov.encodeJPEG(JPEG_QUALITY)); 40 + return new Response(jpeg.buffer as ArrayBuffer, { 41 + status: 200, 42 + headers: { 43 + "content-type": "image/jpeg", 44 + "content-length": String(jpeg.byteLength), 45 + "cache-control": 46 + "public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400", 47 + "etag": `${profile.bannerCid}-og`, 48 + "content-disposition": 'inline; filename="og-banner.jpg"', 49 + "access-control-allow-origin": "*", 50 + "cross-origin-resource-policy": "cross-origin", 51 + }, 48 52 }); 49 - return new Response( 50 - new Blob([new Uint8Array(jpeg)], { type: "image/jpeg" }), 51 - { status: 200, headers }, 52 - ); 53 53 } catch (err) { 54 54 console.warn("[og-banner] resize failed, serving raw bytes:", err); 55 55 const ct = upstream.headers.get("content-type") ?? 56 56 profile.bannerMime ?? "application/octet-stream"; 57 - return new Response(new Blob([new Uint8Array(buf)], { type: ct }), { 57 + return new Response(buf.buffer as ArrayBuffer, { 58 58 status: 200, 59 59 headers: { 60 + "content-type": ct, 61 + "content-length": String(buf.byteLength), 60 62 "cache-control": 61 63 "public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400", 62 64 "etag": profile.bannerCid,