64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
// 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<AuditRecord[]> {
|
|
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);
|
|
}
|