diff --git a/src/routes/admin.js b/src/routes/admin.js index f795ad48..5fe59cf5 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -406,10 +406,8 @@ router.post('/gemini-accounts/generate-auth-url', authenticateAdmin, async (req, try { const { state } = req.body; - // 构建 redirect_uri,使用当前服务的地址 - const protocol = req.protocol; - const host = req.get('host'); - const redirectUri = `${protocol}://${host}/web/auth_gemini`; + // 使用固定的 localhost:45462 作为回调地址 + const redirectUri = 'http://localhost:45462'; logger.info(`Generating Gemini OAuth URL with redirect_uri: ${redirectUri}`); @@ -420,7 +418,7 @@ router.post('/gemini-accounts/generate-auth-url', authenticateAdmin, async (req, await redis.setOAuthSession(sessionId, { state: authState, type: 'gemini', - redirectUri, // 保存 redirect_uri 用于 token 交换 + redirectUri: redirectUri, // 保存固定的 redirect_uri 用于 token 交换 createdAt: new Date().toISOString() }); @@ -470,15 +468,9 @@ router.post('/gemini-accounts/exchange-code', authenticateAdmin, async (req, res return res.status(400).json({ error: 'Authorization code is required' }); } - // 如果提供了 sessionId,从会话中获取 redirect_uri - let redirectUri = null; - if (sessionId) { - const oauthSession = await redis.getOAuthSession(sessionId); - if (oauthSession && oauthSession.redirectUri) { - redirectUri = oauthSession.redirectUri; - logger.info(`Using redirect_uri from session: ${redirectUri}`); - } - } + // 使用固定的 localhost:45462 作为 redirect_uri + const redirectUri = 'http://localhost:45462'; + logger.info(`Using fixed redirect_uri: ${redirectUri}`); const tokens = await geminiAccountService.exchangeCodeForTokens(code, redirectUri); diff --git a/src/routes/web.js b/src/routes/web.js index a1a01509..eac589b2 100644 --- a/src/routes/web.js +++ b/src/routes/web.js @@ -371,174 +371,5 @@ router.get('/style.css', (req, res) => { }); // 🔑 Gemini OAuth 回调页面 -router.get('/auth_gemini', (req, res) => { - try { - const code = req.query.code || ''; - const state = req.query.state || ''; - const error = req.query.error || ''; - const errorDescription = req.query.error_description || ''; - - // 简单的 HTML 页面,用于显示授权码 - const html = ` - - - - - - Gemini 授权回调 - - - -
- ${error ? ` -

授权失败

-
-

错误: ${error}

- ${errorDescription ? `

描述: ${errorDescription}

` : ''} -
-
-

请关闭此页面并返回管理界面重试。

-
- ` : ` -

授权成功

-

请复制下面的授权码:

-
- ${code} -
- - -
-

接下来的步骤:

-
1. 点击上方按钮复制授权码
-
2. 返回到管理界面的创建账户页面
-
3. 将授权码粘贴到"授权码"输入框中
-
4. 点击"使用授权码创建账户"按钮完成创建
-
- `} -
- - - - - `; - - res.setHeader('Content-Type', 'text/html; charset=utf-8'); - res.send(html); - - logger.info(`📄 Served Gemini OAuth callback page: ${error ? 'error' : 'success'}`); - } catch (error) { - logger.error('❌ Error serving Gemini OAuth callback:', error); - res.status(500).send('Internal server error'); - } -}); module.exports = router; \ No newline at end of file diff --git a/web/admin/app.js b/web/admin/app.js index 7e2f8ccc..0432e69d 100644 --- a/web/admin/app.js +++ b/web/admin/app.js @@ -336,6 +336,13 @@ const app = createApp({ this.loadCurrentTabData(); }, immediate: false + }, + 'geminiOauthData.code': { + handler(newValue) { + if (newValue) { + this.handleGeminiAuthCodeInput(newValue); + } + } } }, @@ -1110,6 +1117,47 @@ const app = createApp({ } }, + // 处理 Gemini OAuth 授权码输入 + handleGeminiAuthCodeInput(value, isUserTyping = false) { + if (!value || typeof value !== 'string') return; + + const trimmedValue = value.trim(); + + // 如果内容为空,不处理 + if (!trimmedValue) return; + + // 检查是否是 URL 格式(包含 http:// 或 https://) + const isUrl = trimmedValue.startsWith('http://') || trimmedValue.startsWith('https://'); + + // 如果是 URL 格式 + if (isUrl) { + // 检查是否是正确的 localhost:45462 开头的 URL + if (trimmedValue.startsWith('http://localhost:45462')) { + try { + const url = new URL(trimmedValue); + const code = url.searchParams.get('code'); + + if (code) { + // 成功提取授权码 + this.geminiOauthData.code = code; + this.showToast('成功提取授权码!', 'success', '提取成功'); + console.log('Successfully extracted authorization code from URL'); + } else { + // URL 中没有 code 参数 + this.showToast('URL 中未找到授权码参数,请检查链接是否正确', 'error', '提取失败'); + } + } catch (error) { + // URL 解析失败 + console.error('Failed to parse URL:', error); + this.showToast('链接格式错误,请检查是否为完整的 URL', 'error', '解析失败'); + } + } else { + // 错误的 URL(不是 localhost:45462 开头) + this.showToast('请粘贴以 http://localhost:45462 开头的链接', 'error', '链接错误'); + } + } + // 如果不是 URL,保持原值(兼容直接输入授权码) + }, // 根据当前标签页加载数据 loadCurrentTabData() { diff --git a/web/admin/index.html b/web/admin/index.html index 1163ec62..294ba9c0 100644 --- a/web/admin/index.html +++ b/web/admin/index.html @@ -2855,10 +2855,15 @@
操作说明
  1. 点击下方的授权链接,在新页面中完成Google账号登录
  2. -
  3. 查看并授权所请求的权限
  4. -
  5. 授权完成后,页面会显示授权码
  6. -
  7. 复制授权码并粘贴到下方输入框中
  8. +
  9. 点击"登录"按钮后可能会加载很慢(这是正常的)
  10. +
  11. 如果超过1分钟还在加载,请按 F5 刷新页面
  12. +
  13. 授权完成后会跳转到 http://localhost:45462 (可能显示无法访问)
  14. +
  15. 复制浏览器地址栏的完整链接并粘贴到下方输入框
+
+ + 提示:如果页面一直无法跳转,可以打开浏览器开发者工具(F12),F5刷新一下授权页再点击页面的登录按钮,在"网络"标签中找到以 localhost:45462 开头的请求,复制其完整URL。 +
@@ -2891,18 +2896,24 @@
-

- - 授权完成后,从回调页面复制授权码并粘贴到此处 -

+
+

+ + 支持粘贴完整链接,系统会自动提取授权码 +

+

+ + 也可以直接粘贴授权码(code参数的值) +

+