a tool for shared writing and social publishing

use authed bsky agent for threads if available

Changed files
+38 -4
app
api
bsky
thread
+38 -4
app/api/bsky/thread/route.ts
··· 1 1 import { Agent, lexToJson } from "@atproto/api"; 2 2 import { ThreadViewPost } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 3 + import { cookies } from "next/headers"; 3 4 import { NextRequest } from "next/server"; 5 + import { createOauthClient } from "src/atproto-oauth"; 6 + import { supabaseServerClient } from "supabase/serverClient"; 4 7 5 8 export const runtime = "nodejs"; 6 9 10 + async 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 + 7 38 export async function GET(req: NextRequest) { 8 39 try { 9 40 const searchParams = req.nextUrl.searchParams; ··· 18 49 ); 19 50 } 20 51 21 - // Fetch thread from Bluesky 22 - let agent = new Agent({ 23 - service: "https://public.api.bsky.app", 24 - }); 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 + } 25 59 26 60 const response = await agent.getPostThread({ 27 61 uri,