mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
chore: fix prettier formatting
This commit is contained in:
@@ -120,7 +120,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
// 如果超过限制,立即减少计数
|
||||
await redis.decrConcurrency(validation.keyData.id)
|
||||
logger.security(
|
||||
`🚦 Concurrency limit exceeded for key: ${validation.keyData.id} (${validation.keyData.name}), current: ${currentConcurrency - 1}, limit: ${concurrencyLimit}`
|
||||
`🚦 Concurrency limit exceeded for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), current: ${currentConcurrency - 1}, limit: ${concurrencyLimit}`
|
||||
)
|
||||
return res.status(429).json({
|
||||
error: 'Concurrency limit exceeded',
|
||||
@@ -275,7 +277,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
const remainingMinutes = Math.ceil((resetTime - now) / 60000)
|
||||
|
||||
logger.security(
|
||||
`💰 Rate limit exceeded (cost) for key: ${validation.keyData.id} (${validation.keyData.name}), cost: $${currentCost.toFixed(2)}/$${rateLimitCost}`
|
||||
`💰 Rate limit exceeded (cost) for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), cost: $${currentCost.toFixed(2)}/$${rateLimitCost}`
|
||||
)
|
||||
|
||||
return res.status(429).json({
|
||||
@@ -315,7 +319,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
|
||||
if (totalCost >= totalUsageLimit) {
|
||||
logger.security(
|
||||
`📉 Total usage limit exceeded for key: ${validation.keyData.id} (${validation.keyData.name}), cost: $${totalCost.toFixed(2)}/$${totalUsageLimit.toFixed(2)}`
|
||||
`📉 Total usage limit exceeded for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), cost: $${totalCost.toFixed(2)}/$${totalUsageLimit.toFixed(2)}`
|
||||
)
|
||||
|
||||
return res.status(429).json({
|
||||
@@ -327,7 +333,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
}
|
||||
|
||||
logger.api(
|
||||
`📉 Total usage for key: ${validation.keyData.id} (${validation.keyData.name}), cost: $${totalCost.toFixed(2)}/$${totalUsageLimit.toFixed(2)}`
|
||||
`📉 Total usage for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), cost: $${totalCost.toFixed(2)}/$${totalUsageLimit.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -338,7 +346,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
|
||||
if (dailyCost >= dailyCostLimit) {
|
||||
logger.security(
|
||||
`💰 Daily cost limit exceeded for key: ${validation.keyData.id} (${validation.keyData.name}), cost: $${dailyCost.toFixed(2)}/$${dailyCostLimit}`
|
||||
`💰 Daily cost limit exceeded for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), cost: $${dailyCost.toFixed(2)}/$${dailyCostLimit}`
|
||||
)
|
||||
|
||||
return res.status(429).json({
|
||||
@@ -352,7 +362,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
|
||||
// 记录当前费用使用情况
|
||||
logger.api(
|
||||
`💰 Cost usage for key: ${validation.keyData.id} (${validation.keyData.name}), current: $${dailyCost.toFixed(2)}/$${dailyCostLimit}`
|
||||
`💰 Cost usage for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), current: $${dailyCost.toFixed(2)}/$${dailyCostLimit}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -363,7 +375,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
|
||||
if (totalCost >= totalCostLimit) {
|
||||
logger.security(
|
||||
`💰 Total cost limit exceeded for key: ${validation.keyData.id} (${validation.keyData.name}), cost: $${totalCost.toFixed(2)}/$${totalCostLimit}`
|
||||
`💰 Total cost limit exceeded for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), cost: $${totalCost.toFixed(2)}/$${totalCostLimit}`
|
||||
)
|
||||
|
||||
return res.status(429).json({
|
||||
@@ -375,7 +389,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
}
|
||||
|
||||
logger.api(
|
||||
`💰 Total cost usage for key: ${validation.keyData.id} (${validation.keyData.name}), current: $${totalCost.toFixed(2)}/$${totalCostLimit}`
|
||||
`💰 Total cost usage for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), current: $${totalCost.toFixed(2)}/$${totalCostLimit}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -392,7 +408,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
|
||||
if (weeklyOpusCost >= weeklyOpusCostLimit) {
|
||||
logger.security(
|
||||
`💰 Weekly Opus cost limit exceeded for key: ${validation.keyData.id} (${validation.keyData.name}), cost: $${weeklyOpusCost.toFixed(2)}/$${weeklyOpusCostLimit}`
|
||||
`💰 Weekly Opus cost limit exceeded for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), cost: $${weeklyOpusCost.toFixed(2)}/$${weeklyOpusCostLimit}`
|
||||
)
|
||||
|
||||
// 计算下周一的重置时间
|
||||
@@ -414,7 +432,9 @@ const authenticateApiKey = async (req, res, next) => {
|
||||
|
||||
// 记录当前 Opus 费用使用情况
|
||||
logger.api(
|
||||
`💰 Opus weekly cost usage for key: ${validation.keyData.id} (${validation.keyData.name}), current: $${weeklyOpusCost.toFixed(2)}/$${weeklyOpusCostLimit}`
|
||||
`💰 Opus weekly cost usage for key: ${validation.keyData.id} (${
|
||||
validation.keyData.name
|
||||
}), current: $${weeklyOpusCost.toFixed(2)}/$${weeklyOpusCostLimit}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ function getDateInTimezone(date = new Date()) {
|
||||
function getDateStringInTimezone(date = new Date()) {
|
||||
const tzDate = getDateInTimezone(date)
|
||||
// 使用UTC方法获取偏移后的日期部分
|
||||
return `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
return `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}-${String(
|
||||
tzDate.getUTCDate()
|
||||
).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 获取配置时区的小时 (0-23)
|
||||
@@ -219,7 +221,10 @@ class RedisClient {
|
||||
const now = new Date()
|
||||
const today = getDateStringInTimezone(now)
|
||||
const tzDate = getDateInTimezone(now)
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
const currentHour = `${today}:${String(getHourInTimezone(now)).padStart(2, '0')}` // 新增小时级别
|
||||
|
||||
const daily = `usage:daily:${keyId}:${today}`
|
||||
@@ -414,7 +419,10 @@ class RedisClient {
|
||||
const now = new Date()
|
||||
const today = getDateStringInTimezone(now)
|
||||
const tzDate = getDateInTimezone(now)
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
const currentHour = `${today}:${String(getHourInTimezone(now)).padStart(2, '0')}`
|
||||
|
||||
// 账户级别统计的键
|
||||
@@ -551,7 +559,10 @@ class RedisClient {
|
||||
const today = getDateStringInTimezone()
|
||||
const dailyKey = `usage:daily:${keyId}:${today}`
|
||||
const tzDate = getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
const monthlyKey = `usage:monthly:${keyId}:${currentMonth}`
|
||||
|
||||
const [total, daily, monthly] = await Promise.all([
|
||||
@@ -694,7 +705,10 @@ class RedisClient {
|
||||
async incrementDailyCost(keyId, amount) {
|
||||
const today = getDateStringInTimezone()
|
||||
const tzDate = getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
const currentHour = `${today}:${String(getHourInTimezone(new Date())).padStart(2, '0')}`
|
||||
|
||||
const dailyKey = `usage:cost:daily:${keyId}:${today}`
|
||||
@@ -724,7 +738,10 @@ class RedisClient {
|
||||
async getCostStats(keyId) {
|
||||
const today = getDateStringInTimezone()
|
||||
const tzDate = getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
const currentHour = `${today}:${String(getHourInTimezone(new Date())).padStart(2, '0')}`
|
||||
|
||||
const [daily, monthly, hourly, total] = await Promise.all([
|
||||
@@ -827,7 +844,10 @@ class RedisClient {
|
||||
const today = getDateStringInTimezone()
|
||||
const accountDailyKey = `account_usage:daily:${accountId}:${today}`
|
||||
const tzDate = getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
const accountMonthlyKey = `account_usage:monthly:${accountId}:${currentMonth}`
|
||||
|
||||
const [total, daily, monthly] = await Promise.all([
|
||||
@@ -1463,14 +1483,18 @@ class RedisClient {
|
||||
if (remainingTTL < renewalThreshold) {
|
||||
await this.client.expire(key, fullTTL)
|
||||
logger.debug(
|
||||
`🔄 Renewed sticky session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}min, renewed to ${ttlHours}h)`
|
||||
`🔄 Renewed sticky session TTL: ${sessionHash} (was ${Math.round(
|
||||
remainingTTL / 60
|
||||
)}min, renewed to ${ttlHours}h)`
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
// 剩余时间充足,无需续期
|
||||
logger.debug(
|
||||
`✅ Sticky session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}min)`
|
||||
`✅ Sticky session TTL sufficient: ${sessionHash} (remaining ${Math.round(
|
||||
remainingTTL / 60
|
||||
)}min)`
|
||||
)
|
||||
return true
|
||||
} catch (error) {
|
||||
|
||||
@@ -155,7 +155,10 @@ router.get('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
const currentDate = new Date(start)
|
||||
while (currentDate <= end) {
|
||||
const tzDate = redisClient.getDateInTimezone(currentDate)
|
||||
const dateStr = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
const dateStr = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
searchPatterns.push(`usage:daily:*:${dateStr}`)
|
||||
currentDate.setDate(currentDate.getDate() + 1)
|
||||
}
|
||||
@@ -163,7 +166,10 @@ router.get('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
// 今日 - 使用时区日期
|
||||
const redisClient = require('../models/redis')
|
||||
const tzDate = redisClient.getDateInTimezone(now)
|
||||
const dateStr = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
const dateStr = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
searchPatterns.push(`usage:daily:*:${dateStr}`)
|
||||
} else if (timeRange === '7days') {
|
||||
// 最近7天
|
||||
@@ -172,14 +178,20 @@ router.get('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
const date = new Date(now)
|
||||
date.setDate(date.getDate() - i)
|
||||
const tzDate = redisClient.getDateInTimezone(date)
|
||||
const dateStr = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
const dateStr = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}-${String(tzDate.getUTCDate()).padStart(2, '0')}`
|
||||
searchPatterns.push(`usage:daily:*:${dateStr}`)
|
||||
}
|
||||
} else if (timeRange === 'monthly') {
|
||||
// 本月
|
||||
const redisClient = require('../models/redis')
|
||||
const tzDate = redisClient.getDateInTimezone(now)
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
searchPatterns.push(`usage:monthly:*:${currentMonth}`)
|
||||
}
|
||||
|
||||
@@ -299,7 +311,10 @@ router.get('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
const redisClient = require('../models/redis')
|
||||
const tzToday = redisClient.getDateStringInTimezone(now)
|
||||
const tzDate = redisClient.getDateInTimezone(now)
|
||||
const tzMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const tzMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
|
||||
let modelKeys = []
|
||||
if (timeRange === 'custom' && startDate && endDate) {
|
||||
@@ -310,7 +325,9 @@ router.get('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
|
||||
while (currentDate <= end) {
|
||||
const tzDateForKey = redisClient.getDateInTimezone(currentDate)
|
||||
const dateStr = `${tzDateForKey.getUTCFullYear()}-${String(tzDateForKey.getUTCMonth() + 1).padStart(2, '0')}-${String(tzDateForKey.getUTCDate()).padStart(2, '0')}`
|
||||
const dateStr = `${tzDateForKey.getUTCFullYear()}-${String(
|
||||
tzDateForKey.getUTCMonth() + 1
|
||||
).padStart(2, '0')}-${String(tzDateForKey.getUTCDate()).padStart(2, '0')}`
|
||||
const dayKeys = await client.keys(`usage:${apiKey.id}:model:daily:*:${dateStr}`)
|
||||
modelKeys = modelKeys.concat(dayKeys)
|
||||
currentDate.setDate(currentDate.getDate() + 1)
|
||||
@@ -320,8 +337,8 @@ router.get('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
timeRange === 'today'
|
||||
? await client.keys(`usage:${apiKey.id}:model:daily:*:${tzToday}`)
|
||||
: timeRange === '7days'
|
||||
? await client.keys(`usage:${apiKey.id}:model:daily:*:*`)
|
||||
: await client.keys(`usage:${apiKey.id}:model:monthly:*:${tzMonth}`)
|
||||
? await client.keys(`usage:${apiKey.id}:model:daily:*:*`)
|
||||
: await client.keys(`usage:${apiKey.id}:model:monthly:*:${tzMonth}`)
|
||||
}
|
||||
|
||||
const modelStatsMap = new Map()
|
||||
@@ -617,11 +634,7 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
|
||||
return res.status(400).json({ error: 'All tags must be non-empty strings' })
|
||||
}
|
||||
|
||||
if (
|
||||
totalUsageLimit !== undefined &&
|
||||
totalUsageLimit !== null &&
|
||||
totalUsageLimit !== ''
|
||||
) {
|
||||
if (totalUsageLimit !== undefined && totalUsageLimit !== null && totalUsageLimit !== '') {
|
||||
const usageLimit = Number(totalUsageLimit)
|
||||
if (Number.isNaN(usageLimit) || usageLimit < 0) {
|
||||
return res.status(400).json({ error: 'Total usage limit must be a non-negative number' })
|
||||
@@ -1313,7 +1326,9 @@ router.patch('/api-keys/:keyId/expiration', authenticateAdmin, async (req, res)
|
||||
updates.expiresAt = newExpiresAt.toISOString()
|
||||
|
||||
logger.success(
|
||||
`🔓 API key manually activated by admin: ${keyId} (${keyData.name}), expires at ${newExpiresAt.toISOString()}`
|
||||
`🔓 API key manually activated by admin: ${keyId} (${
|
||||
keyData.name
|
||||
}), expires at ${newExpiresAt.toISOString()}`
|
||||
)
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
@@ -1378,7 +1393,11 @@ router.delete('/api-keys/batch', authenticateAdmin, async (req, res) => {
|
||||
// 参数验证
|
||||
if (!keyIds || !Array.isArray(keyIds) || keyIds.length === 0) {
|
||||
logger.warn(
|
||||
`🚨 Invalid keyIds: ${JSON.stringify({ keyIds, type: typeof keyIds, isArray: Array.isArray(keyIds) })}`
|
||||
`🚨 Invalid keyIds: ${JSON.stringify({
|
||||
keyIds,
|
||||
type: typeof keyIds,
|
||||
isArray: Array.isArray(keyIds)
|
||||
})}`
|
||||
)
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request',
|
||||
@@ -1842,13 +1861,13 @@ router.post('/claude-accounts/exchange-code', authenticateAdmin, async (req, res
|
||||
codeLength: req.body.callbackUrl
|
||||
? req.body.callbackUrl.length
|
||||
: req.body.authorizationCode
|
||||
? req.body.authorizationCode.length
|
||||
: 0,
|
||||
? req.body.authorizationCode.length
|
||||
: 0,
|
||||
codePrefix: req.body.callbackUrl
|
||||
? `${req.body.callbackUrl.substring(0, 10)}...`
|
||||
: req.body.authorizationCode
|
||||
? `${req.body.authorizationCode.substring(0, 10)}...`
|
||||
: 'N/A'
|
||||
? `${req.body.authorizationCode.substring(0, 10)}...`
|
||||
: 'N/A'
|
||||
})
|
||||
return res
|
||||
.status(500)
|
||||
@@ -1964,13 +1983,13 @@ router.post('/claude-accounts/exchange-setup-token-code', authenticateAdmin, asy
|
||||
codeLength: req.body.callbackUrl
|
||||
? req.body.callbackUrl.length
|
||||
: req.body.authorizationCode
|
||||
? req.body.authorizationCode.length
|
||||
: 0,
|
||||
? req.body.authorizationCode.length
|
||||
: 0,
|
||||
codePrefix: req.body.callbackUrl
|
||||
? `${req.body.callbackUrl.substring(0, 10)}...`
|
||||
: req.body.authorizationCode
|
||||
? `${req.body.authorizationCode.substring(0, 10)}...`
|
||||
: 'N/A'
|
||||
? `${req.body.authorizationCode.substring(0, 10)}...`
|
||||
: 'N/A'
|
||||
})
|
||||
return res
|
||||
.status(500)
|
||||
@@ -2399,7 +2418,9 @@ router.put(
|
||||
}
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled Claude account schedulable status: ${accountId} -> ${newSchedulable ? 'schedulable' : 'not schedulable'}`
|
||||
`🔄 Admin toggled Claude account schedulable status: ${accountId} -> ${
|
||||
newSchedulable ? 'schedulable' : 'not schedulable'
|
||||
}`
|
||||
)
|
||||
return res.json({ success: true, schedulable: newSchedulable })
|
||||
} catch (error) {
|
||||
@@ -2687,7 +2708,9 @@ router.put('/claude-console-accounts/:accountId/toggle', authenticateAdmin, asyn
|
||||
await claudeConsoleAccountService.updateAccount(accountId, { isActive: newStatus })
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled Claude Console account status: ${accountId} -> ${newStatus ? 'active' : 'inactive'}`
|
||||
`🔄 Admin toggled Claude Console account status: ${accountId} -> ${
|
||||
newStatus ? 'active' : 'inactive'
|
||||
}`
|
||||
)
|
||||
return res.json({ success: true, isActive: newStatus })
|
||||
} catch (error) {
|
||||
@@ -2728,7 +2751,9 @@ router.put(
|
||||
}
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled Claude Console account schedulable status: ${accountId} -> ${newSchedulable ? 'schedulable' : 'not schedulable'}`
|
||||
`🔄 Admin toggled Claude Console account schedulable status: ${accountId} -> ${
|
||||
newSchedulable ? 'schedulable' : 'not schedulable'
|
||||
}`
|
||||
)
|
||||
return res.json({ success: true, schedulable: newSchedulable })
|
||||
} catch (error) {
|
||||
@@ -3113,7 +3138,9 @@ router.put('/ccr-accounts/:accountId/toggle-schedulable', authenticateAdmin, asy
|
||||
}
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled CCR account schedulable status: ${accountId} -> ${newSchedulable ? 'schedulable' : 'not schedulable'}`
|
||||
`🔄 Admin toggled CCR account schedulable status: ${accountId} -> ${
|
||||
newSchedulable ? 'schedulable' : 'not schedulable'
|
||||
}`
|
||||
)
|
||||
return res.json({ success: true, schedulable: newSchedulable })
|
||||
} catch (error) {
|
||||
@@ -3436,7 +3463,9 @@ router.put('/bedrock-accounts/:accountId/toggle', authenticateAdmin, async (req,
|
||||
}
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled Bedrock account status: ${accountId} -> ${newStatus ? 'active' : 'inactive'}`
|
||||
`🔄 Admin toggled Bedrock account status: ${accountId} -> ${
|
||||
newStatus ? 'active' : 'inactive'
|
||||
}`
|
||||
)
|
||||
return res.json({ success: true, isActive: newStatus })
|
||||
} catch (error) {
|
||||
@@ -3485,7 +3514,9 @@ router.put(
|
||||
}
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled Bedrock account schedulable status: ${accountId} -> ${newSchedulable ? 'schedulable' : 'not schedulable'}`
|
||||
`🔄 Admin toggled Bedrock account schedulable status: ${accountId} -> ${
|
||||
newSchedulable ? 'schedulable' : 'not schedulable'
|
||||
}`
|
||||
)
|
||||
return res.json({ success: true, schedulable: newSchedulable })
|
||||
} catch (error) {
|
||||
@@ -3908,7 +3939,9 @@ router.put(
|
||||
}
|
||||
|
||||
logger.success(
|
||||
`🔄 Admin toggled Gemini account schedulable status: ${accountId} -> ${actualSchedulable ? 'schedulable' : 'not schedulable'}`
|
||||
`🔄 Admin toggled Gemini account schedulable status: ${accountId} -> ${
|
||||
actualSchedulable ? 'schedulable' : 'not schedulable'
|
||||
}`
|
||||
)
|
||||
|
||||
// 返回实际的数据库值,确保前端状态与后端一致
|
||||
@@ -4383,7 +4416,10 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
const { period = 'daily', startDate, endDate } = req.query // daily, monthly, 支持自定义时间范围
|
||||
const today = redis.getDateStringInTimezone()
|
||||
const tzDate = redis.getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
|
||||
logger.info(
|
||||
`📊 Getting global model stats, period: ${period}, startDate: ${startDate}, endDate: ${endDate}, today: ${today}, currentMonth: ${currentMonth}`
|
||||
@@ -4854,7 +4890,10 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
|
||||
const client = redis.getClientSafe()
|
||||
const today = redis.getDateStringInTimezone()
|
||||
const tzDate = redis.getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
|
||||
let searchPatterns = []
|
||||
|
||||
@@ -5385,7 +5424,10 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
const client = redis.getClientSafe()
|
||||
const today = redis.getDateStringInTimezone()
|
||||
const tzDate = redis.getDateInTimezone()
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(2, '0')}`
|
||||
const currentMonth = `${tzDate.getUTCFullYear()}-${String(tzDate.getUTCMonth() + 1).padStart(
|
||||
2,
|
||||
'0'
|
||||
)}`
|
||||
|
||||
let pattern
|
||||
if (period === 'today') {
|
||||
@@ -5401,7 +5443,9 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() - i)
|
||||
const currentTzDate = redis.getDateInTimezone(date)
|
||||
const dateStr = `${currentTzDate.getUTCFullYear()}-${String(currentTzDate.getUTCMonth() + 1).padStart(2, '0')}-${String(currentTzDate.getUTCDate()).padStart(2, '0')}`
|
||||
const dateStr = `${currentTzDate.getUTCFullYear()}-${String(
|
||||
currentTzDate.getUTCMonth() + 1
|
||||
).padStart(2, '0')}-${String(currentTzDate.getUTCDate()).padStart(2, '0')}`
|
||||
const dayPattern = `usage:model:daily:*:${dateStr}`
|
||||
|
||||
const dayKeys = await client.keys(dayPattern)
|
||||
@@ -5454,7 +5498,9 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
totalCosts.totalCost += costResult.costs.total
|
||||
|
||||
logger.info(
|
||||
`💰 Model ${model} (7days): ${usage.inputTokens + usage.outputTokens + usage.cacheCreateTokens + usage.cacheReadTokens} tokens, cost: ${costResult.formatted.total}`
|
||||
`💰 Model ${model} (7days): ${
|
||||
usage.inputTokens + usage.outputTokens + usage.cacheCreateTokens + usage.cacheReadTokens
|
||||
} tokens, cost: ${costResult.formatted.total}`
|
||||
)
|
||||
|
||||
// 记录模型费用
|
||||
@@ -5542,7 +5588,12 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
totalCosts.totalCost += costResult.costs.total
|
||||
|
||||
logger.info(
|
||||
`💰 Model ${model}: ${usage.inputTokens + usage.outputTokens + usage.cacheCreateTokens + usage.cacheReadTokens} tokens, cost: ${costResult.formatted.total}`
|
||||
`💰 Model ${model}: ${
|
||||
usage.inputTokens +
|
||||
usage.outputTokens +
|
||||
usage.cacheCreateTokens +
|
||||
usage.cacheReadTokens
|
||||
} tokens, cost: ${costResult.formatted.total}`
|
||||
)
|
||||
|
||||
// 记录模型费用
|
||||
@@ -6905,7 +6956,9 @@ router.post('/azure-openai-accounts', authenticateAdmin, async (req, res) => {
|
||||
|
||||
// 测试连接
|
||||
try {
|
||||
const testUrl = `${azureEndpoint}/openai/deployments/${deploymentName}?api-version=${apiVersion || '2024-02-01'}`
|
||||
const testUrl = `${azureEndpoint}/openai/deployments/${deploymentName}?api-version=${
|
||||
apiVersion || '2024-02-01'
|
||||
}`
|
||||
await axios.get(testUrl, {
|
||||
headers: {
|
||||
'api-key': apiKey
|
||||
|
||||
@@ -319,11 +319,7 @@ router.post('/api-keys', authenticateUser, async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
totalUsageLimit !== undefined &&
|
||||
totalUsageLimit !== null &&
|
||||
totalUsageLimit !== ''
|
||||
) {
|
||||
if (totalUsageLimit !== undefined && totalUsageLimit !== null && totalUsageLimit !== '') {
|
||||
const usageLimit = Number(totalUsageLimit)
|
||||
if (Number.isNaN(usageLimit) || usageLimit < 0) {
|
||||
return res.status(400).json({
|
||||
|
||||
@@ -168,7 +168,9 @@ class ApiKeyService {
|
||||
await redis.setApiKey(keyData.id, keyData)
|
||||
|
||||
logger.success(
|
||||
`🔓 API key activated: ${keyData.id} (${keyData.name}), will expire in ${activationDays} days at ${expiresAt.toISOString()}`
|
||||
`🔓 API key activated: ${keyData.id} (${
|
||||
keyData.name
|
||||
}), will expire in ${activationDays} days at ${expiresAt.toISOString()}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -903,7 +905,9 @@ class ApiKeyService {
|
||||
// 记录 Opus 周费用
|
||||
await redis.incrementWeeklyOpusCost(keyId, cost)
|
||||
logger.database(
|
||||
`💰 Recorded Opus weekly cost for ${keyId}: $${cost.toFixed(6)}, model: ${model}, account type: ${accountType}`
|
||||
`💰 Recorded Opus weekly cost for ${keyId}: $${cost.toFixed(
|
||||
6
|
||||
)}, model: ${model}, account type: ${accountType}`
|
||||
)
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to record Opus cost:', error)
|
||||
@@ -978,7 +982,9 @@ class ApiKeyService {
|
||||
// 记录详细的缓存费用(如果有)
|
||||
if (costInfo.ephemeral5mCost > 0 || costInfo.ephemeral1hCost > 0) {
|
||||
logger.database(
|
||||
`💰 Cache costs - 5m: $${costInfo.ephemeral5mCost.toFixed(6)}, 1h: $${costInfo.ephemeral1hCost.toFixed(6)}`
|
||||
`💰 Cache costs - 5m: $${costInfo.ephemeral5mCost.toFixed(
|
||||
6
|
||||
)}, 1h: $${costInfo.ephemeral1hCost.toFixed(6)}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user