mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 03:10:50 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b9abdf9b1 | ||
|
|
a208a484ff | ||
|
|
369cf52346 | ||
|
|
dcfffc716b | ||
|
|
7de5280824 | ||
|
|
86d60aad77 | ||
|
|
020fccc032 | ||
|
|
c162ab3a54 | ||
|
|
85d12e15d8 | ||
|
|
ebffb49f52 | ||
|
|
316c1ffc0d | ||
|
|
b3e54e7f14 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -27,6 +27,8 @@ jobs:
|
||||
|
||||
- name: Build all-in-one HTML
|
||||
run: npm run build
|
||||
env:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
|
||||
- name: Prepare release assets
|
||||
run: |
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -20,3 +20,8 @@ package-lock.json
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
CLAUDE.md
|
||||
.claude
|
||||
AGENTS.md
|
||||
.serena
|
||||
@@ -10,7 +10,7 @@ Example URL:
|
||||
https://remote.router-for.me/
|
||||
|
||||
Minimum required version: ≥ 6.0.0
|
||||
Recommended version: ≥ 6.0.19
|
||||
Recommended version: ≥ 6.1.3
|
||||
|
||||
Since version 6.0.19, the WebUI has been rolled into the main program. You can access it by going to `/management.html` on the external port after firing up the main project.
|
||||
|
||||
@@ -43,7 +43,6 @@ Since version 6.0.19, the WebUI has been rolled into the main program. You can a
|
||||
- Download existing authentication files
|
||||
- Delete single or all authentication files
|
||||
- Display file details
|
||||
- **Gemini Web Token**: Direct authentication using browser cookies
|
||||
|
||||
### Usage Statistics
|
||||
- **Real-time Analytics**: Track API usage with interactive charts
|
||||
|
||||
@@ -8,7 +8,9 @@ https://github.com/router-for-me/CLIProxyAPI
|
||||
https://remote.router-for.me/
|
||||
|
||||
最低可用版本 ≥ 6.0.0
|
||||
推荐版本 ≥ 6.0.19
|
||||
|
||||
推荐版本 ≥ 6.1.3
|
||||
|
||||
自6.0.19起WebUI已经集成在主程序中 可以通过主项目开启的外部端口的`/management.html`访问
|
||||
|
||||
## 功能特点
|
||||
@@ -40,7 +42,6 @@ https://remote.router-for.me/
|
||||
- 下载现有认证文件
|
||||
- 删除单个或所有认证文件
|
||||
- 显示文件详细信息
|
||||
- **Gemini Web Token**: 使用浏览器 Cookie 直接认证
|
||||
|
||||
### 使用统计
|
||||
- **实时分析**: 通过交互式图表跟踪 API 使用情况
|
||||
|
||||
34
build.js
34
build.js
@@ -49,6 +49,35 @@ function escapeForStyle(content) {
|
||||
return content.replace(/<\/(style)/gi, '<\\/$1');
|
||||
}
|
||||
|
||||
function getVersion() {
|
||||
// 1. 优先从环境变量获取(GitHub Actions 会设置)
|
||||
if (process.env.VERSION) {
|
||||
return process.env.VERSION;
|
||||
}
|
||||
|
||||
// 2. 尝试从 git tag 获取
|
||||
try {
|
||||
const { execSync } = require('child_process');
|
||||
const gitTag = execSync('git describe --tags --exact-match 2>/dev/null || git describe --tags 2>/dev/null || echo ""', { encoding: 'utf8' }).trim();
|
||||
if (gitTag) {
|
||||
return gitTag;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('无法从 git 获取版本号');
|
||||
}
|
||||
|
||||
// 3. 回退到 package.json
|
||||
try {
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
||||
return 'v' + packageJson.version;
|
||||
} catch (err) {
|
||||
console.warn('无法从 package.json 读取版本号');
|
||||
}
|
||||
|
||||
// 4. 最后使用默认值
|
||||
return 'v0.0.0-dev';
|
||||
}
|
||||
|
||||
function ensureDistDir() {
|
||||
if (fs.existsSync(distDir)) {
|
||||
fs.rmSync(distDir, { recursive: true, force: true });
|
||||
@@ -83,6 +112,11 @@ function build() {
|
||||
const i18n = escapeForScript(readFile(sourceFiles.i18n));
|
||||
const app = escapeForScript(readFile(sourceFiles.app));
|
||||
|
||||
// 获取版本号并替换
|
||||
const version = getVersion();
|
||||
console.log(`使用版本号: ${version}`);
|
||||
html = html.replace(/__VERSION__/g, version);
|
||||
|
||||
html = html.replace(
|
||||
'<link rel="stylesheet" href="styles.css">',
|
||||
`<style>
|
||||
|
||||
136
i18n.js
136
i18n.js
@@ -82,6 +82,8 @@ const i18n = {
|
||||
'nav.ai_providers': 'AI 提供商',
|
||||
'nav.auth_files': '认证文件',
|
||||
'nav.usage_stats': '使用统计',
|
||||
'nav.config_management': '配置管理',
|
||||
'nav.logs': '日志查看',
|
||||
'nav.system_info': '系统信息',
|
||||
|
||||
// 基础设置
|
||||
@@ -99,6 +101,10 @@ const i18n = {
|
||||
'basic_settings.quota_title': '配额超出行为',
|
||||
'basic_settings.quota_switch_project': '自动切换项目',
|
||||
'basic_settings.quota_switch_preview': '切换到预览模型',
|
||||
'basic_settings.usage_statistics_title': '使用统计',
|
||||
'basic_settings.usage_statistics_enable': '启用使用统计',
|
||||
'basic_settings.logging_title': '日志记录',
|
||||
'basic_settings.logging_to_file_enable': '启用日志记录到文件',
|
||||
|
||||
// API 密钥管理
|
||||
'api_keys.title': 'API 密钥管理',
|
||||
@@ -214,17 +220,6 @@ const i18n = {
|
||||
'auth_files.delete_all_success': '成功删除',
|
||||
'auth_files.files_count': '个文件',
|
||||
|
||||
// Gemini Web Token
|
||||
'auth_login.gemini_web_title': 'Gemini Web Token',
|
||||
'auth_login.gemini_web_button': '保存 Gemini Web Token',
|
||||
'auth_login.gemini_web_hint': '从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。',
|
||||
'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:',
|
||||
'auth_login.secure_1psid_placeholder': '输入 __Secure-1PSID cookie 值',
|
||||
'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:',
|
||||
'auth_login.secure_1psidts_placeholder': '输入 __Secure-1PSIDTS cookie 值',
|
||||
'auth_login.gemini_web_label_label': '标签 (可选):',
|
||||
'auth_login.gemini_web_label_placeholder': '输入标签名称 (可选)',
|
||||
'auth_login.gemini_web_saved': 'Gemini Web Token 保存成功',
|
||||
|
||||
// Codex OAuth
|
||||
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||
@@ -314,6 +309,45 @@ const i18n = {
|
||||
'usage_stats.models': '模型统计',
|
||||
'usage_stats.success_rate': '成功率',
|
||||
|
||||
// 日志查看
|
||||
'logs.title': '日志查看',
|
||||
'logs.refresh_button': '刷新日志',
|
||||
'logs.clear_button': '清空日志',
|
||||
'logs.download_button': '下载日志',
|
||||
'logs.empty_title': '暂无日志记录',
|
||||
'logs.empty_desc': '当启用"日志记录到文件"功能后,日志将显示在这里',
|
||||
'logs.log_content': '日志内容',
|
||||
'logs.loading': '正在加载日志...',
|
||||
'logs.load_error': '加载日志失败',
|
||||
'logs.clear_confirm': '确定要清空所有日志吗?此操作不可恢复!',
|
||||
'logs.clear_success': '日志已清空',
|
||||
'logs.download_success': '日志下载成功',
|
||||
'logs.auto_refresh': '自动刷新',
|
||||
'logs.auto_refresh_enabled': '自动刷新已开启',
|
||||
'logs.auto_refresh_disabled': '自动刷新已关闭',
|
||||
'logs.lines': '行',
|
||||
'logs.removed': '已删除',
|
||||
'logs.upgrade_required_title': '需要升级 CLI Proxy API',
|
||||
'logs.upgrade_required_desc': '当前服务器版本不支持日志查看功能,请升级到最新版本的 CLI Proxy API 以使用此功能。',
|
||||
|
||||
// 配置管理
|
||||
'config_management.title': '配置管理',
|
||||
'config_management.editor_title': '配置文件',
|
||||
'config_management.reload': '重新加载',
|
||||
'config_management.save': '保存',
|
||||
'config_management.description': '查看并编辑服务器上的 config.yaml 配置文件。保存前请确认语法正确。',
|
||||
'config_management.status_idle': '等待操作',
|
||||
'config_management.status_loading': '加载配置中...',
|
||||
'config_management.status_loaded': '配置已加载',
|
||||
'config_management.status_dirty': '有未保存的更改',
|
||||
'config_management.status_disconnected': '请先连接服务器以加载配置',
|
||||
'config_management.status_load_failed': '加载失败',
|
||||
'config_management.status_saving': '正在保存配置...',
|
||||
'config_management.status_saved': '配置保存完成',
|
||||
'config_management.status_save_failed': '保存失败',
|
||||
'config_management.save_success': '配置已保存',
|
||||
'config_management.error_yaml_not_supported': '服务器未返回 YAML 格式,请确认 /config.yaml 接口可用',
|
||||
|
||||
// 系统信息
|
||||
'system_info.title': '系统信息',
|
||||
'system_info.connection_status_title': '连接状态',
|
||||
@@ -332,6 +366,8 @@ const i18n = {
|
||||
'notification.retry_updated': '重试设置已更新',
|
||||
'notification.quota_switch_project_updated': '项目切换设置已更新',
|
||||
'notification.quota_switch_preview_updated': '预览模型切换设置已更新',
|
||||
'notification.usage_statistics_updated': '使用统计设置已更新',
|
||||
'notification.logging_to_file_updated': '日志记录设置已更新',
|
||||
'notification.api_key_added': 'API密钥添加成功',
|
||||
'notification.api_key_updated': 'API密钥更新成功',
|
||||
'notification.api_key_deleted': 'API密钥删除成功',
|
||||
@@ -415,6 +451,8 @@ const i18n = {
|
||||
'common.required': 'Required',
|
||||
'common.api_key': 'Key',
|
||||
'common.base_url': 'Address',
|
||||
'common.proxy_url': 'Proxy',
|
||||
'common.alias': 'Alias',
|
||||
|
||||
// Page titles
|
||||
'title.main': 'CLI Proxy API Management Center',
|
||||
@@ -459,6 +497,8 @@ const i18n = {
|
||||
'nav.ai_providers': 'AI Providers',
|
||||
'nav.auth_files': 'Auth Files',
|
||||
'nav.usage_stats': 'Usage Statistics',
|
||||
'nav.config_management': 'Config Management',
|
||||
'nav.logs': 'Logs Viewer',
|
||||
'nav.system_info': 'System Info',
|
||||
|
||||
// Basic settings
|
||||
@@ -476,6 +516,10 @@ const i18n = {
|
||||
'basic_settings.quota_title': 'Quota Exceeded Behavior',
|
||||
'basic_settings.quota_switch_project': 'Auto Switch Project',
|
||||
'basic_settings.quota_switch_preview': 'Switch to Preview Model',
|
||||
'basic_settings.usage_statistics_title': 'Usage Statistics',
|
||||
'basic_settings.usage_statistics_enable': 'Enable usage statistics',
|
||||
'basic_settings.logging_title': 'Logging',
|
||||
'basic_settings.logging_to_file_enable': 'Enable logging to file',
|
||||
|
||||
// API Keys management
|
||||
'api_keys.title': 'API Keys Management',
|
||||
@@ -515,9 +559,12 @@ const i18n = {
|
||||
'ai_providers.codex_add_modal_key_placeholder': 'Please enter Codex API key',
|
||||
'ai_providers.codex_add_modal_url_label': 'Base URL (Optional):',
|
||||
'ai_providers.codex_add_modal_url_placeholder': 'e.g.: https://api.example.com',
|
||||
'ai_providers.codex_add_modal_proxy_label': 'Proxy URL (Optional):',
|
||||
'ai_providers.codex_add_modal_proxy_placeholder': 'e.g.: socks5://proxy.example.com:1080',
|
||||
'ai_providers.codex_edit_modal_title': 'Edit Codex API Configuration',
|
||||
'ai_providers.codex_edit_modal_key_label': 'API Key:',
|
||||
'ai_providers.codex_edit_modal_url_label': 'Base URL (Optional):',
|
||||
'ai_providers.codex_edit_modal_proxy_label': 'Proxy URL (Optional):',
|
||||
'ai_providers.codex_delete_confirm': 'Are you sure you want to delete this Codex configuration?',
|
||||
|
||||
'ai_providers.claude_title': 'Claude API Configuration',
|
||||
@@ -530,9 +577,12 @@ const i18n = {
|
||||
'ai_providers.claude_add_modal_key_placeholder': 'Please enter Claude API key',
|
||||
'ai_providers.claude_add_modal_url_label': 'Base URL (Optional):',
|
||||
'ai_providers.claude_add_modal_url_placeholder': 'e.g.: https://api.anthropic.com',
|
||||
'ai_providers.claude_add_modal_proxy_label': 'Proxy URL (Optional):',
|
||||
'ai_providers.claude_add_modal_proxy_placeholder': 'e.g.: socks5://proxy.example.com:1080',
|
||||
'ai_providers.claude_edit_modal_title': 'Edit Claude API Configuration',
|
||||
'ai_providers.claude_edit_modal_key_label': 'API Key:',
|
||||
'ai_providers.claude_edit_modal_url_label': 'Base URL (Optional):',
|
||||
'ai_providers.claude_edit_modal_proxy_label': 'Proxy URL (Optional):',
|
||||
'ai_providers.claude_delete_confirm': 'Are you sure you want to delete this Claude configuration?',
|
||||
|
||||
'ai_providers.openai_title': 'OpenAI Compatible Providers',
|
||||
@@ -546,10 +596,19 @@ const i18n = {
|
||||
'ai_providers.openai_add_modal_url_placeholder': 'e.g.: https://openrouter.ai/api/v1',
|
||||
'ai_providers.openai_add_modal_keys_label': 'API Keys (one per line):',
|
||||
'ai_providers.openai_add_modal_keys_placeholder': 'sk-key1\nsk-key2',
|
||||
'ai_providers.openai_add_modal_keys_proxy_label': 'Proxy URL (one per line, optional):',
|
||||
'ai_providers.openai_add_modal_keys_proxy_placeholder': 'socks5://proxy.example.com:1080\n',
|
||||
'ai_providers.openai_add_modal_models_label': 'Model List (name[, alias] one per line):',
|
||||
'ai_providers.openai_models_hint': 'Example: gpt-4o-mini or moonshotai/kimi-k2:free, kimi-k2',
|
||||
'ai_providers.openai_model_name_placeholder': 'Model name, e.g. moonshotai/kimi-k2:free',
|
||||
'ai_providers.openai_model_alias_placeholder': 'Model alias (optional)',
|
||||
'ai_providers.openai_models_add_btn': 'Add Model',
|
||||
'ai_providers.openai_edit_modal_title': 'Edit OpenAI Compatible Provider',
|
||||
'ai_providers.openai_edit_modal_name_label': 'Provider Name:',
|
||||
'ai_providers.openai_edit_modal_url_label': 'Base URL:',
|
||||
'ai_providers.openai_edit_modal_keys_label': 'API Keys (one per line):',
|
||||
'ai_providers.openai_edit_modal_keys_proxy_label': 'Proxy URL (one per line, optional):',
|
||||
'ai_providers.openai_edit_modal_models_label': 'Model List (name[, alias] one per line):',
|
||||
'ai_providers.openai_delete_confirm': 'Are you sure you want to delete this OpenAI provider?',
|
||||
'ai_providers.openai_keys_count': 'Keys Count',
|
||||
'ai_providers.openai_models_count': 'Models Count',
|
||||
@@ -576,18 +635,6 @@ const i18n = {
|
||||
'auth_files.delete_all_success': 'Successfully deleted',
|
||||
'auth_files.files_count': 'files',
|
||||
|
||||
// Gemini Web Token
|
||||
'auth_login.gemini_web_title': 'Gemini Web Token',
|
||||
'auth_login.gemini_web_button': 'Save Gemini Web Token',
|
||||
'auth_login.gemini_web_hint': 'Obtain the Cookie value of the Gemini web version from the browser\'s developer tools, used for direct authentication to access Gemini.',
|
||||
'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:',
|
||||
'auth_login.secure_1psid_placeholder': 'Enter __Secure-1PSID cookie value',
|
||||
'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:',
|
||||
'auth_login.secure_1psidts_placeholder': 'Enter __Secure-1PSIDTS cookie value',
|
||||
'auth_login.gemini_web_label_label': 'Label (Optional):',
|
||||
'auth_login.gemini_web_label_placeholder': 'Enter label name (optional)',
|
||||
'auth_login.gemini_web_saved': 'Gemini Web Token saved successfully',
|
||||
|
||||
// Codex OAuth
|
||||
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||
'auth_login.codex_oauth_button': 'Start Codex Login',
|
||||
@@ -676,6 +723,45 @@ const i18n = {
|
||||
'usage_stats.models': 'Model Statistics',
|
||||
'usage_stats.success_rate': 'Success Rate',
|
||||
|
||||
// Logs viewer
|
||||
'logs.title': 'Logs Viewer',
|
||||
'logs.refresh_button': 'Refresh Logs',
|
||||
'logs.clear_button': 'Clear Logs',
|
||||
'logs.download_button': 'Download Logs',
|
||||
'logs.empty_title': 'No Logs Available',
|
||||
'logs.empty_desc': 'When "Enable logging to file" is enabled, logs will be displayed here',
|
||||
'logs.log_content': 'Log Content',
|
||||
'logs.loading': 'Loading logs...',
|
||||
'logs.load_error': 'Failed to load logs',
|
||||
'logs.clear_confirm': 'Are you sure you want to clear all logs? This action cannot be undone!',
|
||||
'logs.clear_success': 'Logs cleared successfully',
|
||||
'logs.download_success': 'Logs downloaded successfully',
|
||||
'logs.auto_refresh': 'Auto Refresh',
|
||||
'logs.auto_refresh_enabled': 'Auto refresh enabled',
|
||||
'logs.auto_refresh_disabled': 'Auto refresh disabled',
|
||||
'logs.lines': 'lines',
|
||||
'logs.removed': 'Removed',
|
||||
'logs.upgrade_required_title': 'Please Upgrade CLI Proxy API',
|
||||
'logs.upgrade_required_desc': 'The current server version does not support the logs viewing feature. Please upgrade to the latest version of CLI Proxy API to use this feature.',
|
||||
|
||||
// Config management
|
||||
'config_management.title': 'Config Management',
|
||||
'config_management.editor_title': 'Configuration File',
|
||||
'config_management.reload': 'Reload',
|
||||
'config_management.save': 'Save',
|
||||
'config_management.description': 'View and edit the server-side config.yaml file. Validate the syntax before saving.',
|
||||
'config_management.status_idle': 'Waiting for action',
|
||||
'config_management.status_loading': 'Loading configuration...',
|
||||
'config_management.status_loaded': 'Configuration loaded',
|
||||
'config_management.status_dirty': 'Unsaved changes',
|
||||
'config_management.status_disconnected': 'Connect to the server to load the configuration',
|
||||
'config_management.status_load_failed': 'Load failed',
|
||||
'config_management.status_saving': 'Saving configuration...',
|
||||
'config_management.status_saved': 'Configuration saved',
|
||||
'config_management.status_save_failed': 'Save failed',
|
||||
'config_management.save_success': 'Configuration saved successfully',
|
||||
'config_management.error_yaml_not_supported': 'Server did not return YAML. Verify the /config.yaml endpoint is available.',
|
||||
|
||||
// System info
|
||||
'system_info.title': 'System Information',
|
||||
'system_info.connection_status_title': 'Connection Status',
|
||||
@@ -694,6 +780,8 @@ const i18n = {
|
||||
'notification.retry_updated': 'Retry settings updated',
|
||||
'notification.quota_switch_project_updated': 'Project switch settings updated',
|
||||
'notification.quota_switch_preview_updated': 'Preview model switch settings updated',
|
||||
'notification.usage_statistics_updated': 'Usage statistics settings updated',
|
||||
'notification.logging_to_file_updated': 'Logging settings updated',
|
||||
'notification.api_key_added': 'API key added successfully',
|
||||
'notification.api_key_updated': 'API key updated successfully',
|
||||
'notification.api_key_deleted': 'API key deleted successfully',
|
||||
@@ -706,6 +794,8 @@ const i18n = {
|
||||
'notification.claude_config_added': 'Claude configuration added successfully',
|
||||
'notification.claude_config_updated': 'Claude configuration updated successfully',
|
||||
'notification.claude_config_deleted': 'Claude configuration deleted successfully',
|
||||
'notification.field_required': 'Required fields cannot be empty',
|
||||
'notification.openai_provider_required': 'Please fill in provider name and Base URL',
|
||||
'notification.openai_provider_added': 'OpenAI provider added successfully',
|
||||
'notification.openai_provider_updated': 'OpenAI provider updated successfully',
|
||||
'notification.openai_provider_deleted': 'OpenAI provider deleted successfully',
|
||||
|
||||
158
index.html
158
index.html
@@ -7,7 +7,13 @@
|
||||
<title data-i18n="title.login">CLI Proxy API Management Center</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/yaml/yaml.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/edit/closebrackets.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/comment/comment.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js"></script>
|
||||
<script src="i18n.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -171,6 +177,12 @@
|
||||
<li data-tooltip="使用统计"><a href="#usage-stats" class="nav-item" data-section="usage-stats">
|
||||
<i class="fas fa-chart-line"></i> <span data-i18n="nav.usage_stats">使用统计</span>
|
||||
</a></li>
|
||||
<li data-tooltip="配置管理"><a href="#config-management" class="nav-item" data-section="config-management">
|
||||
<i class="fas fa-cog"></i> <span data-i18n="nav.config_management">配置管理</span>
|
||||
</a></li>
|
||||
<li id="logs-nav-item" data-tooltip="日志查看" style="display: none;"><a href="#logs" class="nav-item" data-section="logs">
|
||||
<i class="fas fa-scroll"></i> <span data-i18n="nav.logs">日志查看</span>
|
||||
</a></li>
|
||||
<li data-tooltip="系统信息"><a href="#system-info" class="nav-item" data-section="system-info">
|
||||
<i class="fas fa-info-circle"></i> <span data-i18n="nav.system_info">系统信息</span>
|
||||
</a></li>
|
||||
@@ -275,6 +287,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 使用统计设置 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-chart-bar"></i> <span
|
||||
data-i18n="basic_settings.usage_statistics_title">使用统计</span></h3>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="toggle-group">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="usage-statistics-enabled-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label"
|
||||
data-i18n="basic_settings.usage_statistics_enable">启用使用统计</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 日志记录设置 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-file-alt"></i> <span
|
||||
data-i18n="basic_settings.logging_title">日志记录</span></h3>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="toggle-group">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="logging-to-file-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label"
|
||||
data-i18n="basic_settings.logging_to_file_enable">启用日志记录到文件</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- API 密钥管理 -->
|
||||
@@ -372,45 +420,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gemini Web Token -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fab fa-google"></i> <span
|
||||
data-i18n="auth_login.gemini_web_title">Gemini Web Token</span></h3>
|
||||
<button id="gemini-web-token-btn" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> <span data-i18n="auth_login.gemini_web_button">保存
|
||||
Gemini Web Token</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p class="form-hint" style="margin-bottom: 20px;"
|
||||
data-i18n="auth_login.gemini_web_hint">
|
||||
从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="secure-1psid-input"
|
||||
data-i18n="auth_login.secure_1psid_label">__Secure-1PSID Cookie:</label>
|
||||
<input type="text" id="secure-1psid-input"
|
||||
data-i18n="auth_login.secure_1psid_placeholder"
|
||||
placeholder="输入 __Secure-1PSID cookie 值">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="secure-1psidts-input"
|
||||
data-i18n="auth_login.secure_1psidts_label">__Secure-1PSIDTS Cookie:</label>
|
||||
<input type="text" id="secure-1psidts-input"
|
||||
data-i18n="auth_login.secure_1psidts_placeholder"
|
||||
placeholder="输入 __Secure-1PSIDTS cookie 值">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="gemini-web-label-input"
|
||||
data-i18n="auth_login.gemini_web_label_label">Label (Optional):</label>
|
||||
<input type="text" id="gemini-web-label-input"
|
||||
data-i18n="auth_login.gemini_web_label_placeholder"
|
||||
placeholder="输入标签名称 (可选)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 认证文件 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
@@ -622,6 +631,40 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 日志查看 -->
|
||||
<section id="logs" class="content-section">
|
||||
<h2 data-i18n="logs.title">日志查看</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-scroll"></i> <span data-i18n="logs.log_content">日志内容</span></h3>
|
||||
<div class="header-actions">
|
||||
<div class="toggle-group" style="margin-right: 15px;">
|
||||
<label class="toggle-switch" style="margin-right: 5px;">
|
||||
<input type="checkbox" id="logs-auto-refresh-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label" data-i18n="logs.auto_refresh" style="font-size: 0.9em;">自动刷新</span>
|
||||
</div>
|
||||
<button id="refresh-logs" class="btn btn-primary">
|
||||
<i class="fas fa-sync-alt"></i> <span data-i18n="logs.refresh_button">刷新日志</span>
|
||||
</button>
|
||||
<button id="download-logs" class="btn btn-secondary">
|
||||
<i class="fas fa-download"></i> <span data-i18n="logs.download_button">下载日志</span>
|
||||
</button>
|
||||
<button id="clear-logs" class="btn btn-danger">
|
||||
<i class="fas fa-trash"></i> <span data-i18n="logs.clear_button">清空日志</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div id="logs-content" class="logs-container">
|
||||
<div class="loading-placeholder" data-i18n="logs.loading">正在加载日志...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 使用统计 -->
|
||||
<section id="usage-stats" class="content-section">
|
||||
<h2 data-i18n="usage_stats.title">使用统计</h2>
|
||||
@@ -732,6 +775,32 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 配置管理 -->
|
||||
<section id="config-management" class="content-section">
|
||||
<h2 data-i18n="config_management.title">配置管理</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-file-code"></i> <span data-i18n="config_management.editor_title">配置文件</span></h3>
|
||||
<div class="editor-actions">
|
||||
<button id="config-reload-btn" class="btn btn-secondary">
|
||||
<i class="fas fa-sync-alt"></i> <span data-i18n="config_management.reload">重新加载</span>
|
||||
</button>
|
||||
<button id="config-save-btn" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> <span data-i18n="config_management.save">保存</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p class="form-hint" data-i18n="config_management.description">查看并编辑服务器上的 config.yaml 配置文件。保存前请确认语法正确。</p>
|
||||
<div class="yaml-editor-container">
|
||||
<textarea id="config-editor" class="yaml-editor" spellcheck="false" placeholder="key: value"></textarea>
|
||||
<div id="config-editor-status" class="editor-status" data-i18n="config_management.status_idle">等待操作</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 系统信息 -->
|
||||
<section id="system-info" class="content-section">
|
||||
<h2 data-i18n="system_info.title">系统信息</h2>
|
||||
@@ -751,13 +820,6 @@
|
||||
</div>
|
||||
<div class="info-value" id="display-api-url">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="fas fa-key"></i>
|
||||
<span data-i18n="connection.management_key">管理密钥:</span>
|
||||
</div>
|
||||
<div class="info-value" id="display-management-key">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="fas fa-circle"></i>
|
||||
@@ -806,9 +868,9 @@
|
||||
<!-- 版本信息 -->
|
||||
<footer class="version-footer">
|
||||
<div class="version-info">
|
||||
<span data-i18n="footer.version">版本</span>: v0.1.0
|
||||
<span data-i18n="footer.version">版本</span>: __VERSION__
|
||||
<span class="separator">•</span>
|
||||
<span data-i18n="footer.author">作者</span>: Supra4E8C
|
||||
<span data-i18n="footer.author">作者</span>: CLI Proxy API Team
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
428
styles.css
428
styles.css
@@ -1278,6 +1278,133 @@ textarea::placeholder {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* 配置管理编辑器 */
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.editor-actions .btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.yaml-editor-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#config-management .card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: calc(100vh - 360px);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#config-management .card-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#config-management .yaml-editor-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.yaml-editor {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
min-height: 360px;
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 6px;
|
||||
padding: 12px 14px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
overflow-y: auto;
|
||||
font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
#config-management .CodeMirror {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#config-management .CodeMirror-scroll {
|
||||
min-height: 0;
|
||||
max-height: calc(100vh - 440px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#config-management .CodeMirror.cm-s-default {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
#config-management .CodeMirror-gutters {
|
||||
background: var(--bg-secondary);
|
||||
border-right: 1px solid var(--border-primary);
|
||||
}
|
||||
|
||||
#config-management .CodeMirror-linenumber {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
#config-management .CodeMirror .CodeMirror-lines {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#config-management .CodeMirror .cm-comment {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
[data-theme="dark"] #config-management .CodeMirror.cm-s-default {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #config-management .CodeMirror-gutters {
|
||||
background: var(--bg-secondary);
|
||||
border-right: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] #config-management .CodeMirror .cm-comment {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
#config-management .CodeMirror.cm-readonly {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.editor-status {
|
||||
font-size: 13px;
|
||||
color: var(--text-quaternary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-height: 18px;
|
||||
}
|
||||
|
||||
.editor-status.success {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.editor-status.error {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
@@ -1923,44 +2050,6 @@ input:checked+.slider:before {
|
||||
}
|
||||
|
||||
|
||||
/* Gemini Web Token 模态框样式 */
|
||||
.gemini-web-form .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.gemini-web-form .form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.gemini-web-form .form-group input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
|
||||
.gemini-web-form .form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--border-focus);
|
||||
box-shadow: 0 0 0 3px var(--border-primary);
|
||||
}
|
||||
|
||||
.gemini-web-form .form-hint {
|
||||
margin-top: 6px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 使用统计样式 */
|
||||
.stats-overview {
|
||||
display: grid;
|
||||
@@ -2703,3 +2792,266 @@ input:checked+.slider:before {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
/* 日志查看样式 */
|
||||
.logs-container {
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
min-height: 500px;
|
||||
max-height: calc(100vh - 400px);
|
||||
overflow-y: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
|
||||
.logs-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--border-primary);
|
||||
font-size: 13px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.logs-info span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.logs-text {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
max-height: calc(100vh - 480px);
|
||||
min-height: 450px;
|
||||
overflow-y: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.logs-text::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.logs-text::-webkit-scrollbar-track {
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.logs-text::-webkit-scrollbar-thumb {
|
||||
background: var(--border-secondary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.logs-text::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-quaternary);
|
||||
}
|
||||
|
||||
/* 空状态和错误状态 */
|
||||
.logs-container .empty-state,
|
||||
.logs-container .error-state,
|
||||
.logs-container .upgrade-notice {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 300px;
|
||||
text-align: center;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.logs-container .empty-state i,
|
||||
.logs-container .error-state i,
|
||||
.logs-container .upgrade-notice i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
color: var(--border-secondary);
|
||||
}
|
||||
|
||||
.logs-container .error-state i {
|
||||
color: var(--error-text);
|
||||
}
|
||||
|
||||
.logs-container .upgrade-notice i {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.logs-container .empty-state p,
|
||||
.logs-container .error-state p,
|
||||
.logs-container .upgrade-notice p {
|
||||
margin: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.logs-container .empty-state p:first-of-type,
|
||||
.logs-container .error-state p:first-of-type {
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.logs-container .upgrade-notice h3 {
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.logs-container .upgrade-notice p {
|
||||
color: var(--text-tertiary);
|
||||
max-width: 500px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 日志页面头部操作区域 */
|
||||
#logs .card-header .header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#logs .card-header .header-actions .toggle-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 响应式设计 - 日志查看 */
|
||||
@media (max-width: 768px) {
|
||||
.logs-container {
|
||||
min-height: 300px;
|
||||
max-height: calc(100vh - 350px);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.logs-text {
|
||||
font-size: 11px;
|
||||
padding: 12px;
|
||||
max-height: calc(100vh - 430px);
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.logs-info {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#logs .card-header .header-actions {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
#logs .card-header .header-actions .btn {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#logs .card-header .header-actions .btn span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#logs .card-header .header-actions .toggle-group {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
/* 暗色主题适配 */
|
||||
[data-theme="dark"] .logs-text {
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border-color: var(--border-primary);
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .logs-container {
|
||||
background: rgba(15, 23, 42, 0.3);
|
||||
}
|
||||
|
||||
/* ===== AI提供商统计徽章样式 ===== */
|
||||
|
||||
/* 统计信息容器 */
|
||||
.item-stats {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* 统计徽章基础样式 */
|
||||
.stat-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* 成功统计徽章 */
|
||||
.stat-badge.stat-success {
|
||||
background-color: var(--success-bg);
|
||||
color: var(--success-text);
|
||||
border: 1px solid var(--success-border);
|
||||
}
|
||||
|
||||
.stat-badge.stat-success i {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 失败统计徽章 */
|
||||
.stat-badge.stat-failure {
|
||||
background-color: var(--error-bg);
|
||||
color: var(--error-text);
|
||||
border: 1px solid var(--error-border);
|
||||
}
|
||||
|
||||
.stat-badge.stat-failure i {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 悬停效果 */
|
||||
.stat-badge:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* 暗色主题适配 */
|
||||
[data-theme="dark"] .stat-badge.stat-success {
|
||||
background-color: rgba(6, 78, 59, 0.3);
|
||||
color: #6ee7b7;
|
||||
border-color: rgba(5, 150, 105, 0.5);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .stat-badge.stat-failure {
|
||||
background-color: rgba(153, 27, 27, 0.3);
|
||||
color: #fca5a5;
|
||||
border-color: rgba(220, 38, 38, 0.5);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.item-stats {
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.stat-badge {
|
||||
padding: 3px 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.stat-badge i {
|
||||
font-size: 11px !important;
|
||||
}
|
||||
}
|
||||
#config-management .CodeMirror .CodeMirror-lines {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user