// CLI Proxy API 管理界面 JavaScript class CLIProxyManager { constructor() { // 仅保存基础地址(不含 /v0/management),请求时自动补齐 this.apiBase = 'http://localhost:8317'; this.apiUrl = this.computeApiUrl(this.apiBase); this.managementKey = ''; this.isConnected = false; this.isLoggedIn = false; // 配置缓存 this.configCache = null; this.cacheTimestamp = null; this.cacheExpiry = 30000; // 30秒缓存过期时间 // 状态更新定时器 this.statusUpdateTimer = null; // 主题管理 this.currentTheme = 'light'; this.init(); } // 简易防抖,减少频繁写 localStorage debounce(fn, delay = 400) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } // 初始化主题 initializeTheme() { // 从本地存储获取用户偏好主题 const savedTheme = localStorage.getItem('preferredTheme'); if (savedTheme && ['light', 'dark'].includes(savedTheme)) { this.currentTheme = savedTheme; } else { if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { this.currentTheme = 'dark'; } else { this.currentTheme = 'light'; } } this.applyTheme(this.currentTheme); this.updateThemeButtons(); // 监听系统主题变化 if (window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { if (!localStorage.getItem('preferredTheme')) { this.currentTheme = e.matches ? 'dark' : 'light'; this.applyTheme(this.currentTheme); this.updateThemeButtons(); } }); } } // 应用主题 applyTheme(theme) { if (theme === 'dark') { document.documentElement.setAttribute('data-theme', 'dark'); } else { document.documentElement.removeAttribute('data-theme'); } this.currentTheme = theme; } // 切换主题 toggleTheme() { const newTheme = this.currentTheme === 'light' ? 'dark' : 'light'; this.applyTheme(newTheme); this.updateThemeButtons(); localStorage.setItem('preferredTheme', newTheme); } // 更新主题按钮状态 updateThemeButtons() { const loginThemeBtn = document.getElementById('theme-toggle'); const mainThemeBtn = document.getElementById('theme-toggle-main'); const updateButton = (btn) => { if (!btn) return; const icon = btn.querySelector('i'); if (this.currentTheme === 'dark') { icon.className = 'fas fa-sun'; btn.title = i18n.t('theme.switch_to_light'); } else { icon.className = 'fas fa-moon'; btn.title = i18n.t('theme.switch_to_dark'); } }; updateButton(loginThemeBtn); updateButton(mainThemeBtn); } init() { this.initializeTheme(); this.checkLoginStatus(); this.bindEvents(); this.setupNavigation(); this.setupLanguageSwitcher(); this.setupThemeSwitcher(); // loadSettings 将在登录成功后调用 } // 检查登录状态 async checkLoginStatus() { // 检查是否有保存的连接信息 const savedBase = localStorage.getItem('apiBase'); const savedKey = localStorage.getItem('managementKey'); const wasLoggedIn = localStorage.getItem('isLoggedIn') === 'true'; // 如果有完整的连接信息且之前已登录,尝试自动登录 if (savedBase && savedKey && wasLoggedIn) { try { console.log('检测到本地连接数据,尝试自动登录...'); this.showAutoLoginLoading(); await this.attemptAutoLogin(savedBase, savedKey); return; // 自动登录成功,不显示登录页面 } catch (error) { console.log('自动登录失败:', error.message); // 清除无效的登录状态 localStorage.removeItem('isLoggedIn'); this.hideAutoLoginLoading(); } } // 如果没有连接信息或自动登录失败,显示登录页面 this.showLoginPage(); this.loadLoginSettings(); } // 显示自动登录加载页面 showAutoLoginLoading() { document.getElementById('auto-login-loading').style.display = 'flex'; document.getElementById('login-page').style.display = 'none'; document.getElementById('main-page').style.display = 'none'; } // 隐藏自动登录加载页面 hideAutoLoginLoading() { document.getElementById('auto-login-loading').style.display = 'none'; } // 尝试自动登录 async attemptAutoLogin(apiBase, managementKey) { try { // 设置API基础地址和密钥 this.setApiBase(apiBase); this.managementKey = managementKey; // 恢复代理设置(如果有) const savedProxy = localStorage.getItem('proxyUrl'); if (savedProxy) { // 代理设置会在后续的API请求中自动使用 } // 测试连接 await this.testConnection(); // 自动登录成功 this.isLoggedIn = true; this.hideAutoLoginLoading(); this.showMainPage(); console.log('自动登录成功'); return true; } catch (error) { console.error('自动登录失败:', error); // 重置状态 this.isLoggedIn = false; this.isConnected = false; throw error; } } // 显示登录页面 showLoginPage() { document.getElementById('login-page').style.display = 'flex'; document.getElementById('main-page').style.display = 'none'; this.isLoggedIn = false; } // 显示主页面 showMainPage() { document.getElementById('login-page').style.display = 'none'; document.getElementById('main-page').style.display = 'block'; this.isLoggedIn = true; this.updateConnectionInfo(); } // 登录验证 async login(apiBase, managementKey) { try { // 设置API基础地址和密钥 this.setApiBase(apiBase); this.managementKey = managementKey; localStorage.setItem('managementKey', this.managementKey); // 测试连接并加载所有数据 await this.testConnection(); // 登录成功 this.isLoggedIn = true; localStorage.setItem('isLoggedIn', 'true'); this.showMainPage(); // 不需要再调用loadSettings,因为内部状态已经在上面设置了 return true; } catch (error) { console.error('登录失败:', error); throw error; } } // 登出 logout() { this.isLoggedIn = false; this.isConnected = false; this.clearCache(); this.stopStatusUpdateTimer(); // 清除本地存储 localStorage.removeItem('isLoggedIn'); localStorage.removeItem('managementKey'); this.showLoginPage(); } // 处理登录表单提交 async handleLogin() { // 获取当前活动的选项卡 const activeTab = document.querySelector('.tab-button.active').getAttribute('data-tab'); let apiUrl, managementKey; if (activeTab === 'local') { // 本地连接:从端口号构建URL const port = document.getElementById('local-port').value.trim(); managementKey = document.getElementById('local-management-key').value.trim(); if (!port || !managementKey) { this.showLoginError(i18n.t('login.error_required')); return; } apiUrl = `http://localhost:${port}`; } else { // 远程连接:使用完整URL apiUrl = document.getElementById('remote-api-url').value.trim(); managementKey = document.getElementById('remote-management-key').value.trim(); if (!apiUrl || !managementKey) { this.showLoginError(i18n.t('login.error_required')); return; } } const proxyUrl = document.getElementById('login-proxy-url').value.trim(); const submitBtn = document.getElementById('login-submit'); const originalText = submitBtn.innerHTML; try { submitBtn.innerHTML = `
${i18n.t('login.submitting')}`; submitBtn.disabled = true; this.hideLoginError(); // 如果设置了代理,先保存代理设置 if (proxyUrl) { localStorage.setItem('proxyUrl', proxyUrl); } await this.login(apiUrl, managementKey); } catch (error) { this.showLoginError(`${i18n.t('login.error_title')}: ${error.message}`); } finally { submitBtn.innerHTML = originalText; submitBtn.disabled = false; } } // 切换登录页面密钥可见性 toggleLoginKeyVisibility(button) { const inputGroup = button.closest('.input-group'); const keyInput = inputGroup.querySelector('input[type="password"], input[type="text"]'); if (keyInput.type === 'password') { keyInput.type = 'text'; button.innerHTML = ''; } else { keyInput.type = 'password'; button.innerHTML = ''; } } // 显示登录错误 showLoginError(message) { const errorDiv = document.getElementById('login-error'); const errorMessage = document.getElementById('login-error-message'); errorMessage.textContent = message; errorDiv.style.display = 'flex'; } // 隐藏登录错误 hideLoginError() { const errorDiv = document.getElementById('login-error'); errorDiv.style.display = 'none'; } // 更新连接信息显示 updateConnectionInfo() { const apiUrlElement = document.getElementById('display-api-url'); const keyElement = document.getElementById('display-management-key'); const statusElement = document.getElementById('display-connection-status'); // 显示API地址 if (apiUrlElement) { apiUrlElement.textContent = this.apiBase || '-'; } // 显示密钥(遮蔽显示) if (keyElement) { if (this.managementKey) { const maskedKey = this.maskApiKey(this.managementKey); keyElement.textContent = maskedKey; } else { keyElement.textContent = '-'; } } // 显示连接状态 if (statusElement) { let statusHtml = ''; if (this.isConnected) { statusHtml = ` ${i18n.t('common.connected')}`; } else { statusHtml = ` ${i18n.t('common.disconnected')}`; } statusElement.innerHTML = statusHtml; } } // 加载登录页面设置 loadLoginSettings() { const savedBase = localStorage.getItem('apiBase'); const savedKey = localStorage.getItem('managementKey'); const savedProxy = localStorage.getItem('proxyUrl'); // 检查元素是否存在(确保在登录页面) const localPortInput = document.getElementById('local-port'); const remoteApiInput = document.getElementById('remote-api-url'); const localKeyInput = document.getElementById('local-management-key'); const remoteKeyInput = document.getElementById('remote-management-key'); const proxyInput = document.getElementById('login-proxy-url'); // 设置本地端口和远程API地址 if (savedBase) { if (savedBase.includes('localhost')) { // 从本地URL中提取端口号 const match = savedBase.match(/localhost:(\d+)/); if (match && localPortInput) { localPortInput.value = match[1]; } } else if (remoteApiInput) { remoteApiInput.value = savedBase; } } // 设置密钥 if (localKeyInput && savedKey) { localKeyInput.value = savedKey; } if (remoteKeyInput && savedKey) { remoteKeyInput.value = savedKey; } // 设置代理 if (proxyInput && savedProxy) { proxyInput.value = savedProxy; } // 设置实时保存监听器 this.setupLoginAutoSave(); } // 设置登录页面自动保存 setupLoginAutoSave() { const localPortInput = document.getElementById('local-port'); const remoteApiInput = document.getElementById('remote-api-url'); const localKeyInput = document.getElementById('local-management-key'); const remoteKeyInput = document.getElementById('remote-management-key'); const proxyInput = document.getElementById('login-proxy-url'); const saveLocalBase = (port) => { if (port.trim()) { const apiUrl = `http://localhost:${port}`; this.setApiBase(apiUrl); } }; const saveLocalBaseDebounced = this.debounce(saveLocalBase, 500); const saveRemoteBase = (val) => { if (val.trim()) { this.setApiBase(val); } }; const saveRemoteBaseDebounced = this.debounce(saveRemoteBase, 500); const saveKey = (val) => { if (val.trim()) { this.managementKey = val; localStorage.setItem('managementKey', this.managementKey); } }; const saveKeyDebounced = this.debounce(saveKey, 500); const saveProxy = (val) => { if (val.trim()) { localStorage.setItem('proxyUrl', val); } }; const saveProxyDebounced = this.debounce(saveProxy, 500); // 绑定本地端口输入框 if (localPortInput) { localPortInput.addEventListener('change', (e) => saveLocalBase(e.target.value)); localPortInput.addEventListener('input', (e) => saveLocalBaseDebounced(e.target.value)); } // 绑定远程API输入框 if (remoteApiInput) { remoteApiInput.addEventListener('change', (e) => saveRemoteBase(e.target.value)); remoteApiInput.addEventListener('input', (e) => saveRemoteBaseDebounced(e.target.value)); } // 绑定本地密钥输入框 if (localKeyInput) { localKeyInput.addEventListener('change', (e) => saveKey(e.target.value)); localKeyInput.addEventListener('input', (e) => saveKeyDebounced(e.target.value)); } // 绑定远程密钥输入框 if (remoteKeyInput) { remoteKeyInput.addEventListener('change', (e) => saveKey(e.target.value)); remoteKeyInput.addEventListener('input', (e) => saveKeyDebounced(e.target.value)); } // 绑定代理输入框 if (proxyInput) { proxyInput.addEventListener('change', (e) => saveProxy(e.target.value)); proxyInput.addEventListener('input', (e) => saveProxyDebounced(e.target.value)); } } // 事件绑定 bindEvents() { // 登录相关(安全绑定) const loginSubmit = document.getElementById('login-submit'); const logoutBtn = document.getElementById('logout-btn'); if (loginSubmit) { loginSubmit.addEventListener('click', () => this.handleLogin()); } if (logoutBtn) { logoutBtn.addEventListener('click', () => this.logout()); } // 选项卡切换事件 this.setupTabSwitching(); // 密钥可见性切换事件 this.setupKeyVisibilityToggle(); // 主页面元素(延迟绑定,在显示主页面时绑定) this.bindMainPageEvents(); } // 设置选项卡切换 setupTabSwitching() { const tabButtons = document.querySelectorAll('.tab-button'); const connectionForms = document.querySelectorAll('.connection-form'); tabButtons.forEach(button => { button.addEventListener('click', () => { const targetTab = button.getAttribute('data-tab'); // 更新选项卡状态 tabButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); // 切换表单 connectionForms.forEach(form => { form.classList.remove('active'); if (form.id === `${targetTab}-form`) { form.classList.add('active'); } }); }); }); } // 设置密钥可见性切换 setupKeyVisibilityToggle() { const toggleButtons = document.querySelectorAll('.toggle-key-visibility'); toggleButtons.forEach(button => { button.addEventListener('click', () => this.toggleLoginKeyVisibility(button)); }); } // 绑定主页面事件 bindMainPageEvents() { // 连接状态检查 const connectionStatus = document.getElementById('connection-status'); const refreshAll = document.getElementById('refresh-all'); if (connectionStatus) { connectionStatus.addEventListener('click', () => this.checkConnectionStatus()); } if (refreshAll) { refreshAll.addEventListener('click', () => this.refreshAllData()); } // 基础设置 const debugToggle = document.getElementById('debug-toggle'); const updateProxy = document.getElementById('update-proxy'); const clearProxy = document.getElementById('clear-proxy'); const updateRetry = document.getElementById('update-retry'); const switchProjectToggle = document.getElementById('switch-project-toggle'); const switchPreviewToggle = document.getElementById('switch-preview-model-toggle'); const allowLocalhostToggle = document.getElementById('allow-localhost-toggle'); if (debugToggle) { debugToggle.addEventListener('change', (e) => this.updateDebug(e.target.checked)); } if (updateProxy) { updateProxy.addEventListener('click', () => this.updateProxyUrl()); } if (clearProxy) { clearProxy.addEventListener('click', () => this.clearProxyUrl()); } if (updateRetry) { updateRetry.addEventListener('click', () => this.updateRequestRetry()); } if (switchProjectToggle) { switchProjectToggle.addEventListener('change', (e) => this.updateSwitchProject(e.target.checked)); } if (switchPreviewToggle) { switchPreviewToggle.addEventListener('change', (e) => this.updateSwitchPreviewModel(e.target.checked)); } if (allowLocalhostToggle) { allowLocalhostToggle.addEventListener('change', (e) => this.updateAllowLocalhost(e.target.checked)); } // API 密钥管理 const addApiKey = document.getElementById('add-api-key'); const addGeminiKey = document.getElementById('add-gemini-key'); const addCodexKey = document.getElementById('add-codex-key'); const addClaudeKey = document.getElementById('add-claude-key'); const addOpenaiProvider = document.getElementById('add-openai-provider'); if (addApiKey) { addApiKey.addEventListener('click', () => this.showAddApiKeyModal()); } if (addGeminiKey) { addGeminiKey.addEventListener('click', () => this.showAddGeminiKeyModal()); } if (addCodexKey) { addCodexKey.addEventListener('click', () => this.showAddCodexKeyModal()); } if (addClaudeKey) { addClaudeKey.addEventListener('click', () => this.showAddClaudeKeyModal()); } if (addOpenaiProvider) { addOpenaiProvider.addEventListener('click', () => this.showAddOpenAIProviderModal()); } // Gemini Web Token const geminiWebTokenBtn = document.getElementById('gemini-web-token-btn'); if (geminiWebTokenBtn) { geminiWebTokenBtn.addEventListener('click', () => this.showGeminiWebTokenModal()); } // 认证文件管理 const uploadAuthFile = document.getElementById('upload-auth-file'); const deleteAllAuthFiles = document.getElementById('delete-all-auth-files'); const authFileInput = document.getElementById('auth-file-input'); if (uploadAuthFile) { uploadAuthFile.addEventListener('click', () => this.uploadAuthFile()); } if (deleteAllAuthFiles) { deleteAllAuthFiles.addEventListener('click', () => this.deleteAllAuthFiles()); } if (authFileInput) { authFileInput.addEventListener('change', (e) => this.handleFileUpload(e)); } // 使用统计 const refreshUsageStats = document.getElementById('refresh-usage-stats'); const requestsHourBtn = document.getElementById('requests-hour-btn'); const requestsDayBtn = document.getElementById('requests-day-btn'); const tokensHourBtn = document.getElementById('tokens-hour-btn'); const tokensDayBtn = document.getElementById('tokens-day-btn'); if (refreshUsageStats) { refreshUsageStats.addEventListener('click', () => this.loadUsageStats()); } if (requestsHourBtn) { requestsHourBtn.addEventListener('click', () => this.switchRequestsPeriod('hour')); } if (requestsDayBtn) { requestsDayBtn.addEventListener('click', () => this.switchRequestsPeriod('day')); } if (tokensHourBtn) { tokensHourBtn.addEventListener('click', () => this.switchTokensPeriod('hour')); } if (tokensDayBtn) { tokensDayBtn.addEventListener('click', () => this.switchTokensPeriod('day')); } // 模态框 const closeBtn = document.querySelector('.close'); if (closeBtn) { closeBtn.addEventListener('click', () => this.closeModal()); } window.addEventListener('click', (e) => { const modal = document.getElementById('modal'); if (modal && e.target === modal) { this.closeModal(); } }); } // 设置导航 setupNavigation() { const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); // 移除所有活动状态 navItems.forEach(nav => nav.classList.remove('active')); document.querySelectorAll('.content-section').forEach(section => section.classList.remove('active')); // 添加活动状态 item.classList.add('active'); const sectionId = item.getAttribute('data-section'); document.getElementById(sectionId).classList.add('active'); }); }); } // 设置语言切换 setupLanguageSwitcher() { const loginToggle = document.getElementById('language-toggle'); const mainToggle = document.getElementById('language-toggle-main'); if (loginToggle) { loginToggle.addEventListener('click', () => this.toggleLanguage()); } if (mainToggle) { mainToggle.addEventListener('click', () => this.toggleLanguage()); } } // 设置主题切换 setupThemeSwitcher() { const loginToggle = document.getElementById('theme-toggle'); const mainToggle = document.getElementById('theme-toggle-main'); if (loginToggle) { loginToggle.addEventListener('click', () => this.toggleTheme()); } if (mainToggle) { mainToggle.addEventListener('click', () => this.toggleTheme()); } } // 切换语言 toggleLanguage() { const currentLang = i18n.currentLanguage; const newLang = currentLang === 'zh-CN' ? 'en-US' : 'zh-CN'; i18n.setLanguage(newLang); // 更新主题按钮文本 this.updateThemeButtons(); // 更新连接状态显示 this.updateConnectionStatus(); // 重新加载所有数据以更新动态内容 if (this.isLoggedIn && this.isConnected) { this.loadAllData(true); } } // 规范化基础地址,移除尾部斜杠与 /v0/management normalizeBase(input) { let base = (input || '').trim(); if (!base) return ''; // 若用户粘贴了完整地址,剥离后缀 base = base.replace(/\/?v0\/management\/?$/i, ''); base = base.replace(/\/+$/i, ''); // 自动补 http:// if (!/^https?:\/\//i.test(base)) { base = 'http://' + base; } return base; } // 由基础地址生成完整管理 API 地址 computeApiUrl(base) { const b = this.normalizeBase(base); if (!b) return ''; return b.replace(/\/$/, '') + '/v0/management'; } setApiBase(newBase) { this.apiBase = this.normalizeBase(newBase); this.apiUrl = this.computeApiUrl(this.apiBase); localStorage.setItem('apiBase', this.apiBase); localStorage.setItem('apiUrl', this.apiUrl); // 兼容旧字段 } // 加载设置(简化版,仅加载内部状态) loadSettings() { const savedBase = localStorage.getItem('apiBase'); const savedUrl = localStorage.getItem('apiUrl'); const savedKey = localStorage.getItem('managementKey'); // 只设置内部状态,不操作DOM元素 if (savedBase) { this.setApiBase(savedBase); } else if (savedUrl) { const base = (savedUrl || '').replace(/\/?v0\/management\/?$/i, ''); this.setApiBase(base); } else { this.setApiBase(this.apiBase); } if (savedKey) { this.managementKey = savedKey; } } // API 请求方法 async makeRequest(endpoint, options = {}) { const url = `${this.apiUrl}${endpoint}`; const headers = { 'Authorization': `Bearer ${this.managementKey}`, 'Content-Type': 'application/json', ...options.headers }; try { const response = await fetch(url, { ...options, headers }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || `HTTP ${response.status}`); } return await response.json(); } catch (error) { console.error('API请求失败:', error); throw error; } } // 显示通知 showNotification(message, type = 'info') { const notification = document.getElementById('notification'); notification.textContent = message; notification.className = `notification ${type}`; notification.classList.add('show'); setTimeout(() => { notification.classList.remove('show'); }, 3000); } // 密钥可见性切换 toggleKeyVisibility() { const keyInput = document.getElementById('management-key'); const toggleButton = document.getElementById('toggle-key-visibility'); if (keyInput.type === 'password') { keyInput.type = 'text'; toggleButton.innerHTML = ''; } else { keyInput.type = 'password'; toggleButton.innerHTML = ''; } } // 测试连接(简化版,用于内部调用) async testConnection() { try { await this.makeRequest('/debug'); this.isConnected = true; this.updateConnectionStatus(); this.startStatusUpdateTimer(); await this.loadAllData(); return true; } catch (error) { this.isConnected = false; this.updateConnectionStatus(); this.stopStatusUpdateTimer(); throw error; } } // 更新连接状态 updateConnectionStatus() { const statusButton = document.getElementById('connection-status'); const apiStatus = document.getElementById('api-status'); const configStatus = document.getElementById('config-status'); const lastUpdate = document.getElementById('last-update'); if (this.isConnected) { statusButton.innerHTML = ` ${i18n.t('common.connected')}`; statusButton.className = 'btn btn-success'; apiStatus.textContent = i18n.t('common.connected'); // 更新配置状态 if (this.isCacheValid()) { const cacheAge = Math.floor((Date.now() - this.cacheTimestamp) / 1000); configStatus.textContent = `${i18n.t('system_info.cache_data')} (${cacheAge}${i18n.t('system_info.seconds_ago')})`; configStatus.style.color = '#f59e0b'; // 橙色表示缓存 } else if (this.configCache) { configStatus.textContent = i18n.t('system_info.real_time_data'); configStatus.style.color = '#10b981'; // 绿色表示实时 } else { configStatus.textContent = i18n.t('system_info.not_loaded'); configStatus.style.color = '#6b7280'; // 灰色表示未加载 } } else { statusButton.innerHTML = ` ${i18n.t('common.disconnected')}`; statusButton.className = 'btn btn-danger'; apiStatus.textContent = i18n.t('common.disconnected'); configStatus.textContent = i18n.t('system_info.not_loaded'); configStatus.style.color = '#6b7280'; } lastUpdate.textContent = new Date().toLocaleString('zh-CN'); // 更新连接信息显示 this.updateConnectionInfo(); } // 检查连接状态 async checkConnectionStatus() { await this.testConnection(); } // 刷新所有数据 async refreshAllData() { if (!this.isConnected) { this.showNotification(i18n.t('notification.connection_required'), 'error'); return; } const button = document.getElementById('refresh-all'); const originalText = button.innerHTML; button.innerHTML = ` ${i18n.t('common.loading')}`; button.disabled = true; try { // 强制刷新,清除缓存 await this.loadAllData(true); this.showNotification(i18n.t('notification.data_refreshed'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.refresh_failed')}: ${error.message}`, 'error'); } finally { button.innerHTML = originalText; button.disabled = false; } } // 检查缓存是否有效 isCacheValid() { if (!this.configCache || !this.cacheTimestamp) { return false; } return (Date.now() - this.cacheTimestamp) < this.cacheExpiry; } // 获取配置(优先使用缓存) async getConfig(forceRefresh = false) { if (!forceRefresh && this.isCacheValid()) { this.updateConnectionStatus(); // 更新状态显示 return this.configCache; } try { const config = await this.makeRequest('/config'); this.configCache = config; this.cacheTimestamp = Date.now(); this.updateConnectionStatus(); // 更新状态显示 return config; } catch (error) { console.error('获取配置失败:', error); throw error; } } // 清除缓存 clearCache() { this.configCache = null; this.cacheTimestamp = null; } // 启动状态更新定时器 startStatusUpdateTimer() { if (this.statusUpdateTimer) { clearInterval(this.statusUpdateTimer); } this.statusUpdateTimer = setInterval(() => { if (this.isConnected) { this.updateConnectionStatus(); } }, 1000); // 每秒更新一次 } // 停止状态更新定时器 stopStatusUpdateTimer() { if (this.statusUpdateTimer) { clearInterval(this.statusUpdateTimer); this.statusUpdateTimer = null; } } // 加载所有数据 - 使用新的 /config 端点一次性获取所有配置 async loadAllData(forceRefresh = false) { try { console.log('使用新的 /config 端点加载所有配置...'); // 使用新的 /config 端点一次性获取所有配置 const config = await this.getConfig(forceRefresh); // 从配置中提取并设置各个设置项 this.updateSettingsFromConfig(config); // 认证文件需要单独加载,因为不在配置中 await this.loadAuthFiles(); // 使用统计需要单独加载 await this.loadUsageStats(); console.log('配置加载完成,使用缓存:', !forceRefresh && this.isCacheValid()); } catch (error) { console.error('加载配置失败:', error); console.log('回退到逐个加载方式...'); // 如果新方法失败,回退到原来的逐个加载方式 await this.loadAllDataLegacy(); } } // 从配置对象更新所有设置 updateSettingsFromConfig(config) { // 调试设置 if (config.debug !== undefined) { document.getElementById('debug-toggle').checked = config.debug; } // 代理设置 if (config['proxy-url'] !== undefined) { document.getElementById('proxy-url').value = config['proxy-url'] || ''; } // 请求重试设置 if (config['request-retry'] !== undefined) { document.getElementById('request-retry').value = config['request-retry']; } // 配额超出行为 if (config['quota-exceeded']) { if (config['quota-exceeded']['switch-project'] !== undefined) { document.getElementById('switch-project-toggle').checked = config['quota-exceeded']['switch-project']; } if (config['quota-exceeded']['switch-preview-model'] !== undefined) { document.getElementById('switch-preview-model-toggle').checked = config['quota-exceeded']['switch-preview-model']; } } // 本地访问设置 if (config['allow-localhost-unauthenticated'] !== undefined) { document.getElementById('allow-localhost-toggle').checked = config['allow-localhost-unauthenticated']; } // API 密钥 if (config['api-keys']) { this.renderApiKeys(config['api-keys']); } // Gemini 密钥 if (config['generative-language-api-key']) { this.renderGeminiKeys(config['generative-language-api-key']); } // Codex 密钥 if (config['codex-api-key']) { this.renderCodexKeys(config['codex-api-key']); } // Claude 密钥 if (config['claude-api-key']) { this.renderClaudeKeys(config['claude-api-key']); } // OpenAI 兼容提供商 if (config['openai-compatibility']) { this.renderOpenAIProviders(config['openai-compatibility']); } } // 回退方法:原来的逐个加载方式 async loadAllDataLegacy() { await Promise.all([ this.loadDebugSettings(), this.loadProxySettings(), this.loadRetrySettings(), this.loadQuotaSettings(), this.loadLocalhostSettings(), this.loadApiKeys(), this.loadGeminiKeys(), this.loadCodexKeys(), this.loadClaudeKeys(), this.loadOpenAIProviders(), this.loadAuthFiles() ]); } // 加载调试设置 async loadDebugSettings() { try { const config = await this.getConfig(); if (config.debug !== undefined) { document.getElementById('debug-toggle').checked = config.debug; } } catch (error) { console.error('加载调试设置失败:', error); } } // 更新调试设置 async updateDebug(enabled) { try { await this.makeRequest('/debug', { method: 'PUT', body: JSON.stringify({ value: enabled }) }); this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.debug_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); // 恢复原状态 document.getElementById('debug-toggle').checked = !enabled; } } // 加载代理设置 async loadProxySettings() { try { const config = await this.getConfig(); if (config['proxy-url'] !== undefined) { document.getElementById('proxy-url').value = config['proxy-url'] || ''; } } catch (error) { console.error('加载代理设置失败:', error); } } // 更新代理URL async updateProxyUrl() { const proxyUrl = document.getElementById('proxy-url').value.trim(); try { await this.makeRequest('/proxy-url', { method: 'PUT', body: JSON.stringify({ value: proxyUrl }) }); this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.proxy_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); } } // 清空代理URL async clearProxyUrl() { try { await this.makeRequest('/proxy-url', { method: 'DELETE' }); document.getElementById('proxy-url').value = ''; this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.proxy_cleared'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); } } // 加载重试设置 async loadRetrySettings() { try { const config = await this.getConfig(); if (config['request-retry'] !== undefined) { document.getElementById('request-retry').value = config['request-retry']; } } catch (error) { console.error('加载重试设置失败:', error); } } // 更新请求重试 async updateRequestRetry() { const retryCount = parseInt(document.getElementById('request-retry').value); try { await this.makeRequest('/request-retry', { method: 'PUT', body: JSON.stringify({ value: retryCount }) }); this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.retry_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); } } // 加载配额设置 async loadQuotaSettings() { try { const config = await this.getConfig(); if (config['quota-exceeded']) { if (config['quota-exceeded']['switch-project'] !== undefined) { document.getElementById('switch-project-toggle').checked = config['quota-exceeded']['switch-project']; } if (config['quota-exceeded']['switch-preview-model'] !== undefined) { document.getElementById('switch-preview-model-toggle').checked = config['quota-exceeded']['switch-preview-model']; } } } catch (error) { console.error('加载配额设置失败:', error); } } // 更新项目切换设置 async updateSwitchProject(enabled) { try { await this.makeRequest('/quota-exceeded/switch-project', { method: 'PUT', body: JSON.stringify({ value: enabled }) }); this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.quota_switch_project_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); document.getElementById('switch-project-toggle').checked = !enabled; } } // 更新预览模型切换设置 async updateSwitchPreviewModel(enabled) { try { await this.makeRequest('/quota-exceeded/switch-preview-model', { method: 'PUT', body: JSON.stringify({ value: enabled }) }); this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.quota_switch_preview_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); document.getElementById('switch-preview-model-toggle').checked = !enabled; } } // 加载本地访问设置 async loadLocalhostSettings() { try { const config = await this.getConfig(); if (config['allow-localhost-unauthenticated'] !== undefined) { document.getElementById('allow-localhost-toggle').checked = config['allow-localhost-unauthenticated']; } } catch (error) { console.error('加载本地访问设置失败:', error); } } // 更新本地访问设置 async updateAllowLocalhost(enabled) { try { await this.makeRequest('/allow-localhost-unauthenticated', { method: 'PUT', body: JSON.stringify({ value: enabled }) }); this.clearCache(); // 清除缓存 this.showNotification(i18n.t('notification.localhost_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); document.getElementById('allow-localhost-toggle').checked = !enabled; } } // 加载API密钥 async loadApiKeys() { try { const config = await this.getConfig(); if (config['api-keys']) { this.renderApiKeys(config['api-keys']); } } catch (error) { console.error('加载API密钥失败:', error); } } // 渲染API密钥列表 renderApiKeys(keys) { const container = document.getElementById('api-keys-list'); if (keys.length === 0) { container.innerHTML = `${i18n.t('api_keys.empty_desc')}
${i18n.t('ai_providers.gemini_empty_desc')}
${i18n.t('ai_providers.codex_empty_desc')}
${i18n.t('ai_providers.claude_empty_desc')}
${i18n.t('ai_providers.openai_empty_desc')}
${i18n.t('auth_files.empty_desc')}
| ${i18n.t('usage_stats.api_endpoint')} | ${i18n.t('usage_stats.requests_count')} | ${i18n.t('usage_stats.tokens_count')} | ${i18n.t('usage_stats.success_rate')} | ${i18n.t('usage_stats.models')} |
|---|---|---|---|---|
| ${endpoint} | ${totalRequests} | ${apiData.total_tokens || 0} | ${successRate !== null ? successRate + '%' : '-'} | ${modelsHtml || '-'} |