a tool for shared writing and social publishing

add pub favicon

+65 -7
-7
app/layout.tsx
··· 14 14 title: "Leaflet", 15 15 description: "tiny interconnected social documents", 16 16 metadataBase: new URL(`https://leaflet.pub`), 17 - icons: [ 18 - { 19 - type: "apple-touch-icon", 20 - sizes: "180x180", 21 - url: "/apple-touch-icon.png", 22 - }, 23 - ], 24 17 appleWebApp: { 25 18 title: "Leaflet", 26 19 statusBarStyle: "black-translucent",
+65
app/lish/[did]/[publication]/icon.ts
··· 1 + import { NextRequest } from "next/server"; 2 + import { IdResolver } from "@atproto/identity"; 3 + import { AtUri } from "@atproto/syntax"; 4 + import { PubLeafletPublication } from "lexicons/api"; 5 + import { supabaseServerClient } from "supabase/serverClient"; 6 + import sharp from "sharp"; 7 + 8 + let idResolver = new IdResolver(); 9 + 10 + export const size = { 11 + width: 32, 12 + height: 32, 13 + }; 14 + 15 + export const contentType = "image/png"; 16 + export default async function Icon({ 17 + params, 18 + }: { 19 + params: { did: string; publication: string }; 20 + }) { 21 + let did = decodeURIComponent(params.did); 22 + let uri; 23 + if (/^(?!\.$|\.\.S)[A-Za-z0-9._:~-]{1,512}$/.test(params.publication)) { 24 + uri = AtUri.make( 25 + did, 26 + "pub.leaflet.publication", 27 + params.publication, 28 + ).toString(); 29 + } 30 + let { data: publication } = await supabaseServerClient 31 + .from("publications") 32 + .select( 33 + `*, 34 + publication_subscriptions(*), 35 + documents_in_publications(documents(*)) 36 + `, 37 + ) 38 + .eq("identity_did", did) 39 + .or(`name.eq."${params.publication}", uri.eq."${uri}"`) 40 + .single(); 41 + 42 + let record = publication?.record as PubLeafletPublication.Record | null; 43 + if (!record?.icon) return null; 44 + 45 + let identity = await idResolver.did.resolve(did); 46 + let service = identity?.service?.find((f) => f.id === "#atproto_pds"); 47 + console.log(identity); 48 + if (!service) return null; 49 + let cid = (record.icon.ref as unknown as { $link: string })["$link"]; 50 + const response = await fetch( 51 + `${service.serviceEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`, 52 + ); 53 + let blob = await response.blob(); 54 + let resizedImage = await sharp(await blob.arrayBuffer()) 55 + .resize({ width: 32, height: 32 }) 56 + .toBuffer(); 57 + console.log("fetched favicon!"); 58 + return new Response(new Uint8Array(resizedImage), { 59 + headers: { 60 + "Content-Type": "image/png", 61 + "CDN-Cache-Control": "s-max-age=86400, stale-while-revalidate=86400", 62 + "Cache-Control": "public, max-age=3600", 63 + }, 64 + }); 65 + }