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

@@ -183,7 +183,10 @@ class DroidAccountService {
? []
: normalizedExisting
.filter((entry) => entry && entry.id && entry.encryptedKey)
.map((entry) => ({ ...entry }))
.map((entry) => ({
...entry,
status: entry.status || 'active' // 确保有默认状态
}))
const hashSet = new Set(entries.map((entry) => entry.hash).filter(Boolean))
@@ -214,7 +217,9 @@ class DroidAccountService {
encryptedKey: this._encryptSensitiveData(trimmed),
createdAt: now,
lastUsedAt: '',
usageCount: '0'
usageCount: '0',
status: 'active', // 新增状态字段
errorMessage: '' // 新增错误信息字段
})
}
@@ -230,7 +235,9 @@ class DroidAccountService {
id: entry.id,
createdAt: entry.createdAt || '',
lastUsedAt: entry.lastUsedAt || '',
usageCount: entry.usageCount || '0'
usageCount: entry.usageCount || '0',
status: entry.status || 'active', // 新增状态字段
errorMessage: entry.errorMessage || '' // 新增错误信息字段
}))
}
@@ -252,7 +259,9 @@ class DroidAccountService {
hash: entry.hash || '',
createdAt: entry.createdAt || '',
lastUsedAt: entry.lastUsedAt || '',
usageCount: Number.isFinite(usageCountNumber) && usageCountNumber >= 0 ? usageCountNumber : 0
usageCount: Number.isFinite(usageCountNumber) && usageCountNumber >= 0 ? usageCountNumber : 0,
status: entry.status || 'active', // 新增状态字段
errorMessage: entry.errorMessage || '' // 新增错误信息字段
}
}
@@ -348,6 +357,56 @@ class DroidAccountService {
}
}
/**
* 标记指定的 Droid API Key 条目为异常状态
*/
async markApiKeyAsError(accountId, keyId, errorMessage = '') {
if (!accountId || !keyId) {
return { marked: false, error: '参数无效' }
}
try {
const accountData = await redis.getDroidAccount(accountId)
if (!accountData) {
return { marked: false, error: '账户不存在' }
}
const entries = this._parseApiKeyEntries(accountData.apiKeys)
if (!entries || entries.length === 0) {
return { marked: false, error: '无API Key条目' }
}
let marked = false
const updatedEntries = entries.map((entry) => {
if (entry && entry.id === keyId) {
marked = true
return {
...entry,
status: 'error',
errorMessage: errorMessage || 'API Key异常'
}
}
return entry
})
if (!marked) {
return { marked: false, error: '未找到指定的API Key' }
}
accountData.apiKeys = JSON.stringify(updatedEntries)
await redis.setDroidAccount(accountId, accountData)
logger.warn(
`⚠️ 已标记 Droid API Key ${keyId} 为异常状态Account: ${accountId}${errorMessage}`
)
return { marked: true }
} catch (error) {
logger.error(`❌ 标记 Droid API Key 异常状态失败:${keyId}Account: ${accountId}`, error)
return { marked: false, error: error.message }
}
}
/**
* 使用 WorkOS Refresh Token 刷新并验证凭证
*/
@@ -979,7 +1038,7 @@ class DroidAccountService {
? updates.apiKeyUpdateMode.trim().toLowerCase()
: ''
let apiKeyUpdateMode = ['append', 'replace', 'delete'].includes(rawApiKeyMode)
let apiKeyUpdateMode = ['append', 'replace', 'delete', 'update'].includes(rawApiKeyMode)
? rawApiKeyMode
: ''
@@ -1041,6 +1100,53 @@ class DroidAccountService {
} else if (removeApiKeysInput.length > 0) {
logger.warn(`⚠️ 删除模式未收到有效的 Droid API Key: ${accountId}`)
}
} else if (apiKeyUpdateMode === 'update') {
// 更新模式:根据提供的 key 匹配现有条目并更新状态
mergedApiKeys = [...existingApiKeyEntries]
const updatedHashes = new Set()
for (const updateItem of newApiKeysInput) {
if (!updateItem || typeof updateItem !== 'object') {
continue
}
const key = updateItem.key || updateItem.apiKey || ''
if (!key || typeof key !== 'string') {
continue
}
const trimmed = key.trim()
if (!trimmed) {
continue
}
const hash = crypto.createHash('sha256').update(trimmed).digest('hex')
updatedHashes.add(hash)
// 查找现有条目
const existingIndex = mergedApiKeys.findIndex(
(entry) => entry && entry.hash === hash
)
if (existingIndex !== -1) {
// 更新现有条目的状态信息
const existingEntry = mergedApiKeys[existingIndex]
mergedApiKeys[existingIndex] = {
...existingEntry,
status: updateItem.status || existingEntry.status || 'active',
errorMessage: updateItem.errorMessage !== undefined ? updateItem.errorMessage : existingEntry.errorMessage || '',
lastUsedAt: updateItem.lastUsedAt !== undefined ? updateItem.lastUsedAt : existingEntry.lastUsedAt || '',
usageCount: updateItem.usageCount !== undefined ? String(updateItem.usageCount) : existingEntry.usageCount || '0'
}
apiKeysUpdated = true
}
}
if (!apiKeysUpdated) {
logger.warn(
`⚠️ 更新模式未匹配任何 Droid API Key: ${accountId} (提供 ${updatedHashes.size} 个哈希)`
)
}
} else {
const clearExisting = apiKeyUpdateMode === 'replace' || wantsClearApiKeys
const baselineCount = clearExisting ? 0 : existingApiKeyEntries.length
@@ -1063,6 +1169,10 @@ class DroidAccountService {
logger.info(
`🔑 删除模式更新 Droid API keys for ${accountId}: 已移除 ${removedCount} 条,剩余 ${mergedApiKeys.length}`
)
} else if (apiKeyUpdateMode === 'update') {
logger.info(
`🔑 更新模式更新 Droid API keys for ${accountId}: 更新了 ${newApiKeysInput.length} 个 API Key 的状态信息`
)
} else if (apiKeyUpdateMode === 'replace' || wantsClearApiKeys) {
logger.info(
`🔑 覆盖模式更新 Droid API keys for ${accountId}: 当前总数 ${mergedApiKeys.length},新增 ${addedCount}`