feat: add safe video key trace fields

This commit is contained in:
xuejianwu
2026-07-30 09:14:48 +08:00
parent 1db9cc1f90
commit 225e3a9663
2 changed files with 27 additions and 2 deletions

View File

@@ -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
## 1020 人试用的部署边界

View File

@@ -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;
}