日志审计
+记录平台管理类操作(登录、配置、删除等),不记录对话正文、终端/WebShell 每次命令与工具调用明细。
+ +From a69bc93fa1dc983dfcdc136b5607e45c16474e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Wed, 20 May 2026 16:05:40 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 198 ++++++++++++++ web/static/i18n/en-US.json | 98 +++++++ web/static/i18n/zh-CN.json | 98 +++++++ web/static/js/audit.js | 523 +++++++++++++++++++++++++++++++++++++ web/static/js/settings.js | 3 + web/templates/index.html | 86 ++++++ 6 files changed, 1006 insertions(+) create mode 100644 web/static/js/audit.js diff --git a/web/static/css/style.css b/web/static/css/style.css index 89c04f27..0b76f1df 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -4268,6 +4268,204 @@ header { line-height: 1.6; } +/* 系统设置 - 日志审计 */ +.audit-logs-toolbar { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + justify-content: space-between; + gap: 16px; + margin-bottom: 20px; +} + +.audit-logs-filters { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + gap: 12px; +} + +.audit-logs-filters > .btn-secondary { + align-self: flex-end; + margin-bottom: 0; +} + +.audit-logs-filters label { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 0.8125rem; + color: var(--text-secondary); +} + +/* 事件类型:两个下拉与「结果」等控件同款边框,无外层套框 */ +.audit-filter-cascade { + display: flex; + align-items: center; + gap: 8px; +} + +.audit-filter-cascade select { + flex: 0 1 auto; + min-width: 120px; + max-width: 148px; +} + +.audit-filter-cascade select:disabled { + opacity: 0.55; + cursor: not-allowed; + background: var(--bg-secondary, #f5f6f8); +} + +.audit-filter-cascade-arrow { + flex-shrink: 0; + font-size: 0.8125rem; + color: var(--text-secondary); + line-height: 1; + user-select: none; + pointer-events: none; +} + +.audit-logs-filters select, +.audit-logs-filters input[type="text"], +.audit-logs-filters input[type="datetime-local"] { + min-width: 140px; + padding: 0.35rem 0.5rem; + border: 1px solid var(--border-color); + border-radius: 6px; + background: var(--bg-primary); + color: var(--text-primary); +} + +.audit-logs-actions { + display: flex; + gap: 8px; +} + +/* 列表 + 底部分页合并为一张卡片,避免双边框/底部分隔线 */ +#settings-section-audit .audit-log-list.c2-event-list { + margin-bottom: 0; + border-bottom: none; + border-radius: 8px 8px 0 0; +} + +#settings-section-audit .audit-logs-pagination { + margin-top: 0; + padding: 0; + border: none; + box-shadow: none; + background: transparent; +} + +#settings-section-audit .audit-logs-pagination .monitor-pagination { + margin-top: 0; + border: 1px solid var(--border-color); + border-radius: 0 0 8px 8px; +} + +.audit-log-item { + cursor: pointer; +} + +.audit-detail-pre { + max-height: 320px; + overflow: auto; + font-size: 0.75rem; + background: var(--bg-secondary); + padding: 12px; + border-radius: 6px; + margin-top: 12px; +} + +.audit-summary-stats { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin: 12px 0 16px; +} + +.audit-stat-card { + flex: 1; + min-width: 120px; + padding: 12px 16px; + border-radius: 8px; + background: var(--bg-secondary, rgba(255, 255, 255, 0.04)); + border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08)); +} + +.audit-stat-card strong { + display: block; + font-size: 1.35rem; + margin-top: 4px; +} + +.audit-stat-label { + font-size: 0.85rem; + opacity: 0.75; +} + +.audit-retention-hint { + margin-top: 4px; + opacity: 0.85; +} + +.audit-export-dropdown { + position: relative; + display: inline-block; +} + +.audit-export-trigger { + display: inline-flex; + align-items: center; + gap: 4px; +} + +.audit-export-caret { + font-size: 0.7rem; + opacity: 0.75; +} + +.audit-export-menu { + position: absolute; + top: calc(100% + 4px); + right: 0; + min-width: 140px; + padding: 4px 0; + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); + z-index: 50; +} + +.audit-export-menu-item { + display: block; + width: 100%; + padding: 8px 14px; + border: none; + background: transparent; + color: var(--text-primary); + font-size: 0.875rem; + text-align: left; + cursor: pointer; +} + +.audit-export-menu-item:hover { + background: var(--bg-secondary); +} + +#settings-section-audit .audit-logs-pagination .pagination-info { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; +} + +.audit-detail-body p { + margin: 0 0 8px; + font-size: 0.875rem; +} + /* 系统设置 - 终端 */ .terminal-wrapper { border: 1px solid var(--border-color); diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index e5efed04..5beff619 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -820,6 +820,7 @@ "robots": "Bots", "terminal": "Terminal", "security": "Security", + "audit": "Audit logs", "infocollect": "Recon" }, "infocollect": { @@ -1782,6 +1783,103 @@ "close": "×", "newTerminal": "+" }, + "settingsAudit": { + "title": "Audit logs", + "description": "Platform admin actions (login, config, deletes). Does not log chat content, per-command terminal/WebShell runs, or per-tool invocations.", + "filterCategory": "Category", + "filterAction": "Action", + "filterEvent": "Event type", + "filterAllCategories": "All categories", + "filterAllActions": "All actions", + "filterCascadeHint": "Select a category to filter by action", + "filterResult": "Result", + "pageSize": "Per page", + "statTotal": "Filtered total", + "statFailures": "Failures", + "statRecent7d": "Last 7 days", + "retentionHint": "Audit records are kept for {{days}} days, then purged automatically.", + "disabledHint": "Audit logging is disabled; new actions are not written.", + "filterSince": "From", + "filterUntil": "Until", + "filterQuery": "Keyword", + "filterQueryPlaceholder": "Message / resource ID / action", + "cat": { + "auth": "Auth", + "config": "Config", + "terminal": "Terminal", + "c2": "C2", + "webshell": "WebShell", + "knowledge": "Knowledge", + "conversation": "Conversation", + "vulnerability": "Vulnerability", + "externalMcp": "External MCP", + "task": "Tasks", + "tool": "Tools", + "file": "Files", + "hitl": "HITL", + "role": "Roles", + "skill": "Skills", + "agent": "Sub-agents" + }, + "act": { + "login": "Login", + "logout": "Logout", + "login_failed": "Login failed", + "password_change": "Password change", + "change_password": "Change password", + "apply": "Apply config", + "update": "Update", + "exec": "Terminal exec", + "exec_stream": "Terminal stream", + "listener_create": "Create listener", + "listener_delete": "Delete listener", + "listener_start": "Start listener", + "listener_stop": "Stop listener", + "session_delete": "Delete session", + "task_create": "Create task", + "task_cancel": "Cancel task", + "task_delete": "Delete task", + "connection_create": "Create connection", + "connection_delete": "Delete connection", + "item_delete": "Delete knowledge item", + "index_rebuild": "Rebuild index", + "delete": "Delete", + "delete_turn": "Delete turn", + "create": "Create", + "upsert": "Upsert external MCP", + "create_queue": "Create batch queue", + "start_queue": "Start batch queue", + "delete_queue": "Delete batch queue", + "pause_queue": "Pause batch queue", + "rerun_queue": "Rerun batch queue", + "delete_batch_task": "Delete batch subtask", + "execution_delete": "Delete execution", + "execution_delete_batch": "Batch delete executions", + "upload": "Upload", + "decision": "HITL decision", + "markdown_create": "Create sub-agent", + "markdown_update": "Update sub-agent", + "markdown_delete": "Delete sub-agent" + }, + "openResource": "Open linked resource", + "filterAll": "All", + "filterBtn": "Filter", + "resetBtn": "Reset", + "exportBtn": "Export", + "exportJson": "Export JSON", + "export": "Export JSON", + "exportCsv": "Export CSV", + "exportDone": "Export complete", + "loading": "Loading...", + "empty": "No audit records", + "paginationShow": "{{start}}-{{end}} of {{total}}", + "detailTitle": "Audit detail", + "detailTime": "Time", + "detailCategory": "Category", + "detailResult": "Result", + "detailMessage": "Message", + "detailSession": "Session" + }, "settingsSecurity": { "changePasswordTitle": "Change password", "changePasswordDesc": "After changing password, sign in again with the new password.", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 7c920f94..0883d42c 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -809,6 +809,7 @@ "robots": "机器人设置", "terminal": "终端", "security": "安全设置", + "audit": "日志审计", "infocollect": "信息收集" }, "infocollect": { @@ -1771,6 +1772,103 @@ "close": "×", "newTerminal": "+" }, + "settingsAudit": { + "title": "日志审计", + "description": "记录平台管理类操作(登录、配置、删除等),不记录对话正文、终端/WebShell 每次命令与工具调用明细。", + "filterCategory": "类别", + "filterAction": "操作", + "filterEvent": "事件类型", + "filterAllCategories": "全部类别", + "filterAllActions": "全部操作", + "filterCascadeHint": "选择类别后可筛选具体操作", + "filterResult": "结果", + "pageSize": "每页", + "statTotal": "当前筛选", + "statFailures": "失败", + "statRecent7d": "近 7 天", + "retentionHint": "审计记录保留 {{days}} 天,超期自动清理。", + "disabledHint": "审计功能已关闭,新操作不会写入审计表。", + "filterSince": "开始时间", + "filterUntil": "结束时间", + "filterQuery": "关键词", + "filterQueryPlaceholder": "消息 / 资源 ID / 操作名", + "cat": { + "auth": "认证", + "config": "配置", + "terminal": "终端", + "c2": "C2", + "webshell": "WebShell", + "knowledge": "知识库", + "conversation": "对话", + "vulnerability": "漏洞", + "externalMcp": "外部 MCP", + "task": "任务", + "tool": "工具", + "file": "文件", + "hitl": "人机协同", + "role": "角色", + "skill": "Skill", + "agent": "子代理" + }, + "act": { + "login": "登录", + "logout": "登出", + "login_failed": "登录失败", + "password_change": "修改密码", + "change_password": "修改密码", + "apply": "应用配置", + "update": "更新", + "exec": "终端执行", + "exec_stream": "终端流式执行", + "listener_create": "创建监听器", + "listener_delete": "删除监听器", + "listener_start": "启动监听器", + "listener_stop": "停止监听器", + "session_delete": "删除会话", + "task_create": "创建任务", + "task_cancel": "取消任务", + "task_delete": "删除任务", + "connection_create": "创建连接", + "connection_delete": "删除连接", + "item_delete": "删除知识项", + "index_rebuild": "重建索引", + "delete": "删除", + "delete_turn": "删除轮次", + "create": "创建", + "upsert": "保存外部 MCP", + "create_queue": "创建批量队列", + "start_queue": "启动批量队列", + "delete_queue": "删除批量队列", + "pause_queue": "暂停批量队列", + "rerun_queue": "重跑批量队列", + "delete_batch_task": "删除批量子任务", + "execution_delete": "删除执行记录", + "execution_delete_batch": "批量删除执行", + "upload": "上传", + "decision": "HITL 决策", + "markdown_create": "创建子代理", + "markdown_update": "更新子代理", + "markdown_delete": "删除子代理" + }, + "openResource": "打开关联资源", + "filterAll": "全部", + "filterBtn": "筛选", + "resetBtn": "重置", + "exportBtn": "导出", + "exportJson": "导出 JSON", + "export": "导出 JSON", + "exportCsv": "导出 CSV", + "exportDone": "导出完成", + "loading": "加载中...", + "empty": "暂无审计记录", + "paginationShow": "显示 {{start}}-{{end}} / 共 {{total}} 条", + "detailTitle": "审计详情", + "detailTime": "时间", + "detailCategory": "类别", + "detailResult": "结果", + "detailMessage": "说明", + "detailSession": "会话" + }, "settingsSecurity": { "changePasswordTitle": "修改密码", "changePasswordDesc": "修改登录密码后,需要使用新密码重新登录。", diff --git a/web/static/js/audit.js b/web/static/js/audit.js new file mode 100644 index 00000000..f3a26f64 --- /dev/null +++ b/web/static/js/audit.js @@ -0,0 +1,523 @@ +/** + * 系统设置 - 平台操作审计日志 + */ +let auditLogsPage = 1; +let auditLogsPageSize = 20; +let auditLogsTotal = 0; + +const AUDIT_PAGE_SIZE_KEY = 'cyberstrike_audit_page_size'; + +/** 按类别列出的操作(用于 datalist 提示,避免超长下拉) */ +const AUDIT_ACTIONS_BY_CATEGORY = { + auth: ['login', 'logout', 'change_password'], + config: ['apply', 'update'], + c2: ['listener_create', 'listener_delete', 'listener_start', 'listener_stop', + 'session_delete', 'task_create', 'task_cancel', 'task_delete'], + webshell: ['connection_create', 'connection_delete'], + knowledge: ['item_delete', 'index_rebuild'], + conversation: ['delete', 'delete_turn'], + vulnerability: ['create', 'update', 'delete'], + external_mcp: ['upsert', 'delete'], + task: ['create_queue', 'start_queue', 'delete_queue', 'pause_queue', 'rerun_queue', 'delete_batch_task'], + tool: ['execution_delete', 'execution_delete_batch'], + file: ['upload', 'delete'], + hitl: ['decision'], + role: ['create', 'update', 'delete'], + skill: ['create', 'update', 'delete'], + agent: ['markdown_create', 'markdown_update', 'markdown_delete'] +}; + +function auditT(key, opts, fallback) { + if (typeof t === 'function') { + const v = t(key, opts); + if (v && v !== key) return v; + } + return fallback != null ? fallback : key; +} + +function auditCategoryI18nKey(category) { + if (!category) return ''; + if (category === 'external_mcp') return 'externalMcp'; + return category; +} + +function auditCategoryLabel(category) { + if (!category) return ''; + const key = 'settingsAudit.cat.' + auditCategoryI18nKey(category); + return auditT(key, null, category); +} + +function auditActionLabel(action) { + if (!action) return ''; + return auditT('settingsAudit.act.' + action, null, action); +} + +function formatAuditTime(iso) { + if (!iso) return ''; + try { + const d = new Date(iso); + if (Number.isNaN(d.getTime())) return iso; + return d.toLocaleString(); + } catch (_) { + return iso; + } +} + +function auditDatetimeLocalToRFC3339(value) { + if (!value || !value.trim()) return ''; + const d = new Date(value); + if (Number.isNaN(d.getTime())) return ''; + return d.toISOString(); +} + +function initAuditPageSizeFromStorage() { + try { + const saved = parseInt(localStorage.getItem(AUDIT_PAGE_SIZE_KEY), 10); + if ([10, 20, 50, 100].indexOf(saved) >= 0) { + auditLogsPageSize = saved; + } + } catch (_) { /* ignore */ } + const sel = document.getElementById('audit-page-size'); + if (sel) sel.value = String(auditLogsPageSize); +} + +function onAuditPageSizeChange() { + const sel = document.getElementById('audit-page-size'); + if (!sel) return; + const n = parseInt(sel.value, 10); + if ([10, 20, 50, 100].indexOf(n) < 0) return; + auditLogsPageSize = n; + try { + localStorage.setItem(AUDIT_PAGE_SIZE_KEY, String(n)); + } catch (_) { /* ignore */ } + auditLogsPage = 1; + loadAuditLogs(1); +} + +function rebuildAuditActionSelect() { + const catEl = document.getElementById('audit-filter-category'); + const actEl = document.getElementById('audit-filter-action'); + if (!actEl) return; + + const category = catEl ? catEl.value : ''; + const prev = actEl.value; + const allLabel = auditT('settingsAudit.filterAllActions', null, '全部操作'); + const hint = auditT('settingsAudit.filterCascadeHint', null, '选择类别后可筛选具体操作'); + actEl.innerHTML = ''; + const allOpt = document.createElement('option'); + allOpt.value = ''; + allOpt.textContent = allLabel; + actEl.appendChild(allOpt); + + if (!category) { + actEl.disabled = true; + actEl.value = ''; + actEl.title = hint; + return; + } + + actEl.disabled = false; + actEl.title = ''; + + const actions = AUDIT_ACTIONS_BY_CATEGORY[category] || []; + actions.forEach(function (action) { + const opt = document.createElement('option'); + opt.value = action; + opt.textContent = auditActionLabel(action); + actEl.appendChild(opt); + }); + if (prev && Array.prototype.some.call(actEl.options, function (o) { return o.value === prev; })) { + actEl.value = prev; + } +} + +function onAuditCategoryFilterChange() { + rebuildAuditActionSelect(); +} + +function buildAuditQueryParams(forExport) { + const params = new URLSearchParams(); + if (!forExport) { + params.set('page', String(auditLogsPage)); + params.set('page_size', String(auditLogsPageSize)); + } + const cat = document.getElementById('audit-filter-category'); + const act = document.getElementById('audit-filter-action'); + const res = document.getElementById('audit-filter-result'); + const q = document.getElementById('audit-filter-q'); + const since = document.getElementById('audit-filter-since'); + const until = document.getElementById('audit-filter-until'); + if (cat && cat.value) params.set('category', cat.value); + if (act && !act.disabled && act.value) params.set('action', act.value); + if (res && res.value) params.set('result', res.value); + if (q && q.value.trim()) params.set('q', q.value.trim()); + const sinceISO = since ? auditDatetimeLocalToRFC3339(since.value) : ''; + const untilISO = until ? auditDatetimeLocalToRFC3339(until.value) : ''; + if (sinceISO) params.set('since', sinceISO); + if (untilISO) params.set('until', untilISO); + return params.toString(); +} + +async function loadAuditMeta() { + if (typeof apiFetch !== 'function') return; + const hint = document.getElementById('audit-retention-hint'); + try { + const r = await apiFetch('/api/audit/meta'); + if (!r.ok) return; + const data = await r.json(); + if (!hint) return; + if (!data.enabled) { + hint.hidden = false; + hint.textContent = auditT('settingsAudit.disabledHint', null, '审计功能已关闭,新操作不会写入审计表。'); + return; + } + const days = data.retention_days; + if (days > 0) { + hint.hidden = false; + hint.textContent = auditT('settingsAudit.retentionHint', { days: days }, + '审计记录保留 ' + days + ' 天,超期自动清理。'); + } else { + hint.hidden = true; + } + } catch (_) { /* ignore */ } +} + +async function loadAuditSummary() { + if (typeof apiFetch !== 'function') return; + const wrap = document.getElementById('audit-summary-stats'); + try { + const r = await apiFetch('/api/audit/summary?' + buildAuditQueryParams(true)); + if (!r.ok) return; + const data = await r.json(); + if (wrap) wrap.hidden = false; + const elTotal = document.getElementById('audit-stat-total'); + const elFail = document.getElementById('audit-stat-failures'); + const elRecent = document.getElementById('audit-stat-recent'); + if (elTotal) elTotal.textContent = String(data.total != null ? data.total : 0); + if (elFail) elFail.textContent = String(data.failures != null ? data.failures : 0); + if (elRecent) elRecent.textContent = String(data.recent_7d != null ? data.recent_7d : 0); + } catch (_) { /* ignore */ } +} + +async function loadAuditLogs(page) { + if (typeof apiFetch !== 'function') return; + auditLogsPage = page != null ? page : auditLogsPage; + const listEl = document.getElementById('audit-log-list'); + if (listEl) { + listEl.innerHTML = '
ID: ' + esc(id) + '
' : ''; +} + +function refreshAuditLogs() { + loadAuditLogs(auditLogsPage); +} + +async function downloadAuditExport(url, filename) { + const r = await apiFetch(url); + if (!r.ok) { + const err = await r.json().catch(function () { return {}; }); + throw new Error(err.error || r.statusText); + } + const blob = await r.blob(); + const objectUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = objectUrl; + a.download = filename; + a.click(); + URL.revokeObjectURL(objectUrl); +} + +function closeAuditExportMenu() { + const menu = document.getElementById('audit-export-menu'); + const trigger = document.getElementById('audit-export-trigger'); + if (menu) menu.hidden = true; + if (trigger) trigger.setAttribute('aria-expanded', 'false'); +} + +function toggleAuditExportMenu(ev) { + if (ev && ev.stopPropagation) ev.stopPropagation(); + const menu = document.getElementById('audit-export-menu'); + const trigger = document.getElementById('audit-export-trigger'); + if (!menu) return; + const willOpen = menu.hidden; + if (willOpen) { + menu.hidden = false; + if (trigger) trigger.setAttribute('aria-expanded', 'true'); + if (!window._auditExportMenuDocBound) { + window._auditExportMenuDocBound = true; + document.addEventListener('click', function () { + closeAuditExportMenu(); + }); + } + } else { + closeAuditExportMenu(); + } +} + +async function runAuditExport(format) { + closeAuditExportMenu(); + if (format === 'csv') { + await exportAuditLogsCsv(); + } else { + await exportAuditLogs(); + } +} + +async function exportAuditLogs() { + if (typeof apiFetch !== 'function') return; + try { + await downloadAuditExport( + '/api/audit/logs/export?' + buildAuditQueryParams(true), + 'audit-logs-' + new Date().toISOString().slice(0, 10) + '.json' + ); + if (typeof showToast === 'function') { + showToast(auditT('settingsAudit.exportDone', null, '导出完成'), 'success'); + } + } catch (e) { + if (typeof showToast === 'function') { + showToast(e.message || String(e), 'error'); + } + } +} + +async function exportAuditLogsCsv() { + if (typeof apiFetch !== 'function') return; + try { + const qs = buildAuditQueryParams(true); + await downloadAuditExport( + '/api/audit/logs/export?' + (qs ? qs + '&' : '') + 'format=csv', + 'audit-logs-' + new Date().toISOString().slice(0, 10) + '.csv' + ); + if (typeof showToast === 'function') { + showToast(auditT('settingsAudit.exportDone', null, '导出完成'), 'success'); + } + } catch (e) { + if (typeof showToast === 'function') { + showToast(e.message || String(e), 'error'); + } + } +} + +function closeAuditDetailModal() { + const el = document.getElementById('audit-detail-modal'); + if (el) el.remove(); +} + +async function showAuditLogDetail(id) { + if (!id || typeof apiFetch !== 'function') return; + const esc = typeof escapeHtml === 'function' ? escapeHtml : function (s) { return String(s || ''); }; + try { + const r = await apiFetch('/api/audit/logs/' + encodeURIComponent(id)); + if (!r.ok) throw new Error('not found'); + const data = await r.json(); + const log = data.log || {}; + const detail = log.detail ? JSON.stringify(log.detail, null, 2) : ''; + closeAuditDetailModal(); + const overlay = document.createElement('div'); + overlay.id = 'audit-detail-modal'; + overlay.className = 'modal'; + overlay.style.display = 'block'; + const catAction = esc(auditCategoryLabel(log.category || '')) + ' / ' + esc(auditActionLabel(log.action || '')); + overlay.innerHTML = + ''; + document.body.appendChild(overlay); + overlay.addEventListener('click', function (ev) { + if (ev.target === overlay) closeAuditDetailModal(); + }); + } catch (e) { + if (typeof showToast === 'function') { + showToast(e.message || String(e), 'error'); + } + } +} + +function initAuditLogsSection() { + if (!document.getElementById('audit-log-list')) return; + initAuditPageSizeFromStorage(); + rebuildAuditActionSelect(); + loadAuditMeta(); + loadAuditLogs(1); +} diff --git a/web/static/js/settings.js b/web/static/js/settings.js index 47c4b766..aea5be88 100644 --- a/web/static/js/settings.js +++ b/web/static/js/settings.js @@ -87,6 +87,9 @@ function switchSettingsSection(section) { if (section === 'terminal' && typeof initTerminal === 'function') { setTimeout(initTerminal, 0); } + if (section === 'audit' && typeof initAuditLogsSection === 'function') { + setTimeout(initAuditLogsSection, 0); + } } // 打开设置 diff --git a/web/templates/index.html b/web/templates/index.html index 52ff8e40..41916a23 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -2041,6 +2041,9 @@记录平台管理类操作(登录、配置、删除等),不记录对话正文、终端/WebShell 每次命令与工具调用明细。
+ +