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 @@
操作说明
- 点击下方的授权链接,在新页面中完成Google账号登录
- - 查看并授权所请求的权限
- - 授权完成后,页面会显示授权码
- - 复制授权码并粘贴到下方输入框中
+ - 点击"登录"按钮后可能会加载很慢(这是正常的)
+ - 如果超过1分钟还在加载,请按 F5 刷新页面
+ - 授权完成后会跳转到 http://localhost:45462 (可能显示无法访问)
+ - 复制浏览器地址栏的完整链接并粘贴到下方输入框
+
+
+ 提示:如果页面一直无法跳转,可以打开浏览器开发者工具(F12),F5刷新一下授权页再点击页面的登录按钮,在"网络"标签中找到以 localhost:45462 开头的请求,复制其完整URL。
+
@@ -2891,18 +2896,24 @@
-
-
- 授权完成后,从回调页面复制授权码并粘贴到此处
-
+
+
+
+ 支持粘贴完整链接,系统会自动提取授权码
+
+
+
+ 也可以直接粘贴授权码(code参数的值)
+
+