From f839429261c450c91d6b9e9a438f62e1561713ef Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 2 Aug 2026 11:43:17 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=BF=AB=E6=89=8B=E5=8F=91=E7=A0=81?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E9=87=87=E9=9B=86=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=96=87=E4=BB=B6=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20integration=20=E6=97=A5=E5=BF=97=E9=98=88=E5=80=BC=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/backend/src/routes/kuaishou-industry.ts | 25 ++++- apps/backend/src/utils/logger.ts | 101 +++++++++++++------ 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/apps/backend/src/routes/kuaishou-industry.ts b/apps/backend/src/routes/kuaishou-industry.ts index d5529da1..358a24e7 100644 --- a/apps/backend/src/routes/kuaishou-industry.ts +++ b/apps/backend/src/routes/kuaishou-industry.ts @@ -8,7 +8,7 @@ import { handleDestroyCode } from '../services/platforms/kuaishou-industry/destr import { handleQueryCode } from '../services/platforms/kuaishou-industry/query-code-service.js' import { handleConsumeCode } from '../services/platforms/kuaishou-industry/consume-code-service.js' import { buildIndustryErrorResponse } from '../services/platforms/kuaishou-industry/response.js' -import { createRequestId, logIntegration } from '../utils/logger.js' +import { createRequestId, logCapture, logIntegration } from '../utils/logger.js' const router = Router() @@ -24,6 +24,7 @@ const industryRateLimit = createRateLimitMiddleware({ router.post('/send-code', industryRateLimit, async (req, res) => { const requestId = createRequestId('ksind') const startedAt = Date.now() + const mergedParams = mergeRequestParams(req) logIntegration('[kuaishou-industry/send-code]', '收到快手行业电子凭证发码请求', { requestId, @@ -34,9 +35,23 @@ router.post('/send-code', industryRateLimit, async (req, res) => { body: req.body, }) + logCapture('[kuaishou-industry/send-code]', '快手通知商家发码消息', { + requestId, + receivedAt: new Date().toISOString(), + method: req.method, + originalUrl: req.originalUrl, + ip: req.ip, + params: mergedParams, + }, { channel: 'send-code', sanitize: false }) + try { const params = mergeRequestParams(req) const result = await handleSendCode(params) + logCapture('[kuaishou-industry/send-code]', '快手通知商家发码处理完成', { + requestId, + durationMs: Date.now() - startedAt, + result, + }, { channel: 'send-code', sanitize: false }) logIntegration('[kuaishou-industry/send-code]', '发码处理完成', { requestId, durationMs: Date.now() - startedAt, @@ -45,6 +60,14 @@ router.post('/send-code', industryRateLimit, async (req, res) => { res.status(200).json(result) } catch (error) { const message = error instanceof Error ? error.message : '系统异常' + logCapture('[kuaishou-industry/send-code]', '快手通知商家发码处理失败', { + requestId, + durationMs: Date.now() - startedAt, + error: { + name: error instanceof Error ? error.name : 'Error', + message, + }, + }, { channel: 'send-code', sanitize: false }) logIntegration('[kuaishou-industry/send-code]', '发码处理失败', { requestId, durationMs: Date.now() - startedAt, diff --git a/apps/backend/src/utils/logger.ts b/apps/backend/src/utils/logger.ts index 7026f16f..e7ebeea0 100644 --- a/apps/backend/src/utils/logger.ts +++ b/apps/backend/src/utils/logger.ts @@ -123,6 +123,25 @@ export function logExternalHttpPacket( return logIntegration(scope, message, normalizeExternalHttpPacketDetail(detail), { level }) } +export function logCapture( + scope: unknown, + message: unknown, + detail: LogDetail = undefined, + { channel = 'capture', sanitize = false }: { channel?: string; sanitize?: boolean } = {}, +): LogEntry | null { + const entry = { + time: new Date().toISOString(), + level: 'info' as LogLevel, + scope: String(scope || 'capture').trim() || 'capture', + message: String(message || '').trim() || '-', + pid: process.pid, + detail: normalizeLogValue(detail, sanitize !== false), + } + + enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time), sanitize !== false, true) + return entry +} + function writeLog( level: LogLevel, scope: unknown, @@ -146,12 +165,19 @@ function writeLog( } writeConsole(entry) - enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time)) + enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time), true) return entry } -function enqueueFileWrite(entry: LogEntry, filePath: string): void { - const line = formatLogEntry(entry, { color: false }) +function enqueueFileWrite( + entry: LogEntry, + filePath: string, + sanitize: boolean, + jsonLine = false, +): void { + const line = jsonLine + ? JSON.stringify(entry) + : formatLogEntry(entry, { color: false, sanitize }) const dateKey = extractLogDateKey(entry.time) writeQueue = writeQueue @@ -204,7 +230,7 @@ export function shouldWriteLog( export function formatLogEntry( entry: Partial, - { color = false }: { color?: boolean } = {}, + { color = false, sanitize = true }: { color?: boolean; sanitize?: boolean } = {}, ): string { const normalizedEntry = { time: String(entry?.time || new Date().toISOString()), @@ -212,7 +238,7 @@ export function formatLogEntry( scope: String(entry?.scope || 'app').trim() || 'app', message: String(entry?.message || '').trim() || '-', pid: Number(entry?.pid || process.pid), - detail: sanitizeLogValue(entry?.detail), + detail: sanitize !== false ? sanitizeLogValue(entry?.detail) : entry?.detail, } const timestamp = colorize(formatLogTimestamp(normalizedEntry.time), ANSI.gray, color) const level = colorize( @@ -248,10 +274,21 @@ export function formatLogEntry( } export function resolveLogFilePath(channel = 'app', time = new Date().toISOString()): string { - const normalizedChannel = String(channel || '').trim() === 'integration' ? 'integration' : 'app' + const normalizedChannel = normalizeLogChannelName(channel) return path.join(LOG_DIR, `${normalizedChannel}-${extractLogDateKey(time)}.log`) } +function normalizeLogChannelName(value: unknown): string { + const normalized = String(value || '').trim().toLowerCase() + if (!normalized) { + return 'app' + } + if (normalized === 'integration') { + return 'integration' + } + return /^[a-z0-9-]{1,40}$/.test(normalized) ? normalized : 'app' +} + export function resolveExpiredLogFilenames( fileNames: string[] = [], referenceTime = new Date().toISOString(), @@ -280,37 +317,37 @@ function resolveDataRoot(): string { return configured ? path.resolve(configured) : path.resolve(PROJECT_ROOT, 'data') } -function normalizeLogValue(value: unknown): unknown { +function normalizeLogValue(value: unknown, sanitize = true): unknown { if (typeof value === 'undefined') { return undefined } + const normalizeCurrent = (current: unknown): unknown => { + if (current instanceof Error) { + const currentError = current as Error & HttpErrorLike + return { + name: currentError.name, + message: sanitize ? sanitizeLogString(currentError.message) : currentError.message, + stack: sanitize ? sanitizeLogString(currentError.stack) : currentError.stack, + statusCode: currentError.statusCode, + errorCode: currentError.errorCode, + context: sanitize ? sanitizeLogValue(currentError.context) : currentError.context, + } + } + + if (typeof current === 'bigint') { + return String(current) + } + + return current + } + try { - return sanitizeLogValue( - JSON.parse( - JSON.stringify(value, (_key, current) => { - if (current instanceof Error) { - const currentError = current as Error & HttpErrorLike - return { - name: current.name, - message: sanitizeLogString(current.message), - stack: sanitizeLogString(current.stack), - statusCode: currentError.statusCode, - errorCode: currentError.errorCode, - context: sanitizeLogValue(currentError.context), - } - } - - if (typeof current === 'bigint') { - return String(current) - } - - return current - }), - ), - ) + return sanitize + ? sanitizeLogValue(JSON.parse(JSON.stringify(value, (_key, current) => normalizeCurrent(current)))) + : JSON.parse(JSON.stringify(value, (_key, current) => normalizeCurrent(current))) } catch { - return sanitizeLogString(String(value)) + return sanitize ? sanitizeLogString(String(value)) : String(value) } } @@ -632,7 +669,7 @@ function shouldDeleteLogFileByDate(fileName: unknown, cutoffDateKey: string): bo return true } - const matched = /^(app|integration)-(\d{4}-\d{2}-\d{2})\.log$/.exec(String(fileName || '').trim()) + const matched = /^([a-z0-9-]{1,40})-(\d{4}-\d{2}-\d{2})\.log$/.exec(String(fileName || '').trim()) if (!matched) { return false From e0e4f7183069343e5e4ee81ceb4c0318535ab781 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 2 Aug 2026 11:43:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?Caddyfile=20=E5=A2=9E=E5=8A=A0=20live.khhao?= =?UTF-8?q?.com=20=E5=8F=8D=E4=BB=A3=20douyu-login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/caddy/Caddyfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deploy/caddy/Caddyfile b/deploy/caddy/Caddyfile index 46d3295f..7093a052 100644 --- a/deploy/caddy/Caddyfile +++ b/deploy/caddy/Caddyfile @@ -18,3 +18,7 @@ file_server } } + +live.khhao.com { + reverse_proxy douyu-login:8800 +}