Download apps/web/src/components/features/NativeAuditLog.tsx from hunian003/message: direct link, hf CLI and curl.
- Browser
- Download file 2.11 kB
-
https://huggingface.co/spaces/hunian003/message/resolve/main/apps/web/src/components/features/NativeAuditLog.tsx
- Command line
-
hf download hf://spaces/hunian003/message/apps/web/src/components/features/NativeAuditLog.tsx
-
curl -L -o NativeAuditLog.tsx https://huggingface.co/spaces/hunian003/message/resolve/main/apps/web/src/components/features/NativeAuditLog.tsx
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>; | |
| } | |