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