message / apps /web /src /components /features /NativeAuditLog.tsx
dot
Add Supabase administrator sessions and profile client key management
9b403c4
Raw History Blame Contribute Delete
2.11 kB
import { useEffect, useState } from 'react';
import type { NativeApi } from '@api/native';
import { Button } from '@components/ui/button';
import type { NativeAuditEvent } from '@typings/native';
export function NativeAuditLog({ api, onError }: { api: NativeApi; onError: (value: unknown) => void }) {
const [items, setItems] = useState<NativeAuditEvent[]>([]);
const [loading, setLoading] = useState(true);
const [refresh, setRefresh] = useState(0);
useEffect(() => {
const controller = new AbortController(); setLoading(true);
void api.audit(controller.signal).then(result => {
if (!controller.signal.aborted) setItems(result.items);
}).catch(value => { if (!controller.signal.aborted) onError(value); })
.finally(() => { if (!controller.signal.aborted) setLoading(false); });
return () => controller.abort();
}, [api, refresh, onError]);
return <div className="space-y-4"><div className="flex items-center justify-between"><p className="text-sm text-console-text-secondary">认证、API key 与管理操作的服务端审计记录</p><Button size="sm" variant="outline" disabled={loading} onClick={() => setRefresh(value => value + 1)}>刷新审计</Button></div>
{loading ? <p role="status">正在读取审计记录…</p> : items.length === 0 ? <p className="text-sm text-console-text-secondary">暂无审计记录</p> : <ol className="space-y-3">{items.map(item => <li key={item.id} className="rounded-md border border-console-border p-4"><div className="flex flex-wrap justify-between gap-2"><strong className="break-all text-sm">{item.event}</strong><time className="text-xs text-console-text-secondary">{new Date(item.created_at).toLocaleString('zh-CN')}</time></div><p className="mt-2 break-all text-xs text-console-text-secondary">操作者:{item.actor || '匿名 / 系统'}{item.profile_id ? ` · Profile:${item.profile_id}` : ''}</p>{item.detail != null && <pre className="mt-2 overflow-auto whitespace-pre-wrap break-all text-xs">{typeof item.detail === 'string' ? item.detail : JSON.stringify(item.detail, null, 2)}</pre>}</li>)}</ol>}
</div>;
}