From e0500f0530a05ed9bb444874b2d20e509a1960be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E5=BA=86=E9=9B=B7?= Date: Tue, 25 Nov 2025 19:06:55 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9AGemini=20OAuth?= =?UTF-8?q?=20=E8=B4=A6=E6=88=B7=20projectId=20=E9=99=8D=E7=BA=A7=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E7=BC=BA=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 3 个端点未使用 tempProjectId 的问题: - /messages - /v1internal:generateContent - /v1internal:streamGenerateContent 优先级链:projectId -> tempProjectId -> 请求参数 -> null --- src/handlers/geminiHandlers.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/handlers/geminiHandlers.js b/src/handlers/geminiHandlers.js index 1e9a3968..7a95c0da 100644 --- a/src/handlers/geminiHandlers.js +++ b/src/handlers/geminiHandlers.js @@ -417,6 +417,9 @@ async function handleMessages(req, res) { } } else { // OAuth 账户:使用现有的 sendGeminiRequest + // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId + const effectiveProjectId = account.projectId || account.tempProjectId || null + geminiResponse = await sendGeminiRequest({ messages, model, @@ -427,7 +430,7 @@ async function handleMessages(req, res) { proxy: account.proxy, apiKeyId: apiKeyData.id, signal: abortController.signal, - projectId: account.projectId, + projectId: effectiveProjectId, accountId: account.id }) } @@ -1101,14 +1104,21 @@ async function handleGenerateContent(req, res) { const client = await geminiAccountService.getOauthClient(accessToken, refreshToken, proxyConfig) - // 智能处理项目ID - const effectiveProjectId = account.projectId || project || null + // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId,最后使用请求参数 + const effectiveProjectId = account.projectId || account.tempProjectId || project || null logger.info('📋 项目ID处理逻辑', { accountProjectId: account.projectId, + accountTempProjectId: account.tempProjectId, requestProjectId: project, effectiveProjectId, - decision: account.projectId ? '使用账户配置' : project ? '使用请求参数' : '不使用项目ID' + decision: account.projectId + ? '使用账户配置' + : account.tempProjectId + ? '使用临时项目ID' + : project + ? '使用请求参数' + : '不使用项目ID' }) const response = await geminiAccountService.generateContent( @@ -1281,14 +1291,21 @@ async function handleStreamGenerateContent(req, res) { const client = await geminiAccountService.getOauthClient(accessToken, refreshToken, proxyConfig) - // 智能处理项目ID - const effectiveProjectId = account.projectId || project || null + // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId,最后使用请求参数 + const effectiveProjectId = account.projectId || account.tempProjectId || project || null logger.info('📋 流式请求项目ID处理逻辑', { accountProjectId: account.projectId, + accountTempProjectId: account.tempProjectId, requestProjectId: project, effectiveProjectId, - decision: account.projectId ? '使用账户配置' : project ? '使用请求参数' : '不使用项目ID' + decision: account.projectId + ? '使用账户配置' + : account.tempProjectId + ? '使用临时项目ID' + : project + ? '使用请求参数' + : '不使用项目ID' }) const streamResponse = await geminiAccountService.generateContentStream( From b61920897012209b59433e7b19528683072ec7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E5=BA=86=E9=9B=B7?= Date: Tue, 25 Nov 2025 19:32:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E8=AF=B7=E6=B1=82=E5=8F=82=E6=95=B0=20projectId=20?= =?UTF-8?q?=E9=99=8D=E7=BA=A7=EF=BC=8C=E6=94=B9=E4=B8=BA=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根本原因:请求参数中的 projectId 是客户端缓存的,属于之前账户, 导致账户切换后使用错误的 projectId,返回 403 权限错误。 修改内容: 1. 移除对 request.project 的降级依赖 2. 当账户无 projectId 和 tempProjectId 时,实时调用 loadCodeAssist 3. 获取后缓存到 tempProjectId 供后续请求使用 4. 如果仍无法获取,返回 403 配置错误 影响端点: - /v1internal:generateContent - /v1internal:streamGenerateContent --- src/handlers/geminiHandlers.js | 74 ++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/src/handlers/geminiHandlers.js b/src/handlers/geminiHandlers.js index 7a95c0da..8acd4c86 100644 --- a/src/handlers/geminiHandlers.js +++ b/src/handlers/geminiHandlers.js @@ -1104,21 +1104,46 @@ async function handleGenerateContent(req, res) { const client = await geminiAccountService.getOauthClient(accessToken, refreshToken, proxyConfig) - // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId,最后使用请求参数 - const effectiveProjectId = account.projectId || account.tempProjectId || project || null + // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId + let effectiveProjectId = account.projectId || account.tempProjectId || null + + // 如果没有任何项目ID,尝试调用 loadCodeAssist 获取 + if (!effectiveProjectId) { + try { + logger.info('📋 No projectId available, attempting to fetch from loadCodeAssist...') + const loadResponse = await geminiAccountService.loadCodeAssist(client, null, proxyConfig) + + if (loadResponse.cloudaicompanionProject) { + effectiveProjectId = loadResponse.cloudaicompanionProject + // 保存临时项目ID + await geminiAccountService.updateTempProjectId(accountId, effectiveProjectId) + logger.info(`📋 Fetched and cached temporary projectId: ${effectiveProjectId}`) + } + } catch (loadError) { + logger.warn('Failed to fetch projectId from loadCodeAssist:', loadError.message) + } + } + + // 如果还是没有项目ID,返回错误 + if (!effectiveProjectId) { + return res.status(403).json({ + error: { + message: + 'This account requires a project ID to be configured. Please configure a project ID in the account settings.', + type: 'configuration_required' + } + }) + } logger.info('📋 项目ID处理逻辑', { accountProjectId: account.projectId, accountTempProjectId: account.tempProjectId, - requestProjectId: project, effectiveProjectId, decision: account.projectId ? '使用账户配置' : account.tempProjectId ? '使用临时项目ID' - : project - ? '使用请求参数' - : '不使用项目ID' + : '从loadCodeAssist获取' }) const response = await geminiAccountService.generateContent( @@ -1291,21 +1316,46 @@ async function handleStreamGenerateContent(req, res) { const client = await geminiAccountService.getOauthClient(accessToken, refreshToken, proxyConfig) - // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId,最后使用请求参数 - const effectiveProjectId = account.projectId || account.tempProjectId || project || null + // 智能处理项目ID:优先使用配置的 projectId,降级到临时 tempProjectId + let effectiveProjectId = account.projectId || account.tempProjectId || null + + // 如果没有任何项目ID,尝试调用 loadCodeAssist 获取 + if (!effectiveProjectId) { + try { + logger.info('📋 No projectId available, attempting to fetch from loadCodeAssist...') + const loadResponse = await geminiAccountService.loadCodeAssist(client, null, proxyConfig) + + if (loadResponse.cloudaicompanionProject) { + effectiveProjectId = loadResponse.cloudaicompanionProject + // 保存临时项目ID + await geminiAccountService.updateTempProjectId(accountId, effectiveProjectId) + logger.info(`📋 Fetched and cached temporary projectId: ${effectiveProjectId}`) + } + } catch (loadError) { + logger.warn('Failed to fetch projectId from loadCodeAssist:', loadError.message) + } + } + + // 如果还是没有项目ID,返回错误 + if (!effectiveProjectId) { + return res.status(403).json({ + error: { + message: + 'This account requires a project ID to be configured. Please configure a project ID in the account settings.', + type: 'configuration_required' + } + }) + } logger.info('📋 流式请求项目ID处理逻辑', { accountProjectId: account.projectId, accountTempProjectId: account.tempProjectId, - requestProjectId: project, effectiveProjectId, decision: account.projectId ? '使用账户配置' : account.tempProjectId ? '使用临时项目ID' - : project - ? '使用请求参数' - : '不使用项目ID' + : '从loadCodeAssist获取' }) const streamResponse = await geminiAccountService.generateContentStream(