mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
- Replace .eslintrc.js with .eslintrc.cjs for better ES module compatibility - Add .prettierrc configuration for consistent code formatting - Update package.json with new lint and format scripts - Add nodemon.json for development hot reloading configuration - Standardize code formatting across all JavaScript and Vue files - Update web admin SPA with improved linting rules and formatting - Add prettier configuration to web admin SPA 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
176 lines
4.3 KiB
JavaScript
176 lines
4.3 KiB
JavaScript
const winston = require('winston')
|
|
const path = require('path')
|
|
const fs = require('fs')
|
|
const { maskToken } = require('./tokenMask')
|
|
|
|
// 确保日志目录存在
|
|
const logDir = path.join(process.cwd(), 'logs')
|
|
if (!fs.existsSync(logDir)) {
|
|
fs.mkdirSync(logDir, { recursive: true })
|
|
}
|
|
|
|
// 创建专用的 token 刷新日志记录器
|
|
const tokenRefreshLogger = winston.createLogger({
|
|
level: 'info',
|
|
format: winston.format.combine(
|
|
winston.format.timestamp({
|
|
format: 'YYYY-MM-DD HH:mm:ss.SSS'
|
|
}),
|
|
winston.format.json(),
|
|
winston.format.printf((info) => JSON.stringify(info, null, 2))
|
|
),
|
|
transports: [
|
|
// 文件传输 - 每日轮转
|
|
new winston.transports.File({
|
|
filename: path.join(logDir, 'token-refresh.log'),
|
|
maxsize: 10 * 1024 * 1024, // 10MB
|
|
maxFiles: 30, // 保留30天
|
|
tailable: true
|
|
}),
|
|
// 错误单独记录
|
|
new winston.transports.File({
|
|
filename: path.join(logDir, 'token-refresh-error.log'),
|
|
level: 'error',
|
|
maxsize: 10 * 1024 * 1024,
|
|
maxFiles: 30
|
|
})
|
|
],
|
|
// 错误处理
|
|
exitOnError: false
|
|
})
|
|
|
|
// 在开发环境添加控制台输出
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
tokenRefreshLogger.add(
|
|
new winston.transports.Console({
|
|
format: winston.format.combine(winston.format.colorize(), winston.format.simple())
|
|
})
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 记录 token 刷新开始
|
|
*/
|
|
function logRefreshStart(accountId, accountName, platform = 'claude', reason = '') {
|
|
tokenRefreshLogger.info({
|
|
event: 'token_refresh_start',
|
|
accountId,
|
|
accountName,
|
|
platform,
|
|
reason,
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 记录 token 刷新成功
|
|
*/
|
|
function logRefreshSuccess(accountId, accountName, platform = 'claude', tokenData = {}) {
|
|
const maskedTokenData = {
|
|
accessToken: tokenData.accessToken ? maskToken(tokenData.accessToken) : '[NOT_PROVIDED]',
|
|
refreshToken: tokenData.refreshToken ? maskToken(tokenData.refreshToken) : '[NOT_PROVIDED]',
|
|
expiresAt: tokenData.expiresAt || tokenData.expiry_date || '[NOT_PROVIDED]',
|
|
scopes: tokenData.scopes || tokenData.scope || '[NOT_PROVIDED]'
|
|
}
|
|
|
|
tokenRefreshLogger.info({
|
|
event: 'token_refresh_success',
|
|
accountId,
|
|
accountName,
|
|
platform,
|
|
tokenData: maskedTokenData,
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 记录 token 刷新失败
|
|
*/
|
|
function logRefreshError(accountId, accountName, platform = 'claude', error, attemptNumber = 1) {
|
|
const errorInfo = {
|
|
message: error.message || error.toString(),
|
|
code: error.code || 'UNKNOWN',
|
|
statusCode: error.response?.status || 'N/A',
|
|
responseData: error.response?.data || 'N/A'
|
|
}
|
|
|
|
tokenRefreshLogger.error({
|
|
event: 'token_refresh_error',
|
|
accountId,
|
|
accountName,
|
|
platform,
|
|
error: errorInfo,
|
|
attemptNumber,
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 记录 token 刷新跳过(由于并发锁)
|
|
*/
|
|
function logRefreshSkipped(accountId, accountName, platform = 'claude', reason = 'locked') {
|
|
tokenRefreshLogger.info({
|
|
event: 'token_refresh_skipped',
|
|
accountId,
|
|
accountName,
|
|
platform,
|
|
reason,
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 记录 token 使用情况
|
|
*/
|
|
function logTokenUsage(accountId, accountName, platform = 'claude', expiresAt, isExpired) {
|
|
tokenRefreshLogger.debug({
|
|
event: 'token_usage_check',
|
|
accountId,
|
|
accountName,
|
|
platform,
|
|
expiresAt,
|
|
isExpired,
|
|
remainingMinutes: expiresAt ? Math.floor((new Date(expiresAt) - Date.now()) / 60000) : 'N/A',
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 记录批量刷新任务
|
|
*/
|
|
function logBatchRefreshStart(totalAccounts, platform = 'all') {
|
|
tokenRefreshLogger.info({
|
|
event: 'batch_refresh_start',
|
|
totalAccounts,
|
|
platform,
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 记录批量刷新结果
|
|
*/
|
|
function logBatchRefreshComplete(results) {
|
|
tokenRefreshLogger.info({
|
|
event: 'batch_refresh_complete',
|
|
results: {
|
|
total: results.total || 0,
|
|
success: results.success || 0,
|
|
failed: results.failed || 0,
|
|
skipped: results.skipped || 0
|
|
},
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
}
|
|
|
|
module.exports = {
|
|
logger: tokenRefreshLogger,
|
|
logRefreshStart,
|
|
logRefreshSuccess,
|
|
logRefreshError,
|
|
logRefreshSkipped,
|
|
logTokenUsage,
|
|
logBatchRefreshStart,
|
|
logBatchRefreshComplete
|
|
}
|