CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
masterplan-lock / check (push) Successful in 14s
CI (SIAX Cloud) / security (push) Failing after 16s
CI (SIAX Cloud) / quality (push) Successful in 52s
Security Scan (SIAX Cloud) / security (push) Successful in 58s
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 5s
CI (SIAX Cloud) / security (pull_request) Successful in 15s
CI (SIAX Cloud) / quality (pull_request) Successful in 37s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 45s
CI (SIAX Cloud) / integration (pull_request) Successful in 1m35s
CI (SIAX Cloud) / security (push) Trivy vulndb 404 (mirror.gcr.io) - infra-flake; re-run pa identisk sha gron
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
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;
|
|
}
|
|
}
|