From ee0d8f82d72ab7f715892d8a28b00c5715d0cece Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 6 Oct 2025 01:48:17 +0800 Subject: [PATCH] Implement multiple OAuth functions, including Anthropic, Gemini CLI, Qwen and iFlow, add relevant UI components and styles, optimize user experience, and enhance the usability and feedback mechanism of the authentication process. --- app.js | 649 +++++++++++++++++++++++++++++++++++++++++++++++++++++ i18n.js | 110 +++++++++ index.html | 153 +++++++++++++ styles.css | 386 +++++++++++++++++++++++++++++++ 4 files changed, 1298 insertions(+) diff --git a/app.js b/app.js index 7123cab..ca0470e 100644 --- a/app.js +++ b/app.js @@ -533,6 +533,66 @@ class CLIProxyManager { codexCopyLink.addEventListener('click', () => this.copyCodexLink()); } + // Anthropic OAuth + const anthropicOauthBtn = document.getElementById('anthropic-oauth-btn'); + const anthropicOpenLink = document.getElementById('anthropic-open-link'); + const anthropicCopyLink = document.getElementById('anthropic-copy-link'); + + if (anthropicOauthBtn) { + anthropicOauthBtn.addEventListener('click', () => this.startAnthropicOAuth()); + } + if (anthropicOpenLink) { + anthropicOpenLink.addEventListener('click', () => this.openAnthropicLink()); + } + if (anthropicCopyLink) { + anthropicCopyLink.addEventListener('click', () => this.copyAnthropicLink()); + } + + // Gemini CLI OAuth + const geminiCliOauthBtn = document.getElementById('gemini-cli-oauth-btn'); + const geminiCliOpenLink = document.getElementById('gemini-cli-open-link'); + const geminiCliCopyLink = document.getElementById('gemini-cli-copy-link'); + + if (geminiCliOauthBtn) { + geminiCliOauthBtn.addEventListener('click', () => this.startGeminiCliOAuth()); + } + if (geminiCliOpenLink) { + geminiCliOpenLink.addEventListener('click', () => this.openGeminiCliLink()); + } + if (geminiCliCopyLink) { + geminiCliCopyLink.addEventListener('click', () => this.copyGeminiCliLink()); + } + + // Qwen OAuth + const qwenOauthBtn = document.getElementById('qwen-oauth-btn'); + const qwenOpenLink = document.getElementById('qwen-open-link'); + const qwenCopyLink = document.getElementById('qwen-copy-link'); + + if (qwenOauthBtn) { + qwenOauthBtn.addEventListener('click', () => this.startQwenOAuth()); + } + if (qwenOpenLink) { + qwenOpenLink.addEventListener('click', () => this.openQwenLink()); + } + if (qwenCopyLink) { + qwenCopyLink.addEventListener('click', () => this.copyQwenLink()); + } + + // iFlow OAuth + const iflowOauthBtn = document.getElementById('iflow-oauth-btn'); + const iflowOpenLink = document.getElementById('iflow-open-link'); + const iflowCopyLink = document.getElementById('iflow-copy-link'); + + if (iflowOauthBtn) { + iflowOauthBtn.addEventListener('click', () => this.startIflowOAuth()); + } + if (iflowOpenLink) { + iflowOpenLink.addEventListener('click', () => this.openIflowLink()); + } + if (iflowCopyLink) { + iflowCopyLink.addEventListener('click', () => this.copyIflowLink()); + } + // 使用统计 const refreshUsageStats = document.getElementById('refresh-usage-stats'); const requestsHourBtn = document.getElementById('requests-hour-btn'); @@ -2586,6 +2646,595 @@ class CLIProxyManager { } } + // ===== Anthropic OAuth 相关方法 ===== + + // 开始 Anthropic OAuth 流程 + async startAnthropicOAuth() { + try { + const response = await this.makeRequest('/anthropic-auth-url?is_webui=1'); + const authUrl = response.url; + const state = this.extractStateFromUrl(authUrl); + + // 显示授权链接 + const urlInput = document.getElementById('anthropic-oauth-url'); + const content = document.getElementById('anthropic-oauth-content'); + const status = document.getElementById('anthropic-oauth-status'); + + if (urlInput) { + urlInput.value = authUrl; + } + if (content) { + content.style.display = 'block'; + } + if (status) { + status.textContent = i18n.t('auth_login.anthropic_oauth_status_waiting'); + status.style.color = 'var(--warning-text)'; + } + + // 开始轮询认证状态 + this.startAnthropicOAuthPolling(state); + + } catch (error) { + this.showNotification(`${i18n.t('auth_login.anthropic_oauth_start_error')} ${error.message}`, 'error'); + } + } + + // 打开 Anthropic 授权链接 + openAnthropicLink() { + const urlInput = document.getElementById('anthropic-oauth-url'); + if (urlInput && urlInput.value) { + window.open(urlInput.value, '_blank'); + } + } + + // 复制 Anthropic 授权链接 + async copyAnthropicLink() { + const urlInput = document.getElementById('anthropic-oauth-url'); + if (urlInput && urlInput.value) { + try { + await navigator.clipboard.writeText(urlInput.value); + this.showNotification('链接已复制到剪贴板', 'success'); + } catch (error) { + // 降级方案:使用传统的复制方法 + urlInput.select(); + document.execCommand('copy'); + this.showNotification('链接已复制到剪贴板', 'success'); + } + } + } + + // 开始轮询 Anthropic OAuth 状态 + startAnthropicOAuthPolling(state) { + if (!state) { + this.showNotification('无法获取认证状态参数', 'error'); + return; + } + + const pollInterval = setInterval(async () => { + try { + const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`); + const status = response.status; + const statusElement = document.getElementById('anthropic-oauth-status'); + + if (status === 'ok') { + // 认证成功 + clearInterval(pollInterval); + // 隐藏授权链接相关内容,恢复到初始状态 + this.resetAnthropicOAuthUI(); + // 显示成功通知 + this.showNotification(i18n.t('auth_login.anthropic_oauth_status_success'), 'success'); + // 刷新认证文件列表 + this.loadAuthFiles(); + } else if (status === 'error') { + // 认证失败 + clearInterval(pollInterval); + const errorMessage = response.error || 'Unknown error'; + // 显示错误信息 + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.anthropic_oauth_status_error')} ${errorMessage}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.anthropic_oauth_status_error')} ${errorMessage}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetAnthropicOAuthUI(); + }, 3000); + } else if (status === 'wait') { + // 继续等待 + if (statusElement) { + statusElement.textContent = i18n.t('auth_login.anthropic_oauth_status_waiting'); + statusElement.style.color = 'var(--warning-text)'; + } + } + } catch (error) { + clearInterval(pollInterval); + const statusElement = document.getElementById('anthropic-oauth-status'); + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.anthropic_oauth_polling_error')} ${error.message}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.anthropic_oauth_polling_error')} ${error.message}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetAnthropicOAuthUI(); + }, 3000); + } + }, 2000); // 每2秒轮询一次 + + // 设置超时,5分钟后停止轮询 + setTimeout(() => { + clearInterval(pollInterval); + }, 5 * 60 * 1000); + } + + // 重置 Anthropic OAuth UI 到初始状态 + resetAnthropicOAuthUI() { + const urlInput = document.getElementById('anthropic-oauth-url'); + const content = document.getElementById('anthropic-oauth-content'); + const status = document.getElementById('anthropic-oauth-status'); + + // 清空并隐藏授权链接输入框 + if (urlInput) { + urlInput.value = ''; + } + + // 隐藏整个授权链接内容区域 + if (content) { + content.style.display = 'none'; + } + + // 清空状态显示 + if (status) { + status.textContent = ''; + status.style.color = ''; + status.className = ''; + } + } + + // ===== Gemini CLI OAuth 相关方法 ===== + + // 开始 Gemini CLI OAuth 流程 + async startGeminiCliOAuth() { + try { + // 获取项目 ID(可选) + const projectId = document.getElementById('gemini-cli-project-id').value.trim(); + + // 构建请求 URL + let requestUrl = '/gemini-cli-auth-url?is_webui=1'; + if (projectId) { + requestUrl += `&project_id=${encodeURIComponent(projectId)}`; + } + + const response = await this.makeRequest(requestUrl); + const authUrl = response.url; + const state = this.extractStateFromUrl(authUrl); + + // 显示授权链接 + const urlInput = document.getElementById('gemini-cli-oauth-url'); + const content = document.getElementById('gemini-cli-oauth-content'); + const status = document.getElementById('gemini-cli-oauth-status'); + + if (urlInput) { + urlInput.value = authUrl; + } + if (content) { + content.style.display = 'block'; + } + if (status) { + status.textContent = i18n.t('auth_login.gemini_cli_oauth_status_waiting'); + status.style.color = 'var(--warning-text)'; + } + + // 开始轮询认证状态 + this.startGeminiCliOAuthPolling(state); + + } catch (error) { + this.showNotification(`${i18n.t('auth_login.gemini_cli_oauth_start_error')} ${error.message}`, 'error'); + } + } + + // 打开 Gemini CLI 授权链接 + openGeminiCliLink() { + const urlInput = document.getElementById('gemini-cli-oauth-url'); + if (urlInput && urlInput.value) { + window.open(urlInput.value, '_blank'); + } + } + + // 复制 Gemini CLI 授权链接 + async copyGeminiCliLink() { + const urlInput = document.getElementById('gemini-cli-oauth-url'); + if (urlInput && urlInput.value) { + try { + await navigator.clipboard.writeText(urlInput.value); + this.showNotification('链接已复制到剪贴板', 'success'); + } catch (error) { + // 降级方案:使用传统的复制方法 + urlInput.select(); + document.execCommand('copy'); + this.showNotification('链接已复制到剪贴板', 'success'); + } + } + } + + // 开始轮询 Gemini CLI OAuth 状态 + startGeminiCliOAuthPolling(state) { + if (!state) { + this.showNotification('无法获取认证状态参数', 'error'); + return; + } + + const pollInterval = setInterval(async () => { + try { + const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`); + const status = response.status; + const statusElement = document.getElementById('gemini-cli-oauth-status'); + + if (status === 'ok') { + // 认证成功 + clearInterval(pollInterval); + // 隐藏授权链接相关内容,恢复到初始状态 + this.resetGeminiCliOAuthUI(); + // 显示成功通知 + this.showNotification(i18n.t('auth_login.gemini_cli_oauth_status_success'), 'success'); + // 刷新认证文件列表 + this.loadAuthFiles(); + } else if (status === 'error') { + // 认证失败 + clearInterval(pollInterval); + const errorMessage = response.error || 'Unknown error'; + // 显示错误信息 + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.gemini_cli_oauth_status_error')} ${errorMessage}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.gemini_cli_oauth_status_error')} ${errorMessage}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetGeminiCliOAuthUI(); + }, 3000); + } else if (status === 'wait') { + // 继续等待 + if (statusElement) { + statusElement.textContent = i18n.t('auth_login.gemini_cli_oauth_status_waiting'); + statusElement.style.color = 'var(--warning-text)'; + } + } + } catch (error) { + clearInterval(pollInterval); + const statusElement = document.getElementById('gemini-cli-oauth-status'); + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.gemini_cli_oauth_polling_error')} ${error.message}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.gemini_cli_oauth_polling_error')} ${error.message}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetGeminiCliOAuthUI(); + }, 3000); + } + }, 2000); // 每2秒轮询一次 + + // 设置超时,5分钟后停止轮询 + setTimeout(() => { + clearInterval(pollInterval); + }, 5 * 60 * 1000); + } + + // 重置 Gemini CLI OAuth UI 到初始状态 + resetGeminiCliOAuthUI() { + const urlInput = document.getElementById('gemini-cli-oauth-url'); + const content = document.getElementById('gemini-cli-oauth-content'); + const status = document.getElementById('gemini-cli-oauth-status'); + + // 清空并隐藏授权链接输入框 + if (urlInput) { + urlInput.value = ''; + } + + // 隐藏整个授权链接内容区域 + if (content) { + content.style.display = 'none'; + } + + // 清空状态显示 + if (status) { + status.textContent = ''; + status.style.color = ''; + status.className = ''; + } + } + + // ===== Qwen OAuth 相关方法 ===== + + // 开始 Qwen OAuth 流程 + async startQwenOAuth() { + try { + const response = await this.makeRequest('/qwen-auth-url?is_webui=1'); + const authUrl = response.url; + const state = this.extractStateFromUrl(authUrl); + + // 显示授权链接 + const urlInput = document.getElementById('qwen-oauth-url'); + const content = document.getElementById('qwen-oauth-content'); + const status = document.getElementById('qwen-oauth-status'); + + if (urlInput) { + urlInput.value = authUrl; + } + if (content) { + content.style.display = 'block'; + } + if (status) { + status.textContent = i18n.t('auth_login.qwen_oauth_status_waiting'); + status.style.color = 'var(--warning-text)'; + } + + // 开始轮询认证状态 + this.startQwenOAuthPolling(response.state); + + } catch (error) { + this.showNotification(`${i18n.t('auth_login.qwen_oauth_start_error')} ${error.message}`, 'error'); + } + } + + // 打开 Qwen 授权链接 + openQwenLink() { + const urlInput = document.getElementById('qwen-oauth-url'); + if (urlInput && urlInput.value) { + window.open(urlInput.value, '_blank'); + } + } + + // 复制 Qwen 授权链接 + async copyQwenLink() { + const urlInput = document.getElementById('qwen-oauth-url'); + if (urlInput && urlInput.value) { + try { + await navigator.clipboard.writeText(urlInput.value); + this.showNotification('链接已复制到剪贴板', 'success'); + } catch (error) { + // 降级方案:使用传统的复制方法 + urlInput.select(); + document.execCommand('copy'); + this.showNotification('链接已复制到剪贴板', 'success'); + } + } + } + + // 开始轮询 Qwen OAuth 状态 + startQwenOAuthPolling(state) { + if (!state) { + this.showNotification('无法获取认证状态参数', 'error'); + return; + } + + const pollInterval = setInterval(async () => { + try { + const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`); + const status = response.status; + const statusElement = document.getElementById('qwen-oauth-status'); + + if (status === 'ok') { + // 认证成功 + clearInterval(pollInterval); + // 隐藏授权链接相关内容,恢复到初始状态 + this.resetQwenOAuthUI(); + // 显示成功通知 + this.showNotification(i18n.t('auth_login.qwen_oauth_status_success'), 'success'); + // 刷新认证文件列表 + this.loadAuthFiles(); + } else if (status === 'error') { + // 认证失败 + clearInterval(pollInterval); + const errorMessage = response.error || 'Unknown error'; + // 显示错误信息 + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.qwen_oauth_status_error')} ${errorMessage}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.qwen_oauth_status_error')} ${errorMessage}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetQwenOAuthUI(); + }, 3000); + } else if (status === 'wait') { + // 继续等待 + if (statusElement) { + statusElement.textContent = i18n.t('auth_login.qwen_oauth_status_waiting'); + statusElement.style.color = 'var(--warning-text)'; + } + } + } catch (error) { + clearInterval(pollInterval); + const statusElement = document.getElementById('qwen-oauth-status'); + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.qwen_oauth_polling_error')} ${error.message}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.qwen_oauth_polling_error')} ${error.message}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetQwenOAuthUI(); + }, 3000); + } + }, 2000); // 每2秒轮询一次 + + // 设置超时,5分钟后停止轮询 + setTimeout(() => { + clearInterval(pollInterval); + }, 5 * 60 * 1000); + } + + // 重置 Qwen OAuth UI 到初始状态 + resetQwenOAuthUI() { + const urlInput = document.getElementById('qwen-oauth-url'); + const content = document.getElementById('qwen-oauth-content'); + const status = document.getElementById('qwen-oauth-status'); + + // 清空并隐藏授权链接输入框 + if (urlInput) { + urlInput.value = ''; + } + + // 隐藏整个授权链接内容区域 + if (content) { + content.style.display = 'none'; + } + + // 清空状态显示 + if (status) { + status.textContent = ''; + status.style.color = ''; + status.className = ''; + } + } + + // ===== iFlow OAuth 相关方法 ===== + + // 开始 iFlow OAuth 流程 + async startIflowOAuth() { + try { + const response = await this.makeRequest('/iflow-auth-url?is_webui=1'); + const authUrl = response.url; + const state = this.extractStateFromUrl(authUrl); + + // 显示授权链接 + const urlInput = document.getElementById('iflow-oauth-url'); + const content = document.getElementById('iflow-oauth-content'); + const status = document.getElementById('iflow-oauth-status'); + + if (urlInput) { + urlInput.value = authUrl; + } + if (content) { + content.style.display = 'block'; + } + if (status) { + status.textContent = i18n.t('auth_login.iflow_oauth_status_waiting'); + status.style.color = 'var(--warning-text)'; + } + + // 开始轮询认证状态 + this.startIflowOAuthPolling(state); + + } catch (error) { + this.showNotification(`${i18n.t('auth_login.iflow_oauth_start_error')} ${error.message}`, 'error'); + } + } + + // 打开 iFlow 授权链接 + openIflowLink() { + const urlInput = document.getElementById('iflow-oauth-url'); + if (urlInput && urlInput.value) { + window.open(urlInput.value, '_blank'); + } + } + + // 复制 iFlow 授权链接 + async copyIflowLink() { + const urlInput = document.getElementById('iflow-oauth-url'); + if (urlInput && urlInput.value) { + try { + await navigator.clipboard.writeText(urlInput.value); + this.showNotification('链接已复制到剪贴板', 'success'); + } catch (error) { + // 降级方案:使用传统的复制方法 + urlInput.select(); + document.execCommand('copy'); + this.showNotification('链接已复制到剪贴板', 'success'); + } + } + } + + // 开始轮询 iFlow OAuth 状态 + startIflowOAuthPolling(state) { + if (!state) { + this.showNotification('无法获取认证状态参数', 'error'); + return; + } + + const pollInterval = setInterval(async () => { + try { + const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`); + const status = response.status; + const statusElement = document.getElementById('iflow-oauth-status'); + + if (status === 'ok') { + // 认证成功 + clearInterval(pollInterval); + // 隐藏授权链接相关内容,恢复到初始状态 + this.resetIflowOAuthUI(); + // 显示成功通知 + this.showNotification(i18n.t('auth_login.iflow_oauth_status_success'), 'success'); + // 刷新认证文件列表 + this.loadAuthFiles(); + } else if (status === 'error') { + // 认证失败 + clearInterval(pollInterval); + const errorMessage = response.error || 'Unknown error'; + // 显示错误信息 + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.iflow_oauth_status_error')} ${errorMessage}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.iflow_oauth_status_error')} ${errorMessage}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetIflowOAuthUI(); + }, 3000); + } else if (status === 'wait') { + // 继续等待 + if (statusElement) { + statusElement.textContent = i18n.t('auth_login.iflow_oauth_status_waiting'); + statusElement.style.color = 'var(--warning-text)'; + } + } + } catch (error) { + clearInterval(pollInterval); + const statusElement = document.getElementById('iflow-oauth-status'); + if (statusElement) { + statusElement.textContent = `${i18n.t('auth_login.iflow_oauth_polling_error')} ${error.message}`; + statusElement.style.color = 'var(--error-text)'; + } + this.showNotification(`${i18n.t('auth_login.iflow_oauth_polling_error')} ${error.message}`, 'error'); + // 3秒后重置UI,让用户能够重新开始 + setTimeout(() => { + this.resetIflowOAuthUI(); + }, 3000); + } + }, 2000); // 每2秒轮询一次 + + // 设置超时,5分钟后停止轮询 + setTimeout(() => { + clearInterval(pollInterval); + }, 5 * 60 * 1000); + } + + // 重置 iFlow OAuth UI 到初始状态 + resetIflowOAuthUI() { + const urlInput = document.getElementById('iflow-oauth-url'); + const content = document.getElementById('iflow-oauth-content'); + const status = document.getElementById('iflow-oauth-status'); + + // 清空并隐藏授权链接输入框 + if (urlInput) { + urlInput.value = ''; + } + + // 隐藏整个授权链接内容区域 + if (content) { + content.style.display = 'none'; + } + + // 清空状态显示 + if (status) { + status.textContent = ''; + status.style.color = ''; + status.className = ''; + } + } + // ===== 使用统计相关方法 ===== // 初始化图表变量 diff --git a/i18n.js b/i18n.js index 1a6cddb..c6e68b4 100644 --- a/i18n.js +++ b/i18n.js @@ -239,6 +239,61 @@ const i18n = { 'auth_login.codex_oauth_start_error': '启动 Codex OAuth 失败:', 'auth_login.codex_oauth_polling_error': '检查认证状态失败:', + // Anthropic OAuth + 'auth_login.anthropic_oauth_title': 'Anthropic OAuth', + 'auth_login.anthropic_oauth_button': '开始 Anthropic 登录', + 'auth_login.anthropic_oauth_hint': '通过 OAuth 流程登录 Anthropic (Claude) 服务,自动获取并保存认证文件。', + 'auth_login.anthropic_oauth_url_label': '授权链接:', + 'auth_login.anthropic_open_link': '打开链接', + 'auth_login.anthropic_copy_link': '复制链接', + 'auth_login.anthropic_oauth_status_waiting': '等待认证中...', + 'auth_login.anthropic_oauth_status_success': '认证成功!', + 'auth_login.anthropic_oauth_status_error': '认证失败:', + 'auth_login.anthropic_oauth_start_error': '启动 Anthropic OAuth 失败:', + 'auth_login.anthropic_oauth_polling_error': '检查认证状态失败:', + + // Gemini CLI OAuth + 'auth_login.gemini_cli_oauth_title': 'Gemini CLI OAuth', + 'auth_login.gemini_cli_oauth_button': '开始 Gemini CLI 登录', + 'auth_login.gemini_cli_oauth_hint': '通过 OAuth 流程登录 Google Gemini CLI 服务,自动获取并保存认证文件。', + 'auth_login.gemini_cli_project_id_label': 'Google Cloud 项目 ID (可选):', + 'auth_login.gemini_cli_project_id_placeholder': '输入 Google Cloud 项目 ID (可选)', + 'auth_login.gemini_cli_project_id_hint': '如果指定了项目 ID,将使用该项目的认证信息。', + 'auth_login.gemini_cli_oauth_url_label': '授权链接:', + 'auth_login.gemini_cli_open_link': '打开链接', + 'auth_login.gemini_cli_copy_link': '复制链接', + 'auth_login.gemini_cli_oauth_status_waiting': '等待认证中...', + 'auth_login.gemini_cli_oauth_status_success': '认证成功!', + 'auth_login.gemini_cli_oauth_status_error': '认证失败:', + 'auth_login.gemini_cli_oauth_start_error': '启动 Gemini CLI OAuth 失败:', + 'auth_login.gemini_cli_oauth_polling_error': '检查认证状态失败:', + + // Qwen OAuth + 'auth_login.qwen_oauth_title': 'Qwen OAuth', + 'auth_login.qwen_oauth_button': '开始 Qwen 登录', + 'auth_login.qwen_oauth_hint': '通过设备授权流程登录 Qwen 服务,自动获取并保存认证文件。', + 'auth_login.qwen_oauth_url_label': '授权链接:', + 'auth_login.qwen_open_link': '打开链接', + 'auth_login.qwen_copy_link': '复制链接', + 'auth_login.qwen_oauth_status_waiting': '等待认证中...', + 'auth_login.qwen_oauth_status_success': '认证成功!', + 'auth_login.qwen_oauth_status_error': '认证失败:', + 'auth_login.qwen_oauth_start_error': '启动 Qwen OAuth 失败:', + 'auth_login.qwen_oauth_polling_error': '检查认证状态失败:', + + // iFlow OAuth + 'auth_login.iflow_oauth_title': 'iFlow OAuth', + 'auth_login.iflow_oauth_button': '开始 iFlow 登录', + 'auth_login.iflow_oauth_hint': '通过 OAuth 流程登录 iFlow 服务,自动获取并保存认证文件。', + 'auth_login.iflow_oauth_url_label': '授权链接:', + 'auth_login.iflow_open_link': '打开链接', + 'auth_login.iflow_copy_link': '复制链接', + 'auth_login.iflow_oauth_status_waiting': '等待认证中...', + 'auth_login.iflow_oauth_status_success': '认证成功!', + 'auth_login.iflow_oauth_status_error': '认证失败:', + 'auth_login.iflow_oauth_start_error': '启动 iFlow OAuth 失败:', + 'auth_login.iflow_oauth_polling_error': '检查认证状态失败:', + // 使用统计 'usage_stats.title': '使用统计', 'usage_stats.total_requests': '总请求数', @@ -546,6 +601,61 @@ const i18n = { 'auth_login.codex_oauth_start_error': 'Failed to start Codex OAuth:', 'auth_login.codex_oauth_polling_error': 'Failed to check authentication status:', + // Anthropic OAuth + 'auth_login.anthropic_oauth_title': 'Anthropic OAuth', + 'auth_login.anthropic_oauth_button': 'Start Anthropic Login', + 'auth_login.anthropic_oauth_hint': 'Login to Anthropic (Claude) service through OAuth flow, automatically obtain and save authentication files.', + 'auth_login.anthropic_oauth_url_label': 'Authorization URL:', + 'auth_login.anthropic_open_link': 'Open Link', + 'auth_login.anthropic_copy_link': 'Copy Link', + 'auth_login.anthropic_oauth_status_waiting': 'Waiting for authentication...', + 'auth_login.anthropic_oauth_status_success': 'Authentication successful!', + 'auth_login.anthropic_oauth_status_error': 'Authentication failed:', + 'auth_login.anthropic_oauth_start_error': 'Failed to start Anthropic OAuth:', + 'auth_login.anthropic_oauth_polling_error': 'Failed to check authentication status:', + + // Gemini CLI OAuth + 'auth_login.gemini_cli_oauth_title': 'Gemini CLI OAuth', + 'auth_login.gemini_cli_oauth_button': 'Start Gemini CLI Login', + 'auth_login.gemini_cli_oauth_hint': 'Login to Google Gemini CLI service through OAuth flow, automatically obtain and save authentication files.', + 'auth_login.gemini_cli_project_id_label': 'Google Cloud Project ID (Optional):', + 'auth_login.gemini_cli_project_id_placeholder': 'Enter Google Cloud Project ID (optional)', + 'auth_login.gemini_cli_project_id_hint': 'If a project ID is specified, authentication information for that project will be used.', + 'auth_login.gemini_cli_oauth_url_label': 'Authorization URL:', + 'auth_login.gemini_cli_open_link': 'Open Link', + 'auth_login.gemini_cli_copy_link': 'Copy Link', + 'auth_login.gemini_cli_oauth_status_waiting': 'Waiting for authentication...', + 'auth_login.gemini_cli_oauth_status_success': 'Authentication successful!', + 'auth_login.gemini_cli_oauth_status_error': 'Authentication failed:', + 'auth_login.gemini_cli_oauth_start_error': 'Failed to start Gemini CLI OAuth:', + 'auth_login.gemini_cli_oauth_polling_error': 'Failed to check authentication status:', + + // Qwen OAuth + 'auth_login.qwen_oauth_title': 'Qwen OAuth', + 'auth_login.qwen_oauth_button': 'Start Qwen Login', + 'auth_login.qwen_oauth_hint': 'Login to Qwen service through device authorization flow, automatically obtain and save authentication files.', + 'auth_login.qwen_oauth_url_label': 'Authorization URL:', + 'auth_login.qwen_open_link': 'Open Link', + 'auth_login.qwen_copy_link': 'Copy Link', + 'auth_login.qwen_oauth_status_waiting': 'Waiting for authentication...', + 'auth_login.qwen_oauth_status_success': 'Authentication successful!', + 'auth_login.qwen_oauth_status_error': 'Authentication failed:', + 'auth_login.qwen_oauth_start_error': 'Failed to start Qwen OAuth:', + 'auth_login.qwen_oauth_polling_error': 'Failed to check authentication status:', + + // iFlow OAuth + 'auth_login.iflow_oauth_title': 'iFlow OAuth', + 'auth_login.iflow_oauth_button': 'Start iFlow Login', + 'auth_login.iflow_oauth_hint': 'Login to iFlow service through OAuth flow, automatically obtain and save authentication files.', + 'auth_login.iflow_oauth_url_label': 'Authorization URL:', + 'auth_login.iflow_open_link': 'Open Link', + 'auth_login.iflow_copy_link': 'Copy Link', + 'auth_login.iflow_oauth_status_waiting': 'Waiting for authentication...', + 'auth_login.iflow_oauth_status_success': 'Authentication successful!', + 'auth_login.iflow_oauth_status_error': 'Authentication failed:', + 'auth_login.iflow_oauth_start_error': 'Failed to start iFlow OAuth:', + 'auth_login.iflow_oauth_polling_error': 'Failed to check authentication status:', + // Usage Statistics 'usage_stats.title': 'Usage Statistics', 'usage_stats.total_requests': 'Total Requests', diff --git a/index.html b/index.html index 056d680..312a8ab 100644 --- a/index.html +++ b/index.html @@ -467,6 +467,159 @@ + + +
+
+

Anthropic OAuth

+ +
+
+

+ 通过 OAuth 流程登录 Anthropic (Claude) 服务,自动获取并保存认证文件。 +

+ +
+
+ + +
+
+

Gemini CLI OAuth

+ +
+
+

+ 通过 OAuth 流程登录 Google Gemini CLI 服务,自动获取并保存认证文件。 +

+
+ + +
+ 如果指定了项目 ID,将使用该项目的认证信息。 +
+
+ +
+
+ + +
+
+

Qwen + OAuth

+ +
+
+

+ 通过设备授权流程登录 Qwen 服务,自动获取并保存认证文件。 +

+ +
+
+ + +
+
+

iFlow OAuth

+ +
+
+

+ 通过 OAuth 流程登录 iFlow 服务,自动获取并保存认证文件。 +

+ +
+
diff --git a/styles.css b/styles.css index 83361db..69c98b7 100644 --- a/styles.css +++ b/styles.css @@ -2316,4 +2316,390 @@ input:checked+.slider:before { #codex-oauth-url { font-size: 12px; } +} + +/* Anthropic OAuth 样式 */ +#anthropic-oauth-content { + transition: all 0.3s ease; +} + +#anthropic-oauth-url { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + color: var(--text-secondary); +} + +#anthropic-oauth-url:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +#anthropic-oauth-status { + font-weight: 500; + padding: 8px 12px; + border-radius: 6px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + transition: all 0.3s ease; +} + +#anthropic-oauth-status.success { + background: var(--success-bg); + border-color: var(--success-border); + color: var(--success-text); +} + +#anthropic-oauth-status.error { + background: var(--error-bg); + border-color: var(--error-border); + color: var(--error-text); +} + +#anthropic-oauth-status.warning { + background: var(--warning-bg); + border-color: var(--warning-border); + color: var(--warning-text); +} + +/* Anthropic OAuth 按钮样式 */ +#anthropic-open-link, +#anthropic-copy-link { + min-width: 100px; + white-space: nowrap; +} + +#anthropic-open-link { + background: var(--primary-color); + border-color: var(--primary-color); +} + +#anthropic-open-link:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); +} + +#anthropic-copy-link { + background: var(--bg-secondary); + border-color: var(--border-primary); + color: var(--text-secondary); +} + +#anthropic-copy-link:hover { + background: var(--bg-tertiary); + border-color: var(--border-secondary); + color: var(--text-primary); +} + +/* 响应式设计 - Anthropic OAuth */ +@media (max-width: 768px) { + #anthropic-oauth-content .input-group { + flex-direction: column; + align-items: stretch; + } + + #anthropic-open-link, + #anthropic-copy-link { + width: 100%; + margin-top: 8px; + } + + #anthropic-oauth-url { + font-size: 12px; + } +} + +/* Gemini CLI OAuth 样式 */ +#gemini-cli-oauth-content { + transition: all 0.3s ease; +} + +#gemini-cli-oauth-url { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + color: var(--text-secondary); +} + +#gemini-cli-oauth-url:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +#gemini-cli-oauth-status { + font-weight: 500; + padding: 8px 12px; + border-radius: 6px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + transition: all 0.3s ease; +} + +#gemini-cli-oauth-status.success { + background: var(--success-bg); + border-color: var(--success-border); + color: var(--success-text); +} + +#gemini-cli-oauth-status.error { + background: var(--error-bg); + border-color: var(--error-border); + color: var(--error-text); +} + +#gemini-cli-oauth-status.warning { + background: var(--warning-bg); + border-color: var(--warning-border); + color: var(--warning-text); +} + +/* Gemini CLI OAuth 按钮样式 */ +#gemini-cli-open-link, +#gemini-cli-copy-link { + min-width: 100px; + white-space: nowrap; +} + +#gemini-cli-open-link { + background: var(--primary-color); + border-color: var(--primary-color); +} + +#gemini-cli-open-link:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); +} + +#gemini-cli-copy-link { + background: var(--bg-secondary); + border-color: var(--border-primary); + color: var(--text-secondary); +} + +#gemini-cli-copy-link:hover { + background: var(--bg-tertiary); + border-color: var(--border-secondary); + color: var(--text-primary); +} + +/* Gemini CLI 项目 ID 输入框样式 */ +#gemini-cli-project-id { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + color: var(--text-secondary); +} + +#gemini-cli-project-id:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* 响应式设计 - Gemini CLI OAuth */ +@media (max-width: 768px) { + #gemini-cli-oauth-content .input-group { + flex-direction: column; + align-items: stretch; + } + + #gemini-cli-open-link, + #gemini-cli-copy-link { + width: 100%; + margin-top: 8px; + } + + #gemini-cli-oauth-url { + font-size: 12px; + } + + #gemini-cli-project-id { + font-size: 12px; + } +} + +/* Qwen OAuth 样式 */ +#qwen-oauth-content { + transition: all 0.3s ease; +} + +#qwen-oauth-url { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + color: var(--text-secondary); +} + +#qwen-oauth-url:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +#qwen-oauth-status { + font-weight: 500; + padding: 8px 12px; + border-radius: 6px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + transition: all 0.3s ease; +} + +#qwen-oauth-status.success { + background: var(--success-bg); + border-color: var(--success-border); + color: var(--success-text); +} + +#qwen-oauth-status.error { + background: var(--error-bg); + border-color: var(--error-border); + color: var(--error-text); +} + +#qwen-oauth-status.warning { + background: var(--warning-bg); + border-color: var(--warning-border); + color: var(--warning-text); +} + +/* Qwen OAuth 按钮样式 */ +#qwen-open-link, +#qwen-copy-link { + min-width: 100px; + white-space: nowrap; +} + +#qwen-open-link { + background: var(--primary-color); + border-color: var(--primary-color); +} + +#qwen-open-link:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); +} + +#qwen-copy-link { + background: var(--bg-secondary); + border-color: var(--border-primary); + color: var(--text-secondary); +} + +#qwen-copy-link:hover { + background: var(--bg-tertiary); + border-color: var(--border-secondary); + color: var(--text-primary); +} + +/* 响应式设计 - Qwen OAuth */ +@media (max-width: 768px) { + #qwen-oauth-content .input-group { + flex-direction: column; + align-items: stretch; + } + + #qwen-open-link, + #qwen-copy-link { + width: 100%; + margin-top: 8px; + } + + #qwen-oauth-url { + font-size: 12px; + } +} + +/* iFlow OAuth 样式 */ +#iflow-oauth-content { + transition: all 0.3s ease; +} + +#iflow-oauth-url { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + color: var(--text-secondary); +} + +#iflow-oauth-url:focus { + border-color: var(--border-focus); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +#iflow-oauth-status { + font-weight: 500; + padding: 8px 12px; + border-radius: 6px; + background: var(--bg-tertiary); + border: 1px solid var(--border-primary); + transition: all 0.3s ease; +} + +#iflow-oauth-status.success { + background: var(--success-bg); + border-color: var(--success-border); + color: var(--success-text); +} + +#iflow-oauth-status.error { + background: var(--error-bg); + border-color: var(--error-border); + color: var(--error-text); +} + +#iflow-oauth-status.warning { + background: var(--warning-bg); + border-color: var(--warning-border); + color: var(--warning-text); +} + +/* iFlow OAuth 按钮样式 */ +#iflow-open-link, +#iflow-copy-link { + min-width: 100px; + white-space: nowrap; +} + +#iflow-open-link { + background: var(--primary-color); + border-color: var(--primary-color); +} + +#iflow-open-link:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); +} + +#iflow-copy-link { + background: var(--bg-secondary); + border-color: var(--border-primary); + color: var(--text-secondary); +} + +#iflow-copy-link:hover { + background: var(--bg-tertiary); + border-color: var(--border-secondary); + color: var(--text-primary); +} + +/* 响应式设计 - iFlow OAuth */ +@media (max-width: 768px) { + #iflow-oauth-content .input-group { + flex-direction: column; + align-items: stretch; + } + + #iflow-open-link, + #iflow-copy-link { + width: 100%; + margin-top: 8px; + } + + #iflow-oauth-url { + font-size: 12px; + } } \ No newline at end of file