import { withEditNotes } from "@/lib/report/guard"; import { PostsRefused, StaleToken, updatePosts } from "@/lib/report/manifest.mjs"; import { resolveReport } from "@/lib/report/serve.mjs"; export const dynamic = "force-dynamic"; // The two decisions a person makes about a post once it is in the manifest: // which clip it rides on (`attachTo`, null for the automatic one) and whether // it is shown (`hide`). Adding, removing or rewording posts is not here -- // they are written into the manifest by whoever cites them. // // PUT is the Posts table's one save: `{ project, posts: { : { attachTo?, // hide? } }, token }`. One unknown id, one bad value, or a result the build's // validatePosts refuses fails the WHOLE batch and nothing is written. The // client sends a project id, never a path. Read through GET // /api/report/onscreen, which carries the rows and the token. const noStore = { "cache-control": "no-store" }; export async function PUT(request: Request) { let body: Record; try { body = await request.json(); } catch { return Response.json({ error: "expected JSON" }, { status: 400 }); } const r = await resolveReport(String(body.project ?? "")); if ("error" in r) return Response.json({ error: r.error }, { status: r.status }); try { const { result: res, editNotes } = await withEditNotes(r.project, () => updatePosts( r.project.dir, body.posts as Record, { token: body.token === undefined ? null : String(body.token) }, ), ); return Response.json({ ok: true, posts: res.posts, token: res.token, editNotes }, { headers: noStore }); } catch (e) { if (e instanceof StaleToken) { return Response.json( { error: e.message, expected: e.expected, got: e.got, stale: true }, { status: 409 }, ); } if (e instanceof PostsRefused) { return Response.json({ error: e.message, errors: e.errors }, { status: 400 }); } return Response.json({ error: e instanceof Error ? e.message : String(e) }, { status: 400 }); } }