From afa115e890034c33fb036d1ebcb531b7f9e2245d Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Thu, 11 Dec 2025 03:17:44 +0000 Subject: [PATCH] fix pub favicons --- app/lish/[did]/[publication]/icon.ts | 68 -------------------------------------------------------------------- app/lish/[did]/[publication]/layout.tsx | 8 ++++++++ app/lish/[did]/[publication]/[rkey]/page.tsx | 7 +++++++ app/lish/[did]/[publication]/icon/route.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 file(s) changed, 82 insertion(s)(+), 68 deletion(s)(-) diff --git a/app/lish/[did]/[publication]/icon.ts b/app/lish/[did]/[publication]/icon.ts deleted file mode 100644 --- a/app/lish/[did]/[publication]/icon.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { NextRequest } from "next/server"; -import { IdResolver } from "@atproto/identity"; -import { AtUri } from "@atproto/syntax"; -import { PubLeafletPublication } from "lexicons/api"; -import { supabaseServerClient } from "supabase/serverClient"; -import sharp from "sharp"; -import { redirect } from "next/navigation"; - -let idResolver = new IdResolver(); - -export const size = { - width: 32, - height: 32, -}; - -export const contentType = "image/png"; -export default async function Icon(props: { - params: Promise<{ did: string; publication: string }>; -}) { - const params = await props.params; - try { - let did = decodeURIComponent(params.did); - let uri; - if (/^(?!\.$|\.\.S)[A-Za-z0-9._:~-]{1,512}$/.test(params.publication)) { - uri = AtUri.make( - did, - "pub.leaflet.publication", - params.publication, - ).toString(); - } - let { data: publication } = await supabaseServerClient - .from("publications") - .select( - `*, - publication_subscriptions(*), - documents_in_publications(documents(*)) - `, - ) - .eq("identity_did", did) - .or(`name.eq."${params.publication}", uri.eq."${uri}"`) - .single(); - - let record = publication?.record as PubLeafletPublication.Record | null; - if (!record?.icon) return redirect("/icon.png"); - - let identity = await idResolver.did.resolve(did); - let service = identity?.service?.find((f) => f.id === "#atproto_pds"); - if (!service) return null; - let cid = (record.icon.ref as unknown as { $link: string })["$link"]; - const response = await fetch( - `${service.serviceEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`, - ); - let blob = await response.blob(); - let resizedImage = await sharp(await blob.arrayBuffer()) - .resize({ width: 32, height: 32 }) - .toBuffer(); - return new Response(new Uint8Array(resizedImage), { - headers: { - "Content-Type": "image/png", - "CDN-Cache-Control": "s-maxage=86400, stale-while-revalidate=86400", - "Cache-Control": - "public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400", - }, - }); - } catch (e) { - return redirect("/icon.png"); - } -} diff --git a/app/lish/[did]/[publication]/layout.tsx b/app/lish/[did]/[publication]/layout.tsx --- a/app/lish/[did]/[publication]/layout.tsx +++ b/app/lish/[did]/[publication]/layout.tsx @@ -47,6 +47,14 @@ title: pubRecord?.name || "Untitled Publication", description: pubRecord?.description || "", icons: { + icon: { + url: + process.env.NODE_ENV === "development" + ? `/lish/${did}/${publication_name}/icon` + : "/icon", + sizes: "32x32", + type: "image/png", + }, other: { rel: "alternate", url: publication.uri, diff --git a/app/lish/[did]/[publication]/[rkey]/page.tsx b/app/lish/[did]/[publication]/[rkey]/page.tsx --- a/app/lish/[did]/[publication]/[rkey]/page.tsx +++ b/app/lish/[did]/[publication]/[rkey]/page.tsx @@ -25,6 +25,13 @@ return { icons: { + icon: { + + url: + process.env.NODE_ENV === "development" + ? `/lish/${did}/${params.publication}/icon` + : "/icon", + , sizes: "32x32", type: "image/png" }, other: { rel: "alternate", url: document.uri, diff --git a/app/lish/[did]/[publication]/icon/route.ts b/app/lish/[did]/[publication]/icon/route.ts new file mode 100644 --- /dev/null +++ b/app/lish/[did]/[publication]/icon/route.ts @@ -0,0 +1,67 @@ +import { NextRequest } from "next/server"; +import { IdResolver } from "@atproto/identity"; +import { AtUri } from "@atproto/syntax"; +import { PubLeafletPublication } from "lexicons/api"; +import { supabaseServerClient } from "supabase/serverClient"; +import sharp from "sharp"; +import { redirect } from "next/navigation"; + +let idResolver = new IdResolver(); + +export const dynamic = "force-dynamic"; + +export async function GET( + request: NextRequest, + props: { params: Promise<{ did: string; publication: string }> } +) { + console.log("are we getting here?"); + const params = await props.params; + try { + let did = decodeURIComponent(params.did); + let uri; + if (/^(?!\.$|\.\.S)[A-Za-z0-9._:~-]{1,512}$/.test(params.publication)) { + uri = AtUri.make( + did, + "pub.leaflet.publication", + params.publication, + ).toString(); + } + let { data: publication } = await supabaseServerClient + .from("publications") + .select( + `*, + publication_subscriptions(*), + documents_in_publications(documents(*)) + `, + ) + .eq("identity_did", did) + .or(`name.eq."${params.publication}", uri.eq."${uri}"`) + .single(); + + let record = publication?.record as PubLeafletPublication.Record | null; + if (!record?.icon) return redirect("/icon.png"); + + let identity = await idResolver.did.resolve(did); + let service = identity?.service?.find((f) => f.id === "#atproto_pds"); + if (!service) return null; + let cid = (record.icon.ref as unknown as { $link: string })["$link"]; + const response = await fetch( + `${service.serviceEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`, + ); + let blob = await response.blob(); + let resizedImage = await sharp(await blob.arrayBuffer()) + .resize({ width: 32, height: 32 }) + .toBuffer(); + return new Response(new Uint8Array(resizedImage), { + headers: { + "Content-Type": "image/png", + "CDN-Cache-Control": "s-maxage=86400, stale-while-revalidate=86400", + "Cache-Control": + "public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400", + }, + }); + } catch (e) { + console.log(e); + return redirect("/icon.png"); + } +} -- tangled.sh