// Duenner Client der AUD-03-Export-API (internal/audit.ExportHandler) — // keine eigene Aggregations-/Filterlogik im Frontend (Ticket-Vorgabe: // "reiner Konsument der Export-API"). Die JSON-Lines-Antwort wird nur // dekodiert, nicht neu berechnet oder gefiltert. export type AuditRecord = { occurred_at: string; tenant_slug: string; actor: string; action: string; target: string; metadata: unknown; }; export type AuditFilter = { tenant: string; actor: string; action: string; from: string; // RFC3339, leer = kein Filter to: string; }; function apiBase(): string { const base = process.env.NEXT_PUBLIC_AUDITLOG_API_URL; if (!base) { throw new Error( "NEXT_PUBLIC_AUDITLOG_API_URL ist nicht gesetzt (Umgebungsvariable erforderlich)" ); } return base; } function buildParams(caller: string, filter: AuditFilter, format: "json" | "csv"): URLSearchParams { const params = new URLSearchParams({ caller, format }); if (filter.tenant) params.set("tenant", filter.tenant); if (filter.actor) params.set("actor", filter.actor); if (filter.action) params.set("action", filter.action); if (filter.from) params.set("from", filter.from); if (filter.to) params.set("to", filter.to); return params; } // exportURL liefert die Adresse desselben Endpunkts, den auch die // Export-Datei-Funktion (Akzeptanzkriterium 2) verwendet — Liste und Export // sind bewusst DERSELBE API-Aufruf mit unterschiedlichem "format". export function exportURL(caller: string, filter: AuditFilter, format: "json" | "csv"): string { return `${apiBase()}/audit/export?${buildParams(caller, filter, format)}`; } // fetchRecords laedt die Liste als JSON-Lines und dekodiert Zeile fuer // Zeile — dieselben Filterparameter wie ein direkter API-Aufruf // (Akzeptanzkriterium 1 / Pruefung 1). export async function fetchRecords(caller: string, filter: AuditFilter): Promise { const res = await fetch(exportURL(caller, filter, "json"), { cache: "no-store" }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(text || `Anfrage fehlgeschlagen (${res.status})`); } const text = await res.text(); return text .split("\n") .filter((line) => line.trim() !== "") .map((line) => JSON.parse(line) as AuditRecord); }