From cd8787b77dadf752826a967d404b718b3ec92601 Mon Sep 17 00:00:00 2001 From: yyamashita Date: Sat, 9 May 2026 11:21:28 +0900 Subject: Add JSON API endpoints and CLI script for band/artist management Co-Authored-By: Claude Sonnet 4.6 --- app/routes/api-artists.tsx | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 app/routes/api-artists.tsx (limited to 'app/routes/api-artists.tsx') diff --git a/app/routes/api-artists.tsx b/app/routes/api-artists.tsx new file mode 100644 index 0000000..ed762bf --- /dev/null +++ b/app/routes/api-artists.tsx @@ -0,0 +1,47 @@ +import type { ActionFunctionArgs } from "react-router"; +import { createArtist, getIpAddress, listArtists, toSlug } from "~/lib/db.server"; + +export function loader() { + return Response.json(listArtists()); +} + +export async function action({ request }: ActionFunctionArgs) { + if (request.method !== "POST") { + return Response.json({ error: "Method not allowed" }, { status: 405 }); + } + + let body: Record; + try { + body = await request.json(); + } catch { + return Response.json({ error: "Invalid JSON body" }, { status: 400 }); + } + + const name = (body.name as string | undefined)?.trim(); + if (!name) return Response.json({ error: "name is required" }, { status: 400 }); + + const slug = (body.slug as string | undefined)?.trim() || toSlug(name); + if (!slug) return Response.json({ error: "could not derive slug from name" }, { status: 400 }); + + const id = crypto.randomUUID(); + try { + const artist = createArtist({ + id, + slug, + name, + links: (body.links as { label: string; url: string }[]) || [], + message: (body.message as string) || "API import", + ip_address: getIpAddress(request), + }); + return Response.json(artist, { status: 201 }); + } catch (e) { + if (e instanceof Error && e.message.includes("UNIQUE constraint failed: artists.slug")) { + return Response.json({ error: "slug already in use" }, { status: 409 }); + } + throw e; + } +} + +export default function () { + return null; +} -- cgit v1.2.3