This commit is contained in:
SunSeekerX
2026-01-04 12:05:53 +08:00
parent 90023d1551
commit f5e982632d
28 changed files with 481 additions and 213 deletions

View File

@@ -18,7 +18,9 @@ class AccountGroupService {
async ensureReverseIndexes() {
try {
const client = redis.getClientSafe()
if (!client) return
if (!client) {
return
}
// 检查是否已迁移
const migrated = await client.get(this.REVERSE_INDEX_MIGRATED_KEY)
@@ -39,10 +41,14 @@ class AccountGroupService {
for (const groupId of allGroupIds) {
const group = await client.hgetall(`${this.GROUP_PREFIX}${groupId}`)
if (!group || !group.platform) continue
if (!group || !group.platform) {
continue
}
const members = await client.smembers(`${this.GROUP_MEMBERS_PREFIX}${groupId}`)
if (members.length === 0) continue
if (members.length === 0) {
continue
}
const pipeline = client.pipeline()
for (const accountId of members) {

View File

@@ -71,7 +71,9 @@ class ApiKeyIndexService {
* 扫描所有 API Key确保 hash -> keyId 映射存在
*/
async rebuildHashMap() {
if (!this.redis) return
if (!this.redis) {
return
}
try {
const client = this.redis.getClientSafe()
@@ -187,7 +189,9 @@ class ApiKeyIndexService {
const pipeline = client.pipeline()
for (const apiKey of apiKeys) {
if (!apiKey || !apiKey.id) continue
if (!apiKey || !apiKey.id) {
continue
}
const keyId = apiKey.id
const createdAt = apiKey.createdAt ? new Date(apiKey.createdAt).getTime() : 0
@@ -249,7 +253,9 @@ class ApiKeyIndexService {
* 添加单个 API Key 到索引
*/
async addToIndex(apiKey) {
if (!this.redis || !apiKey || !apiKey.id) return
if (!this.redis || !apiKey || !apiKey.id) {
return
}
try {
const client = this.redis.getClientSafe()
@@ -297,7 +303,9 @@ class ApiKeyIndexService {
* 更新索引(状态、名称、标签变化时调用)
*/
async updateIndex(keyId, updates, oldData = {}) {
if (!this.redis || !keyId) return
if (!this.redis || !keyId) {
return
}
try {
const client = this.redis.getClientSafe()
@@ -376,7 +384,9 @@ class ApiKeyIndexService {
* 从索引中移除 API Key
*/
async removeFromIndex(keyId, oldData = {}) {
if (!this.redis || !keyId) return
if (!this.redis || !keyId) {
return
}
try {
const client = this.redis.getClientSafe()
@@ -598,7 +608,9 @@ class ApiKeyIndexService {
* 更新 lastUsedAt 索引(供 recordUsage 调用)
*/
async updateLastUsedAt(keyId, lastUsedAt) {
if (!this.redis || !keyId) return
if (!this.redis || !keyId) {
return
}
try {
const client = this.redis.getClientSafe()

View File

@@ -921,7 +921,9 @@ class ApiKeyService {
return keyIds
.map((id, i) => {
const [err, fields] = results[i]
if (err) return null
if (err) {
return null
}
return {
id,
claudeAccountId: fields[0] || null,

View File

@@ -127,7 +127,7 @@ class BedrockAccountService {
// 📋 获取所有账户列表
async getAllAccounts() {
try {
const client = redis.getClientSafe()
const _client = redis.getClientSafe()
const accountIds = await redis.getAllIdsByIndex(
'bedrock_account:index',
'bedrock_account:*',

View File

@@ -2,7 +2,6 @@ const { v4: uuidv4 } = require('uuid')
const ProxyHelper = require('../utils/proxyHelper')
const redis = require('../models/redis')
const logger = require('../utils/logger')
const config = require('../../config/config')
const { createEncryptor } = require('../utils/commonHelper')
class CcrAccountService {

View File

@@ -5,7 +5,11 @@
const redis = require('../models/redis')
const logger = require('../utils/logger')
const { getCachedConfig, setCachedConfig, deleteCachedConfig } = require('../utils/performanceOptimizer')
const {
getCachedConfig,
setCachedConfig,
deleteCachedConfig
} = require('../utils/performanceOptimizer')
class ClaudeCodeHeadersService {
constructor() {

View File

@@ -24,9 +24,7 @@ const {
// structuredClone polyfill for Node < 17
const safeClone =
typeof structuredClone === 'function'
? structuredClone
: (obj) => JSON.parse(JSON.stringify(obj))
typeof structuredClone === 'function' ? structuredClone : (obj) => JSON.parse(JSON.stringify(obj))
class ClaudeRelayService {
constructor() {

View File

@@ -94,7 +94,9 @@ class CostInitService {
}
}
logger.info(`💰 Found ${apiKeyIds.length} active API Keys to process (filtered ${allKeyIds.length - apiKeyIds.length} deleted)`)
logger.info(
`💰 Found ${apiKeyIds.length} active API Keys to process (filtered ${allKeyIds.length - apiKeyIds.length} deleted)`
)
let processedCount = 0
let errorCount = 0
@@ -155,7 +157,9 @@ class CostInitService {
for (let j = 0; j < results.length; j++) {
const [err, values] = results[j]
if (err) continue
if (err) {
continue
}
// 将数组转换为对象
const data = {}
@@ -182,7 +186,9 @@ class CostInitService {
const match = key.match(
/usage:(.+):model:(daily|monthly|hourly):(.+):(\d{4}-\d{2}(?:-\d{2})?(?::\d{2})?)$/
)
if (!match) continue
if (!match) {
continue
}
const [, , period, model, dateStr] = match
@@ -301,7 +307,9 @@ class CostInitService {
cursor = newCursor
for (const usageKey of usageKeys) {
if (samplesChecked >= maxSamples) break
if (samplesChecked >= maxSamples) {
break
}
const match = usageKey.match(/usage:(.+):model:daily:(.+):(\d{4}-\d{2}-\d{2})$/)
if (match) {
@@ -319,7 +327,9 @@ class CostInitService {
}
}
if (samplesChecked >= maxSamples) break
if (samplesChecked >= maxSamples) {
break
}
} while (cursor !== '0')
logger.info('💰 Cost data appears to be up to date')

View File

@@ -2,7 +2,6 @@ const { v4: uuidv4 } = require('uuid')
const crypto = require('crypto')
const axios = require('axios')
const redis = require('../models/redis')
const config = require('../../config/config')
const logger = require('../utils/logger')
const { maskToken } = require('../utils/tokenMask')
const ProxyHelper = require('../utils/proxyHelper')
@@ -30,10 +29,13 @@ class DroidAccountService {
this._encryptor = createEncryptor('droid-account-salt')
// 🧹 定期清理缓存每10分钟
setInterval(() => {
this._encryptor.clearCache()
logger.info('🧹 Droid decrypt cache cleanup completed', this._encryptor.getStats())
}, 10 * 60 * 1000)
setInterval(
() => {
this._encryptor.clearCache()
logger.info('🧹 Droid decrypt cache cleanup completed', this._encryptor.getStats())
},
10 * 60 * 1000
)
this.supportedEndpointTypes = new Set(['anthropic', 'openai', 'comm'])
}

View File

@@ -2,7 +2,12 @@ const droidAccountService = require('./droidAccountService')
const accountGroupService = require('./accountGroupService')
const redis = require('../models/redis')
const logger = require('../utils/logger')
const { isTruthy, isAccountHealthy, sortAccountsByPriority, normalizeEndpointType } = require('../utils/commonHelper')
const {
isTruthy,
isAccountHealthy,
sortAccountsByPriority,
normalizeEndpointType
} = require('../utils/commonHelper')
class DroidScheduler {
constructor() {
@@ -16,8 +21,12 @@ class DroidScheduler {
_matchesEndpoint(account, endpointType) {
const normalizedEndpoint = normalizeEndpointType(endpointType)
const accountEndpoint = normalizeEndpointType(account?.endpointType)
if (normalizedEndpoint === accountEndpoint) return true
if (normalizedEndpoint === 'comm') return true
if (normalizedEndpoint === accountEndpoint) {
return true
}
if (normalizedEndpoint === 'comm') {
return true
}
const sharedEndpoints = new Set(['anthropic', 'openai'])
return sharedEndpoints.has(normalizedEndpoint) && sharedEndpoints.has(accountEndpoint)
}

View File

@@ -1,7 +1,6 @@
const redisClient = require('../models/redis')
const { v4: uuidv4 } = require('uuid')
const https = require('https')
const config = require('../../config/config')
const logger = require('../utils/logger')
const { OAuth2Client } = require('google-auth-library')
const { maskToken } = require('../utils/tokenMask')
@@ -18,6 +17,8 @@ const { createEncryptor } = require('../utils/commonHelper')
// Gemini 账户键前缀
const GEMINI_ACCOUNT_KEY_PREFIX = 'gemini_account:'
const SHARED_GEMINI_ACCOUNTS_KEY = 'shared_gemini_accounts'
const ACCOUNT_SESSION_MAPPING_PREFIX = 'gemini_session_account_mapping:'
// Gemini CLI OAuth 配置 - 这些是公开的 Gemini CLI 凭据
const OAUTH_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'
@@ -572,7 +573,7 @@ async function deleteAccount(accountId) {
// 获取所有账户
async function getAllAccounts() {
const client = redisClient.getClientSafe()
const _client = redisClient.getClientSafe()
const accountIds = await redisClient.getAllIdsByIndex(
'gemini_account:index',
`${GEMINI_ACCOUNT_KEY_PREFIX}*`,

View File

@@ -666,7 +666,7 @@ async function deleteAccount(accountId) {
// 获取所有账户
async function getAllAccounts() {
const client = redisClient.getClientSafe()
const _client = redisClient.getClientSafe()
const accountIds = await redisClient.getAllIdsByIndex(
'openai:account:index',
`${OPENAI_ACCOUNT_KEY_PREFIX}*`,

View File

@@ -201,7 +201,9 @@ class OpenAIResponsesAccountService {
`${this.ACCOUNT_KEY_PREFIX}*`,
/^openai_responses_account:(.+)$/
)
if (accountIds.length === 0) return []
if (accountIds.length === 0) {
return []
}
const keys = accountIds.map((id) => `${this.ACCOUNT_KEY_PREFIX}${id}`)
// Pipeline 批量查询所有账户数据
@@ -210,11 +212,15 @@ class OpenAIResponsesAccountService {
const results = await pipeline.exec()
const accounts = []
results.forEach(([err, accountData], index) => {
if (err || !accountData || !accountData.id) return
results.forEach(([err, accountData]) => {
if (err || !accountData || !accountData.id) {
return
}
// 过滤非活跃账户
if (!includeInactive && accountData.isActive !== 'true') return
if (!includeInactive && accountData.isActive !== 'true') {
return
}
// 隐藏敏感信息
accountData.apiKey = '***'

View File

@@ -26,11 +26,7 @@ class UnifiedGeminiScheduler {
if (apiKeyData.geminiAccountId.startsWith('api:')) {
const accountId = apiKeyData.geminiAccountId.replace('api:', '')
const boundAccount = await geminiApiAccountService.getAccount(accountId)
if (
boundAccount &&
isActive(boundAccount.isActive) &&
boundAccount.status !== 'error'
) {
if (boundAccount && isActive(boundAccount.isActive) && boundAccount.status !== 'error') {
logger.info(
`🎯 Using bound Gemini-API account: ${boundAccount.name} (${accountId}) for API key ${apiKeyData.name}`
)
@@ -63,11 +59,7 @@ class UnifiedGeminiScheduler {
// 普通 Gemini OAuth 专属账户
else {
const boundAccount = await geminiAccountService.getAccount(apiKeyData.geminiAccountId)
if (
boundAccount &&
isActive(boundAccount.isActive) &&
boundAccount.status !== 'error'
) {
if (boundAccount && isActive(boundAccount.isActive) && boundAccount.status !== 'error') {
logger.info(
`🎯 Using bound dedicated Gemini account: ${boundAccount.name} (${apiKeyData.geminiAccountId}) for API key ${apiKeyData.name}`
)
@@ -183,11 +175,7 @@ class UnifiedGeminiScheduler {
if (apiKeyData.geminiAccountId.startsWith('api:')) {
const accountId = apiKeyData.geminiAccountId.replace('api:', '')
const boundAccount = await geminiApiAccountService.getAccount(accountId)
if (
boundAccount &&
isActive(boundAccount.isActive) &&
boundAccount.status !== 'error'
) {
if (boundAccount && isActive(boundAccount.isActive) && boundAccount.status !== 'error') {
const isRateLimited = await this.isAccountRateLimited(accountId)
if (!isRateLimited) {
// 检查模型支持
@@ -234,11 +222,7 @@ class UnifiedGeminiScheduler {
// 普通 Gemini OAuth 账户
else if (!apiKeyData.geminiAccountId.startsWith('group:')) {
const boundAccount = await geminiAccountService.getAccount(apiKeyData.geminiAccountId)
if (
boundAccount &&
isActive(boundAccount.isActive) &&
boundAccount.status !== 'error'
) {
if (boundAccount && isActive(boundAccount.isActive) && boundAccount.status !== 'error') {
const isRateLimited = await this.isAccountRateLimited(boundAccount.id)
if (!isRateLimited) {
// 检查模型支持

View File

@@ -56,9 +56,9 @@ class UnifiedOpenAIScheduler {
let rateLimitChecked = false
let stillLimited = false
let isSchedulable = isSchedulable(account.schedulable)
const accountSchedulable = isSchedulable(account.schedulable)
if (!isSchedulable) {
if (!accountSchedulable) {
if (!hasRateLimitFlag) {
return { canUse: false, reason: 'not_schedulable' }
}
@@ -75,7 +75,6 @@ class UnifiedOpenAIScheduler {
} else {
account.schedulable = 'true'
}
isSchedulable = true
logger.info(`✅ OpenAI账号 ${account.name || accountId} 已解除限流,恢复调度权限`)
}