feat: 添加Droid账户API Key管理功能

(cherry picked from commit 0cf3ca6c7eafcf28a2da7e8bfd6814b4883bb752)
This commit is contained in:
AAEE86
2025-10-13 18:19:17 +08:00
parent 268f041588
commit 1f9afc788b
6 changed files with 716 additions and 41 deletions

View File

@@ -121,12 +121,18 @@ class DroidRelayService {
throw new Error(`Droid account ${account.id} 未配置任何 API Key`)
}
// 过滤掉异常状态的API Key
const activeEntries = entries.filter((entry) => entry.status !== 'error')
if (!activeEntries || activeEntries.length === 0) {
throw new Error(`Droid account ${account.id} 没有可用的 API Key所有API Key均已异常`)
}
const stickyKey = this._composeApiKeyStickyKey(account.id, endpointType, sessionHash)
if (stickyKey) {
const mappedKeyId = await redis.getSessionAccountMapping(stickyKey)
if (mappedKeyId) {
const mappedEntry = entries.find((entry) => entry.id === mappedKeyId)
const mappedEntry = activeEntries.find((entry) => entry.id === mappedKeyId)
if (mappedEntry) {
await redis.extendSessionAccountMappingTTL(stickyKey)
await droidAccountService.touchApiKeyUsage(account.id, mappedEntry.id)
@@ -138,7 +144,7 @@ class DroidRelayService {
}
}
const selectedEntry = entries[Math.floor(Math.random() * entries.length)]
const selectedEntry = activeEntries[Math.floor(Math.random() * activeEntries.length)]
if (!selectedEntry) {
throw new Error(`Droid account ${account.id} 没有可用的 API Key`)
}
@@ -150,7 +156,7 @@ class DroidRelayService {
await droidAccountService.touchApiKeyUsage(account.id, selectedEntry.id)
logger.info(
`🔐 随机选取 Droid API Key ${selectedEntry.id}Account: ${account.id}, Keys: ${entries.length}`
`🔐 随机选取 Droid API Key ${selectedEntry.id}Account: ${account.id}, Active Keys: ${activeEntries.length}/${entries.length}`
)
return selectedEntry
@@ -1144,39 +1150,52 @@ class DroidRelayService {
if (authMethod === 'api_key') {
if (selectedAccountApiKey?.id) {
let removalResult = null
let markResult = null
const errorMessage = `上游返回 ${statusCode} 错误`
try {
removalResult = await droidAccountService.removeApiKeyEntry(
// 标记API Key为异常状态而不是删除
markResult = await droidAccountService.markApiKeyAsError(
accountId,
selectedAccountApiKey.id
selectedAccountApiKey.id,
errorMessage
)
} catch (error) {
logger.error(
`移除 Droid API Key ${selectedAccountApiKey.id}Account: ${accountId})失败:`,
`标记 Droid API Key ${selectedAccountApiKey.id} 异常状态Account: ${accountId})失败:`,
error
)
}
await this._clearApiKeyStickyMapping(accountId, normalizedEndpoint, sessionHash)
if (removalResult?.removed) {
if (markResult?.marked) {
logger.warn(
`🚫 上游返回 ${statusCode},已移除 Droid API Key ${selectedAccountApiKey.id}Account: ${accountId}`
`⚠️ 上游返回 ${statusCode},已标记 Droid API Key ${selectedAccountApiKey.id} 为异常状态Account: ${accountId}`
)
} else {
logger.warn(
`⚠️ 上游返回 ${statusCode},但未能移除 Droid API Key ${selectedAccountApiKey.id}Account: ${accountId}`
`⚠️ 上游返回 ${statusCode},但未能标记 Droid API Key ${selectedAccountApiKey.id} 异常状态Account: ${accountId}${markResult?.error || '未知错误'}`
)
}
if (!removalResult || removalResult.remainingCount === 0) {
await this._stopDroidAccountScheduling(accountId, statusCode, 'API Key 已全部失效')
// 检查是否还有可用的API Key
try {
const availableEntries = await droidAccountService.getDecryptedApiKeyEntries(accountId)
const activeEntries = availableEntries.filter(entry => entry.status !== 'error')
if (activeEntries.length === 0) {
await this._stopDroidAccountScheduling(accountId, statusCode, '所有API Key均已异常')
await this._clearAccountStickyMapping(normalizedEndpoint, sessionHash, clientApiKeyId)
} else {
logger.info(
` Droid 账号 ${accountId} 仍有 ${activeEntries.length} 个可用 API Key`
)
}
} catch (error) {
logger.error(`❌ 检查可用API Key失败Account: ${accountId}`, error)
await this._stopDroidAccountScheduling(accountId, statusCode, 'API Key检查失败')
await this._clearAccountStickyMapping(normalizedEndpoint, sessionHash, clientApiKeyId)
} else {
logger.info(
` Droid 账号 ${accountId} 仍有 ${removalResult.remainingCount} 个 API Key 可用`
)
}
return