| 1234567891011121314151617181920212223242526272829303132333435363738394041 |
- <script setup lang="ts">
- import { onMounted, ref, watch } from 'vue'
- import PageHeader from '../components/PageHeader.vue'
- import { admin, errorMessage, type AuditRow } from '../lib/api'
- import { formatTime, toast } from '../lib/ui'
- const KINDS = { '': '全部', command: '命令执行', file_access_denied: '越权文件访问', network_denied: '网络拦截', approval: '审批', gateway_call: '网关工具', admin: '后台操作' } as const
- const rows = ref<AuditRow[]>([])
- const kind = ref<keyof typeof KINDS>('')
- const expanded = ref<number | null>(null)
- async function load() {
- rows.value = await admin.audit({ kind: kind.value || undefined, limit: 500 }).catch(err => (toast(errorMessage(err), 'error'), []))
- }
- watch(kind, load)
- onMounted(load)
- </script>
- <template>
- <div class="h-full flex flex-col">
- <PageHeader title="审计日志" subtitle="Agent 执行的命令、被拦截的访问、审批记录与后台操作">
- <select v-model="kind" class="field w-40" aria-label="类型">
- <option v-for="(label, k) in KINDS" :key="k" :value="k">{{ label }}</option>
- </select>
- <button type="button" class="btn-secondary" @click="load"><span class="codicon codicon-refresh" /></button>
- </PageHeader>
- <div class="flex-1 overflow-y-auto">
- <div v-for="r in rows" :key="r.id" class="border-b border-b-solid border-border">
- <button type="button" class="w-full flex items-baseline gap-3 px-6 py-1.5 border-none bg-transparent cursor-pointer text-left hover:bg-hover" @click="expanded = expanded === r.id ? null : r.id">
- <span class="text-fg-muted w-36 flex-none text-[12px]">{{ formatTime(r.ts) }}</span>
- <span class="w-24 flex-none text-[12px]" :class="r.kind.endsWith('denied') ? 'text-error' : 'text-fg-muted'">{{ KINDS[r.kind as keyof typeof KINDS] ?? r.kind }}</span>
- <span class="flex-1 min-w-0 truncate font-mono text-[12px]">{{ r.summary }}</span>
- <span class="text-fg-muted text-[11px] flex-none">{{ r.actor }}</span>
- </button>
- <pre v-if="expanded === r.id" class="m-0 px-6 pb-2 text-[11.5px] font-mono whitespace-pre-wrap break-all text-fg-muted">session={{ r.sessionId ?? '—' }}
- {{ JSON.stringify(r.detail, null, 2) }}</pre>
- </div>
- <div v-if="!rows.length" class="p-6 hint">没有记录</div>
- </div>
- </div>
- </template>
|