AUD-04: backend-authorizer + dev-server + next.js audit-log-ansicht
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user