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.

This commit is contained in:
Luis Pater
2025-10-06 01:48:17 +08:00
parent 0bbb397df5
commit ee0d8f82d7
4 changed files with 1298 additions and 0 deletions

649
app.js
View File

@@ -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 = '';
}
}
// ===== 使用统计相关方法 =====
// 初始化图表变量

110
i18n.js
View File

@@ -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',

View File

@@ -467,6 +467,159 @@
</div>
</div>
</div>
<!-- Anthropic OAuth -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-brain"></i> <span
data-i18n="auth_login.anthropic_oauth_title">Anthropic OAuth</span></h3>
<button id="anthropic-oauth-btn" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> <span
data-i18n="auth_login.anthropic_oauth_button">开始 Anthropic 登录</span>
</button>
</div>
<div class="card-content">
<p class="form-hint" style="margin-bottom: 20px;"
data-i18n="auth_login.anthropic_oauth_hint">
通过 OAuth 流程登录 Anthropic (Claude) 服务,自动获取并保存认证文件。
</p>
<div id="anthropic-oauth-content" style="display: none;">
<div class="form-group">
<label data-i18n="auth_login.anthropic_oauth_url_label">授权链接:</label>
<div class="input-group">
<input type="text" id="anthropic-oauth-url" readonly>
<button id="anthropic-open-link" class="btn btn-primary">
<i class="fas fa-external-link-alt"></i> <span
data-i18n="auth_login.anthropic_open_link">打开链接</span>
</button>
<button id="anthropic-copy-link" class="btn btn-secondary">
<i class="fas fa-copy"></i> <span
data-i18n="auth_login.anthropic_copy_link">复制链接</span>
</button>
</div>
</div>
<div id="anthropic-oauth-status" class="form-hint" style="margin-top: 10px;">
</div>
</div>
</div>
</div>
<!-- Gemini CLI OAuth -->
<div class="card">
<div class="card-header">
<h3><i class="fab fa-google"></i> <span
data-i18n="auth_login.gemini_cli_oauth_title">Gemini CLI OAuth</span></h3>
<button id="gemini-cli-oauth-btn" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> <span
data-i18n="auth_login.gemini_cli_oauth_button">开始 Gemini CLI 登录</span>
</button>
</div>
<div class="card-content">
<p class="form-hint" style="margin-bottom: 20px;"
data-i18n="auth_login.gemini_cli_oauth_hint">
通过 OAuth 流程登录 Google Gemini CLI 服务,自动获取并保存认证文件。
</p>
<div class="form-group" style="margin-bottom: 20px;">
<label for="gemini-cli-project-id"
data-i18n="auth_login.gemini_cli_project_id_label">Google Cloud 项目 ID
(可选):</label>
<input type="text" id="gemini-cli-project-id"
data-i18n="auth_login.gemini_cli_project_id_placeholder"
placeholder="输入 Google Cloud 项目 ID (可选)">
<div class="form-hint" data-i18n="auth_login.gemini_cli_project_id_hint">
如果指定了项目 ID将使用该项目的认证信息。
</div>
</div>
<div id="gemini-cli-oauth-content" style="display: none;">
<div class="form-group">
<label data-i18n="auth_login.gemini_cli_oauth_url_label">授权链接:</label>
<div class="input-group">
<input type="text" id="gemini-cli-oauth-url" readonly>
<button id="gemini-cli-open-link" class="btn btn-primary">
<i class="fas fa-external-link-alt"></i> <span
data-i18n="auth_login.gemini_cli_open_link">打开链接</span>
</button>
<button id="gemini-cli-copy-link" class="btn btn-secondary">
<i class="fas fa-copy"></i> <span
data-i18n="auth_login.gemini_cli_copy_link">复制链接</span>
</button>
</div>
</div>
<div id="gemini-cli-oauth-status" class="form-hint" style="margin-top: 10px;">
</div>
</div>
</div>
</div>
<!-- Qwen OAuth -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-robot"></i> <span data-i18n="auth_login.qwen_oauth_title">Qwen
OAuth</span></h3>
<button id="qwen-oauth-btn" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> <span
data-i18n="auth_login.qwen_oauth_button">开始 Qwen 登录</span>
</button>
</div>
<div class="card-content">
<p class="form-hint" style="margin-bottom: 20px;"
data-i18n="auth_login.qwen_oauth_hint">
通过设备授权流程登录 Qwen 服务,自动获取并保存认证文件。
</p>
<div id="qwen-oauth-content" style="display: none;">
<div class="form-group">
<label data-i18n="auth_login.qwen_oauth_url_label">授权链接:</label>
<div class="input-group">
<input type="text" id="qwen-oauth-url" readonly>
<button id="qwen-open-link" class="btn btn-primary">
<i class="fas fa-external-link-alt"></i> <span
data-i18n="auth_login.qwen_open_link">打开链接</span>
</button>
<button id="qwen-copy-link" class="btn btn-secondary">
<i class="fas fa-copy"></i> <span
data-i18n="auth_login.qwen_copy_link">复制链接</span>
</button>
</div>
</div>
<div id="qwen-oauth-status" class="form-hint" style="margin-top: 10px;"></div>
</div>
</div>
</div>
<!-- iFlow OAuth -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-stream"></i> <span
data-i18n="auth_login.iflow_oauth_title">iFlow OAuth</span></h3>
<button id="iflow-oauth-btn" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> <span
data-i18n="auth_login.iflow_oauth_button">开始 iFlow 登录</span>
</button>
</div>
<div class="card-content">
<p class="form-hint" style="margin-bottom: 20px;"
data-i18n="auth_login.iflow_oauth_hint">
通过 OAuth 流程登录 iFlow 服务,自动获取并保存认证文件。
</p>
<div id="iflow-oauth-content" style="display: none;">
<div class="form-group">
<label data-i18n="auth_login.iflow_oauth_url_label">授权链接:</label>
<div class="input-group">
<input type="text" id="iflow-oauth-url" readonly>
<button id="iflow-open-link" class="btn btn-primary">
<i class="fas fa-external-link-alt"></i> <span
data-i18n="auth_login.iflow_open_link">打开链接</span>
</button>
<button id="iflow-copy-link" class="btn btn-secondary">
<i class="fas fa-copy"></i> <span
data-i18n="auth_login.iflow_copy_link">复制链接</span>
</button>
</div>
</div>
<div id="iflow-oauth-status" class="form-hint" style="margin-top: 10px;"></div>
</div>
</div>
</div>
</section>
<!-- 使用统计 -->

View File

@@ -2317,3 +2317,389 @@ input:checked+.slider:before {
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;
}
}