this repo has no description
10
fork

Configure Feed

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

fix: og:image URL double-prefixed by socialImageUrl when pageMeta.imageUrl is absolute

socialImageUrl prepended FRESH_PUBLIC_SITE_URL even when the input was
already an absolute https:// URL, producing a broken double-prefixed OG
image URL that crawlers (Bluesky, Slack, etc.) could not resolve.

Fixes:
- socialImageUrl now returns early if the path already starts with http(s)://
- Simplify [handle].tsx to pass a root-relative path for ogImageUrl so
socialImageUrl correctly makes it absolute in all environments
- Add og:image:secure_url + og:image:type meta tags (Bluesky prefers these)
- Pass imageType, imageWidth, imageHeight explicitly from project banner

Made-with: Cursor

+14 -4
+8
routes/_app.tsx
··· 4 4 5 5 /** Open Graph / social crawlers prefer absolute image URLs. Set FRESH_PUBLIC_SITE_URL on Deno Deploy (e.g. https://atmosphereaccount.com). */ 6 6 function socialImageUrl(path: string): string { 7 + // Already absolute — return as-is so callers that pass a full URL 8 + // (e.g. pageMeta.imageUrl built with ctx.url.origin) don't get double-prefixed. 9 + if (path.startsWith("http://") || path.startsWith("https://")) return path; 7 10 const base = Deno.env.get("FRESH_PUBLIC_SITE_URL")?.replace(/\/$/, ""); 8 11 if (base) return `${base}${path.startsWith("/") ? path : `/${path}`}`; 9 12 return path; ··· 296 299 <meta property="og:locale" content={locale} /> 297 300 <meta property="og:type" content={pageOgType} /> 298 301 <meta property="og:image" content={pageOgImage} /> 302 + <meta property="og:image:secure_url" content={pageOgImage} /> 303 + <meta 304 + property="og:image:type" 305 + content={pageMeta.imageType ?? "image/jpeg"} 306 + /> 299 307 <meta property="og:image:width" content={String(pageOgImageWidth)} /> 300 308 <meta property="og:image:height" content={String(pageOgImageHeight)} /> 301 309 <meta property="og:image:alt" content={pageOgImageAlt} />
+4 -4
routes/explore/[handle].tsx
··· 85 85 const pageDescription = profile.description || 86 86 messages.detail.missingProfile; 87 87 const ogImageUrl = profile.bannerCid 88 - ? new URL( 89 - `/api/registry/banner/${encodeURIComponent(profile.did)}`, 90 - ctx.url.origin, 91 - ).toString() 88 + ? `/api/registry/banner/${encodeURIComponent(profile.did)}` 92 89 : undefined; 93 90 ctx.state.pageMeta = { 94 91 title: pageTitle, ··· 98 95 imageAlt: profile.bannerCid 99 96 ? messages.detail.share.bannerAlt(profile.name) 100 97 : undefined, 98 + imageType: profile.bannerMime ?? "image/jpeg", 99 + imageWidth: 1200, 100 + imageHeight: 630, 101 101 }; 102 102 } 103 103 /**
+2
utils.ts
··· 23 23 imageUrl?: string; 24 24 /** Alt text for the share image. */ 25 25 imageAlt?: string; 26 + /** MIME type for og:image:type (defaults to "image/jpeg"). */ 27 + imageType?: string; 26 28 /** OG image dimensions, when known. Defaults match the site-wide OG image. */ 27 29 imageWidth?: number; 28 30 imageHeight?: number;