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