a tool for shared writing and social publishing
at feature/thread-viewer 78 lines 2.4 kB view raw
1import { Agent, lexToJson } from "@atproto/api"; 2import { ThreadViewPost } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 3import { cookies } from "next/headers"; 4import { NextRequest } from "next/server"; 5import { createOauthClient } from "src/atproto-oauth"; 6import { supabaseServerClient } from "supabase/serverClient"; 7 8export const runtime = "nodejs"; 9 10async function getAuthenticatedAgent(): Promise<Agent | null> { 11 try { 12 const cookieStore = await cookies(); 13 const authToken = 14 cookieStore.get("auth_token")?.value || 15 cookieStore.get("external_auth_token")?.value; 16 17 if (!authToken || authToken === "null") return null; 18 19 const { data } = await supabaseServerClient 20 .from("email_auth_tokens") 21 .select("identities(atp_did)") 22 .eq("id", authToken) 23 .eq("confirmed", true) 24 .single(); 25 26 const did = data?.identities?.atp_did; 27 if (!did) return null; 28 29 const oauthClient = await createOauthClient(); 30 const session = await oauthClient.restore(did); 31 return new Agent(session); 32 } catch (error) { 33 console.error("Failed to get authenticated agent:", error); 34 return null; 35 } 36} 37 38export async function GET(req: NextRequest) { 39 try { 40 const searchParams = req.nextUrl.searchParams; 41 const uri = searchParams.get("uri"); 42 const depth = searchParams.get("depth"); 43 const parentHeight = searchParams.get("parentHeight"); 44 45 if (!uri) { 46 return Response.json( 47 { error: "uri parameter is required" }, 48 { status: 400 }, 49 ); 50 } 51 52 // Try to use authenticated agent if user is logged in, otherwise fall back to public API 53 let agent = await getAuthenticatedAgent(); 54 if (!agent) { 55 agent = new Agent({ 56 service: "https://public.api.bsky.app", 57 }); 58 } 59 60 const response = await agent.getPostThread({ 61 uri, 62 depth: depth ? parseInt(depth, 10) : 6, 63 parentHeight: parentHeight ? parseInt(parentHeight, 10) : 80, 64 }); 65 66 const thread = lexToJson(response.data.thread); 67 68 return Response.json(thread, { 69 headers: { 70 // Cache for 5 minutes on CDN, allow stale content for 1 hour while revalidating 71 "Cache-Control": "public, s-maxage=300, stale-while-revalidate=3600", 72 }, 73 }); 74 } catch (error) { 75 console.error("Error fetching Bluesky thread:", error); 76 return Response.json({ error: "Failed to fetch thread" }, { status: 500 }); 77 } 78}