Categorize audit log by threat level with filtering UI

This commit is contained in:
Gan, Jimmy
2026-02-22 12:38:59 +08:00
parent a32588a7cc
commit 4a666c2914
2 changed files with 67 additions and 10 deletions
+31 -7
View File
@@ -114,14 +114,20 @@
saving = false;
}
let auditLines = $state([]);
let auditEntries = $state([]);
let auditLoading = $state(false);
let auditFilter = $state("all");
const levelColors = { high: "text-rose-400", medium: "text-amber-400", low: "text-emerald-400", info: "text-surface-400" };
const levelLabels = { high: "HIGH", medium: "MED", low: "LOW", info: "INFO" };
let filteredEntries = $derived(auditFilter === "all" ? auditEntries : auditEntries.filter(e => e.level === auditFilter));
async function loadAuditLog() {
auditLoading = true;
try {
const res = await get("/system/audit-log?lines=100");
auditLines = res.lines.reverse();
const res = await get("/system/audit-log?lines=200");
auditEntries = res.entries.reverse();
} catch {}
auditLoading = false;
}
@@ -206,12 +212,30 @@
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-semibold text-surface-700">Audit Log</h3>
<button onclick={loadAuditLog} disabled={auditLoading} class="px-3 py-1.5 text-xs font-medium text-primary-600 bg-primary-50 hover:bg-primary-100 rounded-lg transition-colors disabled:opacity-50">
{auditLoading ? "Loading..." : auditLines.length ? "Refresh" : "Load"}
{auditLoading ? "Loading..." : auditEntries.length ? "Refresh" : "Load"}
</button>
</div>
{#if auditLines.length > 0}
<div class="max-h-80 overflow-auto rounded-lg bg-surface-950 p-3">
<pre class="text-xs text-surface-300 font-mono whitespace-pre leading-5">{auditLines.join('\n')}</pre>
{#if auditEntries.length > 0}
<div class="flex gap-1.5 mb-3">
{#each ["all", "high", "medium", "low", "info"] as lvl}
<button onclick={() => auditFilter = lvl} class="px-2.5 py-1 text-xs rounded-md transition-colors {auditFilter === lvl ? 'bg-surface-800 text-white' : 'bg-surface-100 text-surface-500 hover:bg-surface-200'}">
{lvl === "all" ? "All" : levelLabels[lvl] || lvl}
{#if lvl !== "all"}
<span class="ml-1 opacity-60">{auditEntries.filter(e => e.level === lvl).length}</span>
{/if}
</button>
{/each}
</div>
<div class="max-h-80 overflow-auto rounded-lg bg-surface-950 p-3 space-y-0.5">
{#each filteredEntries as e}
<div class="text-xs font-mono flex gap-2 leading-5">
<span class="{levelColors[e.level]} w-8 shrink-0">{levelLabels[e.level]}</span>
<span class="text-surface-500 shrink-0">{e.ts.replace('T', ' ')}</span>
<span class="text-surface-400 shrink-0 w-10">{e.status}</span>
<span class="text-surface-300 shrink-0">{e.user}</span>
<span class="text-surface-500">{e.method} {e.path}</span>
</div>
{/each}
</div>
{:else if !auditLoading}
<p class="text-sm text-surface-400">Click Load to view recent audit log entries</p>