"use client"; import { useState } from "react"; import { RefreshCw } from "lucide-react"; import { api, type AuditRow } from "@/lib/api"; import { useAsync, useDebounced } from "@/lib/hooks"; import { useAuth } from "@/lib/auth"; import { fmtInt, fmtTs } from "@/lib/format"; import { Banner, Card, ErrorState, Field, PageHeader, StatusBadge } from "@/components/ui"; import { DataTable } from "@/components/DataTable"; import { JsonView } from "@/components/JsonView"; const ACTIONS = ["", "auth.", "auth.login", "auth.mfa", "admin.", "admin.enqueue", "admin.key_create", "admin.key_revoke", "export.", "query.sql"]; const HIDDEN = new Set(["id", "at", "action", "actor", "principal", "target", "outcome", "ip", "request_id", "detail", "user_agent"]); export default function AuditPage() { const { isOwner } = useAuth(); const [action, setAction] = useState(""); const [limit, setLimit] = useState(200); const da = useDebounced(action.trim(), 300); const { data, error, loading, reload } = useAsync((s) => api.get<{ data: AuditRow[] }>("/admin/audit", { action: da || undefined, limit }, s), [da, limit], { enabled: isOwner }); if (!isOwner) return The audit log is restricted to the owner.; return ( <> setAction(e.target.value)} placeholder="admin." /> {ACTIONS.filter(Boolean).map((a) => ( } /> {error && } String(r.id ?? `${r.at}-${i}`)} mobile="cards" maxHeight="75dvh" columns={[ { key: "at", header: "At (UTC)", render: (r) => {fmtTs(r.at)} }, { key: "action", header: "Action", mono: true }, { key: "actor", header: "Actor", render: (r) => {String(r.actor ?? r.principal ?? "—")} }, { key: "target", header: "Target", render: (r) => {r.target ?? "—"} }, { key: "outcome", header: "Outcome", render: (r) => (r.outcome ? : —) }, { key: "ip", header: "IP", hideSm: true, mono: true }, { key: "detail", header: "Detail", render: (r) => { const d = r.detail ?? Object.fromEntries(Object.entries(r).filter(([k]) => !HIDDEN.has(k))); return Object.keys(d).length ? : —; }, }, { key: "request_id", header: "Request", hideSm: true, render: (r) => {r.request_id ?? "—"} }, ]} empty="No audit entries match." footer={{fmtInt(data?.data.length ?? 0)} entries} /> ); }