import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { getTempoMapsForSong, insertTempoMap, query } from "@/lib/db/client"; import { validateCTP } from "@/lib/ctp/validate"; // ─── GET /api/tracks?mbid= ───────────────────────────────────────────── const GetSchema = z.object({ mbid: z.string().uuid(), }); export async function GET(req: NextRequest) { const params = Object.fromEntries(req.nextUrl.searchParams); const parsed = GetSchema.safeParse(params); if (!parsed.success) { return NextResponse.json( { error: "mbid (UUID) is required" }, { status: 400 } ); } const maps = await getTempoMapsForSong(parsed.data.mbid); return NextResponse.json({ maps }); } // ─── POST /api/tracks ───────────────────────────────────────────────────────── // Body: a raw CTP document JSON export async function POST(req: NextRequest) { let body: unknown; try { body = await req.json(); } catch { return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); } const validation = validateCTP(body); if (!validation.success) { return NextResponse.json( { error: "CTP document validation failed", details: validation.errors.flatten(), }, { status: 422 } ); } const doc = validation.data; if (!doc.metadata.mbid) { return NextResponse.json( { error: "CTP document must include a metadata.mbid to be stored" }, { status: 422 } ); } // Ensure the song exists const { rowCount } = await query("SELECT 1 FROM songs WHERE mbid = $1", [ doc.metadata.mbid, ]); if (!rowCount || rowCount === 0) { return NextResponse.json( { error: "Song not found. Search for the song first to register it.", mbid: doc.metadata.mbid, }, { status: 404 } ); } const map = await insertTempoMap({ song_mbid: doc.metadata.mbid, ctp_data: body as Record, contributed_by: doc.metadata.contributed_by, }); return NextResponse.json({ map }, { status: 201 }); }