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
72 Commits
focus_plus
...
v0.2.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f71b0d811 | ||
|
|
edb723c12b | ||
|
|
295befe42b | ||
|
|
a07faddeff | ||
|
|
5be40092f7 | ||
|
|
d422606f99 | ||
|
|
8b07159c35 | ||
|
|
5b1be05eb9 | ||
|
|
a4fd672458 | ||
|
|
6f1c7b168d | ||
|
|
1d7408cb25 | ||
|
|
3468fd8373 | ||
|
|
4f15c3f5c5 | ||
|
|
72cd117aab | ||
|
|
5d62cd91f2 | ||
|
|
6837100dec | ||
|
|
8542041981 | ||
|
|
35ceab0dae | ||
|
|
d3fe186df7 | ||
|
|
5aff22a20b | ||
|
|
aa1dedc932 | ||
|
|
61e75eee97 | ||
|
|
3a2d96725f | ||
|
|
8283e99909 | ||
|
|
181cba6886 | ||
|
|
aa729914c5 | ||
|
|
f98f31f2ed | ||
|
|
1e79f918e2 | ||
|
|
257260b1d2 | ||
|
|
8372906820 | ||
|
|
5feea2e345 | ||
|
|
825ad53c2c | ||
|
|
3e9413172c | ||
|
|
89099b58ff | ||
|
|
7509a1eddc | ||
|
|
e92784f951 | ||
|
|
d26695da76 | ||
|
|
8964030ade | ||
|
|
0b9abdf9b1 | ||
|
|
a208a484ff | ||
|
|
369cf52346 | ||
|
|
dcfffc716b | ||
|
|
7de5280824 | ||
|
|
86d60aad77 | ||
|
|
020fccc032 | ||
|
|
c162ab3a54 | ||
|
|
85d12e15d8 | ||
|
|
ebffb49f52 | ||
|
|
316c1ffc0d | ||
|
|
b3e54e7f14 | ||
|
|
fe11bfb48f | ||
|
|
ee0d8f82d7 | ||
|
|
0bbb397df5 | ||
|
|
27948b3d5c | ||
|
|
dff28db227 | ||
|
|
34b16ca886 | ||
|
|
fa86f76289 | ||
|
|
41ca99978f | ||
|
|
6ef674487f | ||
|
|
2be7ced21a | ||
|
|
b61155d215 | ||
|
|
5488d6153d | ||
|
|
30f5300bb4 | ||
|
|
52169200f1 | ||
|
|
80b2597611 | ||
|
|
04f21eea98 | ||
|
|
f6a4bae8c6 | ||
|
|
c9f09ccf37 | ||
|
|
5b8fd04ba3 | ||
|
|
3c791a2313 | ||
|
|
2ef64d8064 | ||
|
|
f2dc4bcf98 |
63
.github/workflows/release.yml
vendored
Normal file
63
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build all-in-one HTML
|
||||
run: npm run build
|
||||
env:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
|
||||
- name: Prepare release assets
|
||||
run: |
|
||||
cd dist
|
||||
mv index.html management.html
|
||||
ls -lh management.html
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: dist/management.html
|
||||
body: |
|
||||
## CLI Proxy API Management Center - ${{ github.ref_name }}
|
||||
|
||||
### Download and Usage
|
||||
1. Download the `management.html` file
|
||||
2. Open it directly in your browser
|
||||
3. All assets (CSS, JavaScript, images) are bundled into this single file
|
||||
|
||||
### Features
|
||||
- Single file, no external dependencies required
|
||||
- Complete management interface for CLI Proxy API
|
||||
- Support for local and remote connections
|
||||
- Multi-language support (Chinese/English)
|
||||
- Dark/Light theme support
|
||||
|
||||
---
|
||||
🤖 Generated with GitHub Actions
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Node modules
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Temporary build files
|
||||
index.build.html
|
||||
|
||||
# npm lock files
|
||||
package-lock.json
|
||||
|
||||
# IDE and editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
CLAUDE.md
|
||||
.claude
|
||||
AGENTS.md
|
||||
.serena
|
||||
69
BUILD_RELEASE.md
Normal file
69
BUILD_RELEASE.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Build and Release Instructions
|
||||
|
||||
## Overview
|
||||
|
||||
This project uses webpack to bundle all HTML, CSS, JavaScript, and images into a single all-in-one HTML file. The GitHub workflow automatically builds and releases this file when you create a new tag.
|
||||
|
||||
## How to Create a Release
|
||||
|
||||
1. Make sure all your changes are committed
|
||||
2. Create and push a new tag:
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
3. The GitHub workflow will automatically:
|
||||
- Install dependencies
|
||||
- Build the all-in-one HTML file using webpack
|
||||
- Create a new release with the tag
|
||||
- Upload the bundled HTML file to the release
|
||||
|
||||
## Manual Build
|
||||
|
||||
To build locally:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build the all-in-one HTML file
|
||||
npm run build
|
||||
```
|
||||
|
||||
The output will be in the `dist/` directory as `index.html`.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **build-scripts/prepare-html.js**: Pre-build script
|
||||
- Reads the original `index.html`
|
||||
- Removes local CSS and JavaScript references
|
||||
- Generates temporary `index.build.html` for webpack
|
||||
|
||||
2. **webpack.config.js**: Configures webpack to bundle all assets
|
||||
- Uses `style-loader` to inline CSS
|
||||
- Uses `asset/inline` to embed images as base64
|
||||
- Uses `html-inline-script-webpack-plugin` to inline JavaScript
|
||||
- Uses `index.build.html` as template (generated dynamically)
|
||||
|
||||
3. **bundle-entry.js**: Entry point that imports all resources
|
||||
- Imports CSS files
|
||||
- Imports JavaScript modules
|
||||
- Imports and sets logo image
|
||||
|
||||
4. **package.json scripts**:
|
||||
- `prebuild`: Automatically runs before build to generate `index.build.html`
|
||||
- `build`: Runs webpack to bundle everything
|
||||
- `postbuild`: Cleans up temporary `index.build.html` file
|
||||
|
||||
5. **.github/workflows/release.yml**: GitHub workflow
|
||||
- Triggers on tag push
|
||||
- Builds the project (prebuild → build → postbuild)
|
||||
- Creates a release with the bundled HTML file
|
||||
|
||||
## External Dependencies
|
||||
|
||||
The bundled HTML file still relies on these CDN resources:
|
||||
- Font Awesome (icons)
|
||||
- Chart.js (charts and graphs)
|
||||
|
||||
These are loaded from CDN to keep the file size reasonable and leverage browser caching.
|
||||
103
README.md
103
README.md
@@ -1,13 +1,18 @@
|
||||
# Cli-Proxy-API-Management-Center
|
||||
This is a modern web interface for managing the CLI Proxy API.
|
||||
|
||||
[中文](README_CN.md)
|
||||
[中文文档](README_CN.md)
|
||||
|
||||
Main Project:
|
||||
https://github.com/router-for-me/CLIProxyAPI
|
||||
|
||||
Minimum required version: ≥ 5.0.0
|
||||
Recommended version: ≥ 5.1.1
|
||||
Example URL:
|
||||
https://remote.router-for.me/
|
||||
|
||||
Minimum required version: ≥ 6.0.0
|
||||
Recommended version: ≥ 6.2.32
|
||||
|
||||
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.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -15,13 +20,16 @@ Recommended version: ≥ 5.1.1
|
||||
- Supports management key authentication
|
||||
- Configurable API base address
|
||||
- Real-time connection status detection
|
||||
- Auto-login with saved credentials
|
||||
- Language and theme switching
|
||||
|
||||
### Basic Settings
|
||||
- **Debug Mode**: Enable/disable debugging
|
||||
- **Proxy Settings**: Configure proxy server URL
|
||||
- **Request Retries**: Set the number of request retries
|
||||
- **Quota Management**: Configure behavior when the quota is exceeded
|
||||
- **Local Access**: Manage local unauthenticated access
|
||||
- Auto-switch project when quota exceeded
|
||||
- Switch to preview models when quota exceeded
|
||||
|
||||
### API Key Management
|
||||
- **Proxy Service Authentication Key**: Manage API keys for the proxy service
|
||||
@@ -36,13 +44,31 @@ Recommended version: ≥ 5.1.1
|
||||
- Delete single or all authentication files
|
||||
- Display file details
|
||||
|
||||
### Usage Statistics
|
||||
- **Real-time Analytics**: Track API usage with interactive charts
|
||||
- **Request Trends**: Visualize request patterns by hour/day
|
||||
- **Token Usage**: Monitor token consumption over time
|
||||
- **API Details**: Detailed statistics for each API endpoint
|
||||
- **Success/Failure Rates**: Track API reliability metrics
|
||||
|
||||
### System Information
|
||||
- **Connection Status**: Real-time connection monitoring
|
||||
- **Configuration Status**: Track configuration loading state
|
||||
- **Server Information**: Display server address and management key
|
||||
- **Last Update**: Show when data was last refreshed
|
||||
|
||||
|
||||
## How to Use
|
||||
|
||||
### 1. Direct Use (Recommended)
|
||||
### 1. Using After CLI Proxy API Program Launch (Recommended)
|
||||
Once the CLI Proxy API program is up and running, you can access the WebUI at `http://your-server-IP:8317/management.html`.
|
||||
|
||||
### 2. Direct Use
|
||||
Simply open the `index.html` file directly in your browser to use it.
|
||||
|
||||
### 2. Use a Local Server
|
||||
### 3. Use a Local Server
|
||||
|
||||
#### Option A: Using Node.js (npm)
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
@@ -51,10 +77,19 @@ npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
### 3. Configure API Connection
|
||||
#### Option B: Using Python
|
||||
```bash
|
||||
# Python 3.x
|
||||
python -m http.server 8000
|
||||
|
||||
```
|
||||
|
||||
Then open `http://localhost:8000` in your browser.
|
||||
|
||||
### 3. Configure Connection
|
||||
1. Open the management interface.
|
||||
2. On the login screen, enter:
|
||||
- **Remote Address**: `http://localhost:8317` (`/v0/management` will be auto-completed for you)
|
||||
- **Remote Address**: The current version automatically picks up the remote address from where you're connecting. But you can also set your own address if you prefer.
|
||||
- **Management Key**: Your management key
|
||||
3. Click the "Connect" button.
|
||||
4. Once connected successfully, all features will be available.
|
||||
@@ -66,8 +101,16 @@ npm start
|
||||
- **API Keys**: Management of keys for various API services.
|
||||
- **AI Providers**: Configuration for AI service providers.
|
||||
- **Auth Files**: Upload and download management for authentication files.
|
||||
- **Usage Stats**: Real-time analytics and usage statistics with interactive charts.
|
||||
- **System Info**: Connection status and system information.
|
||||
|
||||
### Login Interface
|
||||
- **Auto-connection**: Automatically attempts to connect using saved credentials
|
||||
- **Custom Connection**: Manual configuration of API base address
|
||||
- **Current Address Detection**: Automatically detects and uses current access address
|
||||
- **Language Switching**: Support for multiple languages (English/Chinese)
|
||||
- **Theme Switching**: Light and dark theme support
|
||||
|
||||
## Feature Highlights
|
||||
|
||||
### Modern UI
|
||||
@@ -75,27 +118,45 @@ npm start
|
||||
- Beautiful gradient colors and shadow effects
|
||||
- Smooth animations and transition effects
|
||||
- Intuitive icons and status indicators
|
||||
- Dark/Light theme support with system preference detection
|
||||
- Mobile-friendly sidebar with overlay
|
||||
|
||||
### Real-time Updates
|
||||
- Configuration changes take effect immediately
|
||||
- Real-time status feedback
|
||||
- Automatic data refresh
|
||||
- Live usage statistics with interactive charts
|
||||
- Real-time connection status monitoring
|
||||
|
||||
### Security Features
|
||||
- Masked display for keys
|
||||
- Secure credential storage
|
||||
- Auto-login with encrypted local storage
|
||||
|
||||
### Responsive Design
|
||||
- Perfectly adapts to desktop and mobile devices
|
||||
- Adaptive layout
|
||||
- Adaptive layout with collapsible sidebar
|
||||
- Touch-friendly interactions
|
||||
- Mobile menu with overlay
|
||||
|
||||
### Analytics & Monitoring
|
||||
- Interactive charts powered by Chart.js
|
||||
- Real-time usage statistics
|
||||
- Request trend visualization
|
||||
- Token consumption tracking
|
||||
- API performance metrics
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Frontend**: Plain HTML, CSS, JavaScript
|
||||
- **Styling**: CSS3 + Flexbox/Grid
|
||||
- **Frontend**: Plain HTML, CSS, JavaScript (ES6+)
|
||||
- **Styling**: CSS3 + Flexbox/Grid with CSS Variables
|
||||
- **Icons**: Font Awesome 6.4.0
|
||||
- **Charts**: Chart.js for interactive data visualization
|
||||
- **Fonts**: Segoe UI system font
|
||||
- **API**: RESTful API calls
|
||||
- **API**: RESTful API calls with automatic authentication
|
||||
- **Internationalization**: Custom i18n system with English/Chinese support
|
||||
- **Theme System**: CSS custom properties for dynamic theming
|
||||
- **Storage**: LocalStorage for user preferences and credentials
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -115,12 +176,20 @@ npm start
|
||||
### File Structure
|
||||
```
|
||||
webui/
|
||||
├── index.html # Main page
|
||||
├── styles.css # Stylesheet
|
||||
├── app.js # Application logic
|
||||
├── index.html # Main page with responsive layout
|
||||
├── styles.css # Stylesheet with theme support
|
||||
├── app.js # Application logic and API management
|
||||
├── i18n.js # Internationalization support (EN/CN)
|
||||
├── package.json # Project configuration
|
||||
├── i18n.js # Internationalization support
|
||||
└── README.md # README document
|
||||
├── build.js # Build script for production
|
||||
├── bundle-entry.js # Entry point for bundling
|
||||
├── build-scripts/ # Build utilities
|
||||
│ └── prepare-html.js # HTML preparation script
|
||||
├── logo.jpg # Application logo
|
||||
├── LICENSE # MIT License
|
||||
├── README.md # English documentation
|
||||
├── README_CN.md # Chinese documentation
|
||||
└── BUILD_RELEASE.md # Build and release notes
|
||||
```
|
||||
|
||||
### API Calls
|
||||
|
||||
103
README_CN.md
103
README_CN.md
@@ -1,10 +1,17 @@
|
||||
# Cli-Proxy-API-Management-Center
|
||||
这是一个用于管理 CLI Proxy API 的现代化 Web 界面。
|
||||
|
||||
主项目
|
||||
https://github.com/router-for-me/CLIProxyAPI
|
||||
|
||||
最低可用版本 ≥ 5.0.0
|
||||
推荐版本 ≥ 5.1.1
|
||||
示例网站:
|
||||
https://remote.router-for.me/
|
||||
|
||||
最低可用版本 ≥ 6.0.0
|
||||
|
||||
推荐版本 ≥ 6.2.32
|
||||
|
||||
自6.0.19起WebUI已经集成在主程序中 可以通过主项目开启的外部端口的`/management.html`访问
|
||||
|
||||
## 功能特点
|
||||
|
||||
@@ -12,13 +19,16 @@ https://github.com/router-for-me/CLIProxyAPI
|
||||
- 支持管理密钥认证
|
||||
- 可配置 API 基础地址
|
||||
- 实时连接状态检测
|
||||
- 自动登录保存的凭据
|
||||
- 语言和主题切换
|
||||
|
||||
### 基础设置
|
||||
- **调试模式**: 开启/关闭调试功能
|
||||
- **代理设置**: 配置代理服务器 URL
|
||||
- **请求重试**: 设置请求重试次数
|
||||
- **配额管理**: 配置超出配额时的行为
|
||||
- **本地访问**: 管理本地未认证访问
|
||||
- 超出配额时自动切换项目
|
||||
- 超出配额时切换到预览模型
|
||||
|
||||
### API 密钥管理
|
||||
- **代理服务认证密钥**: 管理用于代理服务的 API 密钥
|
||||
@@ -33,13 +43,31 @@ https://github.com/router-for-me/CLIProxyAPI
|
||||
- 删除单个或所有认证文件
|
||||
- 显示文件详细信息
|
||||
|
||||
### 使用统计
|
||||
- **实时分析**: 通过交互式图表跟踪 API 使用情况
|
||||
- **请求趋势**: 按小时/天可视化请求模式
|
||||
- **Token 使用**: 监控 Token 消耗随时间变化
|
||||
- **API 详情**: 每个 API 端点的详细统计
|
||||
- **成功率/失败率**: 跟踪 API 可靠性指标
|
||||
|
||||
### 系统信息
|
||||
- **连接状态**: 实时连接监控
|
||||
- **配置状态**: 跟踪配置加载状态
|
||||
- **服务器信息**: 显示服务器地址和管理密钥
|
||||
- **最后更新**: 显示数据最后刷新时间
|
||||
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 直接使用(推荐)
|
||||
### 1. 在CLI Proxy API程序启动后使用 (推荐)
|
||||
在启动了CLI Proxy API程序后 访问`http://您的服务器IP:8317/management.html`使用
|
||||
|
||||
### 2. 直接使用
|
||||
直接用浏览器打开 `index.html` 文件即可使用。
|
||||
|
||||
### 2. 使用本地服务器
|
||||
### 3. 使用本地服务器
|
||||
|
||||
#### 方法A:使用 Node.js (npm)
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
@@ -48,10 +76,19 @@ npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
### 3. 配置 API 连接
|
||||
#### 方法B:使用 Python
|
||||
```bash
|
||||
# Python 3.x
|
||||
python -m http.server 8000
|
||||
|
||||
```
|
||||
|
||||
然后在浏览器中打开 `http://localhost:8000`。
|
||||
|
||||
### 3. 配置连接
|
||||
1. 打开管理界面
|
||||
2. 在登录界面上输入:
|
||||
- **远程地址**: `http://localhost:8317`/v0/management将会自动为您补全
|
||||
- **远程地址**: 现版本远程地址将会自动从您的访问地址中获取 当然您也可以自定义
|
||||
- **管理密钥**: 您的管理密钥
|
||||
3. 点击"连接"按钮
|
||||
4. 连接成功后即可使用所有功能
|
||||
@@ -63,8 +100,16 @@ npm start
|
||||
- **API 密钥**: 各种 API 服务的密钥管理
|
||||
- **AI 提供商**: AI 服务提供商配置
|
||||
- **认证文件**: 认证文件的上传下载管理
|
||||
- **使用统计**: 实时分析和使用统计,包含交互式图表
|
||||
- **系统信息**: 连接状态和系统信息
|
||||
|
||||
### 登录界面
|
||||
- **自动连接**: 使用保存的凭据自动尝试连接
|
||||
- **自定义连接**: 手动配置 API 基础地址
|
||||
- **当前地址检测**: 自动检测并使用当前访问地址
|
||||
- **语言切换**: 支持多种语言(英文/中文)
|
||||
- **主题切换**: 支持明暗主题
|
||||
|
||||
## 特性亮点
|
||||
|
||||
### 现代化 UI
|
||||
@@ -72,27 +117,45 @@ npm start
|
||||
- 美观的渐变色彩和阴影效果
|
||||
- 流畅的动画和过渡效果
|
||||
- 直观的图标和状态指示
|
||||
- 明暗主题支持,自动检测系统偏好
|
||||
- 移动端友好的侧边栏和遮罩
|
||||
|
||||
### 实时更新
|
||||
- 配置更改立即生效
|
||||
- 实时状态反馈
|
||||
- 自动数据刷新
|
||||
- 实时使用统计和交互式图表
|
||||
- 实时连接状态监控
|
||||
|
||||
### 安全特性
|
||||
- 密钥遮蔽显示
|
||||
- 安全凭据存储
|
||||
- 加密本地存储自动登录
|
||||
|
||||
### 响应式设计
|
||||
- 完美适配桌面和移动设备
|
||||
- 自适应布局
|
||||
- 自适应布局,可折叠侧边栏
|
||||
- 触摸友好的交互
|
||||
- 移动端菜单和遮罩
|
||||
|
||||
### 分析与监控
|
||||
- Chart.js 驱动的交互式图表
|
||||
- 实时使用统计
|
||||
- 请求趋势可视化
|
||||
- Token 消耗跟踪
|
||||
- API 性能指标
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **前端**: 纯 HTML、CSS、JavaScript
|
||||
- **样式**: CSS3 + Flexbox/Grid
|
||||
- **前端**: 纯 HTML、CSS、JavaScript (ES6+)
|
||||
- **样式**: CSS3 + Flexbox/Grid,支持 CSS 变量
|
||||
- **图标**: Font Awesome 6.4.0
|
||||
- **图表**: Chart.js 交互式数据可视化
|
||||
- **字体**: Segoe UI 系统字体
|
||||
- **API**: RESTful API 调用
|
||||
- **API**: RESTful API 调用,自动认证
|
||||
- **国际化**: 自定义 i18n 系统,支持中英文
|
||||
- **主题系统**: CSS 自定义属性动态主题
|
||||
- **存储**: LocalStorage 用户偏好和凭据存储
|
||||
|
||||
## 故障排除
|
||||
|
||||
@@ -112,12 +175,20 @@ npm start
|
||||
### 文件结构
|
||||
```
|
||||
webui/
|
||||
├── index.html # 主页面
|
||||
├── styles.css # 样式文件
|
||||
├── app.js # 应用逻辑
|
||||
├── index.html # 主页面,响应式布局
|
||||
├── styles.css # 样式文件,支持主题
|
||||
├── app.js # 应用逻辑和 API 管理
|
||||
├── i18n.js # 国际化支持(中英文)
|
||||
├── package.json # 项目配置
|
||||
├── i18n.js # 国际化支持
|
||||
└── README.md # 说明文档
|
||||
├── build.js # 生产环境构建脚本
|
||||
├── bundle-entry.js # 打包入口文件
|
||||
├── build-scripts/ # 构建工具
|
||||
│ └── prepare-html.js # HTML 准备脚本
|
||||
├── logo.jpg # 应用图标
|
||||
├── LICENSE # MIT 许可证
|
||||
├── README.md # 英文文档
|
||||
├── README_CN.md # 中文文档
|
||||
└── BUILD_RELEASE.md # 构建和发布说明
|
||||
```
|
||||
|
||||
### API 调用
|
||||
|
||||
30
build-scripts/prepare-html.js
Normal file
30
build-scripts/prepare-html.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Read the original index.html
|
||||
const indexPath = path.resolve(__dirname, '../index.html');
|
||||
const outputPath = path.resolve(__dirname, '../index.build.html');
|
||||
|
||||
let htmlContent = fs.readFileSync(indexPath, 'utf8');
|
||||
|
||||
// Remove local CSS reference
|
||||
htmlContent = htmlContent.replace(
|
||||
/<link rel="stylesheet" href="styles\.css">\n?/g,
|
||||
''
|
||||
);
|
||||
|
||||
// Remove local JavaScript references
|
||||
htmlContent = htmlContent.replace(
|
||||
/<script src="i18n\.js"><\/script>\n?/g,
|
||||
''
|
||||
);
|
||||
|
||||
htmlContent = htmlContent.replace(
|
||||
/<script src="app\.js"><\/script>\n?/g,
|
||||
''
|
||||
);
|
||||
|
||||
// Write the modified HTML to a temporary build file
|
||||
fs.writeFileSync(outputPath, htmlContent, 'utf8');
|
||||
|
||||
console.log('✓ Generated index.build.html for webpack processing');
|
||||
166
build.js
Normal file
166
build.js
Normal file
@@ -0,0 +1,166 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const projectRoot = __dirname;
|
||||
const distDir = path.join(projectRoot, 'dist');
|
||||
|
||||
const sourceFiles = {
|
||||
html: path.join(projectRoot, 'index.html'),
|
||||
css: path.join(projectRoot, 'styles.css'),
|
||||
i18n: path.join(projectRoot, 'i18n.js'),
|
||||
app: path.join(projectRoot, 'app.js')
|
||||
};
|
||||
|
||||
const logoCandidates = ['logo.png', 'logo.jpg', 'logo.jpeg', 'logo.svg', 'logo.webp', 'logo.gif'];
|
||||
const logoMimeMap = {
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.webp': 'image/webp',
|
||||
'.gif': 'image/gif'
|
||||
};
|
||||
|
||||
function readFile(filePath) {
|
||||
try {
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
} catch (err) {
|
||||
console.error(`读取文件失败: ${filePath}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function readBinary(filePath) {
|
||||
try {
|
||||
return fs.readFileSync(filePath);
|
||||
} catch (err) {
|
||||
console.error(`读取文件失败: ${filePath}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeForScript(content) {
|
||||
return content.replace(/<\/(script)/gi, '<\\/$1');
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
fs.mkdirSync(distDir);
|
||||
}
|
||||
|
||||
function loadLogoDataUrl() {
|
||||
for (const candidate of logoCandidates) {
|
||||
const filePath = path.join(projectRoot, candidate);
|
||||
if (!fs.existsSync(filePath)) continue;
|
||||
|
||||
const ext = path.extname(candidate).toLowerCase();
|
||||
const mime = logoMimeMap[ext];
|
||||
if (!mime) {
|
||||
console.warn(`未知 Logo 文件类型,跳过内联: ${candidate}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const buffer = readBinary(filePath);
|
||||
const base64 = buffer.toString('base64');
|
||||
return `data:${mime};base64,${base64}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function build() {
|
||||
ensureDistDir();
|
||||
|
||||
let html = readFile(sourceFiles.html);
|
||||
const css = escapeForStyle(readFile(sourceFiles.css));
|
||||
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>
|
||||
${css}
|
||||
</style>`
|
||||
);
|
||||
|
||||
html = html.replace(
|
||||
'<script src="i18n.js"></script>',
|
||||
`<script>
|
||||
${i18n}
|
||||
</script>`
|
||||
);
|
||||
|
||||
html = html.replace(
|
||||
'<script src="app.js"></script>',
|
||||
`<script>
|
||||
${app}
|
||||
</script>`
|
||||
);
|
||||
|
||||
const logoDataUrl = loadLogoDataUrl();
|
||||
if (logoDataUrl) {
|
||||
const logoScript = `<script>window.__INLINE_LOGO__ = "${logoDataUrl}";</script>`;
|
||||
if (html.includes('</body>')) {
|
||||
html = html.replace('</body>', `${logoScript}\n</body>`);
|
||||
} else {
|
||||
html += `\n${logoScript}`;
|
||||
}
|
||||
} else {
|
||||
console.warn('未找到可内联的 Logo 文件,将保持运行时加载。');
|
||||
}
|
||||
|
||||
const outputPath = path.join(distDir, 'index.html');
|
||||
fs.writeFileSync(outputPath, html, 'utf8');
|
||||
|
||||
console.log('构建完成: dist/index.html');
|
||||
}
|
||||
|
||||
try {
|
||||
build();
|
||||
} catch (error) {
|
||||
console.error('构建失败:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
25
bundle-entry.js
Normal file
25
bundle-entry.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// Import CSS
|
||||
import './styles.css';
|
||||
|
||||
// Import JavaScript modules
|
||||
import './i18n.js';
|
||||
import './app.js';
|
||||
|
||||
// Import logo image
|
||||
import logoImg from './logo.jpg';
|
||||
|
||||
// Set logo after DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const loginLogo = document.getElementById('login-logo');
|
||||
const siteLogo = document.getElementById('site-logo');
|
||||
|
||||
if (loginLogo) {
|
||||
loginLogo.src = logoImg;
|
||||
loginLogo.style.display = 'block';
|
||||
}
|
||||
|
||||
if (siteLogo) {
|
||||
siteLogo.src = logoImg;
|
||||
siteLogo.style.display = 'block';
|
||||
}
|
||||
});
|
||||
619
i18n.js
619
i18n.js
@@ -36,6 +36,18 @@ const i18n = {
|
||||
'common.required': '必填',
|
||||
'common.api_key': '密钥',
|
||||
'common.base_url': '地址',
|
||||
'common.proxy_url': '代理',
|
||||
'common.alias': '别名',
|
||||
'common.failure': '失败',
|
||||
'common.unknown_error': '未知错误',
|
||||
'common.copy': '复制',
|
||||
'common.custom_headers_label': '自定义请求头',
|
||||
'common.custom_headers_hint': '可选,设置需要附带到请求中的 HTTP 头,名称和值均不能为空。',
|
||||
'common.custom_headers_add': '添加请求头',
|
||||
'common.custom_headers_key_placeholder': 'Header 名称,例如 X-Custom-Header',
|
||||
'common.custom_headers_value_placeholder': 'Header 值',
|
||||
'common.model_name_placeholder': '模型名称,例如 claude-3-5-sonnet-20241022',
|
||||
'common.model_alias_placeholder': '模型别名 (可选)',
|
||||
|
||||
// 页面标题
|
||||
'title.main': 'CLI Proxy API Management Center',
|
||||
@@ -47,19 +59,13 @@ const i18n = {
|
||||
|
||||
// 登录页面
|
||||
'login.subtitle': '请输入连接信息以访问管理界面',
|
||||
'login.tab_local_title': 'Local',
|
||||
'login.tab_local_subtitle': '在本地运行 Cli Web 服务器',
|
||||
'login.tab_remote_title': 'Remote',
|
||||
'login.tab_remote_subtitle': '远程连接到 Cli Web 服务器',
|
||||
'login.proxy_label': '代理服务器 (可选):',
|
||||
'login.proxy_placeholder': 'http://ip:port 或 https://ip:port 或 socks5://user:pass@ip:port',
|
||||
'login.local_port_label': '端口号:',
|
||||
'login.local_port_placeholder': '8317',
|
||||
'login.local_url_hint': '将连接到 http://localhost:端口/v0/management',
|
||||
'login.api_url_label': 'API 基础地址:',
|
||||
'login.api_url_placeholder': '例如: http://localhost:8317 或 127.0.0.1:8317',
|
||||
'login.remote_api_url_placeholder': '例如: https://example.com:8317',
|
||||
'login.api_url_hint': '将自动补全 /v0/management',
|
||||
'login.connection_title': '连接地址',
|
||||
'login.connection_current': '当前地址',
|
||||
'login.connection_auto_hint': '系统将自动使用当前访问地址进行连接',
|
||||
'login.custom_connection_label': '自定义连接地址:',
|
||||
'login.custom_connection_placeholder': '例如: https://example.com:8317',
|
||||
'login.custom_connection_hint': '默认使用当前访问地址,若需要可手动输入其他地址。',
|
||||
'login.use_current_address': '使用当前地址',
|
||||
'login.management_key_label': '管理密钥:',
|
||||
'login.management_key_placeholder': '请输入管理密钥',
|
||||
'login.connect_button': '连接',
|
||||
@@ -85,6 +91,9 @@ const i18n = {
|
||||
'nav.api_keys': 'API 密钥',
|
||||
'nav.ai_providers': 'AI 提供商',
|
||||
'nav.auth_files': '认证文件',
|
||||
'nav.usage_stats': '使用统计',
|
||||
'nav.config_management': '配置管理',
|
||||
'nav.logs': '日志查看',
|
||||
'nav.system_info': '系统信息',
|
||||
|
||||
// 基础设置
|
||||
@@ -102,8 +111,13 @@ const i18n = {
|
||||
'basic_settings.quota_title': '配额超出行为',
|
||||
'basic_settings.quota_switch_project': '自动切换项目',
|
||||
'basic_settings.quota_switch_preview': '切换到预览模型',
|
||||
'basic_settings.localhost_title': '本地访问',
|
||||
'basic_settings.localhost_allow': '允许本地未认证访问',
|
||||
'basic_settings.usage_statistics_title': '使用统计',
|
||||
'basic_settings.usage_statistics_enable': '启用使用统计',
|
||||
'basic_settings.logging_title': '日志记录',
|
||||
'basic_settings.logging_to_file_enable': '启用日志记录到文件',
|
||||
'basic_settings.request_log_enable': '启用请求日志',
|
||||
'basic_settings.ws_auth_title': 'WebSocket 鉴权',
|
||||
'basic_settings.ws_auth_enable': '启用 /ws/* 鉴权',
|
||||
|
||||
// API 密钥管理
|
||||
'api_keys.title': 'API 密钥管理',
|
||||
@@ -127,8 +141,11 @@ const i18n = {
|
||||
'ai_providers.gemini_empty_desc': '点击上方按钮添加第一个密钥',
|
||||
'ai_providers.gemini_item_title': 'Gemini密钥',
|
||||
'ai_providers.gemini_add_modal_title': '添加Gemini API密钥',
|
||||
'ai_providers.gemini_add_modal_key_label': 'API密钥:',
|
||||
'ai_providers.gemini_add_modal_key_placeholder': '请输入Gemini API密钥',
|
||||
'ai_providers.gemini_add_modal_key_label': 'API密钥',
|
||||
'ai_providers.gemini_add_modal_key_placeholder': '输入 Gemini API 密钥',
|
||||
'ai_providers.gemini_add_modal_key_hint': '逐条输入密钥,可同时指定可选 Base URL。',
|
||||
'ai_providers.gemini_keys_add_btn': '添加密钥',
|
||||
'ai_providers.gemini_base_url_placeholder': '可选 Base URL,如 https://generativelanguage.googleapis.com',
|
||||
'ai_providers.gemini_edit_modal_title': '编辑Gemini API密钥',
|
||||
'ai_providers.gemini_edit_modal_key_label': 'API密钥:',
|
||||
'ai_providers.gemini_delete_confirm': '确定要删除这个Gemini密钥吗?',
|
||||
@@ -141,11 +158,14 @@ const i18n = {
|
||||
'ai_providers.codex_add_modal_title': '添加Codex API配置',
|
||||
'ai_providers.codex_add_modal_key_label': 'API密钥:',
|
||||
'ai_providers.codex_add_modal_key_placeholder': '请输入Codex API密钥',
|
||||
'ai_providers.codex_add_modal_url_label': 'Base URL (可选):',
|
||||
'ai_providers.codex_add_modal_url_label': 'Base URL (必填):',
|
||||
'ai_providers.codex_add_modal_url_placeholder': '例如: https://api.example.com',
|
||||
'ai_providers.codex_add_modal_proxy_label': '代理 URL (可选):',
|
||||
'ai_providers.codex_add_modal_proxy_placeholder': '例如: socks5://proxy.example.com:1080',
|
||||
'ai_providers.codex_edit_modal_title': '编辑Codex API配置',
|
||||
'ai_providers.codex_edit_modal_key_label': 'API密钥:',
|
||||
'ai_providers.codex_edit_modal_url_label': 'Base URL (可选):',
|
||||
'ai_providers.codex_edit_modal_url_label': 'Base URL (必填):',
|
||||
'ai_providers.codex_edit_modal_proxy_label': '代理 URL (可选):',
|
||||
'ai_providers.codex_delete_confirm': '确定要删除这个Codex配置吗?',
|
||||
|
||||
'ai_providers.claude_title': 'Claude API 配置',
|
||||
@@ -158,10 +178,17 @@ const i18n = {
|
||||
'ai_providers.claude_add_modal_key_placeholder': '请输入Claude API密钥',
|
||||
'ai_providers.claude_add_modal_url_label': 'Base URL (可选):',
|
||||
'ai_providers.claude_add_modal_url_placeholder': '例如: https://api.anthropic.com',
|
||||
'ai_providers.claude_add_modal_proxy_label': '代理 URL (可选):',
|
||||
'ai_providers.claude_add_modal_proxy_placeholder': '例如: socks5://proxy.example.com:1080',
|
||||
'ai_providers.claude_edit_modal_title': '编辑Claude API配置',
|
||||
'ai_providers.claude_edit_modal_key_label': 'API密钥:',
|
||||
'ai_providers.claude_edit_modal_url_label': 'Base URL (可选):',
|
||||
'ai_providers.claude_edit_modal_proxy_label': '代理 URL (可选):',
|
||||
'ai_providers.claude_delete_confirm': '确定要删除这个Claude配置吗?',
|
||||
'ai_providers.claude_models_label': '自定义模型 (可选):',
|
||||
'ai_providers.claude_models_hint': '为空表示使用全部模型;可填写 name[, alias] 以限制或重命名模型。',
|
||||
'ai_providers.claude_models_add_btn': '添加模型',
|
||||
'ai_providers.claude_models_count': '模型数量',
|
||||
|
||||
'ai_providers.openai_title': 'OpenAI 兼容提供商',
|
||||
'ai_providers.openai_add_button': '添加提供商',
|
||||
@@ -172,12 +199,22 @@ const i18n = {
|
||||
'ai_providers.openai_add_modal_name_placeholder': '例如: openrouter',
|
||||
'ai_providers.openai_add_modal_url_label': 'Base URL:',
|
||||
'ai_providers.openai_add_modal_url_placeholder': '例如: https://openrouter.ai/api/v1',
|
||||
'ai_providers.openai_add_modal_keys_label': 'API密钥 (每行一个):',
|
||||
'ai_providers.openai_add_modal_keys_placeholder': 'sk-key1\nsk-key2',
|
||||
'ai_providers.openai_add_modal_keys_label': 'API密钥',
|
||||
'ai_providers.openai_edit_modal_keys_label': 'API密钥',
|
||||
'ai_providers.openai_keys_hint': '每个密钥可搭配一个可选代理地址,更便于管理。',
|
||||
'ai_providers.openai_keys_add_btn': '添加密钥',
|
||||
'ai_providers.openai_key_placeholder': '输入 sk- 开头的密钥',
|
||||
'ai_providers.openai_proxy_placeholder': '可选代理 URL (如 socks5://...)',
|
||||
'ai_providers.openai_add_modal_models_label': '模型列表 (name[, alias] 每行一个):',
|
||||
'ai_providers.openai_models_hint': '示例:gpt-4o-mini 或 moonshotai/kimi-k2:free, kimi-k2',
|
||||
'ai_providers.openai_model_name_placeholder': '模型名称,如 moonshotai/kimi-k2:free',
|
||||
'ai_providers.openai_model_alias_placeholder': '模型别名 (可选)',
|
||||
'ai_providers.openai_models_add_btn': '添加模型',
|
||||
'ai_providers.openai_edit_modal_title': '编辑OpenAI兼容提供商',
|
||||
'ai_providers.openai_edit_modal_name_label': '提供商名称:',
|
||||
'ai_providers.openai_edit_modal_url_label': 'Base URL:',
|
||||
'ai_providers.openai_edit_modal_keys_label': 'API密钥 (每行一个):',
|
||||
'ai_providers.openai_edit_modal_keys_label': 'API密钥',
|
||||
'ai_providers.openai_edit_modal_models_label': '模型列表 (name[, alias] 每行一个):',
|
||||
'ai_providers.openai_delete_confirm': '确定要删除这个OpenAI提供商吗?',
|
||||
'ai_providers.openai_keys_count': '密钥数量',
|
||||
'ai_providers.openai_models_count': '模型数量',
|
||||
@@ -186,33 +223,206 @@ const i18n = {
|
||||
// 认证文件管理
|
||||
'auth_files.title': '认证文件管理',
|
||||
'auth_files.title_section': '认证文件',
|
||||
'auth_files.description': '这里管理 Qwen 和 Gemini 的认证配置文件。上传 JSON 格式的认证文件以启用相应的 AI 服务。',
|
||||
'auth_files.description': '这里集中管理 CLI Proxy 支持的所有 JSON 认证文件(如 Qwen、Gemini、Vertex 等),上传后即可在运行时启用相应的 AI 服务。',
|
||||
'auth_files.upload_button': '上传文件',
|
||||
'auth_files.delete_all_button': '删除全部',
|
||||
'auth_files.empty_title': '暂无认证文件',
|
||||
'auth_files.empty_desc': '点击上方按钮上传第一个文件',
|
||||
'auth_files.search_empty_title': '没有匹配的配置文件',
|
||||
'auth_files.search_empty_desc': '请调整筛选条件或清空搜索关键字再试一次。',
|
||||
'auth_files.file_size': '大小',
|
||||
'auth_files.file_modified': '修改时间',
|
||||
'auth_files.download_button': '下载',
|
||||
'auth_files.delete_button': '删除',
|
||||
'auth_files.delete_confirm': '确定要删除文件',
|
||||
'auth_files.delete_all_confirm': '确定要删除所有认证文件吗?此操作不可恢复!',
|
||||
'auth_files.delete_filtered_confirm': '确定要删除筛选出的 {type} 认证文件吗?此操作不可恢复!',
|
||||
'auth_files.upload_error_json': '只能上传JSON文件',
|
||||
'auth_files.upload_success': '文件上传成功',
|
||||
'auth_files.download_success': '文件下载成功',
|
||||
'auth_files.delete_success': '文件删除成功',
|
||||
'auth_files.delete_all_success': '成功删除',
|
||||
'auth_files.delete_filtered_success': '成功删除 {count} 个 {type} 认证文件',
|
||||
'auth_files.delete_filtered_partial': '{type} 认证文件删除完成,成功 {success} 个,失败 {failed} 个',
|
||||
'auth_files.delete_filtered_none': '当前筛选类型 ({type}) 下没有可删除的认证文件',
|
||||
'auth_files.files_count': '个文件',
|
||||
'auth_files.pagination_prev': '上一页',
|
||||
'auth_files.pagination_next': '下一页',
|
||||
'auth_files.pagination_info': '第 {current} / {total} 页 · 共 {count} 个文件',
|
||||
'auth_files.search_label': '搜索配置文件',
|
||||
'auth_files.search_placeholder': '输入名称、类型或提供方关键字',
|
||||
'auth_files.page_size_label': '单页数量',
|
||||
'auth_files.page_size_unit': '个/页',
|
||||
'auth_files.filter_all': '全部',
|
||||
'auth_files.filter_qwen': 'Qwen',
|
||||
'auth_files.filter_gemini': 'Gemini',
|
||||
'auth_files.filter_gemini-cli': 'GeminiCLI',
|
||||
'auth_files.filter_aistudio': 'AIStudio',
|
||||
'auth_files.filter_claude': 'Claude',
|
||||
'auth_files.filter_codex': 'Codex',
|
||||
'auth_files.filter_iflow': 'iFlow',
|
||||
'auth_files.filter_vertex': 'Vertex',
|
||||
'auth_files.filter_empty': '空文件',
|
||||
'auth_files.filter_unknown': '其他',
|
||||
'auth_files.type_qwen': 'Qwen',
|
||||
'auth_files.type_gemini': 'Gemini',
|
||||
'auth_files.type_gemini-cli': 'GeminiCLI',
|
||||
'auth_files.type_aistudio': 'AIStudio',
|
||||
'auth_files.type_claude': 'Claude',
|
||||
'auth_files.type_codex': 'Codex',
|
||||
'auth_files.type_iflow': 'iFlow',
|
||||
'auth_files.type_vertex': 'Vertex',
|
||||
'auth_files.type_empty': '空文件',
|
||||
'auth_files.type_unknown': '其他',
|
||||
'vertex_import.title': 'Vertex AI 凭证导入',
|
||||
'vertex_import.description': '上传 Google 服务账号 JSON,使用 CLI vertex-import 同步规则写入 auth-dir/vertex-<project>.json。',
|
||||
'vertex_import.location_label': '目标区域 (可选)',
|
||||
'vertex_import.location_placeholder': 'us-central1',
|
||||
'vertex_import.location_hint': '留空表示使用默认区域 us-central1。',
|
||||
'vertex_import.file_label': '服务账号密钥 JSON',
|
||||
'vertex_import.file_hint': '仅支持 Google Cloud service account key JSON 文件,私钥会自动规范化。',
|
||||
'vertex_import.file_placeholder': '尚未选择文件',
|
||||
'vertex_import.choose_file': '选择文件',
|
||||
'vertex_import.import_button': '导入 Vertex 凭证',
|
||||
'vertex_import.file_required': '请先选择 .json 凭证文件',
|
||||
'vertex_import.success': 'Vertex 凭证导入成功',
|
||||
'vertex_import.result_title': '凭证已保存',
|
||||
'vertex_import.result_project': '项目 ID',
|
||||
'vertex_import.result_email': '服务账号',
|
||||
'vertex_import.result_location': '区域',
|
||||
'vertex_import.result_file': '存储文件',
|
||||
|
||||
// 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_saved': 'Gemini Web Token 保存成功',
|
||||
|
||||
// Codex OAuth
|
||||
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||
'auth_login.codex_oauth_button': '开始 Codex 登录',
|
||||
'auth_login.codex_oauth_hint': '通过 OAuth 流程登录 Codex 服务,自动获取并保存认证文件。',
|
||||
'auth_login.codex_oauth_url_label': '授权链接:',
|
||||
'auth_login.codex_open_link': '打开链接',
|
||||
'auth_login.codex_copy_link': '复制链接',
|
||||
'auth_login.codex_oauth_status_waiting': '等待认证中...',
|
||||
'auth_login.codex_oauth_status_success': '认证成功!',
|
||||
'auth_login.codex_oauth_status_error': '认证失败:',
|
||||
'auth_login.codex_oauth_start_error': '启动 Codex OAuth 失败:',
|
||||
'auth_login.codex_oauth_polling_error': '检查认证状态失败:',
|
||||
|
||||
// Anthropic OAuth
|
||||
'auth_login.anthropic_oauth_title': 'Anthropic OAuth',
|
||||
'auth_login.anthropic_oauth_button': '开始 Anthropic 登录',
|
||||
'auth_login.anthropic_oauth_hint': '通过 OAuth 流程登录 Anthropic (Claude) 服务,自动获取并保存认证文件。',
|
||||
'auth_login.anthropic_oauth_url_label': '授权链接:',
|
||||
'auth_login.anthropic_open_link': '打开链接',
|
||||
'auth_login.anthropic_copy_link': '复制链接',
|
||||
'auth_login.anthropic_oauth_status_waiting': '等待认证中...',
|
||||
'auth_login.anthropic_oauth_status_success': '认证成功!',
|
||||
'auth_login.anthropic_oauth_status_error': '认证失败:',
|
||||
'auth_login.anthropic_oauth_start_error': '启动 Anthropic OAuth 失败:',
|
||||
'auth_login.anthropic_oauth_polling_error': '检查认证状态失败:',
|
||||
|
||||
// Gemini CLI OAuth
|
||||
'auth_login.gemini_cli_oauth_title': 'Gemini CLI OAuth',
|
||||
'auth_login.gemini_cli_oauth_button': '开始 Gemini CLI 登录',
|
||||
'auth_login.gemini_cli_oauth_hint': '通过 OAuth 流程登录 Google Gemini CLI 服务,自动获取并保存认证文件。',
|
||||
'auth_login.gemini_cli_project_id_label': 'Google Cloud 项目 ID (可选):',
|
||||
'auth_login.gemini_cli_project_id_placeholder': '输入 Google Cloud 项目 ID (可选)',
|
||||
'auth_login.gemini_cli_project_id_hint': '如果指定了项目 ID,将使用该项目的认证信息。',
|
||||
'auth_login.gemini_cli_oauth_url_label': '授权链接:',
|
||||
'auth_login.gemini_cli_open_link': '打开链接',
|
||||
'auth_login.gemini_cli_copy_link': '复制链接',
|
||||
'auth_login.gemini_cli_oauth_status_waiting': '等待认证中...',
|
||||
'auth_login.gemini_cli_oauth_status_success': '认证成功!',
|
||||
'auth_login.gemini_cli_oauth_status_error': '认证失败:',
|
||||
'auth_login.gemini_cli_oauth_start_error': '启动 Gemini CLI OAuth 失败:',
|
||||
'auth_login.gemini_cli_oauth_polling_error': '检查认证状态失败:',
|
||||
|
||||
// Qwen OAuth
|
||||
'auth_login.qwen_oauth_title': 'Qwen OAuth',
|
||||
'auth_login.qwen_oauth_button': '开始 Qwen 登录',
|
||||
'auth_login.qwen_oauth_hint': '通过设备授权流程登录 Qwen 服务,自动获取并保存认证文件。',
|
||||
'auth_login.qwen_oauth_url_label': '授权链接:',
|
||||
'auth_login.qwen_open_link': '打开链接',
|
||||
'auth_login.qwen_copy_link': '复制链接',
|
||||
'auth_login.qwen_oauth_status_waiting': '等待认证中...',
|
||||
'auth_login.qwen_oauth_status_success': '认证成功!',
|
||||
'auth_login.qwen_oauth_status_error': '认证失败:',
|
||||
'auth_login.qwen_oauth_start_error': '启动 Qwen OAuth 失败:',
|
||||
'auth_login.qwen_oauth_polling_error': '检查认证状态失败:',
|
||||
'auth_login.missing_state': '无法获取认证状态参数',
|
||||
|
||||
// iFlow OAuth
|
||||
'auth_login.iflow_oauth_title': 'iFlow OAuth',
|
||||
'auth_login.iflow_oauth_button': '开始 iFlow 登录',
|
||||
'auth_login.iflow_oauth_hint': '通过 OAuth 流程登录 iFlow 服务,自动获取并保存认证文件。',
|
||||
'auth_login.iflow_oauth_url_label': '授权链接:',
|
||||
'auth_login.iflow_open_link': '打开链接',
|
||||
'auth_login.iflow_copy_link': '复制链接',
|
||||
'auth_login.iflow_oauth_status_waiting': '等待认证中...',
|
||||
'auth_login.iflow_oauth_status_success': '认证成功!',
|
||||
'auth_login.iflow_oauth_status_error': '认证失败:',
|
||||
'auth_login.iflow_oauth_start_error': '启动 iFlow OAuth 失败:',
|
||||
'auth_login.iflow_oauth_polling_error': '检查认证状态失败:',
|
||||
|
||||
// 使用统计
|
||||
'usage_stats.title': '使用统计',
|
||||
'usage_stats.total_requests': '总请求数',
|
||||
'usage_stats.success_requests': '成功请求',
|
||||
'usage_stats.failed_requests': '失败请求',
|
||||
'usage_stats.total_tokens': '总Token数',
|
||||
'usage_stats.requests_trend': '请求趋势',
|
||||
'usage_stats.tokens_trend': 'Token 使用趋势',
|
||||
'usage_stats.api_details': 'API 详细统计',
|
||||
'usage_stats.by_hour': '按小时',
|
||||
'usage_stats.by_day': '按天',
|
||||
'usage_stats.refresh': '刷新',
|
||||
'usage_stats.no_data': '暂无数据',
|
||||
'usage_stats.loading_error': '加载失败',
|
||||
'usage_stats.api_endpoint': 'API端点',
|
||||
'usage_stats.requests_count': '请求次数',
|
||||
'usage_stats.tokens_count': 'Token数量',
|
||||
'usage_stats.models': '模型统计',
|
||||
'usage_stats.success_rate': '成功率',
|
||||
'stats.success': '成功',
|
||||
'stats.failure': '失败',
|
||||
|
||||
// 日志查看
|
||||
'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 接口可用',
|
||||
'config_management.editor_placeholder': 'key: value',
|
||||
|
||||
// 系统信息
|
||||
'system_info.title': '系统信息',
|
||||
@@ -232,22 +442,32 @@ const i18n = {
|
||||
'notification.retry_updated': '重试设置已更新',
|
||||
'notification.quota_switch_project_updated': '项目切换设置已更新',
|
||||
'notification.quota_switch_preview_updated': '预览模型切换设置已更新',
|
||||
'notification.localhost_updated': '本地访问设置已更新',
|
||||
'notification.usage_statistics_updated': '使用统计设置已更新',
|
||||
'notification.logging_to_file_updated': '日志记录设置已更新',
|
||||
'notification.request_log_updated': '请求日志设置已更新',
|
||||
'notification.ws_auth_updated': 'WebSocket 鉴权设置已更新',
|
||||
'notification.api_key_added': 'API密钥添加成功',
|
||||
'notification.api_key_updated': 'API密钥更新成功',
|
||||
'notification.api_key_deleted': 'API密钥删除成功',
|
||||
'notification.gemini_key_added': 'Gemini密钥添加成功',
|
||||
'notification.gemini_key_updated': 'Gemini密钥更新成功',
|
||||
'notification.gemini_key_deleted': 'Gemini密钥删除成功',
|
||||
'notification.gemini_multi_input_required': '请先输入至少一个Gemini密钥',
|
||||
'notification.gemini_multi_failed': 'Gemini密钥批量添加失败',
|
||||
'notification.gemini_multi_summary': 'Gemini批量添加完成:成功 {success},跳过 {skipped},失败 {failed}',
|
||||
'notification.codex_config_added': 'Codex配置添加成功',
|
||||
'notification.codex_config_updated': 'Codex配置更新成功',
|
||||
'notification.codex_config_deleted': 'Codex配置删除成功',
|
||||
'notification.codex_base_url_required': '请填写Codex Base URL',
|
||||
'notification.claude_config_added': 'Claude配置添加成功',
|
||||
'notification.claude_config_updated': 'Claude配置更新成功',
|
||||
'notification.claude_config_deleted': 'Claude配置删除成功',
|
||||
'notification.field_required': '必填字段不能为空',
|
||||
'notification.openai_provider_required': '请填写提供商名称和Base URL',
|
||||
'notification.openai_provider_added': 'OpenAI提供商添加成功',
|
||||
'notification.openai_provider_updated': 'OpenAI提供商更新成功',
|
||||
'notification.openai_provider_deleted': 'OpenAI提供商删除成功',
|
||||
'notification.openai_model_name_required': '请填写模型名称',
|
||||
'notification.data_refreshed': '数据刷新成功',
|
||||
'notification.connection_required': '请先建立连接',
|
||||
'notification.refresh_failed': '刷新失败',
|
||||
@@ -264,6 +484,7 @@ const i18n = {
|
||||
'notification.gemini_api_key': 'Gemini API密钥',
|
||||
'notification.codex_api_key': 'Codex API密钥',
|
||||
'notification.claude_api_key': 'Claude API密钥',
|
||||
'notification.link_copied': '链接已复制到剪贴板',
|
||||
|
||||
// 语言切换
|
||||
'language.switch': '语言',
|
||||
@@ -276,7 +497,15 @@ const i18n = {
|
||||
'theme.dark': '暗色',
|
||||
'theme.switch_to_light': '切换到亮色模式',
|
||||
'theme.switch_to_dark': '切换到暗色模式',
|
||||
'theme.auto': '跟随系统'
|
||||
'theme.auto': '跟随系统',
|
||||
|
||||
// 侧边栏
|
||||
'sidebar.toggle_expand': '展开侧边栏',
|
||||
'sidebar.toggle_collapse': '收起侧边栏',
|
||||
|
||||
// 页脚
|
||||
'footer.version': '版本',
|
||||
'footer.author': '作者'
|
||||
},
|
||||
|
||||
'en-US': {
|
||||
@@ -309,6 +538,18 @@ const i18n = {
|
||||
'common.required': 'Required',
|
||||
'common.api_key': 'Key',
|
||||
'common.base_url': 'Address',
|
||||
'common.proxy_url': 'Proxy',
|
||||
'common.alias': 'Alias',
|
||||
'common.failure': 'Failure',
|
||||
'common.unknown_error': 'Unknown error',
|
||||
'common.copy': 'Copy',
|
||||
'common.custom_headers_label': 'Custom Headers',
|
||||
'common.custom_headers_hint': 'Optional HTTP headers to send with the request. Leave blank to remove.',
|
||||
'common.custom_headers_add': 'Add Header',
|
||||
'common.custom_headers_key_placeholder': 'Header name, e.g. X-Custom-Header',
|
||||
'common.custom_headers_value_placeholder': 'Header value',
|
||||
'common.model_name_placeholder': 'Model name, e.g. claude-3-5-sonnet-20241022',
|
||||
'common.model_alias_placeholder': 'Model alias (optional)',
|
||||
|
||||
// Page titles
|
||||
'title.main': 'CLI Proxy API Management Center',
|
||||
@@ -320,21 +561,15 @@ const i18n = {
|
||||
|
||||
// Login page
|
||||
'login.subtitle': 'Please enter connection information to access the management interface',
|
||||
'login.tab_local_title': 'Local',
|
||||
'login.tab_local_subtitle': 'Run Cli Web server on your local machine',
|
||||
'login.tab_remote_title': 'Remote',
|
||||
'login.tab_remote_subtitle': 'Remote connection for a remote Cli Web server',
|
||||
'login.proxy_label': 'Proxy Server (Optional):',
|
||||
'login.proxy_placeholder': 'http://ip:port or https://ip:port or socks5://user:pass@ip:port',
|
||||
'login.local_port_label': 'Port:',
|
||||
'login.local_port_placeholder': '8317',
|
||||
'login.local_url_hint': 'Will connect to http://localhost:port/v0/management',
|
||||
'login.api_url_label': 'API Base URL:',
|
||||
'login.api_url_placeholder': 'e.g.: http://localhost:8317 or 127.0.0.1:8317',
|
||||
'login.remote_api_url_placeholder': 'e.g.: https://example.com:8317',
|
||||
'login.api_url_hint': 'Will automatically append /v0/management',
|
||||
'login.connection_title': 'Connection Address',
|
||||
'login.connection_current': 'Current URL',
|
||||
'login.connection_auto_hint': 'The system will automatically use the current URL for connection',
|
||||
'login.custom_connection_label': 'Custom Connection URL:',
|
||||
'login.custom_connection_placeholder': 'Eg: https://example.com:8317',
|
||||
'login.custom_connection_hint': 'By default the current URL is used. Override it here if needed.',
|
||||
'login.use_current_address': 'Use Current URL',
|
||||
'login.management_key_label': 'Management Key:',
|
||||
'login.management_key_placeholder': 'Please enter management key',
|
||||
'login.management_key_placeholder': 'Enter the management key',
|
||||
'login.connect_button': 'Connect',
|
||||
'login.submit_button': 'Login',
|
||||
'login.submitting': 'Connecting...',
|
||||
@@ -358,6 +593,9 @@ const i18n = {
|
||||
'nav.api_keys': 'API Keys',
|
||||
'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
|
||||
@@ -375,8 +613,13 @@ 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.localhost_title': 'Local Access',
|
||||
'basic_settings.localhost_allow': 'Allow Localhost Unauthenticated Access',
|
||||
'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',
|
||||
'basic_settings.request_log_enable': 'Enable request logging',
|
||||
'basic_settings.ws_auth_title': 'WebSocket Authentication',
|
||||
'basic_settings.ws_auth_enable': 'Require auth for /ws/*',
|
||||
|
||||
// API Keys management
|
||||
'api_keys.title': 'API Keys Management',
|
||||
@@ -400,8 +643,11 @@ const i18n = {
|
||||
'ai_providers.gemini_empty_desc': 'Click the button above to add the first key',
|
||||
'ai_providers.gemini_item_title': 'Gemini Key',
|
||||
'ai_providers.gemini_add_modal_title': 'Add Gemini API Key',
|
||||
'ai_providers.gemini_add_modal_key_label': 'API Key:',
|
||||
'ai_providers.gemini_add_modal_key_placeholder': 'Please enter Gemini API key',
|
||||
'ai_providers.gemini_add_modal_key_label': 'API Keys:',
|
||||
'ai_providers.gemini_add_modal_key_placeholder': 'Enter Gemini API key',
|
||||
'ai_providers.gemini_add_modal_key_hint': 'Add keys one by one and optionally specify a Base URL.',
|
||||
'ai_providers.gemini_keys_add_btn': 'Add Key',
|
||||
'ai_providers.gemini_base_url_placeholder': 'Optional Base URL, e.g. https://generativelanguage.googleapis.com',
|
||||
'ai_providers.gemini_edit_modal_title': 'Edit Gemini API Key',
|
||||
'ai_providers.gemini_edit_modal_key_label': 'API Key:',
|
||||
'ai_providers.gemini_delete_confirm': 'Are you sure you want to delete this Gemini key?',
|
||||
@@ -414,11 +660,14 @@ const i18n = {
|
||||
'ai_providers.codex_add_modal_title': 'Add Codex API Configuration',
|
||||
'ai_providers.codex_add_modal_key_label': 'API Key:',
|
||||
'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_label': 'Base URL (Required):',
|
||||
'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_url_label': 'Base URL (Required):',
|
||||
'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',
|
||||
@@ -431,10 +680,17 @@ 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.claude_models_label': 'Custom Models (Optional):',
|
||||
'ai_providers.claude_models_hint': 'Leave empty to allow all models, or add name[, alias] entries to limit/alias them.',
|
||||
'ai_providers.claude_models_add_btn': 'Add Model',
|
||||
'ai_providers.claude_models_count': 'Models Count',
|
||||
|
||||
'ai_providers.openai_title': 'OpenAI Compatible Providers',
|
||||
'ai_providers.openai_add_button': 'Add Provider',
|
||||
@@ -445,12 +701,22 @@ const i18n = {
|
||||
'ai_providers.openai_add_modal_name_placeholder': 'e.g.: openrouter',
|
||||
'ai_providers.openai_add_modal_url_label': 'Base URL:',
|
||||
'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_label': 'API Keys',
|
||||
'ai_providers.openai_edit_modal_keys_label': 'API Keys',
|
||||
'ai_providers.openai_keys_hint': 'Add each key separately with an optional proxy URL to keep things organized.',
|
||||
'ai_providers.openai_keys_add_btn': 'Add Key',
|
||||
'ai_providers.openai_key_placeholder': 'sk-... key',
|
||||
'ai_providers.openai_proxy_placeholder': 'Optional proxy URL (e.g. socks5://...)',
|
||||
'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_label': 'API Keys',
|
||||
'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',
|
||||
@@ -459,33 +725,205 @@ const i18n = {
|
||||
// Auth files management
|
||||
'auth_files.title': 'Auth Files Management',
|
||||
'auth_files.title_section': 'Auth Files',
|
||||
'auth_files.description': 'Here you can manage authentication configuration files for Qwen and Gemini. Upload JSON format authentication files to enable the corresponding AI services.',
|
||||
'auth_files.description': 'Manage all CLI Proxy JSON auth files here (e.g. Qwen, Gemini, Vertex). Uploading a credential immediately enables the corresponding AI integration.',
|
||||
'auth_files.upload_button': 'Upload File',
|
||||
'auth_files.delete_all_button': 'Delete All',
|
||||
'auth_files.empty_title': 'No Auth Files',
|
||||
'auth_files.empty_desc': 'Click the button above to upload the first file',
|
||||
'auth_files.search_empty_title': 'No matching files',
|
||||
'auth_files.search_empty_desc': 'Try changing the filters or clearing the search box.',
|
||||
'auth_files.file_size': 'Size',
|
||||
'auth_files.file_modified': 'Modified',
|
||||
'auth_files.download_button': 'Download',
|
||||
'auth_files.delete_button': 'Delete',
|
||||
'auth_files.delete_confirm': 'Are you sure you want to delete file',
|
||||
'auth_files.delete_all_confirm': 'Are you sure you want to delete all auth files? This operation cannot be undone!',
|
||||
'auth_files.delete_filtered_confirm': 'Are you sure you want to delete all {type} auth files? This operation cannot be undone!',
|
||||
'auth_files.upload_error_json': 'Only JSON files are allowed',
|
||||
'auth_files.upload_success': 'File uploaded successfully',
|
||||
'auth_files.download_success': 'File downloaded successfully',
|
||||
'auth_files.delete_success': 'File deleted successfully',
|
||||
'auth_files.delete_all_success': 'Successfully deleted',
|
||||
'auth_files.delete_filtered_success': 'Deleted {count} {type} auth files successfully',
|
||||
'auth_files.delete_filtered_partial': '{type} auth files deletion finished: {success} succeeded, {failed} failed',
|
||||
'auth_files.delete_filtered_none': 'No deletable auth files under the current filter ({type})',
|
||||
'auth_files.files_count': 'files',
|
||||
'auth_files.pagination_prev': 'Previous',
|
||||
'auth_files.pagination_next': 'Next',
|
||||
'auth_files.pagination_info': 'Page {current} / {total} · {count} files',
|
||||
'auth_files.search_label': 'Search configs',
|
||||
'auth_files.search_placeholder': 'Filter by name, type, or provider',
|
||||
'auth_files.page_size_label': 'Per page',
|
||||
'auth_files.page_size_unit': 'items',
|
||||
'auth_files.filter_all': 'All',
|
||||
'auth_files.filter_qwen': 'Qwen',
|
||||
'auth_files.filter_gemini': 'Gemini',
|
||||
'auth_files.filter_gemini-cli': 'GeminiCLI',
|
||||
'auth_files.filter_aistudio': 'AIStudio',
|
||||
'auth_files.filter_claude': 'Claude',
|
||||
'auth_files.filter_codex': 'Codex',
|
||||
'auth_files.filter_iflow': 'iFlow',
|
||||
'auth_files.filter_vertex': 'Vertex',
|
||||
'auth_files.filter_empty': 'Empty',
|
||||
'auth_files.filter_unknown': 'Other',
|
||||
'auth_files.type_qwen': 'Qwen',
|
||||
'auth_files.type_gemini': 'Gemini',
|
||||
'auth_files.type_gemini-cli': 'GeminiCLI',
|
||||
'auth_files.type_aistudio': 'AIStudio',
|
||||
'auth_files.type_claude': 'Claude',
|
||||
'auth_files.type_codex': 'Codex',
|
||||
'auth_files.type_iflow': 'iFlow',
|
||||
'auth_files.type_vertex': 'Vertex',
|
||||
'auth_files.type_empty': 'Empty',
|
||||
'auth_files.type_unknown': 'Other',
|
||||
'vertex_import.title': 'Vertex AI Credential Import',
|
||||
'vertex_import.description': 'Upload a Google service account JSON to store it as auth-dir/vertex-<project>.json using the same rules as the CLI vertex-import helper.',
|
||||
'vertex_import.location_label': 'Region (optional)',
|
||||
'vertex_import.location_placeholder': 'us-central1',
|
||||
'vertex_import.location_hint': 'Leave empty to use the default region us-central1.',
|
||||
'vertex_import.file_label': 'Service account key JSON',
|
||||
'vertex_import.file_hint': 'Only Google Cloud service account key JSON files are accepted.',
|
||||
'vertex_import.file_placeholder': 'No file selected',
|
||||
'vertex_import.choose_file': 'Choose File',
|
||||
'vertex_import.import_button': 'Import Vertex Credential',
|
||||
'vertex_import.file_required': 'Select a .json credential file first',
|
||||
'vertex_import.success': 'Vertex credential imported successfully',
|
||||
'vertex_import.result_title': 'Credential saved',
|
||||
'vertex_import.result_project': 'Project ID',
|
||||
'vertex_import.result_email': 'Service account',
|
||||
'vertex_import.result_location': 'Region',
|
||||
'vertex_import.result_file': 'Persisted file',
|
||||
|
||||
// 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_saved': 'Gemini Web Token saved successfully',
|
||||
// Codex OAuth
|
||||
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||
'auth_login.codex_oauth_button': 'Start Codex Login',
|
||||
'auth_login.codex_oauth_hint': 'Login to Codex service through OAuth flow, automatically obtain and save authentication files.',
|
||||
'auth_login.codex_oauth_url_label': 'Authorization URL:',
|
||||
'auth_login.codex_open_link': 'Open Link',
|
||||
'auth_login.codex_copy_link': 'Copy Link',
|
||||
'auth_login.codex_oauth_status_waiting': 'Waiting for authentication...',
|
||||
'auth_login.codex_oauth_status_success': 'Authentication successful!',
|
||||
'auth_login.codex_oauth_status_error': 'Authentication failed:',
|
||||
'auth_login.codex_oauth_start_error': 'Failed to start Codex OAuth:',
|
||||
'auth_login.codex_oauth_polling_error': 'Failed to check authentication status:',
|
||||
|
||||
// Anthropic OAuth
|
||||
'auth_login.anthropic_oauth_title': 'Anthropic OAuth',
|
||||
'auth_login.anthropic_oauth_button': 'Start Anthropic Login',
|
||||
'auth_login.anthropic_oauth_hint': 'Login to Anthropic (Claude) service through OAuth flow, automatically obtain and save authentication files.',
|
||||
'auth_login.anthropic_oauth_url_label': 'Authorization URL:',
|
||||
'auth_login.anthropic_open_link': 'Open Link',
|
||||
'auth_login.anthropic_copy_link': 'Copy Link',
|
||||
'auth_login.anthropic_oauth_status_waiting': 'Waiting for authentication...',
|
||||
'auth_login.anthropic_oauth_status_success': 'Authentication successful!',
|
||||
'auth_login.anthropic_oauth_status_error': 'Authentication failed:',
|
||||
'auth_login.anthropic_oauth_start_error': 'Failed to start Anthropic OAuth:',
|
||||
'auth_login.anthropic_oauth_polling_error': 'Failed to check authentication status:',
|
||||
|
||||
// Gemini CLI OAuth
|
||||
'auth_login.gemini_cli_oauth_title': 'Gemini CLI OAuth',
|
||||
'auth_login.gemini_cli_oauth_button': 'Start Gemini CLI Login',
|
||||
'auth_login.gemini_cli_oauth_hint': 'Login to Google Gemini CLI service through OAuth flow, automatically obtain and save authentication files.',
|
||||
'auth_login.gemini_cli_project_id_label': 'Google Cloud Project ID (Optional):',
|
||||
'auth_login.gemini_cli_project_id_placeholder': 'Enter Google Cloud Project ID (optional)',
|
||||
'auth_login.gemini_cli_project_id_hint': 'If a project ID is specified, authentication information for that project will be used.',
|
||||
'auth_login.gemini_cli_oauth_url_label': 'Authorization URL:',
|
||||
'auth_login.gemini_cli_open_link': 'Open Link',
|
||||
'auth_login.gemini_cli_copy_link': 'Copy Link',
|
||||
'auth_login.gemini_cli_oauth_status_waiting': 'Waiting for authentication...',
|
||||
'auth_login.gemini_cli_oauth_status_success': 'Authentication successful!',
|
||||
'auth_login.gemini_cli_oauth_status_error': 'Authentication failed:',
|
||||
'auth_login.gemini_cli_oauth_start_error': 'Failed to start Gemini CLI OAuth:',
|
||||
'auth_login.gemini_cli_oauth_polling_error': 'Failed to check authentication status:',
|
||||
|
||||
// Qwen OAuth
|
||||
'auth_login.qwen_oauth_title': 'Qwen OAuth',
|
||||
'auth_login.qwen_oauth_button': 'Start Qwen Login',
|
||||
'auth_login.qwen_oauth_hint': 'Login to Qwen service through device authorization flow, automatically obtain and save authentication files.',
|
||||
'auth_login.qwen_oauth_url_label': 'Authorization URL:',
|
||||
'auth_login.qwen_open_link': 'Open Link',
|
||||
'auth_login.qwen_copy_link': 'Copy Link',
|
||||
'auth_login.qwen_oauth_status_waiting': 'Waiting for authentication...',
|
||||
'auth_login.qwen_oauth_status_success': 'Authentication successful!',
|
||||
'auth_login.qwen_oauth_status_error': 'Authentication failed:',
|
||||
'auth_login.qwen_oauth_start_error': 'Failed to start Qwen OAuth:',
|
||||
'auth_login.qwen_oauth_polling_error': 'Failed to check authentication status:',
|
||||
'auth_login.missing_state': 'Unable to retrieve authentication state parameter',
|
||||
|
||||
// iFlow OAuth
|
||||
'auth_login.iflow_oauth_title': 'iFlow OAuth',
|
||||
'auth_login.iflow_oauth_button': 'Start iFlow Login',
|
||||
'auth_login.iflow_oauth_hint': 'Login to iFlow service through OAuth flow, automatically obtain and save authentication files.',
|
||||
'auth_login.iflow_oauth_url_label': 'Authorization URL:',
|
||||
'auth_login.iflow_open_link': 'Open Link',
|
||||
'auth_login.iflow_copy_link': 'Copy Link',
|
||||
'auth_login.iflow_oauth_status_waiting': 'Waiting for authentication...',
|
||||
'auth_login.iflow_oauth_status_success': 'Authentication successful!',
|
||||
'auth_login.iflow_oauth_status_error': 'Authentication failed:',
|
||||
'auth_login.iflow_oauth_start_error': 'Failed to start iFlow OAuth:',
|
||||
'auth_login.iflow_oauth_polling_error': 'Failed to check authentication status:',
|
||||
|
||||
// Usage Statistics
|
||||
'usage_stats.title': 'Usage Statistics',
|
||||
'usage_stats.total_requests': 'Total Requests',
|
||||
'usage_stats.success_requests': 'Success Requests',
|
||||
'usage_stats.failed_requests': 'Failed Requests',
|
||||
'usage_stats.total_tokens': 'Total Tokens',
|
||||
'usage_stats.requests_trend': 'Request Trends',
|
||||
'usage_stats.tokens_trend': 'Token Usage Trends',
|
||||
'usage_stats.api_details': 'API Details',
|
||||
'usage_stats.by_hour': 'By Hour',
|
||||
'usage_stats.by_day': 'By Day',
|
||||
'usage_stats.refresh': 'Refresh',
|
||||
'usage_stats.no_data': 'No Data Available',
|
||||
'usage_stats.loading_error': 'Loading Failed',
|
||||
'usage_stats.api_endpoint': 'API Endpoint',
|
||||
'usage_stats.requests_count': 'Request Count',
|
||||
'usage_stats.tokens_count': 'Token Count',
|
||||
'usage_stats.models': 'Model Statistics',
|
||||
'usage_stats.success_rate': 'Success Rate',
|
||||
'stats.success': 'Success',
|
||||
'stats.failure': 'Failure',
|
||||
|
||||
// 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.',
|
||||
'config_management.editor_placeholder': 'key: value',
|
||||
|
||||
// System info
|
||||
'system_info.title': 'System Information',
|
||||
@@ -505,22 +943,32 @@ 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.localhost_updated': 'Localhost access settings updated',
|
||||
'notification.usage_statistics_updated': 'Usage statistics settings updated',
|
||||
'notification.logging_to_file_updated': 'Logging settings updated',
|
||||
'notification.request_log_updated': 'Request logging setting updated',
|
||||
'notification.ws_auth_updated': 'WebSocket authentication setting 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',
|
||||
'notification.gemini_key_added': 'Gemini key added successfully',
|
||||
'notification.gemini_key_updated': 'Gemini key updated successfully',
|
||||
'notification.gemini_key_deleted': 'Gemini key deleted successfully',
|
||||
'notification.gemini_multi_input_required': 'Please enter at least one Gemini key',
|
||||
'notification.gemini_multi_failed': 'Gemini bulk add failed',
|
||||
'notification.gemini_multi_summary': 'Gemini bulk add finished: {success} added, {skipped} skipped, {failed} failed',
|
||||
'notification.codex_config_added': 'Codex configuration added successfully',
|
||||
'notification.codex_config_updated': 'Codex configuration updated successfully',
|
||||
'notification.codex_config_deleted': 'Codex configuration deleted successfully',
|
||||
'notification.codex_base_url_required': 'Please enter the Codex Base URL',
|
||||
'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',
|
||||
'notification.openai_model_name_required': 'Model name is required',
|
||||
'notification.data_refreshed': 'Data refreshed successfully',
|
||||
'notification.connection_required': 'Please establish connection first',
|
||||
'notification.refresh_failed': 'Refresh failed',
|
||||
@@ -537,6 +985,7 @@ const i18n = {
|
||||
'notification.gemini_api_key': 'Gemini API key',
|
||||
'notification.codex_api_key': 'Codex API key',
|
||||
'notification.claude_api_key': 'Claude API key',
|
||||
'notification.link_copied': 'Link copied to clipboard',
|
||||
|
||||
// Language switch
|
||||
'language.switch': 'Language',
|
||||
@@ -549,7 +998,15 @@ const i18n = {
|
||||
'theme.dark': 'Dark',
|
||||
'theme.switch_to_light': 'Switch to light mode',
|
||||
'theme.switch_to_dark': 'Switch to dark mode',
|
||||
'theme.auto': 'Follow system'
|
||||
'theme.auto': 'Follow system',
|
||||
|
||||
// Sidebar
|
||||
'sidebar.toggle_expand': 'Expand sidebar',
|
||||
'sidebar.toggle_collapse': 'Collapse sidebar',
|
||||
|
||||
// Footer
|
||||
'footer.version': 'Version',
|
||||
'footer.author': 'Author'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -596,6 +1053,30 @@ const i18n = {
|
||||
}
|
||||
});
|
||||
|
||||
// 更新所有包含 data-i18n-placeholder 的输入框占位符
|
||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-placeholder');
|
||||
element.placeholder = this.t(key);
|
||||
});
|
||||
|
||||
// 更新 data-i18n-title
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-title');
|
||||
element.title = this.t(key);
|
||||
});
|
||||
|
||||
// 更新 data-i18n-tooltip
|
||||
document.querySelectorAll('[data-i18n-tooltip]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-tooltip');
|
||||
element.setAttribute('data-tooltip', this.t(key));
|
||||
});
|
||||
|
||||
// 更新 data-i18n-text(常用于按钮或标签)
|
||||
document.querySelectorAll('[data-i18n-text]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-text');
|
||||
element.textContent = this.t(key);
|
||||
});
|
||||
|
||||
// 更新所有带有 data-i18n-html 属性的元素(支持HTML)
|
||||
document.querySelectorAll('[data-i18n-html]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-html');
|
||||
|
||||
826
index.html
826
index.html
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"start": "npx serve .",
|
||||
"dev": "npx serve . --port 3000",
|
||||
"build": "echo '无需构建,直接使用静态文件'",
|
||||
"build": "node build.js",
|
||||
"lint": "echo '使用浏览器开发者工具检查代码'"
|
||||
},
|
||||
"keywords": [
|
||||
@@ -26,6 +26,5 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "local"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
|
||||
3505
styles.css
3505
styles.css
File diff suppressed because it is too large
Load Diff
64
webpack.config.js
Normal file
64
webpack.config.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const HtmlInlineScriptPlugin = require('html-inline-script-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
main: './bundle-entry.js'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].bundle.js',
|
||||
clean: true
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|jpeg|gif|svg)$/i,
|
||||
type: 'asset/inline'
|
||||
},
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
sources: {
|
||||
list: [
|
||||
{
|
||||
tag: 'img',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: './index.build.html',
|
||||
filename: 'index.html',
|
||||
inject: 'body',
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true
|
||||
}
|
||||
}),
|
||||
new HtmlInlineScriptPlugin({
|
||||
htmlMatchPattern: [/index.html$/],
|
||||
scriptMatchPattern: [/.js$/]
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
minimize: true
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user