From 8863075fdee0e701a4a5937bf5f0519c7c1faef7 Mon Sep 17 00:00:00 2001 From: shaw Date: Sun, 23 Nov 2025 22:28:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84Gemini-Api=E8=B4=A6?= =?UTF-8?q?=E6=88=B7=E7=9B=B8=E5=85=B3=E7=9A=84=E6=95=B0=E6=8D=AE=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/admin.js | 36 ++++++++++++++++++------- src/services/apiKeyService.js | 13 +++++++++ web/admin-spa/src/views/ApiKeysView.vue | 6 ++--- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/routes/admin.js b/src/routes/admin.js index 3cca87ed..be522c8d 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -5842,16 +5842,32 @@ router.get('/account-usage-trend', authenticateAdmin, async (req, res) => { }) ] } else if (group === 'gemini') { - const geminiAccounts = await geminiAccountService.getAllAccounts() - accounts = geminiAccounts.map((account) => { - const id = String(account.id || '') - const shortId = id ? id.slice(0, 8) : '未知' - return { - id, - name: account.name || account.email || `Gemini账号 ${shortId}`, - platform: 'gemini' - } - }) + const geminiApiAccountService = require('../services/geminiApiAccountService') + const [geminiAccounts, geminiApiAccounts] = await Promise.all([ + geminiAccountService.getAllAccounts(), + geminiApiAccountService.getAllAccounts(true) + ]) + + accounts = [ + ...geminiAccounts.map((account) => { + const id = String(account.id || '') + const shortId = id ? id.slice(0, 8) : '未知' + return { + id, + name: account.name || account.email || `Gemini账号 ${shortId}`, + platform: 'gemini' + } + }), + ...geminiApiAccounts.map((account) => { + const id = String(account.id || '') + const shortId = id ? id.slice(0, 8) : '未知' + return { + id, + name: account.name || `Gemini-API账号 ${shortId}`, + platform: 'gemini-api' + } + }) + ] } else if (group === 'droid') { const droidAccounts = await droidAccountService.getAllAccounts() accounts = droidAccounts.map((account) => { diff --git a/src/services/apiKeyService.js b/src/services/apiKeyService.js index 98fb2eaf..c7047128 100644 --- a/src/services/apiKeyService.js +++ b/src/services/apiKeyService.js @@ -11,6 +11,7 @@ const ACCOUNT_TYPE_CONFIG = { 'openai-responses': { prefix: 'openai_responses_account:' }, 'azure-openai': { prefix: 'azure_openai:account:' }, gemini: { prefix: 'gemini_account:' }, + 'gemini-api': { prefix: 'gemini_api_account:' }, droid: { prefix: 'droid:account:' } } @@ -21,6 +22,7 @@ const ACCOUNT_TYPE_PRIORITY = [ 'claude', 'claude-console', 'gemini', + 'gemini-api', 'droid' ] @@ -31,6 +33,7 @@ const ACCOUNT_CATEGORY_MAP = { 'openai-responses': 'openai', 'azure-openai': 'openai', gemini: 'gemini', + 'gemini-api': 'gemini', droid: 'droid' } @@ -48,6 +51,9 @@ function normalizeAccountTypeKey(type) { if (lower === 'azure_openai' || lower === 'azureopenai' || lower === 'azure-openai') { return 'azure-openai' } + if (lower === 'gemini_api' || lower === 'gemini-api') { + return 'gemini-api' + } return lower } @@ -58,6 +64,9 @@ function sanitizeAccountIdForType(accountId, accountType) { if (accountType === 'openai-responses') { return accountId.replace(/^responses:/, '') } + if (accountType === 'gemini-api') { + return accountId.replace(/^api:/, '') + } return accountId } @@ -1322,6 +1331,9 @@ class ApiKeyService { if (typeof rawAccountId === 'string' && rawAccountId.startsWith('responses:')) { candidateIds.add(rawAccountId.replace(/^responses:/, '')) } + if (typeof rawAccountId === 'string' && rawAccountId.startsWith('api:')) { + candidateIds.add(rawAccountId.replace(/^api:/, '')) + } } if (candidateIds.size === 0) { @@ -1346,6 +1358,7 @@ class ApiKeyService { pushType('azure-openai') } else if (lowerModel.includes('gemini')) { pushType('gemini') + pushType('gemini-api') } else if (lowerModel.includes('claude') || lowerModel.includes('anthropic')) { pushType('claude') pushType('claude-console') diff --git a/web/admin-spa/src/views/ApiKeysView.vue b/web/admin-spa/src/views/ApiKeysView.vue index d7b68f7d..20db79a2 100644 --- a/web/admin-spa/src/views/ApiKeysView.vue +++ b/web/admin-spa/src/views/ApiKeysView.vue @@ -3814,7 +3814,7 @@ const normalizeFrontendAccountCategory = (type) => { ) { return 'openai' } - if (lower === 'gemini') { + if (lower === 'gemini' || lower === 'gemini-api' || lower === 'gemini_api') { return 'gemini' } if (lower === 'droid') { @@ -3843,8 +3843,8 @@ const isLikelyDeletedUsage = (info) => { const looksLikeUuid = UUID_PATTERN.test(rawId) const nameMissingOrSame = !accountName || accountName === rawId - const typeUnknown = - !accountType || accountType === 'unknown' || ACCOUNT_TYPE_LABELS[accountType] === undefined + const normalizedType = normalizeFrontendAccountCategory(accountType) + const typeUnknown = !accountType || accountType === 'unknown' || normalizedType === 'other' return looksLikeUuid && nameMissingOrSame && typeUnknown }