mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-24 09:41:17 +00:00
Revert: 撤销 584fa8c 之后的所有提交
This commit is contained in:
@@ -18,9 +18,7 @@ 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)
|
||||
@@ -41,14 +39,10 @@ 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) {
|
||||
|
||||
@@ -71,9 +71,7 @@ class ApiKeyIndexService {
|
||||
* 扫描所有 API Key,确保 hash -> keyId 映射存在
|
||||
*/
|
||||
async rebuildHashMap() {
|
||||
if (!this.redis) {
|
||||
return
|
||||
}
|
||||
if (!this.redis) return
|
||||
|
||||
try {
|
||||
const client = this.redis.getClientSafe()
|
||||
@@ -189,9 +187,7 @@ 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
|
||||
@@ -253,9 +249,7 @@ 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()
|
||||
@@ -303,9 +297,7 @@ class ApiKeyIndexService {
|
||||
* 更新索引(状态、名称、标签变化时调用)
|
||||
*/
|
||||
async updateIndex(keyId, updates, oldData = {}) {
|
||||
if (!this.redis || !keyId) {
|
||||
return
|
||||
}
|
||||
if (!this.redis || !keyId) return
|
||||
|
||||
try {
|
||||
const client = this.redis.getClientSafe()
|
||||
@@ -384,9 +376,7 @@ class ApiKeyIndexService {
|
||||
* 从索引中移除 API Key
|
||||
*/
|
||||
async removeFromIndex(keyId, oldData = {}) {
|
||||
if (!this.redis || !keyId) {
|
||||
return
|
||||
}
|
||||
if (!this.redis || !keyId) return
|
||||
|
||||
try {
|
||||
const client = this.redis.getClientSafe()
|
||||
@@ -608,9 +598,7 @@ class ApiKeyIndexService {
|
||||
* 更新 lastUsedAt 索引(供 recordUsage 调用)
|
||||
*/
|
||||
async updateLastUsedAt(keyId, lastUsedAt) {
|
||||
if (!this.redis || !keyId) {
|
||||
return
|
||||
}
|
||||
if (!this.redis || !keyId) return
|
||||
|
||||
try {
|
||||
const client = this.redis.getClientSafe()
|
||||
|
||||
@@ -921,9 +921,7 @@ 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,
|
||||
|
||||
@@ -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:*',
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 config = require('../../config/config')
|
||||
const { createEncryptor } = require('../utils/commonHelper')
|
||||
|
||||
class CcrAccountService {
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
|
||||
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() {
|
||||
|
||||
@@ -24,7 +24,9 @@ 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() {
|
||||
|
||||
@@ -94,9 +94,7 @@ 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
|
||||
@@ -157,9 +155,7 @@ class CostInitService {
|
||||
|
||||
for (let j = 0; j < results.length; j++) {
|
||||
const [err, values] = results[j]
|
||||
if (err) {
|
||||
continue
|
||||
}
|
||||
if (err) continue
|
||||
|
||||
// 将数组转换为对象
|
||||
const data = {}
|
||||
@@ -186,9 +182,7 @@ 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
|
||||
|
||||
@@ -307,9 +301,7 @@ 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) {
|
||||
@@ -327,9 +319,7 @@ class CostInitService {
|
||||
}
|
||||
}
|
||||
|
||||
if (samplesChecked >= maxSamples) {
|
||||
break
|
||||
}
|
||||
if (samplesChecked >= maxSamples) break
|
||||
} while (cursor !== '0')
|
||||
|
||||
logger.info('💰 Cost data appears to be up to date')
|
||||
|
||||
@@ -2,7 +2,7 @@ const { v4: uuidv4 } = require('uuid')
|
||||
const crypto = require('crypto')
|
||||
const axios = require('axios')
|
||||
const redis = require('../models/redis')
|
||||
// const config = require('../../config/config')
|
||||
const config = require('../../config/config')
|
||||
const logger = require('../utils/logger')
|
||||
const { maskToken } = require('../utils/tokenMask')
|
||||
const ProxyHelper = require('../utils/proxyHelper')
|
||||
@@ -30,13 +30,10 @@ 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'])
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@ 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() {
|
||||
@@ -21,12 +16,8 @@ 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)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const redisClient = require('../models/redis')
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
const https = require('https')
|
||||
// const config = require('../../config/config')
|
||||
const config = require('../../config/config')
|
||||
const logger = require('../utils/logger')
|
||||
const { OAuth2Client } = require('google-auth-library')
|
||||
const { maskToken } = require('../utils/tokenMask')
|
||||
@@ -19,8 +19,6 @@ 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'
|
||||
const OAUTH_CLIENT_SECRET = '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'
|
||||
@@ -574,7 +572,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}*`,
|
||||
|
||||
@@ -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}*`,
|
||||
|
||||
@@ -201,9 +201,7 @@ 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 批量查询所有账户数据
|
||||
@@ -212,15 +210,11 @@ class OpenAIResponsesAccountService {
|
||||
const results = await pipeline.exec()
|
||||
|
||||
const accounts = []
|
||||
results.forEach(([err, accountData], _index) => {
|
||||
if (err || !accountData || !accountData.id) {
|
||||
return
|
||||
}
|
||||
results.forEach(([err, accountData], index) => {
|
||||
if (err || !accountData || !accountData.id) return
|
||||
|
||||
// 过滤非活跃账户
|
||||
if (!includeInactive && accountData.isActive !== 'true') {
|
||||
return
|
||||
}
|
||||
if (!includeInactive && accountData.isActive !== 'true') return
|
||||
|
||||
// 隐藏敏感信息
|
||||
accountData.apiKey = '***'
|
||||
|
||||
@@ -26,7 +26,11 @@ 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}`
|
||||
)
|
||||
@@ -59,7 +63,11 @@ 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}`
|
||||
)
|
||||
@@ -175,7 +183,11 @@ 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) {
|
||||
// 检查模型支持
|
||||
@@ -222,7 +234,11 @@ 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) {
|
||||
// 检查模型支持
|
||||
|
||||
@@ -56,9 +56,9 @@ class UnifiedOpenAIScheduler {
|
||||
let rateLimitChecked = false
|
||||
let stillLimited = false
|
||||
|
||||
let _isSchedulable = isSchedulable(account.schedulable)
|
||||
let isSchedulable = isSchedulable(account.schedulable)
|
||||
|
||||
if (!_isSchedulable) {
|
||||
if (!isSchedulable) {
|
||||
if (!hasRateLimitFlag) {
|
||||
return { canUse: false, reason: 'not_schedulable' }
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class UnifiedOpenAIScheduler {
|
||||
} else {
|
||||
account.schedulable = 'true'
|
||||
}
|
||||
_isSchedulable = true
|
||||
isSchedulable = true
|
||||
logger.info(`✅ OpenAI账号 ${account.name || accountId} 已解除限流,恢复调度权限`)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user