import { NextRequest, NextResponse } from "next/server"; import { listOwnedOrders } from "@/lib/orders-store"; import { requireOrderOwner } from "@/src/auth/require-order-owner"; import { isSiaxError, siaxErrorResponseForLegacy } from "../../siax-error-mapping"; /** * GET /api/orders/export (våg 3.4, DoD §56) — CSV-export of the authenticated * owner's orders. Same fail-closed auth as GET/POST /api/orders (this path is * outside middleware.ts's /api/0pt matcher, so the Zitadel verifier runs here). * * RFC 4180: CRLF line endings, fields containing comma, quote or newline are * quoted with doubled quotes, header row always present. * * Locale note: deposit_ore / total_ore are exported as RAW ÖRE — öre is the * canonical unit in the order contract; formatting (SEK, thousand separators) * happens in the UI layer, never in the export. * * Cap note (DoD §56): NO row truncation/limiting — an export must be complete. */ function csvEscape(value: string): string { if (/[",\r\n]/.test(value)) return `"${value.replace(/"/g, '""')}"`; return value; } export async function GET(req: NextRequest) { const auth = await requireOrderOwner(req); if (auth.response) return auth.response; try { const orders = await listOwnedOrders(auth.owner.tenantId); const header = ["order_id", "flow", "status", "deposit_ore", "total_ore"]; const rows = orders.map((o) => [o.order_id, o.flow, o.status, String(o.deposit_ore), String(o.total_ore)].map(csvEscape).join(","), ); const csv = [header.join(","), ...rows].join("\r\n") + "\r\n"; const date = new Date().toISOString().slice(0, 10); return new NextResponse(csv, { status: 200, headers: { "Content-Type": "text/csv; charset=utf-8", "Content-Disposition": `attachment; filename="orders-${date}.csv"`, }, }); } catch (err: unknown) { if (isSiaxError(err)) return siaxErrorResponseForLegacy(err); throw err; } }