'use client'; import { useEffect, useMemo, useState } from 'react'; import { Clock3, RefreshCw, Search, ShieldCheck } from 'lucide-react'; import { RouteGuard } from '@/components/auth/Guard'; import { type AuditEvent, type AuditQuery, listAuditEvents } from '@/lib/audit-api'; const EMPTY_QUERY: AuditQuery = { take: '50' }; export default function AuditPage() { return ( ); } function AuditPageContent() { const [query, setQuery] = useState(EMPTY_QUERY); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const fetchEvents = async () => { setLoading(true); setError(null); try { setEvents(await listAuditEvents(query)); } catch (e: any) { setError(e?.message ?? '读取审计失败'); } finally { setLoading(false); } }; useEffect(() => { void fetchEvents(); }, []); const entityTypes = useMemo( () => Array.from(new Set(events.map((event) => event.entityType))).sort(), [events], ); return (

审计事件

{events.length}
setQuery((prev) => ({ ...prev, actorId }))} /> setQuery((prev) => ({ ...prev, entityType }))} /> setQuery((prev) => ({ ...prev, entityId }))} /> setQuery((prev) => ({ ...prev, productId }))} /> setQuery((prev) => ({ ...prev, projectId }))} /> setQuery((prev) => ({ ...prev, versionId }))} /> setQuery((prev) => ({ ...prev, take }))} /> {entityTypes.map((type) =>
{error && (
{error}
)}
{events.map((event) => ( ))} {events.length === 0 && ( )}
时间 操作 实体 操作人 作用域 结果
{event.action} {event.entityType}{event.entityId} {event.actorName || event.actorId || '-'} {formatScope(event)}
查看 JSON
{JSON.stringify({ before: event.before, after: event.after, metadata: event.metadata }, null, 2)}
{loading ? '加载中...' : '暂无审计事件'}
); } function FilterInput({ label, value, onChange, list }: { label: string; value?: string; onChange: (value: string) => void; list?: string }) { return ( ); } function TimeLabel({ value }: { value: string }) { return ( {formatDateTime(value)} ); } function formatScope(event: AuditEvent) { return [ event.productId ? `P:${event.productId}` : '', event.projectId ? `J:${event.projectId}` : '', event.versionId ? `V:${event.versionId}` : '', ].filter(Boolean).join(' / ') || '-'; } function formatDateTime(value: string) { const date = new Date(value); if (!Number.isFinite(date.getTime())) return value; return date.toISOString().slice(0, 16).replace('T', ' '); }