import { NextRequest, NextResponse } from "next/server"; import { getAuthenticatedAgent } from "@/lib/agent"; import { getSession } from "@/lib/session"; export const dynamic = "force-dynamic"; /** Only these ATProto collections are allowed through /api/records. */ const ALLOWED_COLLECTIONS = new Set([ "org.impactindexer.review.comment", "org.impactindexer.review.like", ]); /** * POST /api/records — Create a new record * Body: { collection, rkey?, record } */ export async function POST(request: NextRequest) { try { const session = await getSession(); if (!session.did) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const agent = await getAuthenticatedAgent(); if (!agent) { return NextResponse.json( { error: "Failed to authenticate" }, { status: 401 } ); } const body = await request.json(); const { collection, rkey, record } = body; if (!collection || typeof collection !== "string") { return NextResponse.json( { error: "Collection is required" }, { status: 400 } ); } if (!ALLOWED_COLLECTIONS.has(collection)) { return NextResponse.json( { error: "Collection not allowed" }, { status: 403 } ); } if (!record || typeof record !== "object") { return NextResponse.json( { error: "Record is required" }, { status: 400 } ); } const res = await agent.com.atproto.repo.createRecord({ repo: session.did, collection, rkey: rkey || undefined, record, }); return NextResponse.json({ success: true, uri: res.data.uri, cid: res.data.cid, }); } catch (error) { console.error("Failed to create record:", error); return NextResponse.json( { error: "Failed to create record" }, { status: 500 } ); } } /** * PUT /api/records — Update an existing record * Body: { collection, rkey, record } */ export async function PUT(request: NextRequest) { try { const session = await getSession(); if (!session.did) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const agent = await getAuthenticatedAgent(); if (!agent) { return NextResponse.json( { error: "Failed to authenticate" }, { status: 401 } ); } const body = await request.json(); const { collection, rkey, record } = body; if (!collection || typeof collection !== "string") { return NextResponse.json( { error: "Collection is required" }, { status: 400 } ); } if (!ALLOWED_COLLECTIONS.has(collection)) { return NextResponse.json( { error: "Collection not allowed" }, { status: 403 } ); } if (!rkey || typeof rkey !== "string") { return NextResponse.json( { error: "Record key is required" }, { status: 400 } ); } if (!record || typeof record !== "object") { return NextResponse.json( { error: "Record is required" }, { status: 400 } ); } await agent.com.atproto.repo.putRecord({ repo: session.did, collection, rkey, record, }); return NextResponse.json({ success: true }); } catch (error) { console.error("Failed to update record:", error); return NextResponse.json( { error: "Failed to update record" }, { status: 500 } ); } } /** * DELETE /api/records?collection=...&rkey=... * Deletes a record from the authenticated user's repo. */ export async function DELETE(request: NextRequest) { try { const session = await getSession(); if (!session.did) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const agent = await getAuthenticatedAgent(); if (!agent) { return NextResponse.json( { error: "Failed to authenticate" }, { status: 401 } ); } const { searchParams } = new URL(request.url); const collection = searchParams.get("collection"); const rkey = searchParams.get("rkey"); if (!collection) { return NextResponse.json( { error: "Collection is required" }, { status: 400 } ); } if (!ALLOWED_COLLECTIONS.has(collection)) { return NextResponse.json( { error: "Collection not allowed" }, { status: 403 } ); } if (!rkey) { return NextResponse.json( { error: "Record key is required" }, { status: 400 } ); } await agent.com.atproto.repo.deleteRecord({ repo: session.did, collection, rkey, }); return NextResponse.json({ success: true }); } catch (error) { console.error("Failed to delete record:", error); return NextResponse.json( { error: "Failed to delete record" }, { status: 500 } ); } }