From 5ae33502f93a32a13b19ec29a3419a2dfaf34259 Mon Sep 17 00:00:00 2001 From: freedakgmail Date: Sun, 14 Jun 2026 11:08:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(logs):=20=E5=AE=A1=E8=AE=A1=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E5=88=B0=E5=85=B3=E9=94=AE=E5=8F=AA=E8=AF=BB=E6=93=8D?= =?UTF-8?q?=E4=BD=9C(=E5=AF=BC=E5=87=BA/=E6=9F=A5=E7=9C=8B=E6=8A=A5?= =?UTF-8?q?=E5=91=8A/=E6=9F=A5=E7=9C=8B=E8=AF=84=E4=BC=B0=E8=AF=A6?= =?UTF-8?q?=E6=83=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 写操作(POST/PUT/DELETE)+登录原已全量记录;校准(应用预测校准)亦在内 - 新增 deriveReadActionLabel:对有审计价值的 GET 记录(导出报告/查看报告/查看评估详情), 跳过看板轮询/列表/聚合等高频只读端点,避免日志噪音 - 修正审计中间件:GET 按只读白名单记录,非关键 GET 不记 --- src/server/index.ts | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/server/index.ts b/src/server/index.ts index 4105cca..ef4cf4d 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -192,15 +192,40 @@ function deriveTargetId(path: string): string | null { return m && m[1] !== undefined ? decodeURIComponent(m[1]) : null; } -// 系统操作审计:记录全部写操作(POST/PUT/DELETE)+ 登录,供系统管理员审计。 +/** + * 关键只读操作的审计标签(GET)。仅记录有审计价值的读操作(导出/查看报告/查看评估详情), + * 跳过看板轮询、列表与聚合等高频只读端点(summary/expiring/overdue/accuracy/calibration 等), + * 避免审计日志被噪音淹没。返回 null 表示该 GET 不记录。 + */ +function deriveReadActionLabel(path: string): string | null { + if (/^\/api\/assessments\/[^/]+\/report\/export$/.test(path)) return '导出报告'; + if (/^\/api\/assessments\/[^/]+\/report$/.test(path)) return '查看报告'; + // 单条评估详情查看(排除聚合/列表类只读端点)。 + if (/^\/api\/assessments\/[^/]+$/.test(path)) { + const id = path.split('/')[3] ?? ''; + const skip = ['summary', 'expiring', 'overdue', 'run', 'profitability']; + if (id !== '' && !skip.includes(id)) return '查看评估详情'; + } + return null; +} + +// 系统操作审计:记录全部写操作(POST/PUT/DELETE)+ 登录 + 关键只读操作(导出/查看报告/查看详情),供系统管理员审计。 app.use('/api/*', async (c, next) => { const method = c.req.method; const start = Date.now(); await next(); - if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') return; + if (method === 'HEAD' || method === 'OPTIONS') return; if (pool === null) return; const path = c.req.path; if (path === '/api/system-logs') return; // 不记录查询日志自身 + + // GET:仅记录有审计价值的关键只读操作,其余(看板轮询/列表/聚合)跳过。 + let readLabel: string | null = null; + if (method === 'GET') { + readLabel = deriveReadActionLabel(path); + if (readLabel === null) return; + } + const payload = (c as import('hono').Context).get('user') as AuthPayload | undefined; let actorId = payload?.uid ?? null; let actorName = payload?.username ?? null; @@ -218,7 +243,7 @@ app.use('/api/*', async (c, next) => { } // 业务动作增强:解析目标项目名 + 决策(通过/驳回等)。 - let action = deriveActionLabel(method, path); + let action = method === 'GET' ? readLabel as string : deriveActionLabel(method, path); let targetName: string | null = null; const targetId = deriveTargetId(path); const isAssessmentPath = /^\/api\/assessments\/[^/]+/.test(path) && targetId !== null && targetId !== 'run' && targetId !== 'profitability' && targetId !== 'summary' && targetId !== 'expiring' && targetId !== 'overdue';