diff options
| author | yyamashita <yyamashita@mosquit.one> | 2026-05-09 11:21:28 +0900 |
|---|---|---|
| committer | yyamashita <yyamashita@mosquit.one> | 2026-05-09 11:21:28 +0900 |
| commit | cd8787b77dadf752826a967d404b718b3ec92601 (patch) | |
| tree | a1e5471ba59404caf5c3c7684a4cfc08027a5a4b /app/routes/api-artists.tsx | |
| parent | 08c410c28eeb3d7a4c41014d8926b765441546c4 (diff) | |
Add JSON API endpoints and CLI script for band/artist management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'app/routes/api-artists.tsx')
| -rw-r--r-- | app/routes/api-artists.tsx | 47 |
1 files changed, 47 insertions, 0 deletions
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<string, unknown>; + 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; +} |
