2025-11-15 14:36:16 +08:00

78 lines
2.5 KiB
TypeScript

import { NextRequest } from 'next/server'
import { z } from 'zod'
import { prisma } from '@/src/lib/prisma'
const ExportSchema = z.object({
ids: z.array(z.string()).min(1),
format: z.enum(['csv', 'json']),
})
export async function POST(req: NextRequest) {
try {
const body = await req.json()
const parsed = ExportSchema.safeParse(body)
if (!parsed.success) return Response.json({ error: 'invalid body' }, { status: 400 })
const { ids, format } = parsed.data
const rows = await prisma.recording.findMany({
where: { id: { in: ids } },
orderBy: { timestamp: 'asc' },
// Cast select to any to accommodate freshly added fields before prisma generate
select: {
id: true,
timestamp: true,
code_data: true,
fit_a: true,
fit_b: true,
sample_count: true,
duration: true,
rec_start_ms: true,
rec_end_ms: true,
} ,
})
if (format === 'json') {
const payload = rows.map((r) => ({
id: r.id,
timestamp: new Date(r.timestamp).toISOString(),
code: r.code_data,
fit: r.fit_a != null ? { a: r.fit_a, b: r.fit_b } : undefined,
sampleCount: r.sample_count,
duration: r.duration,
}))
return new Response(JSON.stringify(payload), {
headers: {
'content-type': 'application/json; charset=utf-8',
'content-disposition': `attachment; filename="export-${Date.now()}.json"`,
},
})
}
// CSV
let csv = 'recordId,timestamp,index,timeSec,code,force\n'
for (const r of rows) {
let step = r.sample_count > 0 ? r.duration / r.sample_count : 0
if (r.rec_start_ms != null && r.rec_end_ms != null && r.sample_count > 0) {
const dtMs = Number((r.rec_end_ms as bigint) - (r.rec_start_ms as bigint))
if (Number.isFinite(dtMs) && dtMs > 0) step = dtMs / 1000 / r.sample_count
}
const hasFit = r.fit_a != null
for (let i = 0; i < r.code_data.length; i++) {
const t = (i * step).toFixed(6)
const code = r.code_data[i]
const force = hasFit ? r.fit_a! * code + (r.fit_b ?? 0) : ''
csv += `${r.id},${new Date(r.timestamp).toISOString()},${i},${t},${code},${hasFit ? force : ''}\n`
}
}
return new Response(csv, {
headers: {
'content-type': 'text/csv; charset=utf-8',
'content-disposition': `attachment; filename="export-${Date.now()}.csv"`,
},
})
} catch (err) {
console.error(err)
return Response.json({ error: 'internal error' }, { status: 500 })
}
}