From e5bef7e2b036cbd40f8ff933126a79c234e30603 Mon Sep 17 00:00:00 2001 From: Supra4E8C <69194597+LTbinglingfeng@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:29:48 +0800 Subject: [PATCH] Add files via upload --- app.js | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++- i18n.js | 24 ++++++ index.html | 33 +++++++ styles.css | 41 +++++++++ 4 files changed, 341 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 7b43c3b..a0b95e9 100644 --- a/app.js +++ b/app.js @@ -38,7 +38,6 @@ class CLIProxyManager { if (savedTheme && ['light', 'dark'].includes(savedTheme)) { this.currentTheme = savedTheme; } else { - // 根据系统偏好自动选择 if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { this.currentTheme = 'dark'; } else { @@ -586,6 +585,13 @@ class CLIProxyManager { addOpenaiProvider.addEventListener('click', () => this.showAddOpenAIProviderModal()); } + + // Gemini Web Token + const geminiWebTokenBtn = document.getElementById('gemini-web-token-btn'); + if (geminiWebTokenBtn) { + geminiWebTokenBtn.addEventListener('click', () => this.saveGeminiWebTokenDirect()); + } + // 认证文件管理 const uploadAuthFile = document.getElementById('upload-auth-file'); const deleteAllAuthFiles = document.getElementById('delete-all-auth-files'); @@ -726,8 +732,7 @@ class CLIProxyManager { this.managementKey = savedKey; } - // 注意:不再处理DOM元素,因为认证配置已改为只读显示 - // DOM更新由updateConnectionInfo()方法处理 + } // API 请求方法 @@ -2187,6 +2192,241 @@ class CLIProxyManager { } } + // === OAuth 认证方法 === + + // 显示/隐藏 Gemini CLI 项目表单 + showGeminiCliProjectForm() { + document.getElementById('gemini-cli-project-group').style.display = 'block'; + } + + hideGeminiCliProjectForm() { + document.getElementById('gemini-cli-project-group').style.display = 'none'; + document.getElementById('gemini-cli-project-id').value = ''; + } + + // 开始 Anthropic (Claude) OAuth 认证 + async startAnthropicAuth() { + try { + const response = await this.makeRequest('/anthropic-auth-url'); + this.handleOAuthFlow(response.url, 'Anthropic (Claude)'); + } catch (error) { + this.showNotification(`启动 Anthropic 认证失败: ${error.message}`, 'error'); + } + } + + // 开始 Codex OAuth 认证 + async startCodexAuth() { + try { + const response = await this.makeRequest('/codex-auth-url'); + this.handleOAuthFlow(response.url, 'Codex'); + } catch (error) { + this.showNotification(`启动 Codex 认证失败: ${error.message}`, 'error'); + } + } + + // 开始 Gemini CLI OAuth 认证 + async startGeminiCliAuth() { + try { + const projectId = document.getElementById('gemini-cli-project-id').value.trim(); + let url = '/gemini-cli-auth-url'; + if (projectId) { + url += `?project_id=${encodeURIComponent(projectId)}`; + } + + const response = await this.makeRequest(url); + this.hideGeminiCliProjectForm(); + this.handleOAuthFlow(response.url, 'Gemini CLI'); + } catch (error) { + this.showNotification(`启动 Gemini CLI 认证失败: ${error.message}`, 'error'); + } + } + + + // 处理 OAuth 流程 + handleOAuthFlow(authUrl, providerName) { + // 从 URL 中提取 state 参数 + const url = new URL(authUrl); + const state = url.searchParams.get('state'); + + // 显示 OAuth 状态模态框 + this.showOAuthStatusModal(authUrl, providerName, state); + } + + // 显示 OAuth 状态模态框 + showOAuthStatusModal(authUrl, providerName, state) { + const modalBody = document.getElementById('modal-body'); + modalBody.innerHTML = ` +
+
+ +
+

${i18n.t('auth_login.oauth_in_progress')}

+

${providerName} ${i18n.t('auth_login.oauth_open_browser')}

+ +
+ ${authUrl} +
+ +
+ + +
+ +

+ ${i18n.t('auth_login.oauth_waiting')} +

+
+ `; + + this.showModal(); + + // 如果有 state,开始轮询认证状态 + if (state) { + this.pollOAuthStatus(state, providerName); + } + } + + // 轮询 OAuth 认证状态 + async pollOAuthStatus(state, providerName) { + const maxAttempts = 60; // 最多轮询 5 分钟(每 5 秒一次) + let attempts = 0; + + const poll = async () => { + attempts++; + + try { + const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`); + + if (response.status === 'ok') { + // 认证成功 + this.showOAuthSuccess(providerName); + this.loadAuthFiles(); // 刷新认证文件列表 + return; + } else if (response.status === 'error') { + // 认证失败 + this.showOAuthError(response.error || '认证失败'); + return; + } else if (response.status === 'wait') { + // 继续等待 + if (attempts < maxAttempts) { + setTimeout(poll, 5000); // 5 秒后重试 + } else { + this.showOAuthError(i18n.t('auth_login.oauth_timeout')); + } + } + } catch (error) { + console.error('OAuth 状态轮询失败:', error); + if (attempts < maxAttempts) { + setTimeout(poll, 5000); // 5 秒后重试 + } else { + this.showOAuthError(`${i18n.t('auth_login.oauth_failed')}: ${error.message}`); + } + } + }; + + // 开始轮询 + setTimeout(poll, 2000); // 2 秒后开始第一次检查 + } + + // 显示 OAuth 认证成功 + showOAuthSuccess(providerName) { + const modalBody = document.getElementById('modal-body'); + modalBody.innerHTML = ` +
+
+ +
+

${i18n.t('auth_login.oauth_success')}

+

${providerName} 认证已完成!

+ +
+ +
+
+ `; + } + + // 显示 OAuth 认证失败 + showOAuthError(errorMessage) { + const modalBody = document.getElementById('modal-body'); + modalBody.innerHTML = ` +
+
+ +
+

${i18n.t('auth_login.oauth_failed')}

+

${errorMessage}

+ +
+ +
+
+ `; + } + + // 显示 Gemini Web Token 模态框 + showGeminiWebTokenModal() { + const modalBody = document.getElementById('modal-body'); + modalBody.innerHTML = ` +

${i18n.t('auth_login.gemini_web_button')}

+
+
+ + +
从浏览器开发者工具 → Application → Cookies 中获取
+
+
+ + +
从浏览器开发者工具 → Application → Cookies 中获取
+
+ +
+ `; + this.showModal(); + } + + // 保存 Gemini Web Token + async saveGeminiWebToken() { + const secure1psid = document.getElementById('modal-secure-1psid').value.trim(); + const secure1psidts = document.getElementById('modal-secure-1psidts').value.trim(); + + if (!secure1psid || !secure1psidts) { + this.showNotification('请填写完整的 Cookie 信息', 'error'); + return; + } + + try { + const response = await this.makeRequest('/gemini-web-token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + secure_1psid: secure1psid, + secure_1psidts: secure1psidts + }) + }); + + this.closeModal(); + this.loadAuthFiles(); // 刷新认证文件列表 + this.showNotification(`${i18n.t('auth_login.gemini_web_saved')}: ${response.file}`, 'success'); + } catch (error) { + this.showNotification(`保存失败: ${error.message}`, 'error'); + } + } + // 关闭模态框 closeModal() { document.getElementById('modal').style.display = 'none'; diff --git a/i18n.js b/i18n.js index 81746a1..b9ffa2d 100644 --- a/i18n.js +++ b/i18n.js @@ -182,9 +182,11 @@ const i18n = { 'ai_providers.openai_keys_count': '密钥数量', 'ai_providers.openai_models_count': '模型数量', + // 认证文件管理 'auth_files.title': '认证文件管理', 'auth_files.title_section': '认证文件', + 'auth_files.description': '这里管理 Qwen 和 Gemini 的认证配置文件。上传 JSON 格式的认证文件以启用相应的 AI 服务。', 'auth_files.upload_button': '上传文件', 'auth_files.delete_all_button': '删除全部', 'auth_files.empty_title': '暂无认证文件', @@ -202,6 +204,16 @@ const i18n = { 'auth_files.delete_all_success': '成功删除', 'auth_files.files_count': '个文件', + // Gemini Web Token + 'auth_login.gemini_web_title': 'Gemini Web Token', + 'auth_login.gemini_web_button': '保存 Gemini Web Token', + 'auth_login.gemini_web_hint': '从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。', + 'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:', + 'auth_login.secure_1psid_placeholder': '输入 __Secure-1PSID cookie 值', + 'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:', + 'auth_login.secure_1psidts_placeholder': '输入 __Secure-1PSIDTS cookie 值', + 'auth_login.gemini_web_saved': 'Gemini Web Token 保存成功', + // 系统信息 'system_info.title': '系统信息', 'system_info.connection_status_title': '连接状态', @@ -443,9 +455,11 @@ const i18n = { 'ai_providers.openai_keys_count': 'Keys Count', 'ai_providers.openai_models_count': 'Models Count', + // Auth files management 'auth_files.title': 'Auth Files Management', 'auth_files.title_section': 'Auth Files', + 'auth_files.description': 'Here you can manage authentication configuration files for Qwen and Gemini. Upload JSON format authentication files to enable the corresponding AI services.', 'auth_files.upload_button': 'Upload File', 'auth_files.delete_all_button': 'Delete All', 'auth_files.empty_title': 'No Auth Files', @@ -463,6 +477,16 @@ const i18n = { 'auth_files.delete_all_success': 'Successfully deleted', 'auth_files.files_count': 'files', + // Gemini Web Token + 'auth_login.gemini_web_title': 'Gemini Web Token', + 'auth_login.gemini_web_button': 'Save Gemini Web Token', + 'auth_login.gemini_web_hint': 'Obtain the Cookie value of the Gemini web version from the browser\'s developer tools, used for direct authentication to access Gemini.', + 'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:', + 'auth_login.secure_1psid_placeholder': 'Enter __Secure-1PSID cookie value', + 'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:', + 'auth_login.secure_1psidts_placeholder': 'Enter __Secure-1PSIDTS cookie value', + 'auth_login.gemini_web_saved': 'Gemini Web Token saved successfully', + // System info 'system_info.title': 'System Information', 'system_info.connection_status_title': 'Connection Status', diff --git a/index.html b/index.html index d191c13..9521276 100644 --- a/index.html +++ b/index.html @@ -394,12 +394,45 @@
+ + + +
+
+

Gemini Web Token

+ +
+
+

+ 从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。 +

+
+ + +
+
+ + +
+
+

认证文件管理

+
+
+

+ 这里管理 Qwen 和 Gemini 的认证配置文件。上传 JSON 格式的认证文件以启用相应的 AI 服务。 +

+
+
+ +

认证文件

diff --git a/styles.css b/styles.css index 203eacf..f819d0b 100644 --- a/styles.css +++ b/styles.css @@ -436,6 +436,7 @@ .local-url-group input[type="number"] { -moz-appearance: textfield; + appearance: textfield; } .login-title { @@ -1572,3 +1573,43 @@ input:checked + .slider:before { opacity: 1; } } + + +/* Gemini Web Token 模态框样式 */ +.gemini-web-form .form-group { + margin-bottom: 20px; +} + +.gemini-web-form .form-group label { + display: block; + margin-bottom: 8px; + color: var(--text-secondary); + font-weight: 600; + font-size: 14px; +} + +.gemini-web-form .form-group input { + width: 100%; + padding: 12px 16px; + border: 2px solid var(--border-primary); + border-radius: 8px; + font-size: 14px; + transition: all 0.3s ease; + background: var(--bg-tertiary); + color: var(--text-primary); + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; +} + +.gemini-web-form .form-group input:focus { + outline: none; + border-color: var(--border-focus); + box-shadow: 0 0 0 3px var(--border-primary); +} + +.gemini-web-form .form-hint { + margin-top: 6px; + color: var(--text-tertiary); + font-size: 12px; + line-height: 1.4; +} +