import { ShortlistError, addPick, createShortlist, deleteShortlist, exportJson, exportMarkdown, isShortlistBody, listShortlists, noteShortlist, readShortlist, removePick, renameShortlist, reorderShortlist, } from "@/lib/shortlist"; export const dynamic = "force-dynamic"; // The shortlists. // // GET every list, as summaries // GET ?list= one list, in full // GET ?list=&format=md the same list for a person to read // GET ?list=&format=json THE EXPORT -- what a renderer consumes // POST create · add · remove · note · reorder · rename · delete // // The two export formats answer different questions and neither is the other's // pretty-printing: the JSON is an ordered list of absolute episode windows on // the same timeline as wav48/ and media/, and the markdown is what you paste // into a note six weeks later to remember why these forty moments. const noStore = { "cache-control": "no-store" }; export async function GET(request: Request) { const url = new URL(request.url); const slug = url.searchParams.get("list"); if (!slug) { return Response.json({ lists: await listShortlists() }, { headers: noStore }); } const list = await readShortlist(slug); if (!list) return Response.json({ error: `no shortlist called ${slug}` }, { status: 404 }); const format = url.searchParams.get("format"); // An EXPORT is a file, so it is served as one. Handing it back inline would // make the obvious next step "select all and copy", which loses the order to // whatever the clipboard does with a wrapped line. if (format === "md") { return new Response(exportMarkdown(list), { headers: { ...noStore, "content-type": "text/markdown; charset=utf-8", "content-disposition": `attachment; filename="${slug}.md"`, }, }); } if (format === "json") { return new Response(JSON.stringify(exportJson(list), null, 1), { headers: { ...noStore, "content-type": "application/json; charset=utf-8", "content-disposition": `attachment; filename="${slug}.json"`, }, }); } return Response.json({ list }, { headers: noStore }); } export async function POST(request: Request) { let body: unknown; try { body = await request.json(); } catch { return Response.json({ error: "bad json" }, { status: 400 }); } if (!isShortlistBody(body)) { return Response.json({ error: "not a shortlist op" }, { status: 400 }); } try { switch (body.op) { case "create": return Response.json( { ok: true, list: await createShortlist(body.name, body.note) }, { headers: noStore }, ); case "add": return Response.json( { ok: true, list: await addPick(body.list, body.pick) }, { headers: noStore }, ); case "remove": return Response.json( { ok: true, list: await removePick(body.list, body.id) }, { headers: noStore }, ); case "note": return Response.json( { ok: true, list: await noteShortlist(body.list, body.note, body.id) }, { headers: noStore }, ); case "reorder": return Response.json( { ok: true, list: await reorderShortlist(body.list, body.ids) }, { headers: noStore }, ); case "rename": return Response.json( { ok: true, list: await renameShortlist(body.list, body.name) }, { headers: noStore }, ); case "delete": return Response.json( { ok: true, ...(await deleteShortlist(body.list)) }, { headers: noStore }, ); } } catch (e) { if (e instanceof ShortlistError) { return Response.json({ error: e.message }, { status: e.status }); } return Response.json({ error: "could not write the shortlist" }, { status: 500 }); } }