import { ChevronRightIcon, Loader2Icon, ReceiptTextIcon, Trash2Icon, } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { apiFetch } from "../hooks/use-api"; import { Button } from "./ui/button"; interface AuditReceipt { action: string; auditId: string; byteSize: number; contentClass: string; createdAtMs: number; decision: "allow" | "deny"; destinationZone: string; effectivePolicy: string; reasonCode: string; } interface AuditPage { nextCursor: string | null; receipts: AuditReceipt[]; } interface AuditStatus { bytes: number; receipts: number; retention: { maxAgeDays: number; maxBytes: number; maxReceipts: number; }; } interface CleanupResult { auditId?: string; checkpointedFrames: number; deleted: number; physicalCleanup: string; remainingWalFrames: number; } export function EgressAuditPanel() { const [receipts, setReceipts] = useState([]); const [nextCursor, setNextCursor] = useState(null); const [status, setStatus] = useState(null); const [selected, setSelected] = useState(null); const [pendingDelete, setPendingDelete] = useState(null); const [purgeConfirmed, setPurgeConfirmed] = useState(false); const [cleanup, setCleanup] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const loadStatus = useCallback(async () => { const response = await apiFetch("/api/egress/audits/status"); if (response.data) setStatus(response.data); else if (response.error) setError(response.error); }, []); const loadPage = useCallback(async (cursor?: string) => { setLoading(true); setError(null); const suffix = cursor ? `?limit=10&cursor=${encodeURIComponent(cursor)}` : "?limit=10"; const response = await apiFetch(`/api/egress/audits${suffix}`); setLoading(false); if (response.error || !response.data) { setError(response.error ?? "Audit list failed"); return; } const page = response.data; setReceipts((current) => cursor ? [...current, ...page.receipts] : page.receipts ); setNextCursor(page.nextCursor); }, []); useEffect(() => { void Promise.all([loadPage(), loadStatus()]); }, [loadPage, loadStatus]); const show = async (auditId: string) => { setError(null); const response = await apiFetch<{ receipt: AuditReceipt }>( `/api/egress/audits/${encodeURIComponent(auditId)}` ); if (response.data) setSelected(response.data.receipt); else setError(response.error ?? "Audit receipt unavailable"); }; const remove = async (auditId: string) => { setError(null); const response = await apiFetch( `/api/egress/audits/${encodeURIComponent(auditId)}`, { method: "DELETE" } ); setPendingDelete(null); if (response.error || !response.data) { setError(response.error ?? "Audit deletion failed"); return; } setCleanup(response.data); setSelected((current) => (current?.auditId === auditId ? null : current)); setReceipts((current) => current.filter((receipt) => receipt.auditId !== auditId) ); await loadStatus(); }; const purge = async () => { setError(null); const response = await apiFetch("/api/egress/audits", { method: "DELETE", }); setPurgeConfirmed(false); if (response.error || !response.data) { setError(response.error ?? "Audit purge failed"); return; } setCleanup(response.data); setReceipts([]); setNextCursor(null); setSelected(null); await loadStatus(); }; return (

Local audit receipts

Content-free decisions; newest first; retained locally.

{status ? `${status.receipts} receipts · ${status.bytes} bytes` : "loading status"}

{error ? (

{error}

) : null}
{receipts.length === 0 && !loading ? (

No audit receipts yet.

) : null} {receipts.map((receipt) => (
{pendingDelete === receipt.auditId ? (
) : ( )}
))}
{nextCursor ? ( ) : null} {selected ? ( {selected.auditId} {selected.effectivePolicy} · {selected.reasonCode} ·{" "} {selected.byteSize} bytes ) : null} {cleanup ? ( Deleted {cleanup.deleted}; cleanup {cleanup.physicalCleanup}; checkpointed {cleanup.checkpointedFrames}; WAL remaining{" "} {cleanup.remainingWalFrames}. ) : null}
); }