diff --git a/config.py b/config.py index 11114c4d..9056efd1 100644 --- a/config.py +++ b/config.py @@ -1,26 +1,26 @@ """ Configuration constants for the Geminicli2api proxy server. Centralizes all configuration to avoid duplication across modules. - -- 启动时加载一次配置到内存 -- 修改配置时调用 reload_config() 重新从数据库加载 """ import os from typing import Any, Optional +from dotenv import load_dotenv +load_dotenv() +from src.i18n import ts -# 全局配置缓存 +# {ts(f"id_649")} _config_cache: dict[str, Any] = {} _config_initialized = False # Client Configuration -# 需要自动封禁的错误码 (默认值,可通过环境变量或配置覆盖) +# {ts(f"id_651")} ({ts('id_650')}) AUTO_BAN_ERROR_CODES = [403] -# ====================== 环境变量映射表 ====================== -# 统一维护环境变量名和配置键名的映射关系 -# 格式: "环境变量名": "配置键名" +# ====================== {ts(f"id_652")} ====================== +# {ts(f"id_653")} +# {ts(f"id_57")}: "{ts('id_654')}": f"{ts('id_655')}" ENV_MAPPINGS = { "CODE_ASSIST_ENDPOINT": "code_assist_endpoint", "CREDENTIALS_DIR": "credentials_dir", @@ -44,13 +44,14 @@ "API_PASSWORD": "api_password", "PANEL_PASSWORD": "panel_password", "PASSWORD": "password", + "I18N_LANG": "i18n_lang", } -# ====================== 配置系统 ====================== +# ====================== {ts(f"id_656")} ====================== async def init_config(): - """初始化配置缓存(启动时调用一次)""" + f"""{ts('id_657')}""" global _config_cache, _config_initialized if _config_initialized: @@ -62,24 +63,24 @@ async def init_config(): _config_cache = await storage_adapter.get_all_config() _config_initialized = True except Exception: - # 初始化失败时使用空缓存 + # {ts(f"id_658")} _config_cache = {} _config_initialized = True async def reload_config(): - """重新加载配置(修改配置后调用)""" + f"""{ts('id_659')}""" global _config_cache, _config_initialized try: from src.storage_adapter import get_storage_adapter storage_adapter = await get_storage_adapter() - # 如果后端支持 reload_config_cache,调用它 + # {ts(f"id_660")} reload_config_cache{ts('id_661')} if hasattr(storage_adapter._backend, 'reload_config_cache'): await storage_adapter._backend.reload_config_cache() - # 重新加载配置缓存 + # {ts(f"id_662")} _config_cache = await storage_adapter.get_all_config() _config_initialized = True except Exception: @@ -87,13 +88,13 @@ async def reload_config(): def _get_cached_config(key: str, default: Any = None) -> Any: - """从内存缓存获取配置(同步)""" + f"""{ts('id_663')}""" return _config_cache.get(key, default) async def get_config_value(key: str, default: Any = None, env_var: Optional[str] = None) -> Any: """Get configuration value with priority: ENV > Storage > default.""" - # 确保配置已初始化 + # {ts(f"id_664")} if not _config_initialized: await init_config() @@ -235,12 +236,12 @@ async def get_api_password() -> str: Database config key: api_password Default: Uses PASSWORD env var for compatibility, otherwise 'pwd' """ - # 优先使用 API_PASSWORD,如果没有则使用通用 PASSWORD 保证兼容性 + # {ts(f"id_667")} API_PASSWORD{ts('id_665')} PASSWORD {ts('id_666')} api_password = await get_config_value("api_password", None, "API_PASSWORD") if api_password is not None: return str(api_password) - # 兼容性:使用通用密码 + # {ts(f"id_668")} return str(await get_config_value("password", "pwd", "PASSWORD")) @@ -252,12 +253,12 @@ async def get_panel_password() -> str: Database config key: panel_password Default: Uses PASSWORD env var for compatibility, otherwise 'pwd' """ - # 优先使用 PANEL_PASSWORD,如果没有则使用通用 PASSWORD 保证兼容性 + # {ts(f"id_667")} PANEL_PASSWORD{ts('id_665')} PASSWORD {ts('id_666')} panel_password = await get_config_value("panel_password", None, "PANEL_PASSWORD") if panel_password is not None: return str(panel_password) - # 兼容性:使用通用密码 + # {ts(f"id_668")} return str(await get_config_value("password", "pwd", "PASSWORD")) @@ -302,8 +303,8 @@ async def get_compatibility_mode_enabled() -> bool: """ Get compatibility mode setting. - 兼容性模式:启用后所有system消息全部转换成user,停用system_instructions。 - 该选项可能会降低模型理解能力,但是能避免流式空回的情况。 + {ts(f"id_669")}system{ts('id_670')}user{ts('id_671')}system_instructions{ts('id_672')} + {ts(f"id_673")} Environment variable: COMPATIBILITY_MODE Database config key: compatibility_mode_enabled @@ -320,8 +321,8 @@ async def get_return_thoughts_to_frontend() -> bool: """ Get return thoughts to frontend setting. - 控制是否将思维链返回到前端。 - 启用后,思维链会在响应中返回;禁用后,思维链会在响应中被过滤掉。 + {ts(f"id_674")} + {ts(f"id_675")} Environment variable: RETURN_THOUGHTS_TO_FRONTEND Database config key: return_thoughts_to_frontend @@ -338,8 +339,8 @@ async def get_antigravity_stream2nostream() -> bool: """ Get use stream for non-stream setting. - 控制antigravity非流式请求是否使用流式API并收集为完整响应。 - 启用后,非流式请求将在后端使用流式API,然后收集所有块后再返回完整响应。 + {ts(f"id_678")}antigravity{ts('id_676')}API{ts('id_677')} + {ts(f"id_680")}API{ts('id_679')} Environment variable: ANTIGRAVITY_STREAM2NOSTREAM Database config key: antigravity_stream2nostream @@ -356,7 +357,7 @@ async def get_oauth_proxy_url() -> str: """ Get OAuth proxy URL setting. - 用于Google OAuth2认证的代理URL。 + {ts(f"id_14")}Google OAuth2{ts('id_59')}URL{ts('id_672')} Environment variable: OAUTH_PROXY_URL Database config key: oauth_proxy_url @@ -373,7 +374,7 @@ async def get_googleapis_proxy_url() -> str: """ Get Google APIs proxy URL setting. - 用于Google APIs调用的代理URL。 + {ts(f"id_14")}Google APIs{ts('id_60')}URL{ts('id_672')} Environment variable: GOOGLEAPIS_PROXY_URL Database config key: googleapis_proxy_url @@ -390,7 +391,7 @@ async def get_resource_manager_api_url() -> str: """ Get Google Cloud Resource Manager API URL setting. - 用于Google Cloud Resource Manager API的URL。 + {ts(f"id_14")}Google Cloud Resource Manager API{ts('id_61')}URL{ts('id_672')} Environment variable: RESOURCE_MANAGER_API_URL Database config key: resource_manager_api_url @@ -409,7 +410,7 @@ async def get_service_usage_api_url() -> str: """ Get Google Cloud Service Usage API URL setting. - 用于Google Cloud Service Usage API的URL。 + {ts(f"id_14")}Google Cloud Service Usage API{ts('id_61')}URL{ts('id_672')} Environment variable: SERVICE_USAGE_API_URL Database config key: service_usage_api_url @@ -426,7 +427,7 @@ async def get_antigravity_api_url() -> str: """ Get Antigravity API URL setting. - 用于Google Antigravity API的URL。 + {ts(f"id_14")}Google Antigravity API{ts('id_61')}URL{ts('id_672')} Environment variable: ANTIGRAVITY_API_URL Database config key: antigravity_api_url @@ -439,3 +440,7 @@ async def get_antigravity_api_url() -> str: "ANTIGRAVITY_API_URL", ) ) + +async def get_i18n_lang() -> str: + """Get i18n language setting.""" + return str(await get_config_value("i18n_lang", "zh", "I18N_LANG")) diff --git a/front/common.js b/front/common.js index b07b9031..9868505d 100644 --- a/front/common.js +++ b/front/common.js @@ -1,47 +1,47 @@ // ===================================================================== -// GCLI2API 控制面板公共JavaScript模块 +// GCLI2API $id_701JavaScript$id_702 // ===================================================================== // ===================================================================== -// 全局状态管理 +// $id_703 // ===================================================================== const AppState = { - // 认证相关 + // $id_704 authToken: '', authInProgress: false, currentProjectId: '', - // Antigravity认证 + // Antigravity$id_251 antigravityAuthState: null, antigravityAuthInProgress: false, - // 凭证管理 + // $id_705 creds: createCredsManager('normal'), antigravityCreds: createCredsManager('antigravity'), - // 文件上传 + // $id_706 uploadFiles: createUploadManager('normal'), antigravityUploadFiles: createUploadManager('antigravity'), - // 配置管理 + // $id_707 currentConfig: {}, envLockedFields: new Set(), - // 日志管理 + // $id_708 logWebSocket: null, allLogs: [], filteredLogs: [], currentLogFilter: 'all', - // 使用统计 + // $id_709 usageStatsData: {}, - // 冷却倒计时 + // $id_710 cooldownTimerInterval: null }; // ===================================================================== -// 凭证管理器工厂 +// $id_711 // ===================================================================== function createCredsManager(type) { const modeParam = type === 'antigravity' ? 'mode=antigravity' : 'mode=geminicli'; @@ -59,7 +59,7 @@ function createCredsManager(type) { currentCooldownFilter: 'all', statsData: { total: 0, normal: 0, disabled: 0 }, - // API端点 + // API$id_58 getEndpoint: (action) => { const endpoints = { status: `./creds/status`, @@ -77,20 +77,20 @@ function createCredsManager(type) { return endpoints[action] || ''; }, - // 获取mode参数 + // $id_712mode$id_226 getModeParam: () => modeParam, - // DOM元素ID前缀 + // DOM$id_713ID$id_365 getElementId: (suffix) => { - // 普通凭证的ID首字母小写,如 credsLoading - // Antigravity的ID是 antigravity + 首字母大写,如 antigravityCredsLoading + // $id_714ID$id_715,$id_716 credsLoading + // Antigravity$id_61ID$id_150 antigravity + $id_717,$id_716 antigravityCredsLoading if (type === 'antigravity') { return 'antigravity' + suffix.charAt(0).toUpperCase() + suffix.slice(1); } return suffix.charAt(0).toLowerCase() + suffix.slice(1); }, - // 刷新凭证列表 + // $id_718 async refresh() { const loading = document.getElementById(this.getElementId('CredsLoading')); const list = document.getElementById(this.getElementId('CredsList')); @@ -125,11 +125,11 @@ function createCredsManager(type) { }); this.totalCount = data.total; - // 使用后端返回的全局统计数据 + // $id_719 if (data.stats) { this.statsData = data.stats; } else { - // 兼容旧版本后端 + // $id_720 this.calculateStats(); } this.updateStatsDisplay(); @@ -137,22 +137,22 @@ function createCredsManager(type) { this.renderList(); this.updatePagination(); - let msg = `已加载 ${data.total} 个${type === 'antigravity' ? 'Antigravity' : ''}凭证文件`; + let msg = `$id_722 ${data.total} $id_723${type === 'antigravity' ? 'Antigravity' : ''}$id_721`; if (this.currentStatusFilter !== 'all') { - msg += ` (筛选: ${this.currentStatusFilter === 'enabled' ? '仅启用' : '仅禁用'})`; + msg += ` ($id_726: ${this.currentStatusFilter === 'enabled' ? '$id_724' : '$id_725'})`; } showStatus(msg, 'success'); } else { - showStatus(`加载失败: ${data.detail || data.error || '未知错误'}`, 'error'); + showStatus(`$id_728: ${data.detail || data.error || '$id_727'}`, 'error'); } } catch (error) { - showStatus(`网络错误: ${error.message}`, 'error'); + showStatus(`$id_729: ${error.message}`, 'error'); } finally { loading.style.display = 'none'; } }, - // 计算统计数据(仅用于兼容旧版本后端) + // $id_730 calculateStats() { this.statsData = { total: this.totalCount, normal: 0, disabled: 0 }; Object.values(this.data).forEach(credInfo => { @@ -164,14 +164,14 @@ function createCredsManager(type) { }); }, - // 更新统计显示 + // $id_731 updateStatsDisplay() { document.getElementById(this.getElementId('StatTotal')).textContent = this.statsData.total; document.getElementById(this.getElementId('StatNormal')).textContent = this.statsData.normal; document.getElementById(this.getElementId('StatDisabled')).textContent = this.statsData.disabled; }, - // 渲染凭证列表 + // $id_732 renderList() { const list = document.getElementById(this.getElementId('CredsList')); list.innerHTML = ''; @@ -179,7 +179,7 @@ function createCredsManager(type) { const entries = Object.entries(this.filteredData); if (entries.length === 0) { - const msg = this.totalCount === 0 ? '暂无凭证文件' : '当前筛选条件下暂无数据'; + const msg = this.totalCount === 0 ? '$id_734' : '$id_733'; list.innerHTML = `
${msg}
`; document.getElementById(this.getElementId('PaginationContainer')).style.display = 'none'; return; @@ -194,25 +194,25 @@ function createCredsManager(type) { this.updateBatchControls(); }, - // 获取总页数 + // $id_735 getTotalPages() { return Math.ceil(this.totalCount / this.pageSize); }, - // 更新分页信息 + // $id_736 updatePagination() { const totalPages = this.getTotalPages(); const startItem = (this.currentPage - 1) * this.pageSize + 1; const endItem = Math.min(this.currentPage * this.pageSize, this.totalCount); document.getElementById(this.getElementId('PaginationInfo')).textContent = - `第 ${this.currentPage} 页,共 ${totalPages} 页 (显示 ${startItem}-${endItem},共 ${this.totalCount} 项)`; + `$id_742 ${this.currentPage} $id_737 ${totalPages} $id_740 ($id_739 ${startItem}-${endItem}$id_738 ${this.totalCount} $id_741)`; document.getElementById(this.getElementId('PrevPageBtn')).disabled = this.currentPage <= 1; document.getElementById(this.getElementId('NextPageBtn')).disabled = this.currentPage >= totalPages; }, - // 切换页面 + // $id_743 changePage(direction) { const newPage = this.currentPage + direction; if (newPage >= 1 && newPage <= this.getTotalPages()) { @@ -221,14 +221,14 @@ function createCredsManager(type) { } }, - // 改变每页大小 + // $id_744 changePageSize() { this.pageSize = parseInt(document.getElementById(this.getElementId('PageSizeSelect')).value); this.currentPage = 1; this.refresh(); }, - // 应用状态筛选 + // $id_745 applyStatusFilter() { this.currentStatusFilter = document.getElementById(this.getElementId('StatusFilter')).value; const errorCodeFilterEl = document.getElementById(this.getElementId('ErrorCodeFilter')); @@ -239,10 +239,10 @@ function createCredsManager(type) { this.refresh(); }, - // 更新批量控件 + // $id_746 updateBatchControls() { const selectedCount = this.selectedFiles.size; - document.getElementById(this.getElementId('SelectedCount')).textContent = `已选择 ${selectedCount} 项`; + document.getElementById(this.getElementId('SelectedCount')).textContent = `$id_747 ${selectedCount} $id_741`; const batchBtns = ['Enable', 'Disable', 'Delete', 'Verify'].map(action => document.getElementById(this.getElementId(`Batch${action}Btn`)) @@ -271,7 +271,7 @@ function createCredsManager(type) { }); }, - // 凭证操作 + // $id_748 async action(filename, action) { try { const response = await fetch(`${this.getEndpoint('action')}?${this.getModeParam()}`, { @@ -283,34 +283,34 @@ function createCredsManager(type) { const data = await response.json(); if (response.ok) { - showStatus(data.message || `操作成功: ${action}`, 'success'); + showStatus(data.message || `$id_749: ${action}`, 'success'); await this.refresh(); } else { - showStatus(`操作失败: ${data.detail || data.error || '未知错误'}`, 'error'); + showStatus(`$id_750: ${data.detail || data.error || '$id_727'}`, 'error'); } } catch (error) { - showStatus(`网络错误: ${error.message}`, 'error'); + showStatus(`$id_729: ${error.message}`, 'error'); } }, - // 批量操作 + // $id_751 async batchAction(action) { const selectedFiles = Array.from(this.selectedFiles); if (selectedFiles.length === 0) { - showStatus('请先选择要操作的文件', 'error'); + showStatus('$id_752', 'error'); return; } - const actionNames = { enable: '启用', disable: '禁用', delete: '删除' }; + const actionNames = { enable: '$id_126', disable: '$id_300', delete: '$id_753' }; const confirmMsg = action === 'delete' - ? `确定要删除选中的 ${selectedFiles.length} 个文件吗?\n注意:此操作不可恢复!` - : `确定要${actionNames[action]}选中的 ${selectedFiles.length} 个文件吗?`; + ? `$id_755 ${selectedFiles.length} $id_756\n$id_754` + : `$id_757${actionNames[action]}$id_758 ${selectedFiles.length} $id_756`; if (!confirm(confirmMsg)) return; try { - showStatus(`正在执行批量${actionNames[action]}操作...`, 'info'); + showStatus(`$id_759${actionNames[action]}$id_760...`, 'info'); const response = await fetch(`${this.getEndpoint('batchAction')}?${this.getModeParam()}`, { method: 'POST', @@ -322,22 +322,22 @@ function createCredsManager(type) { if (response.ok) { const successCount = data.success_count || data.succeeded; - showStatus(`批量操作完成:成功处理 ${successCount}/${selectedFiles.length} 个文件`, 'success'); + showStatus(`$id_761 ${successCount}/${selectedFiles.length} $id_762`, 'success'); this.selectedFiles.clear(); this.updateBatchControls(); await this.refresh(); } else { - showStatus(`批量操作失败: ${data.detail || data.error || '未知错误'}`, 'error'); + showStatus(`$id_763: ${data.detail || data.error || '$id_727'}`, 'error'); } } catch (error) { - showStatus(`批量操作网络错误: ${error.message}`, 'error'); + showStatus(`$id_764: ${error.message}`, 'error'); } } }; } // ===================================================================== -// 文件上传管理器工厂 +// $id_765 // ===================================================================== function createUploadManager(type) { const modeParam = type === 'antigravity' ? 'mode=antigravity' : 'mode=geminicli'; @@ -348,8 +348,8 @@ function createUploadManager(type) { selectedFiles: [], getElementId: (suffix) => { - // 普通上传的ID首字母小写,如 fileList - // Antigravity的ID是 antigravity + 首字母大写,如 antigravityFileList + // $id_766ID$id_715,$id_716 fileList + // Antigravity$id_61ID$id_150 antigravity + $id_717,$id_716 antigravityFileList if (type === 'antigravity') { return 'antigravity' + suffix.charAt(0).toUpperCase() + suffix.slice(1); } @@ -370,7 +370,7 @@ function createUploadManager(type) { this.selectedFiles.push(file); } } else { - showStatus(`文件 ${file.name} 格式不支持,只支持JSON和ZIP文件`, 'error'); + showStatus(`$id_112 ${file.name} $id_767JSON$id_15ZIP$id_112`, 'error'); } }); this.updateFileList(); @@ -396,7 +396,7 @@ function createUploadManager(type) { this.selectedFiles.forEach((file, index) => { const isZip = file.name.endsWith('.zip'); const fileIcon = isZip ? '📦' : '📄'; - const fileType = isZip ? ' (ZIP压缩包)' : ' (JSON文件)'; + const fileType = isZip ? ' (ZIP$id_768)' : ' (JSON$id_112)'; const fileItem = document.createElement('div'); fileItem.className = 'file-item'; @@ -405,7 +405,7 @@ function createUploadManager(type) { ${fileIcon} ${file.name} (${formatFileSize(file.size)}${fileType}) - + `; list.appendChild(fileItem); }); @@ -423,7 +423,7 @@ function createUploadManager(type) { async upload() { if (this.selectedFiles.length === 0) { - showStatus('请选择要上传的文件', 'error'); + showStatus('$id_769', 'error'); return; } @@ -437,12 +437,12 @@ function createUploadManager(type) { this.selectedFiles.forEach(file => formData.append('files', file)); if (this.selectedFiles.some(f => f.name.endsWith('.zip'))) { - showStatus('正在上传并解压ZIP文件...', 'info'); + showStatus('$id_770ZIP$id_112...', 'info'); } try { const xhr = new XMLHttpRequest(); - xhr.timeout = 300000; // 5分钟 + xhr.timeout = 300000; // 5$id_771 xhr.upload.onprogress = (event) => { if (event.lengthComputable) { @@ -456,29 +456,29 @@ function createUploadManager(type) { if (xhr.status === 200) { try { const data = JSON.parse(xhr.responseText); - showStatus(`成功上传 ${data.uploaded_count} 个${type === 'antigravity' ? 'Antigravity' : ''}文件`, 'success'); + showStatus(`$id_772 ${data.uploaded_count} $id_723${type === 'antigravity' ? 'Antigravity' : ''}$id_112`, 'success'); this.clearFiles(); progressSection.classList.add('hidden'); } catch (e) { - showStatus('上传失败: 服务器响应格式错误', 'error'); + showStatus('$id_774: $id_773', 'error'); } } else { try { const error = JSON.parse(xhr.responseText); - showStatus(`上传失败: ${error.detail || error.error || '未知错误'}`, 'error'); + showStatus(`$id_774: ${error.detail || error.error || '$id_727'}`, 'error'); } catch (e) { - showStatus(`上传失败: HTTP ${xhr.status}`, 'error'); + showStatus(`$id_774: HTTP ${xhr.status}`, 'error'); } } }; xhr.onerror = () => { - showStatus(`上传失败:连接中断 - 可能原因:文件过多(${this.selectedFiles.length}个)或网络不稳定。建议分批上传。`, 'error'); + showStatus(`$id_776 - $id_777(${this.selectedFiles.length}$id_723)$id_775`, 'error'); progressSection.classList.add('hidden'); }; xhr.ontimeout = () => { - showStatus('上传失败:请求超时 - 文件处理时间过长,请减少文件数量或检查网络连接', 'error'); + showStatus('$id_779 - $id_778', 'error'); progressSection.classList.add('hidden'); }; @@ -486,37 +486,37 @@ function createUploadManager(type) { xhr.setRequestHeader('Authorization', `Bearer ${AppState.authToken}`); xhr.send(formData); } catch (error) { - showStatus(`上传失败: ${error.message}`, 'error'); + showStatus(`$id_774: ${error.message}`, 'error'); } } }; } // ===================================================================== -// 工具函数 +// $id_780 // ===================================================================== function showStatus(message, type = 'info') { const statusSection = document.getElementById('statusSection'); if (statusSection) { - // 清除之前的定时器 + // $id_781 if (window._statusTimeout) { clearTimeout(window._statusTimeout); } - // 创建新的 toast + // $id_782 toast statusSection.innerHTML = `' + JSON.stringify(result.credentials, null, 2) + ''; document.getElementById('credentialsSection').classList.remove('hidden'); } else if (result.requires_manual_project_id) { - showStatus('需要手动指定项目ID,请在高级选项中填入Google Cloud项目ID后重试', 'error'); + showStatus('$id_912ID$id_911Google Cloud$id_884ID$id_913', 'error'); } else if (result.requires_project_selection) { - let msg = '