init
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
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 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user