import { useEffect, useState } from "react"; import { listAudit, listGuardrailEvents, type AuditEntry, type GuardrailEventItem } from "../api"; // 操作方法配色。 const ACTION_STYLE: Record = { POST: "bg-emerald-50 text-emerald-600", PUT: "bg-amber-50 text-amber-600", DELETE: "bg-rose-50 text-rose-600", PATCH: "bg-violet-50 text-violet-600", }; const statusTone = (s: number) => (s < 300 ? "text-emerald-600" : s < 500 ? "text-amber-600" : "text-rose-500"); // uid 太长,展示时截断(保留首尾,hover 看全)。 const shortId = (id: string) => (id && id.length > 10 ? `${id.slice(0, 4)}…${id.slice(-4)}` : id || "系统"); const fmt = (at: string) => new Date(at).toLocaleString("zh-CN", { hour12: false }); export function AuditPage() { const [audit, setAudit] = useState([]); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [updatedAt, setUpdatedAt] = useState(null); const [err, setErr] = useState(""); const load = async () => { setRefreshing(true); try { const [a, e] = await Promise.all([listAudit(80), listGuardrailEvents(80)]); setAudit(a); setEvents(e); setUpdatedAt(new Date()); setErr(""); } catch (er) { setErr((er as Error).message); } finally { setLoading(false); setRefreshing(false); } }; useEffect(() => { void load(); const t = setInterval(() => void load(), 30000); return () => clearInterval(t); }, []); if (loading) return
加载审计记录中…
; if (err) return
加载失败:{err}
; const blocked = events.filter((e) => e.kind === "blocked").length; const suspect = events.filter((e) => e.kind === "suspect").length; return (
{/* 顶栏 */}

审计 & 安全事件

敏感操作留痕 + 输入护栏命中 · 只增不改,供运维溯源

0} /> 0} />
{updatedAt && 更新于 {updatedAt.toLocaleTimeString("zh-CN", { hour12: false })}}
{/* 安全事件(护栏命中) */}

安全事件

输入护栏拦截 / 灰区放行({events.length})
{events.length === 0 ? (
暂无护栏命中 🎉
) : (
{events.map((e) => (
{e.kind === "blocked" ? "拦截" : "灰区"} {e.reason || "(无原因)"} {e.method} {e.path} {shortId(e.actor)} {e.ip} {fmt(e.at)}
))}
)}
{/* 操作审计 */}

操作审计

改配置 / 改密钥 / 激活提示词 / 审批 等变更操作({audit.length})
{audit.length === 0 ? (
暂无操作留痕
) : (
{audit.map((a) => (
{a.action} {a.path} {a.status} {shortId(a.actor)} {a.ip} {fmt(a.at)}
))}
)}
); } function Stat({ value, label, bad, warn }: { value: number; label: string; bad?: boolean; warn?: boolean }) { return (
{value}
{label}
); }