import { NextResponse } from "next/server" import { randomBytes } from "crypto" import { createSupabaseServerClient } from "@/lib/supabase/server" const MAX_NOTE_LENGTH = 1000 function getAppOrigin(): string | null { const url = process.env.NEXT_PUBLIC_APP_URL if (!url) return null return url.replace(/\/$/, "") } export async function POST(request: Request) { const supabaseClient = await createSupabaseServerClient() const { data: { user }, } = await supabaseClient.auth.getUser() if (!user) { return NextResponse.json({ error: "not authenticated" }, { status: 401 }) } const { data: userProfile } = await supabaseClient .from("user_profiles") .select("tier") .eq("id", user.id) .single() const tier = userProfile?.tier ?? "free" const expiryDays = tier === "pro" || tier === "developer" ? 30 : 7 const expiresAt = new Date( Date.now() + expiryDays * 24 * 60 * 60 * 1000 ).toISOString() const body = await request.json().catch(() => null) if (!body || typeof body !== "object") { return NextResponse.json({ error: "invalid request body" }, { status: 400 }) } const entryIdentifier = body.entryIdentifier as string const rawNote = body.note if (!entryIdentifier || typeof entryIdentifier !== "string") { return NextResponse.json( { error: "entryIdentifier is required" }, { status: 400 } ) } let note: string | null = null if (rawNote !== undefined && rawNote !== null) { if (typeof rawNote !== "string") { return NextResponse.json( { error: "note must be a string" }, { status: 400 } ) } if (rawNote.length > MAX_NOTE_LENGTH) { return NextResponse.json( { error: `note must be ${MAX_NOTE_LENGTH} characters or fewer` }, { status: 400 } ) } note = rawNote.trim() || null } const { data: entryAccess } = await supabaseClient .from("entries") .select("id, feed_id") .eq("id", entryIdentifier) .maybeSingle() if (!entryAccess) { return NextResponse.json( { error: "entry not found or not accessible" }, { status: 404 } ) } const { data: subscriptionAccess } = await supabaseClient .from("subscriptions") .select("id") .eq("feed_id", entryAccess.feed_id) .eq("user_id", user.id) .maybeSingle() if (!subscriptionAccess) { return NextResponse.json( { error: "you do not have access to this entry" }, { status: 403 } ) } const origin = getAppOrigin() if (!origin) { return NextResponse.json( { error: "application URL is not configured" }, { status: 500 } ) } const { data: existingShare } = await supabaseClient .from("shared_entries") .select("share_token") .eq("entry_id", entryIdentifier) .eq("user_id", user.id) .maybeSingle() if (existingShare) { const shareUrl = `${origin}/shared/${existingShare.share_token}` return NextResponse.json({ shareToken: existingShare.share_token, shareUrl, }) } const shareToken = randomBytes(16).toString("base64url") const { error } = await supabaseClient.from("shared_entries").insert({ user_id: user.id, entry_id: entryIdentifier, share_token: shareToken, expires_at: expiresAt, note, }) if (error) { return NextResponse.json( { error: "failed to create share" }, { status: 500 } ) } const shareUrl = `${origin}/shared/${shareToken}` return NextResponse.json({ shareToken, shareUrl }) }