Skip to content

Commit 1ebe9cb

Browse files
ANIIAN91roothea7ennlich0821
authored
Docker服务下添加Web UI,增删改查端点等,反代后可跨域、跨物理机、服务器使用 (#64)
* Convert ccNexus to headless Docker service * fix:已知问题修复 (#63) * resolve merge conflict in main.go, keep remote version * 已知问题修复1215 --------- Co-authored-by: hea7en <hea7enn@qq.com> * feat: adapt to cc 2.0.69 * fix: add input_tokens in message_delta to support auto-compact * v4.4.0 * chore: add webui plugin under cmd * README_DOCKER.md * 修复:在 app/internal/proxy/proxy.go 新增 StartWithMux 并让 Start 复用它,解决 p.StartWithMux undefined 的编译错误(主程序需要在已有 mux 上挂载 WebUI 时使用) --------- Co-authored-by: root <root@ser960574800116.local> Co-authored-by: hea7en <46583161+hea7enn@users.noreply.github.com> Co-authored-by: hea7en <hea7enn@qq.com> Co-authored-by: Changhua <lichanghua0821@gmail.com>
1 parent 7401f82 commit 1ebe9cb

28 files changed

+3643
-23
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ COPY app/ ./
1818
# Ensure module graph is complete (generates go.sum)
1919
RUN go mod tidy
2020

21-
# Build the headless API server
21+
# Build the headless API server (webui is embedded via go:embed)
2222
RUN CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w" -o ccnexus-server ./cmd/server
2323

2424
# Runtime stage

README_DOCKER.md

Lines changed: 191 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,198 @@
1414
- [docker-compose.yml](docker-compose.yml) 仅映射 API 端口(示例 `3021:3000`),挂载数据卷 `/data`,健康检查指向 `/health`。
1515
- 默认环境:`CCNEXUS_DATA_DIR=/data`,`CCNEXUS_DB_PATH=/data/ccnexus.db`,`CCNEXUS_PORT=3000`。
1616

17-
4. 数据与迁移
18-
- 仍支持从旧的 JSON 配置迁移到 SQLite(路径位于数据目录)。
19-
- 将主机目录挂载到 `/data` 可持久化数据库与配置。
20-
21-
5. 使用快速指引
17+
4. 使用快速指引
2218
- 端口占用时可改成 `HOST_PORT:3000`(例如 `3021:3000`)。
2319
- 构建运行:`docker compose up -d --build`。
2420
- 启动后更新数据库中的 endpoint key/model 到真实值,或通过配置文件/环境变量完成覆盖。
2521

26-
此版本专注于 API 代理,无任何桌面/前端界面。
22+
此版本专注于 API 代理,并提供 Web 管理界面用于端点管理和监控。
23+
24+
## 文件结构
25+
26+
```
27+
ccNexus/
28+
├── app/
29+
│ ├── cmd/
30+
│ │ ├── server/
31+
│ │ │ ├── main.go # 主程序(不需要修改)
32+
│ │ │ └── webui_plugin.go # Web UI 插件接口
33+
│ │ └── webui/ # 🔌 Web UI 插件(整个文件夹)
34+
│ │ ├── webui.go
35+
│ │ ├── api/
36+
│ │ └── ui/
37+
```
38+
---
39+
40+
## Web 管理界面
41+
42+
ccNexus 现已内置 Web 管理界面,提供可视化的端点管理和监控功能。
43+
44+
### 访问方式
45+
46+
启动服务后,通过浏览器访问:
47+
48+
```
49+
http://localhost:3021/ui/
50+
```
51+
52+
> 注意:端口号根据您的 docker-compose.yml 配置而定(默认映射为 `3021:3000`
53+
54+
### 功能特性
55+
56+
- **仪表盘**:实时显示请求数、成功率、token 使用量等关键指标
57+
- **端点管理**:通过 Web 界面添加、编辑、删除、启用/禁用 API 端点
58+
- **统计数据**:查看每日、每周、每月的详细统计信息和趋势对比
59+
- **测试功能**:在线测试端点连通性,查看响应时间和返回内容
60+
- **实时监控**:通过 Server-Sent Events 实现数据自动刷新(每 5 秒)
61+
- **深色/浅色主题**:支持主题切换,设置自动保存
62+
63+
### REST API 端点
64+
65+
除了 Web 界面,还可以直接调用 REST API:
66+
67+
#### 端点管理
68+
- `GET /api/endpoints` - 列出所有端点
69+
- `POST /api/endpoints` - 创建新端点
70+
- `PUT /api/endpoints/:name` - 更新端点
71+
- `DELETE /api/endpoints/:name` - 删除端点
72+
- `PATCH /api/endpoints/:name/toggle` - 启用/禁用端点
73+
- `POST /api/endpoints/:name/test` - 测试端点连通性
74+
- `POST /api/endpoints/reorder` - 重新排序端点
75+
- `GET /api/endpoints/current` - 获取当前活动端点
76+
- `POST /api/endpoints/switch` - 切换到指定端点
77+
- `POST /api/endpoints/fetch-models` - 获取可用模型列表
78+
79+
#### 统计数据
80+
- `GET /api/stats/summary` - 总体统计
81+
- `GET /api/stats/daily` - 今日统计
82+
- `GET /api/stats/weekly` - 本周统计
83+
- `GET /api/stats/monthly` - 本月统计
84+
- `GET /api/stats/trends` - 趋势对比数据
85+
86+
#### 配置管理
87+
- `GET /api/config` - 获取配置
88+
- `PUT /api/config` - 更新配置
89+
- `GET /api/config/port` - 获取代理端口
90+
- `PUT /api/config/port` - 更新代理端口
91+
- `GET /api/config/log-level` - 获取日志级别
92+
- `PUT /api/config/log-level` - 设置日志级别
93+
94+
#### 实时更新
95+
- `GET /api/events` - Server-Sent Events 流(用于实时监控)
96+
97+
### 使用示例
98+
99+
#### 通过 Web 界面添加端点
100+
101+
1. 访问 `http://localhost:3021/ui/`
102+
2. 点击左侧导航栏的"Endpoints"(端点)
103+
3. 点击右上角"Add Endpoint"(添加端点)按钮
104+
4. 填写表单:
105+
- **Name**(名称):为端点起一个易识别的名称,如 "Claude Official"
106+
- **API URL**:API 服务地址,如 `https://api.anthropic.com`
107+
- **API Key**:您的 API 密钥,如 `sk-ant-...`
108+
- **Transformer**(转换器):选择 API 类型(claude/openai/gemini/deepseek)
109+
- **Model**(模型):指定模型名称(Claude 可留空,OpenAI 需填写如 `gpt-4`
110+
- **Remark**(备注):可选的说明信息
111+
- **Enabled**(启用):勾选以立即启用该端点
112+
5. 点击"Create"(创建)保存
113+
114+
#### 通过 API 添加端点
115+
116+
```bash
117+
curl -X POST http://localhost:3021/api/endpoints \
118+
-H "Content-Type: application/json" \
119+
-d '{
120+
"name": "Claude Official",
121+
"apiUrl": "https://api.anthropic.com",
122+
"apiKey": "sk-ant-your-key-here",
123+
"transformer": "claude",
124+
"model": "",
125+
"enabled": true,
126+
"remark": "官方 Claude API"
127+
}'
128+
```
129+
#### 查看统计数据
130+
131+
通过 Web 界面:
132+
1. 点击左侧导航栏的"Statistics"(统计)
133+
2. 选择时间范围:Daily(每日)/ Weekly(每周)/ Monthly(每月)
134+
3. 查看各端点的请求数、错误数、token 使用量等详细数据
135+
136+
137+
### 技术特点
138+
139+
- **零依赖前端**:使用原生 JavaScript,无需 npm、webpack 等构建工具
140+
- **嵌入式部署**:前端文件嵌入 Go 二进制,单一可执行文件即可运行
141+
- **实时更新**:通过 SSE 实现数据自动刷新,无需手动刷新页面
142+
- **响应式设计**:支持桌面、平板、手机等各种设备
143+
- **API 密钥保护**:在界面中自动掩码显示(仅显示最后 4 位)
144+
145+
### 安全建议
146+
147+
- **生产环境**:建议配置反向代理(如 Nginx)并启用 HTTPS
148+
- **访问控制**:可通过反向代理添加 HTTP Basic Auth 或其他认证机制
149+
- **CORS 配置**:当前 CORS 对所有来源开放,生产环境建议限制允许的域名
150+
- **防火墙**:确保仅允许可信 IP 访问管理端口
151+
152+
### 故障排除
153+
154+
#### UI 无法访问
155+
- 检查容器是否正常运行:`docker ps`
156+
- 查看容器日志:`docker compose logs ccnexus`
157+
- 确认端口映射正确:检查 docker-compose.yml 中的 ports 配置
158+
- 验证防火墙规则是否允许访问
159+
160+
#### API 返回错误
161+
- 查看详细日志:`docker compose logs -f ccnexus`
162+
- 检查数据库文件权限:确保 `/data` 目录可写
163+
- 验证端点配置:通过 Web 界面或 API 检查端点设置是否正确
164+
- **OpenAI 端点需填写 model**`transformer=openai` 时若 `model` 为空会导致启动反复报错。
165+
- 直接在宿主修复 DB(假设宿主挂载 `/data/ccnexus`,错误端点 id=5):
166+
- 备份:`cp /data/ccnexus.db /data/ccnexus.db.bak-$(date +%Y%m%d%H%M%S)`
167+
- 临时进入工具容器:`docker run --rm -it -v /data/ccnexus:/data alpine sh`
168+
- 安装 sqlite:`apk add --no-cache sqlite`
169+
- 查看端点:`sqlite3 /data/ccnexus.db "SELECT id,name,transformer,model FROM endpoints;"`
170+
- 方案A补模型:`sqlite3 /data/ccnexus.db "UPDATE endpoints SET model='gpt-4o' WHERE id=5;"`
171+
- 方案B删除端点:`sqlite3 /data/ccnexus.db "DELETE FROM endpoints WHERE id=5;"`
172+
- 退出容器 `exit` 后重启服务:`docker compose restart` 或 `docker restart <容器名>`
173+
174+
### 开发与定制
175+
176+
Web UI 使用原生技术栈,修改非常简单:
177+
178+
1. 编辑 `app/ui/` 目录下的文件(HTML/CSS/JS)
179+
2. 重新构建 Docker 镜像:`docker compose up -d --build`
180+
3. 刷新浏览器查看效果
181+
182+
无需安装 Node.js、npm 或任何前端构建工具!
183+
184+
---
185+
186+
## Web UI 插件模式(可插拔)
187+
188+
- **目录结构**:完整插件位于 `app/cmd/webui/`,入口适配在 `app/cmd/server/webui_plugin.go`
189+
- **直接启用(默认)**:保留目录后 `docker compose up -d --build` 即包含 Web UI。
190+
- **移除插件**:删除 `app/cmd/webui``app/cmd/server/webui_plugin.go`,重新构建后只保留代理功能。
191+
- **重新添加**:将备份的 `webui` 目录与 `webui_plugin.go` 复制回原位,再次构建即可。
192+
---
193+
194+
## Web UI 快速开始速览
195+
196+
- **访问入口**:生产 `http://localhost:3021/ui/`(或 `/admin` 重定向),测试 `http://localhost:3022/ui/`
197+
- **常用操作**
198+
- 添加端点:`/ui/#endpoints` → Add Endpoint → 填写名称/API URL/API Key/transformer/model。
199+
- 测试端点:在端点列表点 Test,或 `/ui/#testing` 选择端点后 Send Test Request。
200+
- 查看统计:`/ui/#stats` 选择 Daily/Weekly/Monthly 查看趋势。
201+
- 切换/启用/禁用:在端点列表使用 Switch 或开关;Delete 可移除端点。
202+
- **API 示例**
203+
- 列表端点:`curl http://localhost:3021/api/endpoints`
204+
- 添加端点:`curl -X POST http://localhost:3021/api/endpoints -H "Content-Type: application/json" -d '{"name":"OpenAI","apiUrl":"api.openai.com","apiKey":"sk-...","transformer":"openai","model":"gpt-4"}'`
205+
- 测试端点:`curl -X POST http://localhost:3021/api/endpoints/OpenAI/test`
206+
- **容器运维快捷命令**
207+
- 查看日志:`docker logs -f ccnexus`(测试实例:`ccnexus2`)。
208+
- 重启:`docker compose restart`(测试用 `-f docker-compose.test.yml`)。
209+
- 重建:`docker compose up -d --build`(测试用 `-f docker-compose.test.yml`)。
210+
- 进入容器:`docker exec -it ccnexus sh`(测试实例 `ccnexus2`)。
211+
---

app/cmd/server/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,20 @@ func main() {
6464
statsAdapter := storage.NewStatsStorageAdapter(sqliteStorage)
6565
p := proxy.New(cfg, statsAdapter, deviceID)
6666

67+
// Create HTTP mux
68+
mux := http.NewServeMux()
69+
70+
// Initialize and register Web UI (optional plugin)
71+
// If webui package is not available, this will be skipped at compile time
72+
if err := registerWebUI(mux, cfg, p, sqliteStorage); err != nil {
73+
logger.Warn("Web UI not available: %v", err)
74+
} else {
75+
logger.Info("Web UI available at /ui/")
76+
}
77+
6778
errCh := make(chan error, 1)
6879
go func() {
69-
errCh <- p.Start()
80+
errCh <- p.StartWithMux(mux)
7081
}()
7182

7283
logger.Info("ccNexus headless API listening on :%d (data dir: %s, db: %s)", cfg.GetPort(), dataDir, dbPath)

app/cmd/server/webui_plugin.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/lich0821/ccNexus/internal/config"
7+
"github.com/lich0821/ccNexus/internal/proxy"
8+
"github.com/lich0821/ccNexus/internal/storage"
9+
"github.com/lich0821/ccNexus/cmd/webui"
10+
)
11+
12+
// registerWebUI registers the Web UI routes
13+
func registerWebUI(mux *http.ServeMux, cfg *config.Config, p *proxy.Proxy, storage *storage.SQLiteStorage) error {
14+
ui := webui.New(cfg, p, storage)
15+
return ui.RegisterRoutes(mux)
16+
}

0 commit comments

Comments
 (0)