84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
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(65535, '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]
|
|
console.log(first, json);
|
|
|
|
return Response.json({ error: first?.message ?? 'invalid body' }, { status: 400 })
|
|
}
|
|
const { code, fit, recStartMs, recEndMs } = parsed.data
|
|
if (code.length > 65535) {
|
|
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 })
|
|
}
|
|
}
|