mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
style: format all files with prettier
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,8 @@ const repository =
|
||||
process.env.PRICE_MIRROR_REPO || process.env.GITHUB_REPOSITORY || 'Wei-Shaw/claude-relay-service'
|
||||
const branch = process.env.PRICE_MIRROR_BRANCH || 'price-mirror'
|
||||
const pricingFileName = process.env.PRICE_MIRROR_FILENAME || 'model_prices_and_context_window.json'
|
||||
const hashFileName = process.env.PRICE_MIRROR_HASH_FILENAME || 'model_prices_and_context_window.sha256'
|
||||
const hashFileName =
|
||||
process.env.PRICE_MIRROR_HASH_FILENAME || 'model_prices_and_context_window.sha256'
|
||||
|
||||
const baseUrl = process.env.PRICE_MIRROR_BASE_URL
|
||||
? process.env.PRICE_MIRROR_BASE_URL.replace(/\/$/, '')
|
||||
@@ -11,7 +12,6 @@ const baseUrl = process.env.PRICE_MIRROR_BASE_URL
|
||||
module.exports = {
|
||||
pricingFileName,
|
||||
hashFileName,
|
||||
pricingUrl:
|
||||
process.env.PRICE_MIRROR_JSON_URL || `${baseUrl}/${pricingFileName}`,
|
||||
pricingUrl: process.env.PRICE_MIRROR_JSON_URL || `${baseUrl}/${pricingFileName}`,
|
||||
hashUrl: process.env.PRICE_MIRROR_HASH_URL || `${baseUrl}/${hashFileName}`
|
||||
}
|
||||
|
||||
@@ -1193,7 +1193,9 @@ async function importData() {
|
||||
pipeline.hset(`usage:global:daily:${date}`, field, value)
|
||||
}
|
||||
}
|
||||
logger.info(`📥 Importing ${Object.keys(globalStats.globalDaily).length} global daily stats`)
|
||||
logger.info(
|
||||
`📥 Importing ${Object.keys(globalStats.globalDaily).length} global daily stats`
|
||||
)
|
||||
}
|
||||
|
||||
// 导入全局每月统计
|
||||
@@ -1203,7 +1205,9 @@ async function importData() {
|
||||
pipeline.hset(`usage:global:monthly:${month}`, field, value)
|
||||
}
|
||||
}
|
||||
logger.info(`📥 Importing ${Object.keys(globalStats.globalMonthly).length} global monthly stats`)
|
||||
logger.info(
|
||||
`📥 Importing ${Object.keys(globalStats.globalMonthly).length} global monthly stats`
|
||||
)
|
||||
}
|
||||
|
||||
// 导入每日统计
|
||||
|
||||
@@ -1473,8 +1473,12 @@ class RedisClient {
|
||||
// 多抽样检查:抽取最多 3 个 keyId 检查是否有 alltime 数据
|
||||
const sampleIndices = new Set()
|
||||
sampleIndices.add(0) // 始终包含第一个
|
||||
if (keyIds.length > 1) sampleIndices.add(keyIds.length - 1) // 包含最后一个
|
||||
if (keyIds.length > 2) sampleIndices.add(Math.floor(keyIds.length / 2)) // 包含中间一个
|
||||
if (keyIds.length > 1) {
|
||||
sampleIndices.add(keyIds.length - 1)
|
||||
} // 包含最后一个
|
||||
if (keyIds.length > 2) {
|
||||
sampleIndices.add(Math.floor(keyIds.length / 2))
|
||||
} // 包含中间一个
|
||||
|
||||
let hasAnyAlltimeData = false
|
||||
for (const idx of sampleIndices) {
|
||||
|
||||
@@ -2673,13 +2673,14 @@ router.get('/api-keys/:keyId/usage-records', authenticateAdmin, async (req, res)
|
||||
costFormatted: CostCalculator.formatCost(computedCost),
|
||||
realCost: Number(realCost.toFixed(6)),
|
||||
realCostFormatted: CostCalculator.formatCost(realCost),
|
||||
costBreakdown: record.realCostBreakdown || record.costBreakdown || {
|
||||
input: costData?.costs?.input || 0,
|
||||
output: costData?.costs?.output || 0,
|
||||
cacheCreate: costData?.costs?.cacheWrite || 0,
|
||||
cacheRead: costData?.costs?.cacheRead || 0,
|
||||
total: costData?.costs?.total || computedCost
|
||||
},
|
||||
costBreakdown: record.realCostBreakdown ||
|
||||
record.costBreakdown || {
|
||||
input: costData?.costs?.input || 0,
|
||||
output: costData?.costs?.output || 0,
|
||||
cacheCreate: costData?.costs?.cacheWrite || 0,
|
||||
cacheRead: costData?.costs?.cacheRead || 0,
|
||||
total: costData?.costs?.total || computedCost
|
||||
},
|
||||
responseTime: record.responseTime || null
|
||||
})
|
||||
}
|
||||
@@ -2986,13 +2987,14 @@ router.get('/accounts/:accountId/usage-records', authenticateAdmin, async (req,
|
||||
costFormatted: CostCalculator.formatCost(computedCost),
|
||||
realCost: Number(realCost.toFixed(6)),
|
||||
realCostFormatted: CostCalculator.formatCost(realCost),
|
||||
costBreakdown: record.realCostBreakdown || record.costBreakdown || {
|
||||
input: costData?.costs?.input || 0,
|
||||
output: costData?.costs?.output || 0,
|
||||
cacheCreate: costData?.costs?.cacheWrite || 0,
|
||||
cacheRead: costData?.costs?.cacheRead || 0,
|
||||
total: costData?.costs?.total || computedCost
|
||||
},
|
||||
costBreakdown: record.realCostBreakdown ||
|
||||
record.costBreakdown || {
|
||||
input: costData?.costs?.input || 0,
|
||||
output: costData?.costs?.output || 0,
|
||||
cacheCreate: costData?.costs?.cacheWrite || 0,
|
||||
cacheRead: costData?.costs?.cacheRead || 0,
|
||||
total: costData?.costs?.total || computedCost
|
||||
},
|
||||
responseTime: record.responseTime || null
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,7 +27,14 @@ const {
|
||||
} = require('../services/anthropicGeminiBridgeService')
|
||||
const router = express.Router()
|
||||
|
||||
function queueRateLimitUpdate(rateLimitInfo, usageSummary, model, context = '', keyId = null, accountType = null) {
|
||||
function queueRateLimitUpdate(
|
||||
rateLimitInfo,
|
||||
usageSummary,
|
||||
model,
|
||||
context = '',
|
||||
keyId = null,
|
||||
accountType = null
|
||||
) {
|
||||
if (!rateLimitInfo) {
|
||||
return Promise.resolve({ totalTokens: 0, totalCost: 0 })
|
||||
}
|
||||
|
||||
@@ -828,7 +828,7 @@ router.post('/api/batch-model-stats', async (req, res) => {
|
||||
}
|
||||
|
||||
// 优先使用存储的费用,否则回退到重新计算
|
||||
const hasStoredCost = usage.hasStoredCost
|
||||
const { hasStoredCost } = usage
|
||||
const costData = CostCalculator.calculateCost(usageData, model)
|
||||
|
||||
// 如果有存储的费用,覆盖计算的费用
|
||||
|
||||
@@ -24,7 +24,14 @@ function checkPermissions(apiKeyData, requiredPermission = 'claude') {
|
||||
return apiKeyService.hasPermission(apiKeyData?.permissions, requiredPermission)
|
||||
}
|
||||
|
||||
function queueRateLimitUpdate(rateLimitInfo, usageSummary, model, context = '', keyId = null, accountType = null) {
|
||||
function queueRateLimitUpdate(
|
||||
rateLimitInfo,
|
||||
usageSummary,
|
||||
model,
|
||||
context = '',
|
||||
keyId = null,
|
||||
accountType = null
|
||||
) {
|
||||
if (!rateLimitInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1800,7 +1800,13 @@ function dumpToolsPayload({ vendor, model, tools, toolChoice }) {
|
||||
* 更新速率限制计数器
|
||||
* 跟踪 token 使用量和成本
|
||||
*/
|
||||
async function applyRateLimitTracking(rateLimitInfo, usageSummary, model, context = '', keyId = null) {
|
||||
async function applyRateLimitTracking(
|
||||
rateLimitInfo,
|
||||
usageSummary,
|
||||
model,
|
||||
context = '',
|
||||
keyId = null
|
||||
) {
|
||||
if (!rateLimitInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ class ApiKeyService {
|
||||
const hashedKey = this._hashApiKey(apiKey)
|
||||
|
||||
// 处理 permissions
|
||||
let permissionsValue = permissions
|
||||
const permissionsValue = permissions
|
||||
|
||||
const keyData = {
|
||||
id: keyId,
|
||||
@@ -623,7 +623,9 @@ class ApiKeyService {
|
||||
|
||||
// 统计 API Key 上的标签(trim 后统计)
|
||||
for (const key of apiKeys) {
|
||||
if (key.isDeleted === 'true') continue
|
||||
if (key.isDeleted === 'true') {
|
||||
continue
|
||||
}
|
||||
let tags = []
|
||||
try {
|
||||
const parsed = key.tags ? JSON.parse(key.tags) : []
|
||||
@@ -666,7 +668,9 @@ class ApiKeyService {
|
||||
let affectedCount = 0
|
||||
|
||||
for (const key of apiKeys) {
|
||||
if (key.isDeleted === 'true') continue
|
||||
if (key.isDeleted === 'true') {
|
||||
continue
|
||||
}
|
||||
let tags = []
|
||||
try {
|
||||
const parsed = key.tags ? JSON.parse(key.tags) : []
|
||||
@@ -709,7 +713,9 @@ class ApiKeyService {
|
||||
let foundInKeys = false
|
||||
|
||||
for (const key of apiKeys) {
|
||||
if (key.isDeleted === 'true') continue
|
||||
if (key.isDeleted === 'true') {
|
||||
continue
|
||||
}
|
||||
let tags = []
|
||||
try {
|
||||
const parsed = key.tags ? JSON.parse(key.tags) : []
|
||||
|
||||
@@ -211,7 +211,9 @@ class ServiceRatesService {
|
||||
* 根据账户类型获取服务类型(优先级高于模型推断)
|
||||
*/
|
||||
getServiceFromAccountType(accountType) {
|
||||
if (!accountType) return null
|
||||
if (!accountType) {
|
||||
return null
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
claude: 'claude',
|
||||
|
||||
@@ -94,14 +94,23 @@ function mapToErrorCode(error, options = {}) {
|
||||
|
||||
// 先按 HTTP 状态码快速匹配
|
||||
if (statusCode) {
|
||||
if (statusCode === 401) matchedCode = 'E003'
|
||||
else if (statusCode === 403) matchedCode = 'E009'
|
||||
else if (statusCode === 404) matchedCode = 'E010'
|
||||
else if (statusCode === 429) matchedCode = 'E004'
|
||||
else if (statusCode === 502) matchedCode = 'E007'
|
||||
else if (statusCode === 503) matchedCode = 'E001'
|
||||
else if (statusCode === 504) matchedCode = 'E008'
|
||||
else if (statusCode === 529) matchedCode = 'E012'
|
||||
if (statusCode === 401) {
|
||||
matchedCode = 'E003'
|
||||
} else if (statusCode === 403) {
|
||||
matchedCode = 'E009'
|
||||
} else if (statusCode === 404) {
|
||||
matchedCode = 'E010'
|
||||
} else if (statusCode === 429) {
|
||||
matchedCode = 'E004'
|
||||
} else if (statusCode === 502) {
|
||||
matchedCode = 'E007'
|
||||
} else if (statusCode === 503) {
|
||||
matchedCode = 'E001'
|
||||
} else if (statusCode === 504) {
|
||||
matchedCode = 'E008'
|
||||
} else if (statusCode === 529) {
|
||||
matchedCode = 'E012'
|
||||
}
|
||||
}
|
||||
|
||||
// 再按消息内容精确匹配(可能覆盖状态码匹配)
|
||||
@@ -117,10 +126,15 @@ function mapToErrorCode(error, options = {}) {
|
||||
// 按错误 code 匹配(网络错误)
|
||||
if (errorCode) {
|
||||
const codeStr = String(errorCode).toUpperCase()
|
||||
if (codeStr === 'ENOTFOUND' || codeStr === 'EAI_AGAIN') matchedCode = 'E002'
|
||||
else if (codeStr === 'ECONNREFUSED' || codeStr === 'ECONNRESET') matchedCode = 'E002'
|
||||
else if (codeStr === 'ETIMEDOUT' || codeStr === 'ESOCKETTIMEDOUT') matchedCode = 'E008'
|
||||
else if (codeStr === 'ECONNABORTED') matchedCode = 'E002'
|
||||
if (codeStr === 'ENOTFOUND' || codeStr === 'EAI_AGAIN') {
|
||||
matchedCode = 'E002'
|
||||
} else if (codeStr === 'ECONNREFUSED' || codeStr === 'ECONNRESET') {
|
||||
matchedCode = 'E002'
|
||||
} else if (codeStr === 'ETIMEDOUT' || codeStr === 'ESOCKETTIMEDOUT') {
|
||||
matchedCode = 'E008'
|
||||
} else if (codeStr === 'ECONNABORTED') {
|
||||
matchedCode = 'E002'
|
||||
}
|
||||
}
|
||||
|
||||
const result = ERROR_CODES[matchedCode]
|
||||
@@ -135,12 +149,24 @@ function mapToErrorCode(error, options = {}) {
|
||||
* 提取原始错误消息
|
||||
*/
|
||||
function extractOriginalMessage(error) {
|
||||
if (!error) return ''
|
||||
if (typeof error === 'string') return error
|
||||
if (error.message) return error.message
|
||||
if (error.response?.data?.error?.message) return error.response.data.error.message
|
||||
if (error.response?.data?.error) return String(error.response.data.error)
|
||||
if (error.response?.data?.message) return error.response.data.message
|
||||
if (!error) {
|
||||
return ''
|
||||
}
|
||||
if (typeof error === 'string') {
|
||||
return error
|
||||
}
|
||||
if (error.message) {
|
||||
return error.message
|
||||
}
|
||||
if (error.response?.data?.error?.message) {
|
||||
return error.response.data.error.message
|
||||
}
|
||||
if (error.response?.data?.error) {
|
||||
return String(error.response.data.error)
|
||||
}
|
||||
if (error.response?.data?.message) {
|
||||
return error.response.data.message
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
@@ -188,7 +214,9 @@ function getSafeMessage(error, options = {}) {
|
||||
|
||||
// 兼容旧接口
|
||||
function sanitizeErrorMessage(message) {
|
||||
if (!message) return 'Service temporarily unavailable'
|
||||
if (!message) {
|
||||
return 'Service temporarily unavailable'
|
||||
}
|
||||
return mapToErrorCode({ message }, { logOriginal: false }).message
|
||||
}
|
||||
|
||||
@@ -201,9 +229,13 @@ function extractErrorMessage(body) {
|
||||
}
|
||||
|
||||
function isAccountDisabledError(statusCode, body) {
|
||||
if (statusCode !== 400) return false
|
||||
if (statusCode !== 400) {
|
||||
return false
|
||||
}
|
||||
const message = extractOriginalMessage(body)
|
||||
if (!message) return false
|
||||
if (!message) {
|
||||
return false
|
||||
}
|
||||
const lower = message.toLowerCase()
|
||||
return (
|
||||
lower.includes('organization has been disabled') ||
|
||||
|
||||
@@ -8,7 +8,13 @@ function toNumber(value) {
|
||||
}
|
||||
|
||||
// keyId 和 accountType 用于计算倍率成本
|
||||
async function updateRateLimitCounters(rateLimitInfo, usageSummary, model, keyId = null, accountType = null) {
|
||||
async function updateRateLimitCounters(
|
||||
rateLimitInfo,
|
||||
usageSummary,
|
||||
model,
|
||||
keyId = null,
|
||||
accountType = null
|
||||
) {
|
||||
if (!rateLimitInfo) {
|
||||
return { totalTokens: 0, totalCost: 0, ratedCost: 0 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user