From 225e3a9663272d990e429ab00b926aa22110f275 Mon Sep 17 00:00:00 2001 From: xuejianwu <374601383@qq.com> Date: Thu, 30 Jul 2026 09:14:48 +0800 Subject: [PATCH] feat: add safe video key trace fields --- README.md | 2 +- server.mjs | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a13bf4..6b50c33 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ - 默认目录:`./logs/audit-YYYY-MM-DD-PID.jsonl` - 生产环境:用 `AUDIT_LOG_DIR` 指向持久化磁盘,并由技术配置日志保留/清理周期 - 脱敏:不记录 API Key、Authorization、Cookie 和签名查询参数;提示词仅记录长度和哈希;URL 仅记录域名、路径与查询字段名 -- 关联排查:日志保留 `requestId`、浏览器 `clientId/sessionId`、任务 ID、服务商 `trackId`、状态码和耗时 +- 关联排查:日志保留 `requestId`、浏览器 `clientId/sessionId`、客户视频 Key 的不可逆指纹与末四位、任务 ID、服务商 `trackId`、状态码和耗时;不保存完整 Key ## 10–20 人试用的部署边界 diff --git a/server.mjs b/server.mjs index 2ebbbcf..2e6ba74 100644 --- a/server.mjs +++ b/server.mjs @@ -50,6 +50,8 @@ const server = createServer((request, response) => { status: response.statusCode, durationMs: Date.now() - startedAt, ipHash: hashAuditValue(clientIp(request)), + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", userAgent: String(request.headers["user-agent"] || "").slice(0, 300), referer: sanitizeUrlForLog(request.headers.referer || ""), }); @@ -321,7 +323,15 @@ async function streamVideoTask(request, response, taskId) { const videoToken = requireVideoToken(request, taskId); const startedAt = Date.now(); const context = requestContext.getStore() || {}; - writeAuditLog({ level: "info", event: "video.proxy.start", requestId: context.requestId, sessionId: context.sessionId, taskId }); + writeAuditLog({ + level: "info", + event: "video.proxy.start", + requestId: context.requestId, + sessionId: context.sessionId, + taskId, + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", + }); const task = await callJson(`https://ai-api.kkidc.com/sd/api/v3/contents/generations/tasks/${encodeURIComponent(taskId)}`, { headers: bearerHeaders(videoToken, false), @@ -353,6 +363,8 @@ async function streamVideoTask(request, response, taskId) { requestId: context.requestId, sessionId: context.sessionId, taskId, + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", durationMs: Date.now() - startedAt, contentType, contentLength: Number(upstream.headers.get("content-length") || 0) || null, @@ -364,6 +376,8 @@ async function streamVideoTask(request, response, taskId) { requestId: context.requestId, sessionId: context.sessionId, taskId, + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", durationMs: Date.now() - startedAt, error: sanitizeLogData({ name: error.name, message: error.message }), }); @@ -390,6 +404,8 @@ async function callJson(url, options = {}) { sessionId: context.sessionId, method, target, + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", request: sanitizeLogData(requestBody), }); const controller = new AbortController(); @@ -418,6 +434,8 @@ async function callJson(url, options = {}) { status: response.status, durationMs: Date.now() - startedAt, trackId: result.trackId, + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", response: sanitizeLogData(body), }); return result; @@ -430,6 +448,8 @@ async function callJson(url, options = {}) { method, target, durationMs: Date.now() - startedAt, + videoKeyId: context.videoKeyId || "", + videoKeyHint: context.videoKeyHint || "", error: sanitizeLogData({ name: error.name, message: error.message }), }); throw error; @@ -511,6 +531,11 @@ function requireVideoToken(request, taskId = "") { const token = videoTokenFromRequest(request, taskId); if (!token) throw new ClientError("请先输入生视频 Key", 401); if (!/^sk-[A-Za-z0-9_-]{8,}$/.test(token)) throw new ClientError("生视频 Key 格式不正确", 401); + const context = requestContext.getStore(); + if (context) { + context.videoKeyId = hashAuditValue(token).slice(0, 12); + context.videoKeyHint = token.slice(-4); + } return token; }