import { NextRequest } from 'next/server' import { z } from 'zod' import { prisma } from '@/src/lib/prisma' import { applyFit, basicStats, genId, getSampleRateHz } from '@/src/lib/utils' const DataSchema = z.object({ code: z.array(z.number().int()).min(1, 'code array empty').max(4096, 'too many points'), fit: z.object({ a: z.number(), b: z.number() }).optional(), recStartMs: z.number().int().nonnegative().optional(), recEndMs: z.number().int().nonnegative().optional(), }) export async function POST(req: NextRequest) { try { const json = await req.json() const parsed = DataSchema.safeParse(json) if (!parsed.success) { const first = parsed.error.errors[0] return Response.json({ error: first?.message ?? 'invalid body' }, { status: 400 }) } const { code, fit, recStartMs, recEndMs } = parsed.data if (code.length > 4096) { return Response.json({ error: 'payload too large' }, { status: 413 }) } const sampleCount = code.length const { min: codeMin, max: codeMax, avg: codeAvg } = basicStats(code) let forceMin: number | null = null let forceMax: number | null = null let forceAvg: number | null = null if (fit) { const forces = applyFit(code, fit.a, fit.b) const s = basicStats(forces) forceMin = s.min forceMax = s.max forceAvg = s.avg } let duration: number if ( typeof recStartMs === 'number' && typeof recEndMs === 'number' && Number.isFinite(recStartMs) && Number.isFinite(recEndMs) && recEndMs > recStartMs ) { duration = (recEndMs - recStartMs) / 1000 } else { const sampleRate = getSampleRateHz() duration = sampleCount / sampleRate } const ts = new Date() const id = genId() await prisma.recording.create({ data: { id, timestamp: ts, sample_count: sampleCount, duration, code_data: code.map((n) => Math.trunc(n)), rec_start_ms: typeof recStartMs === 'number' ? BigInt(Math.trunc(recStartMs)) : null, rec_end_ms: typeof recEndMs === 'number' ? BigInt(Math.trunc(recEndMs)) : null, fit_a: fit?.a ?? null, fit_b: fit?.b ?? null, code_min: codeMin, code_max: codeMax, code_avg: codeAvg, force_min: forceMin, force_max: forceMax, force_avg: forceAvg, } , }) return Response.json({ success: true, id, timestamp: ts.toISOString() }) } catch (err) { console.error(err) return Response.json({ error: 'internal error' }, { status: 500 }) } }