78 lines
2.5 KiB
TypeScript
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 })
|
|
}
|
|
}
|