docs: merge dev tasks and doc requirements into unified agent brief
Single English document containing: - Full 5-phase task breakdown (60+ tasks) - Provider interface design and priority matrix - API endpoint specifications - Documentation requirements per task - Testing requirements with examples - Agent prompt instructions and commit rules - Build and verification checklist Signed-off-by: Boos4721 <boos4721@icloud.com>
This commit is contained in:
@@ -1,232 +0,0 @@
|
||||
# Go 后端重构 — 开发任务书
|
||||
|
||||
## 概述
|
||||
|
||||
将 BoosAPI 后端从 Node.js (Fastify/TypeScript) 重构为 Go。前端 (React SPA) 保持不变。
|
||||
|
||||
## 规模
|
||||
|
||||
- 后端源码文件:约 350+ .ts 文件(不含测试)
|
||||
- API 端点:约 80+
|
||||
- Provider 适配器:15+ 个平台
|
||||
- Transformer 转换器:4 个协议(OpenAI、Anthropic、Gemini、Canonical)
|
||||
|
||||
## 开发原则
|
||||
|
||||
1. **API 兼容** — 所有端点 path/request/response 一致,前端零改动
|
||||
2. **按协议推进** — 先 OpenAI(最常用)→ Anthropic → Gemini → Canonical
|
||||
3. **测试先行** — 每个 handler 有 httptest
|
||||
4. **增量替换** — Go binary 与 Node.js 可并存,逐步切流
|
||||
5. **DB schema 兼容** — 复用现有 SQLite/MySQL/Postgres 表结构
|
||||
|
||||
---
|
||||
|
||||
## Phase 1:脚手架 ✅ 已完成
|
||||
|
||||
| 任务 | 状态 | 文件 |
|
||||
|------|------|------|
|
||||
| go mod init | ✅ | `go.mod` |
|
||||
| 目录结构 | ✅ | `cmd/`, `internal/`, `pkg/` |
|
||||
| 配置加载 | ✅ | `internal/config/config.go` — 全部环境变量 |
|
||||
| 数据库连接 | ✅ | `internal/db/db.go` — SQLite/MySQL/Postgres |
|
||||
| 迁移框架 | ✅ | `internal/db/migrate/000001_initial.up.sql` |
|
||||
| Health Check | ✅ | `GET /healthz` — json + DB ping |
|
||||
| 中间件 | ✅ | Logger、Auth(CORS)、Panic Recovery |
|
||||
| chi 路由分组 | ✅ | `/admin`, `/v1`, `/api` |
|
||||
| 优雅关闭 | ✅ | SIGINT/SIGTERM → graceful shutdown |
|
||||
| Dockerfile | ✅ | 多阶段构建 ~20MB |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2:Provider 核心代理(当前阶段)
|
||||
|
||||
### P2-A:HTTP 工具层
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 1 | SSE writer — `text/event-stream` 格式写入 | `pkg/httputil/sse.go` | — |
|
||||
| 2 | HTTP 流式转发 — 将上游 stream 中继到客户端 | `pkg/httputil/stream.go` | 1 |
|
||||
| 3 | HTTP 反向代理工具 — header 传递、URL 重写 | `pkg/httputil/proxy.go` | — |
|
||||
| 4 | 共享 API 类型定义 | `pkg/types/api.go` | — |
|
||||
|
||||
### P2-B:Proxy Core 骨架
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 5 | Provider 接口定义:`Provider` interface | `internal/proxycore/provider.go` | 4 |
|
||||
| 6 | 失败重试/降级策略 | `internal/proxycore/failover.go` | 5 |
|
||||
| 7 | 通道选择与负载均衡 | `internal/proxycore/channel.go` | 5 |
|
||||
| 8 | 核心编排器 `Orchestrator` | `internal/proxycore/orchestrator.go` | 5-7 |
|
||||
| 9 | 请求构建:header 注入、URL 构建 | `internal/proxycore/request.go` | — |
|
||||
|
||||
### P2-C:OpenAI Provider
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 10 | Chat Completion 非流式 | `internal/provider/openai/chat.go` | 5, 9 |
|
||||
| 11 | Chat Completion 流式 (SSE) | `internal/provider/openai/stream.go` | 1, 2, 10 |
|
||||
| 12 | Models 列表 | `internal/provider/openai/models.go` | 5 |
|
||||
| 13 | Images 生成/编辑 | `internal/provider/openai/images.go` | 5 |
|
||||
| 14 | Embeddings | `internal/provider/openai/embeddings.go` | 5 |
|
||||
| 15 | Audio (TTS/STT) | `internal/provider/openai/audio.go` | 5 |
|
||||
| 16 | Files (上传/列表/内容) | `internal/provider/openai/files.go` | 5 |
|
||||
| 17 | Responses API | `internal/provider/openai/responses.go` | 5, 9 |
|
||||
|
||||
### P2-D:Anthropic Provider
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 18 | Messages 非流式 | `internal/provider/anthropic/messages.go` | 5, 9 |
|
||||
| 19 | Messages 流式 | `internal/provider/anthropic/stream.go` | 1, 2, 18 |
|
||||
| 20 | 请求转换(OpenAI → Anthropic 格式) | `internal/transformer/anthropic/inbound.go` | — |
|
||||
| 21 | 响应转换(Anthropic → OpenAI 格式) | `internal/transformer/anthropic/outbound.go` | — |
|
||||
|
||||
### P2-E:Gemini Provider
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 22 | Generate Content 非流式 | `internal/provider/gemini/content.go` | 5, 9 |
|
||||
| 23 | Generate Content 流式 | `internal/provider/gemini/stream.go` | 1, 2, 22 |
|
||||
| 24 | 请求转换(OpenAI → Gemini) | `internal/transformer/gemini/inbound.go` | — |
|
||||
| 25 | 响应转换(Gemini → OpenAI) | `internal/transformer/gemini/outbound.go` | — |
|
||||
|
||||
### P2-F:Canonical 协议(用于多 provider 统一处理)
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 26 | Canonical Request 类型定义 | `internal/transformer/canonical/request.go` | — |
|
||||
| 27 | Canonical Response 类型定义 | `internal/transformer/canonical/response.go` | — |
|
||||
| 28 | Canonical → Provider 转换 | `internal/transformer/canonical/conversion.go` | 26-27 |
|
||||
| 29 | Provider → Canonical 转换 | `internal/transformer/canonical/normalization.go` | 26-27 |
|
||||
|
||||
### P2-G:API 路由注册
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 30 | `/v1/chat/completions` | `internal/server/handler/chat.go` | 10, 11, 18, 19, 22, 23 |
|
||||
| 31 | `/v1/models` | `internal/server/handler/models.go` | 12 |
|
||||
| 32 | `/v1/images/*` | `internal/server/handler/images.go` | 13 |
|
||||
| 33 | `/v1/embeddings` | `internal/server/handler/embeddings.go` | 14 |
|
||||
| 34 | `/v1/audio/*` | `internal/server/handler/audio.go` | 15 |
|
||||
| 35 | `/v1/files/*` | `internal/server/handler/files.go` | 16 |
|
||||
| 36 | `/v1/responses` | `internal/server/handler/responses.go` | 17 |
|
||||
| 37 | 路由统一注册 | `internal/server/router.go` | 30-36 |
|
||||
|
||||
---
|
||||
|
||||
## Phase 3:管理 API
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 38 | 管理员认证 (JWT + Bearer) | `internal/auth/jwt.go` | — |
|
||||
| 39 | 用户 CRUD | `internal/server/handler/admin/users.go` | 38 |
|
||||
| 40 | API Key 管理 | `internal/server/handler/admin/apikeys.go` | 38 |
|
||||
| 41 | 站点管理 (上游 Provider 配置) | `internal/server/handler/admin/sites.go` | 38 |
|
||||
| 42 | 账单/用量统计 | `internal/server/handler/admin/usage.go` | 38 |
|
||||
| 43 | Token 路由配置 | `internal/server/handler/admin/routing.go` | 38 |
|
||||
| 44 | 组织/团队管理 | `internal/server/handler/admin/orgs.go` | 38 |
|
||||
| 45 | 设置页面 (数据库迁移、备份等) | `internal/server/handler/admin/settings.go` | 38 |
|
||||
| 46 | 更新中心 (K3s/Helm) | `internal/server/handler/admin/update.go` | 38 |
|
||||
|
||||
---
|
||||
|
||||
## Phase 4:增值功能
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 47 | ComfyUI 反向代理 | `internal/server/handler/comfyui.go` | — |
|
||||
| 48 | Video Agent (脚本→视频 pipeline) | `internal/server/handler/video_agent.go` | — |
|
||||
| 49 | ComfyUI Agent SSE 对话 | `internal/server/handler/comfyui_agent.go` | 1 |
|
||||
| 50 | 文件存储 (proxyFileStore) | `internal/services/filestore.go` | — |
|
||||
| 51 | 搜索服务 | `internal/server/handler/search.go` | — |
|
||||
|
||||
---
|
||||
|
||||
## Phase 5:通知 + 运维
|
||||
|
||||
| # | 任务 | 文件 | 依赖 |
|
||||
|---|------|------|------|
|
||||
| 52 | 通知服务 (Telegram) | `internal/notify/telegram.go` | — |
|
||||
| 53 | 通知服务 (企业微信/飞书/通用 Webhook) | `internal/notify/webhook.go` | — |
|
||||
| 54 | 通知服务 (Bark/Server酱/SMTP) | `internal/notify/others.go` | — |
|
||||
| 55 | 通知节流 (冷静期合并) | `internal/notify/throttle.go` | 52-54 |
|
||||
| 56 | 桌面版后端适配 | `internal/desktop/adapter.go` | — |
|
||||
| 57 | 桌面版 runtime 状态 API | `internal/server/handler/desktop.go` | 56 |
|
||||
| 58 | OAuth Provider (Claude/Codex/Gemini) | `internal/oauth/provider.go` | — |
|
||||
| 59 | OAuth 会话存储 | `internal/oauth/session.go` | 38 |
|
||||
| 60 | 后台定时任务 (签到、模型探测、日志清理) | `internal/cron/scheduler.go` | — |
|
||||
|
||||
---
|
||||
|
||||
## API 兼容性要求
|
||||
|
||||
所有 Go handler 必须保持与现有 Node.js 相同的:
|
||||
|
||||
### 请求
|
||||
- URL 路径
|
||||
- HTTP Method
|
||||
- Query 参数
|
||||
- Request Body JSON 结构
|
||||
- Header 名称
|
||||
|
||||
### 响应
|
||||
- HTTP Status Code
|
||||
- Response Body JSON 结构
|
||||
- SSE 事件格式 (`data: {...}\n\n`)
|
||||
- Error 格式 (`{"error": {...}}`)
|
||||
- 速率限制 Header (`X-RateLimit-*`)
|
||||
|
||||
### 验证方法
|
||||
```
|
||||
curl -s http://localhost:4000/v1/models \
|
||||
-H "Authorization: Bearer $PROXY_TOKEN" | jq .
|
||||
# 返回格式必须与 Node.js 版一致
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Provider 优先级矩阵
|
||||
|
||||
| Provider | 优先级 | 使用量 | 复杂度 | 阶段 |
|
||||
|----------|--------|--------|--------|------|
|
||||
| OpenAI | P0 | 🔴🔴🔴 | 中 | Phase 2 |
|
||||
| Anthropic | P0 | 🔴🔴🔴 | 高 (转换器) | Phase 2 |
|
||||
| Google Gemini | P1 | 🟡🟡 | 高 (转换器) | Phase 2 |
|
||||
| OneAPI/NewAPI | P1 | 🟡🟡 | 低 | Phase 3 |
|
||||
| Claude (自有平台) | P1 | 🟡🟡 | 中 | Phase 3 |
|
||||
| Codex | P2 | 🟢 | 中 | Phase 4 |
|
||||
| VoAPI/RIX/Super | P2 | 🟢 | 低 | Phase 4 |
|
||||
| Veloera/Sub2/DoneHub | P2 | 🟢 | 低 | Phase 4 |
|
||||
|
||||
---
|
||||
|
||||
## 测试策略
|
||||
|
||||
| 层级 | 工具 | 覆盖 |
|
||||
|------|------|------|
|
||||
| 单元测试 | `testing` | Provider 转换逻辑、工具函数 |
|
||||
| Handler 测试 | `httptest` | 每个 API 端点 |
|
||||
| 集成测试 | 自建 test suite | 完整请求→响应链路 |
|
||||
| Provider 测试 | mock HTTP server | 模拟上游返回,验证转发正确性 |
|
||||
| 兼容性测试 | 对照 Node.js 版输出 | 相同请求 → 相同响应 |
|
||||
|
||||
---
|
||||
|
||||
## 构建与发布
|
||||
|
||||
```bash
|
||||
# 开发构建
|
||||
CGO_ENABLED=0 go build -o boosapi ./cmd/boosapi/
|
||||
|
||||
# 生产构建 (带版本号)
|
||||
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(git describe --tags --always)" \
|
||||
-o boosapi ./cmd/boosapi/
|
||||
|
||||
# Docker 构建
|
||||
docker build -f docker/Dockerfile -t boosapi:latest .
|
||||
|
||||
# 运行
|
||||
./boosapi
|
||||
# 或
|
||||
docker run -p 4000:4000 boosapi:latest
|
||||
```
|
||||
@@ -1,219 +0,0 @@
|
||||
# Go 后端重构 — 文档需求
|
||||
|
||||
## 文档目标
|
||||
|
||||
1. **开发者上手** — 5 分钟内能本地跑起来改代码
|
||||
2. **架构可理解** — 新团队能看懂模块划分和请求链路
|
||||
3. **API 兼容参考** — 前端/客户端开发者知道怎么调用
|
||||
4. **运维可操作** — 部署、日志、监控、问题排查
|
||||
|
||||
---
|
||||
|
||||
## 一、开发者文档
|
||||
|
||||
### 1.1 README.md 更新
|
||||
|
||||
| 需求 | 内容 |
|
||||
|------|------|
|
||||
| 简介 | BoosAPI 是什么,Go 重构版 |
|
||||
| 快速开始 | `go build ./cmd/boosapi` → `./boosapi` |
|
||||
| 前置依赖 | Go 1.26+,SQLite 无需安装 |
|
||||
| 环境变量 | 简表(AUTH_TOKEN、PROXY_TOKEN、PORT、DB_TYPE、DB_URL) |
|
||||
| 示例命令 | 用 `curl` 验证 `/healthz` 和 `/v1/models` |
|
||||
|
||||
### 1.2 CONTRIBUTING.md
|
||||
|
||||
| 需求 | 内容 |
|
||||
|------|------|
|
||||
| 分支策略 | `refactor/go-backend` → `main` |
|
||||
| 编码规范 | Go 标准 `gofmt`、`go vet`、无 lint 警告 |
|
||||
| 提交规范 | Conventional Commits:`feat:` `fix:` `refactor:` `docs:` `test:` |
|
||||
| PR 流程 | 每个 Phase 先开 PR review |
|
||||
| 测试要求 | 新 handler 必须有 httptest;改逻辑不能降低覆盖率 |
|
||||
|
||||
### 1.3 GoDoc 注释
|
||||
|
||||
| 需求 | 内容 |
|
||||
|------|------|
|
||||
| 每个 export 函数 | 一行注释说明作用 |
|
||||
| 每个 export 类型 | 说明用途和字段含义 |
|
||||
| Package 注释 | `package db` — 简短说明包职责 |
|
||||
| Handler 注释 | 说明对应的 API 端点和行为 |
|
||||
|
||||
**示例:**
|
||||
```go
|
||||
// Package provider defines the Provider interface for upstream AI API backends.
|
||||
//
|
||||
// Each provider (OpenAI, Anthropic, Gemini) implements this interface to
|
||||
// support chat completions, streaming, and other capabilities through
|
||||
// a unified abstraction.
|
||||
package provider
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、架构文档
|
||||
|
||||
### 2.1 架构概览
|
||||
|
||||
**位置:** `docs/architecture.md`
|
||||
|
||||
| 需求 | 内容 |
|
||||
|------|------|
|
||||
| 整体架构图 | ASCII 或 mermaid 图:前端 → 路由 → 代理核心 → Provider |
|
||||
| 请求生命周期 | 从 HTTP 入站到上游响应回来的完整路径 |
|
||||
| 模块边界 | `proxycore`、`provider`、`transformer`、`handler` 的职责 |
|
||||
| 关键数据流 | SSE 流式请求/响应、WebSocket 传输路径 |
|
||||
|
||||
### 2.2 Provider 适配器设计
|
||||
|
||||
**位置:** `docs/provider-architecture.md`
|
||||
|
||||
| 需求 | 内容 |
|
||||
|------|------|
|
||||
| Provider 接口定义 | Go interface + 方法签名 |
|
||||
| Provider 注册机制 | 如何添加新的 Provider |
|
||||
| 转换器模式 | Inbound/Outbound converter 模式说明 |
|
||||
| 支持列表 | 当前已支持的 Provider 和对应基准 URL |
|
||||
|
||||
### 2.3 数据库 Schema
|
||||
|
||||
**位置:** `docs/database.md`
|
||||
|
||||
| 需求 | 内容 |
|
||||
|------|------|
|
||||
| ER 图 | mermaid `erDiagram` |
|
||||
| 表结构 | 每张表的字段、类型、索引、外键 |
|
||||
| 迁移流程 | 新增字段/表的操作步骤 |
|
||||
| 兼容性 | SQLite / MySQL / PostgreSQL 差异 |
|
||||
|
||||
---
|
||||
|
||||
## 三、API 文档
|
||||
|
||||
### 3.1 管理 API
|
||||
|
||||
**位置:** `docs/management-api.md`(已有,需更新)
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/admin/users` | GET/POST | 用户列表/创建 |
|
||||
| `/admin/users/:id` | GET/PUT/DELETE | 用户详情/更新/删除 |
|
||||
| `/admin/api-keys` | GET/POST | API Key 管理 |
|
||||
| `/admin/sites` | GET/POST | 上游站点管理 |
|
||||
| `/admin/usage` | GET | 用量统计 |
|
||||
| `/admin/settings` | GET/PUT | 系统设置 |
|
||||
| `/admin/update-center` | GET/POST | K3s 更新中心 |
|
||||
| `/admin/backup` | POST | 手动备份 |
|
||||
|
||||
### 3.2 代理 API(OpenAI 兼容)
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/v1/chat/completions` | POST | Chat 补全(流式+非流式) |
|
||||
| `/v1/models` | GET | 模型列表 |
|
||||
| `/v1/images/generations` | POST | 图片生成 |
|
||||
| `/v1/images/edits` | POST | 图片编辑 |
|
||||
| `/v1/embeddings` | POST | 向量嵌入 |
|
||||
| `/v1/audio/transcriptions` | POST | 语音转文字 |
|
||||
| `/v1/audio/translations` | POST | 语音翻译 |
|
||||
| `/v1/audio/speech` | POST | 文字转语音 |
|
||||
| `/v1/files` | GET/POST | 文件管理 |
|
||||
| `/v1/files/:id` | GET/DELETE | 文件详情 |
|
||||
| `/v1/files/:id/content` | GET | 文件内容 |
|
||||
| `/v1/responses` | POST | Responses API |
|
||||
|
||||
### 3.3 自定义 API
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/comfyui/prompt` | POST | ComfyUI 工作流执行 |
|
||||
| `/api/comfyui/history` | GET | ComfyUI 历史 |
|
||||
| `/api/comfyui/view` | GET | ComfyUI 图片查看 |
|
||||
| `/api/comfyui/proxy/ui*` | GET | ComfyUI UI 代理 |
|
||||
| `/api/video-agent/*` | POST | 视频生成 Agent |
|
||||
| `/api/comfyui-agent/chat` | POST | ComfyUI 对话助手 (SSE) |
|
||||
| `/api/search` | POST | 搜索 |
|
||||
| `/api/events` | GET | SSE 事件流 |
|
||||
| `/api/auth/*` | POST | 登录/认证 |
|
||||
|
||||
---
|
||||
|
||||
## 四、运维文档
|
||||
|
||||
### 4.1 部署文档(已有,需更新)
|
||||
|
||||
**位置:** `docs/deployment.md`
|
||||
|
||||
| 需求 | 说明 |
|
||||
|------|------|
|
||||
| Docker 镜像 | 从 Go 构建改为多阶段,体积变化 |
|
||||
| 环境变量 | 所有 BoosAPI 环境变量(更新 BOOSAPI_*) |
|
||||
| Docker Compose | 示例更新为 Go 版本镜像 |
|
||||
| 健康检查 | `/healthz` 端点使用说明 |
|
||||
| 资源要求 | Go binary 内存/CPU 需求 |
|
||||
|
||||
### 4.2 运维手册(已有,需更新)
|
||||
|
||||
**位置:** `docs/operations.md`
|
||||
|
||||
| 需求 | 说明 |
|
||||
|------|------|
|
||||
| 日志 | Go slog 输出格式与日志级别 |
|
||||
| 监控 | 健康检查、prometheus/metrics |
|
||||
| 故障排查 | 常见错误和排查步骤 |
|
||||
| 备份恢复 | SQLite/MySQL/Postgres 备份命令 |
|
||||
|
||||
### 4.3 CLI 参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `-h` / `--help` | 帮助 |
|
||||
| `-version` | 版本号 |
|
||||
| `-config` | 配置文件路径(可选) |
|
||||
|
||||
---
|
||||
|
||||
## 五、GoDoc 发布(可选)
|
||||
|
||||
| 需求 | 工具 |
|
||||
|------|------|
|
||||
| 在线 GoDoc | `pkg.go.dev/github.com/cita-777/boosapi` |
|
||||
| 本地生成 | `go doc ./...` |
|
||||
|
||||
---
|
||||
|
||||
## 文档模板
|
||||
|
||||
每个 Go package 的 README 模板:
|
||||
|
||||
`````markdown
|
||||
# package `<name>`
|
||||
|
||||
`package <name>` 提供了...
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import "github.com/cita-777/boosapi/internal/<path>"
|
||||
```
|
||||
|
||||
## 类型
|
||||
|
||||
### `<TypeName>`
|
||||
|
||||
```go
|
||||
type TypeName struct {
|
||||
Field1 Type `json:"field1"`
|
||||
Field2 Type `json:"field2"`
|
||||
}
|
||||
```
|
||||
|
||||
## 函数
|
||||
|
||||
### `<FuncName>`
|
||||
|
||||
```go
|
||||
func FuncName(param Type) (ResultType, error)
|
||||
```
|
||||
`````
|
||||
@@ -0,0 +1,746 @@
|
||||
# BoosAPI Go Backend Rewrite — Agent Development Brief
|
||||
|
||||
You are a senior Go developer tasked with rewriting the BoosAPI backend from Node.js/Fastify/TypeScript to Go. The frontend (React SPA) and database schema remain unchanged.
|
||||
|
||||
## Repository
|
||||
|
||||
- **Branch:** `refactor/go-backend`
|
||||
- **Remote:** `gitea` (`https://git.boos.lat/boos/BoosAPI.git`)
|
||||
- **Go version:** 1.26
|
||||
- **Module:** `github.com/cita-777/boosapi`
|
||||
|
||||
## Golden Rules
|
||||
|
||||
1. **API compatibility first.** Every endpoint path, HTTP method, request body, response JSON, SSE event format, and error shape must match the existing Node.js implementation exactly. Frontend must work with zero changes.
|
||||
2. **Test before claim.** Every handler must have an `httptest` test. Every provider must have a mock HTTP server test. Run `go test ./...` and `go vet ./...` before committing.
|
||||
3. **One phase at a time.** Complete all tasks in the current phase before moving to the next.
|
||||
4. **Phase 1 is done.** Do not touch Phase 1 files unless fixing a bug.
|
||||
5. **Commit convention:** `feat:` for new functionality, `fix:` for bugs, `refactor:` for restructuring, `docs:` for documentation, `test:` for tests. Always sign off: `Signed-off-by: Boos4721 <boos4721@icloud.com>`
|
||||
6. **Go standard:** `gofmt`, `go vet`, no lint warnings. Use stdlib where possible.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ React SPA │
|
||||
│ (unchanged) │
|
||||
└────────┬─────────┘
|
||||
│ HTTP / SSE / WebSocket
|
||||
┌────────▼─────────┐
|
||||
│ Go Backend │
|
||||
│ (net/http + chi) │
|
||||
│ │
|
||||
│ ┌───────────┐ │
|
||||
│ │ chi Router │ │
|
||||
│ └─────┬─────┘ │
|
||||
│ │ │
|
||||
│ ┌─────▼─────┐ │
|
||||
│ │ Proxy │ │
|
||||
│ │ Core │ │
|
||||
│ └─────┬─────┘ │
|
||||
│ │ │
|
||||
│ ┌─────▼─────┐ │
|
||||
│ │ Providers │ │
|
||||
│ │ (interfce)│ │
|
||||
│ └─────┬─────┘ │
|
||||
│ │ │
|
||||
│ ┌─────▼─────┐ │
|
||||
│ │ DB Layer │ │
|
||||
│ │ (sql.DB) │ │
|
||||
│ └───────────┘ │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
### Directory Layout
|
||||
|
||||
```
|
||||
cmd/boosapi/main.go # Entry point (Phase 1 done, do not touch)
|
||||
internal/
|
||||
config/config.go # Env var loader (Phase 1 done)
|
||||
db/db.go # DB connection + migration runner (Phase 1 done)
|
||||
db/migrate/ # SQL migration files (Phase 1 done)
|
||||
server/
|
||||
router.go # Route registration (extend as you add handlers)
|
||||
middleware/middleware.go # Auth, CORS, Logger, Recover (Phase 1 done)
|
||||
handler/ # HTTP handlers
|
||||
health.go # GET /healthz (Phase 1 done)
|
||||
chat.go # POST /v1/chat/completions
|
||||
models.go # GET /v1/models
|
||||
images.go # POST /v1/images/*
|
||||
embeddings.go # POST /v1/embeddings
|
||||
audio.go # POST /v1/audio/*
|
||||
files.go # GET/POST /v1/files/*
|
||||
responses.go # POST /v1/responses
|
||||
comfyui.go # ComfyUI reverse proxy
|
||||
video_agent.go # Video generation agent
|
||||
comfyui_agent.go # ComfyUI conversational agent (SSE)
|
||||
search.go # POST /api/search
|
||||
admin/ # Admin handlers
|
||||
users.go
|
||||
apikeys.go
|
||||
sites.go
|
||||
usage.go
|
||||
routing.go
|
||||
orgs.go
|
||||
settings.go
|
||||
update.go
|
||||
proxycore/
|
||||
provider.go # Provider interface
|
||||
orchestrator.go # Core orchestrator (route, failover, fallback)
|
||||
channel.go # Channel selection + load balancing
|
||||
request.go # Request building (headers, URL)
|
||||
failover.go # Retry + degradation strategy
|
||||
provider/
|
||||
openai/
|
||||
chat.go # Chat completion (non-streaming)
|
||||
stream.go # Chat completion (SSE streaming)
|
||||
models.go # Models list
|
||||
images.go # Image generation/edits
|
||||
embeddings.go # Embeddings
|
||||
audio.go # TTS/STT
|
||||
files.go # File upload/list/content
|
||||
responses.go # Responses API
|
||||
anthropic/
|
||||
messages.go # Messages (non-streaming)
|
||||
stream.go # Messages (SSE streaming)
|
||||
gemini/
|
||||
content.go # Generate content (non-streaming)
|
||||
stream.go # Generate content (SSE streaming)
|
||||
transformer/
|
||||
canonical/
|
||||
request.go # Canonical request type
|
||||
response.go # Canonical response type
|
||||
conversion.go # Canonical ↔ Provider conversion
|
||||
anthropic/
|
||||
inbound.go # OpenAI → Anthropic request conversion
|
||||
outbound.go # Anthropic → OpenAI response conversion
|
||||
gemini/
|
||||
inbound.go # OpenAI → Gemini request conversion
|
||||
outbound.go # Gemini → OpenAI response conversion
|
||||
auth/
|
||||
jwt.go # JWT generation + validation
|
||||
session.go # Session store
|
||||
notify/
|
||||
telegram.go # Telegram bot notification
|
||||
webhook.go # Webhook (WeChat/Feishu/generic)
|
||||
others.go # Bark, ServerChan, SMTP
|
||||
throttle.go # Cooldown merging
|
||||
oauth/
|
||||
provider.go # OAuth provider interface
|
||||
session.go # OAuth session storage
|
||||
codex.go # Codex OAuth
|
||||
claude.go # Claude OAuth
|
||||
gemini.go # Gemini OAuth
|
||||
cron/
|
||||
scheduler.go # Background job scheduler
|
||||
desktop/
|
||||
adapter.go # Electron desktop backend adapter
|
||||
runtime.go # Desktop runtime status
|
||||
services/
|
||||
filestore.go # Proxy file storage
|
||||
search.go # Search service
|
||||
pkg/
|
||||
httputil/
|
||||
sse.go # SSE writer utilities
|
||||
stream.go # HTTP streaming relay
|
||||
proxy.go # HTTP reverse proxy tools
|
||||
types/
|
||||
api.go # Shared API types
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Provider Core Proxy
|
||||
|
||||
### P2-A: HTTP Utility Layer
|
||||
|
||||
#### Task 1: SSE Writer (`pkg/httputil/sse.go`)
|
||||
Create a helper for Server-Sent Events over `http.ResponseWriter`.
|
||||
|
||||
```go
|
||||
package httputil
|
||||
|
||||
// SSEWriter wraps http.ResponseWriter for SSE streaming.
|
||||
type SSEWriter struct {
|
||||
w http.ResponseWriter
|
||||
flusher http.Flusher
|
||||
done <-chan struct{} // for client disconnect detection
|
||||
}
|
||||
|
||||
func NewSSEWriter(w http.ResponseWriter) (*SSEWriter, error)
|
||||
func (s *SSEWriter) WriteEvent(eventType string, data []byte) error
|
||||
func (s *SSEWriter) WriteJSON(eventType string, v interface{}) error
|
||||
func (s *SSEWriter) Done() <-chan struct{}
|
||||
```
|
||||
|
||||
- Set `Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`
|
||||
- Each event: `data: {json}\n\n`
|
||||
- Must detect client disconnection via `r.Context().Done()`
|
||||
- Must flush after each event
|
||||
|
||||
#### Task 2: HTTP Stream Relay (`pkg/httputil/stream.go`)
|
||||
Relay an upstream SSE/streaming response to a downstream `SSEWriter`.
|
||||
|
||||
```go
|
||||
// RelayStream copies an upstream streaming response to w. It handles
|
||||
// chunked transfer from an *http.Response body and forwards data.
|
||||
func RelayStream(ctx context.Context, dst *SSEWriter, src *http.Response) error
|
||||
```
|
||||
|
||||
- Copy bytes from `src.Body` to `dst`
|
||||
- Stop when context is cancelled (client disconnect)
|
||||
- Close `src.Body` when done
|
||||
|
||||
#### Task 3: HTTP Proxy Utilities (`pkg/httputil/proxy.go`)
|
||||
```go
|
||||
// CopyHeaders copies headers from src to dst.
|
||||
func CopyHeaders(dst, src http.Header)
|
||||
|
||||
// ForwardedHeaderSet builds the X-Forwarded-* headers.
|
||||
func ForwardedHeaderSet(r *http.Request, host string) http.Header
|
||||
```
|
||||
|
||||
#### Task 4: Shared Types (`pkg/types/api.go`)
|
||||
Define shared request/response types used across the application:
|
||||
- `ChatRequest`, `ChatMessage`, `ChatChoice`
|
||||
- `StreamChoice`, `Delta`
|
||||
- `Model`, `ModelList`
|
||||
- `ErrorResponse`, `Usage`
|
||||
- `ImageRequest`, `ImageResponse`
|
||||
- `EmbeddingRequest`, `EmbeddingResponse`
|
||||
- `AudioRequest`, `AudioResponse`
|
||||
- `FileObject`, `FileList`
|
||||
- `APIResponse`, `PaginatedResponse`
|
||||
- SSE event types: `TextEvent`, `ImageEvent`, `CharacterPrompts`, `SceneEvent`, `TTSEvent`, `WorkflowEvent`, `DoneEvent`
|
||||
|
||||
### P2-B: Proxy Core Skeleton
|
||||
|
||||
#### Task 5: Provider Interface (`internal/proxycore/provider.go`)
|
||||
Define the core `Provider` interface:
|
||||
|
||||
```go
|
||||
// Provider defines the interface for upstream AI API backends.
|
||||
type Provider interface {
|
||||
// Name returns the provider identifier (e.g., "openai", "anthropic").
|
||||
Name() string
|
||||
|
||||
// ChatCompletion sends a non-streaming chat completion request.
|
||||
ChatCompletion(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
|
||||
|
||||
// ChatCompletionStream sends a streaming chat completion request.
|
||||
// Returns a channel of StreamEvents.
|
||||
ChatCompletionStream(ctx context.Context, req *ChatRequest) (<-chan StreamEvent, error)
|
||||
|
||||
// Models returns the list of available models from this provider.
|
||||
Models(ctx context.Context) ([]Model, error)
|
||||
}
|
||||
```
|
||||
|
||||
Start minimal. Only add methods that are actually implemented. The interface will grow as providers are added.
|
||||
|
||||
#### Task 6: Failover Strategy (`internal/proxycore/failover.go`)
|
||||
```go
|
||||
// FailoverPolicy defines how to handle upstream failures.
|
||||
type FailoverPolicy struct {
|
||||
MaxRetries int
|
||||
RetryDelay time.Duration
|
||||
FallbackOrder []string // provider names in fallback order
|
||||
}
|
||||
|
||||
// ShouldRetry determines if a request should be retried based on the error.
|
||||
func ShouldRetry(err error, attempt int) bool
|
||||
```
|
||||
|
||||
#### Task 7: Channel Selection (`internal/proxycore/channel.go`)
|
||||
```go
|
||||
// ChannelSelector picks the best upstream channel for a given model request.
|
||||
type ChannelSelector struct {
|
||||
// ...
|
||||
}
|
||||
|
||||
func (s *ChannelSelector) Select(ctx context.Context, model string, preferProvider string) (*ProviderChannel, error)
|
||||
```
|
||||
|
||||
- Weighted random selection based on site priority and weight
|
||||
- Skip disabled/cooldown sites
|
||||
- Return error if no channel is available
|
||||
|
||||
#### Task 8: Core Orchestrator (`internal/proxycore/orchestrator.go`)
|
||||
```go
|
||||
// Orchestrator manages the full lifecycle of a proxy request.
|
||||
type Orchestrator struct {
|
||||
selector *ChannelSelector
|
||||
failover *FailoverPolicy
|
||||
registry *ProviderRegistry
|
||||
}
|
||||
|
||||
func NewOrchestrator(selector *ChannelSelector, failover *FailoverPolicy, registry *ProviderRegistry) *Orchestrator
|
||||
|
||||
// Execute handles a proxy request: select channel, call provider, handle failover.
|
||||
func (o *Orchestrator) Execute(ctx context.Context, req *ProxyRequest) (*ProxyResponse, error)
|
||||
|
||||
// ExecuteStream handles a streaming proxy request.
|
||||
func (o *Orchestrator) ExecuteStream(ctx context.Context, req *ProxyRequest) (<-chan StreamEvent, error)
|
||||
```
|
||||
|
||||
#### Task 9: Request Builder (`internal/proxycore/request.go`)
|
||||
```go
|
||||
// BuildUpstreamRequest constructs an HTTP request to the upstream provider.
|
||||
// Injects headers (x-boosapi-*, authorization, user-agent, content-type).
|
||||
// Handles URL construction, query parameters, body serialization.
|
||||
func BuildUpstreamRequest(ctx context.Context, channel *ProviderChannel, body []byte) (*http.Request, error)
|
||||
```
|
||||
|
||||
- Inject `Content-Type: application/json`
|
||||
- Set `Authorization: Bearer <channel API key>`
|
||||
- Pass through `x-boosapi-*` headers from downstream
|
||||
- Handle streaming vs non-streaming content type
|
||||
|
||||
### P2-C: OpenAI Provider
|
||||
|
||||
#### Task 10: Chat Completion Non-Streaming (`internal/provider/openai/chat.go`)
|
||||
```go
|
||||
type Provider struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func (p *Provider) ChatCompletion(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
|
||||
```
|
||||
|
||||
- POST `{baseURL}/v1/chat/completions`
|
||||
- Map `ChatRequest` → OpenAI request JSON
|
||||
- Parse OpenAI response JSON → `ChatResponse`
|
||||
- Handle errors (4xx, 5xx, rate limits)
|
||||
|
||||
#### Task 11: Chat Completion Streaming (`internal/provider/openai/stream.go`)
|
||||
```go
|
||||
func (p *Provider) ChatCompletionStream(ctx context.Context, req *ChatRequest) (<-chan StreamEvent, error)
|
||||
```
|
||||
|
||||
- POST `{baseURL}/v1/chat/completions` with `stream: true`
|
||||
- Parse SSE `data: {...}` lines
|
||||
- Each line → `StreamEvent`
|
||||
- On `[DONE]` → close channel
|
||||
- Use `httputil.SSEWriter` for downstream forwarding
|
||||
|
||||
#### Task 12: Models (`internal/provider/openai/models.go`)
|
||||
```go
|
||||
func (p *Provider) Models(ctx context.Context) ([]Model, error)
|
||||
```
|
||||
|
||||
- GET `{baseURL}/v1/models`
|
||||
- Parse response, return model list
|
||||
|
||||
#### Task 13: Images (`internal/provider/openai/images.go`)
|
||||
```go
|
||||
func (p *Provider) GenerateImage(ctx context.Context, req *ImageRequest) (*ImageResponse, error)
|
||||
func (p *Provider) EditImage(ctx context.Context, req *ImageEditRequest) (*ImageResponse, error)
|
||||
```
|
||||
|
||||
- POST `{baseURL}/v1/images/generations`
|
||||
- POST `{baseURL}/v1/images/edits` (multipart form)
|
||||
|
||||
#### Task 14: Embeddings (`internal/provider/openai/embeddings.go`)
|
||||
```go
|
||||
func (p *Provider) CreateEmbedding(ctx context.Context, req *EmbeddingRequest) (*EmbeddingResponse, error)
|
||||
```
|
||||
|
||||
#### Task 15: Audio (`internal/provider/openai/audio.go`)
|
||||
```go
|
||||
func (p *Provider) CreateTranscription(ctx context.Context, req *AudioRequest) (*AudioResponse, error)
|
||||
func (p *Provider) CreateTranslation(ctx context.Context, req *AudioRequest) (*AudioResponse, error)
|
||||
func (p *Provider) CreateSpeech(ctx context.Context, req *TTSRequest) (*http.Response, error)
|
||||
```
|
||||
|
||||
- Transcriptions/Translations: multipart form POST
|
||||
- Speech: JSON POST, return raw audio binary
|
||||
|
||||
#### Task 16: Files (`internal/provider/openai/files.go`)
|
||||
```go
|
||||
func (p *Provider) ListFiles(ctx context.Context) ([]FileObject, error)
|
||||
func (p *Provider) UploadFile(ctx context.Context, file *FileUpload) (*FileObject, error)
|
||||
func (p *Provider) GetFile(ctx context.Context, fileID string) (*FileObject, error)
|
||||
func (p *Provider) GetFileContent(ctx context.Context, fileID string) (io.ReadCloser, error)
|
||||
func (p *Provider) DeleteFile(ctx context.Context, fileID string) error
|
||||
```
|
||||
|
||||
#### Task 17: Responses API (`internal/provider/openai/responses.go`)
|
||||
```go
|
||||
type ResponsesProvider struct {
|
||||
// wraps Provider for POST /v1/responses
|
||||
}
|
||||
func (p *ResponsesProvider) CreateResponse(ctx context.Context, req *ResponsesRequest) (*ResponsesResponse, error)
|
||||
```
|
||||
|
||||
### P2-D: Anthropic Provider
|
||||
|
||||
#### Task 18: Messages Non-Streaming (`internal/provider/anthropic/messages.go`)
|
||||
Implement Anthropic Messages API format.
|
||||
|
||||
```go
|
||||
type Provider struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func (p *Provider) Messages(ctx context.Context, req *AnthropicMessagesRequest) (*AnthropicMessagesResponse, error)
|
||||
```
|
||||
|
||||
- POST `{baseURL}/v1/messages`
|
||||
- Anthropic-specific request/response types
|
||||
- Handle `x-api-key` header auth
|
||||
|
||||
#### Task 19: Messages Streaming (`internal/provider/anthropic/stream.go`)
|
||||
```go
|
||||
func (p *Provider) MessagesStream(ctx context.Context, req *AnthropicMessagesRequest) (<-chan AnthropicStreamEvent, error)
|
||||
```
|
||||
|
||||
- POST with `stream: true`
|
||||
- Parse Anthropic SSE format (`event: message_start`, `event: content_block_delta`, etc.)
|
||||
- Convert to unified StreamEvent
|
||||
|
||||
### P2-E: Gemini Provider
|
||||
|
||||
#### Task 22: Generate Content Non-Streaming (`internal/provider/gemini/content.go`)
|
||||
Implement Google Gemini GenerateContent API.
|
||||
|
||||
```go
|
||||
type Provider struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func (p *Provider) GenerateContent(ctx context.Context, req *GeminiRequest) (*GeminiResponse, error)
|
||||
```
|
||||
|
||||
- POST `{baseURL}/v1beta/models/{model}:generateContent`
|
||||
- Gemini-specific request/response types
|
||||
- API key as query param
|
||||
|
||||
#### Task 23: Generate Content Streaming (`internal/provider/gemini/stream.go`)
|
||||
```go
|
||||
func (p *Provider) GenerateContentStream(ctx context.Context, req *GeminiRequest) (<-chan GeminiStreamEvent, error)
|
||||
```
|
||||
|
||||
### P2-F: Canonical Protocol Transformers
|
||||
|
||||
#### Task 26: Canonical Types (`internal/transformer/canonical/request.go`, `response.go`)
|
||||
Define provider-agnostic request/response types that all providers convert to/from:
|
||||
|
||||
```go
|
||||
type CanonicalRequest struct {
|
||||
Model string
|
||||
Messages []CanonicalMessage
|
||||
Stream bool
|
||||
MaxTokens int
|
||||
Temperature float64
|
||||
// ... other common parameters
|
||||
}
|
||||
|
||||
type CanonicalResponse struct {
|
||||
ID string
|
||||
Model string
|
||||
Choices []CanonicalChoice
|
||||
Usage *Usage
|
||||
}
|
||||
```
|
||||
|
||||
#### Task 27: Conversions (`internal/transformer/canonical/conversion.go`)
|
||||
```go
|
||||
// ToOpenAI converts a canonical request to OpenAI format.
|
||||
func ToOpenAI(req *CanonicalRequest) (*OpenAIChatRequest, error)
|
||||
|
||||
// FromOpenAIResponse converts an OpenAI response to canonical format.
|
||||
func FromOpenAIResponse(resp *OpenAIChatResponse) (*CanonicalResponse, error)
|
||||
|
||||
// ToAnthropic, FromAnthropicResponse, ToGemini, FromGeminiResponse...
|
||||
```
|
||||
|
||||
### P2-G: API Route Registration
|
||||
|
||||
#### Task 30-37: Handlers + Router
|
||||
|
||||
Create handler files under `internal/server/handler/` and register routes in `internal/server/router.go`.
|
||||
|
||||
**Chat handler** (`handler/chat.go`):
|
||||
```go
|
||||
type ChatHandler struct {
|
||||
orchestrator *proxycore.Orchestrator
|
||||
}
|
||||
|
||||
func (h *ChatHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
```
|
||||
|
||||
- Parse `ChatRequest` from body
|
||||
- If `stream: true` → use SSE streaming path
|
||||
- If `stream: false` → use non-streaming path
|
||||
- Return OpenAI-compatible response format
|
||||
|
||||
**Router** (`server/router.go`):
|
||||
```go
|
||||
func RegisterRoutes(r chi.Router, deps *Dependencies) {
|
||||
// /v1 routes (proxy token protected)
|
||||
r.Route("/v1", func(r chi.Router) {
|
||||
r.Use(middleware.ProxyToken(deps.Config.ProxyToken))
|
||||
r.Post("/chat/completions", deps.ChatHandler.ServeHTTP)
|
||||
r.Get("/models", deps.ModelsHandler.ServeHTTP)
|
||||
r.Post("/images/generations", deps.ImagesHandler.Generate)
|
||||
// ...
|
||||
})
|
||||
|
||||
// /api routes
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
r.Post("/comfyui-agent/chat", deps.ComfyUIAgentHandler.ServeHTTP)
|
||||
// ...
|
||||
})
|
||||
|
||||
// /admin routes (auth token protected)
|
||||
r.Route("/admin", func(r chi.Router) {
|
||||
r.Use(middleware.AuthToken(deps.Config.AuthToken))
|
||||
// ...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Management API
|
||||
|
||||
#### Task 38: JWT Auth (`internal/auth/jwt.go`)
|
||||
Implement JWT token generation and validation for admin sessions.
|
||||
|
||||
```go
|
||||
func GenerateToken(secret string, userID string, role string) (string, error)
|
||||
func ValidateToken(secret string, tokenString string) (*Claims, error)
|
||||
```
|
||||
|
||||
#### Task 39-46: Admin Handlers
|
||||
|
||||
Each handler follows this pattern:
|
||||
```go
|
||||
type UsersHandler struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func (h *UsersHandler) List(w http.ResponseWriter, r *http.Request) // GET /admin/users
|
||||
func (h *UsersHandler) Get(w http.ResponseWriter, r *http.Request) // GET /admin/users/{id}
|
||||
func (h *UsersHandler) Create(w http.ResponseWriter, r *http.Request) // POST /admin/users
|
||||
func (h *UsersHandler) Update(w http.ResponseWriter, r *http.Request) // PUT /admin/users/{id}
|
||||
func (h *UsersHandler) Delete(w http.ResponseWriter, r *http.Request) // DELETE /admin/users/{id}
|
||||
```
|
||||
|
||||
Required admin endpoints:
|
||||
| Route | Handler | SQL Query |
|
||||
|-------|---------|-----------|
|
||||
| `GET /admin/users` | `UsersHandler.List` | `SELECT * FROM users ORDER BY created_at DESC` |
|
||||
| `POST /admin/users` | `UsersHandler.Create` | `INSERT INTO users ...` |
|
||||
| `GET /admin/users/{id}` | `UsersHandler.Get` | `SELECT * FROM users WHERE id = ?` |
|
||||
| `PUT /admin/users/{id}` | `UsersHandler.Update` | `UPDATE users SET ... WHERE id = ?` |
|
||||
| `DELETE /admin/users/{id}` | `UsersHandler.Delete` | `DELETE FROM users WHERE id = ?` |
|
||||
| `GET /admin/api-keys` | `APIKeysHandler.List` | `SELECT * FROM api_keys` |
|
||||
| `POST /admin/api-keys` | `APIKeysHandler.Create` | `INSERT INTO api_keys ...` |
|
||||
| `GET /admin/sites` | `SitesHandler.List` | `SELECT * FROM sites ORDER BY priority DESC` |
|
||||
| `POST /admin/sites` | `SitesHandler.Create` | `INSERT INTO sites ...` |
|
||||
| `PUT /admin/sites/{id}` | `SitesHandler.Update` | `UPDATE sites SET ... WHERE id = ?` |
|
||||
| `DELETE /admin/sites/{id}` | `SitesHandler.Delete` | `DELETE FROM sites WHERE id = ?` |
|
||||
| `GET /admin/usage` | `UsageHandler.Summary` | `SELECT ... FROM usage_logs GROUP BY ...` |
|
||||
| `GET /admin/settings` | `SettingsHandler.Get` | Application settings |
|
||||
| `PUT /admin/settings` | `SettingsHandler.Update` | Application settings |
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Value-Added Features
|
||||
|
||||
#### Task 47: ComfyUI Proxy (`internal/server/handler/comfyui.go`)
|
||||
Reverse proxy for ComfyUI (port 8188). Key endpoints:
|
||||
- `POST /api/comfyui/prompt` → `POST /prompt`
|
||||
- `GET /api/comfyui/history` → `GET /history`
|
||||
- `GET /api/comfyui/view` → `GET /view`
|
||||
- `GET /api/comfyui/proxy/ui*` → `GET /*` (with HTML rewriting)
|
||||
|
||||
#### Task 48: Video Agent (`internal/server/handler/video_agent.go`)
|
||||
One-shot script → video pipeline. Uses the gateway's own `/v1/chat/completions` and `/v1/images/generations` and `/v1/audio/speech`.
|
||||
|
||||
Endpoints:
|
||||
- `POST /api/video-agent/analyze`
|
||||
- `POST /api/video-agent/character-prompts`
|
||||
- `POST /api/video-agent/generate-character`
|
||||
- `POST /api/video-agent/scene-prompt`
|
||||
- `POST /api/video-agent/generate-tts`
|
||||
- `POST /api/video-agent/full-workflow`
|
||||
|
||||
#### Task 49: ComfyUI Agent SSE Chat (`internal/server/handler/comfyui_agent.go`)
|
||||
Conversational agent using SSE streaming. The LLM generates structured SSE events directly.
|
||||
|
||||
- `POST /api/comfyui-agent/chat` — receives user message, streams agent response
|
||||
- System prompt instructs LLM to emit SSE-formatted output
|
||||
- Event types: `text`, `character_prompts`, `image`, `scene`, `tts`, `comfyui_workflow`, `done`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Notifications + Operations
|
||||
|
||||
#### Task 52-55: Notification Service (`internal/notify/`)
|
||||
Multi-channel notification dispatch with cooldown merging.
|
||||
|
||||
| Channel | Config key | Delivery |
|
||||
|---------|-----------|----------|
|
||||
| Telegram | `telegramEnabled`, `telegramBotToken`, `telegramChatId` | Bot API |
|
||||
| WeChat Work | `wecom*` | Webhook |
|
||||
| Feishu | `feishu*` | Webhook |
|
||||
| Generic Webhook | `webhookUrl` | JSON POST |
|
||||
| Bark | `bark*` | HTTP (iOS) |
|
||||
| ServerChan | `serverchan*` | HTTP |
|
||||
| SMTP | `smtp*` | Email |
|
||||
|
||||
#### Task 58-59: OAuth (`internal/oauth/`)
|
||||
Provider OAuth flows for Claude, Codex, Gemini:
|
||||
|
||||
```go
|
||||
// OAuthProvider handles the OAuth flow for an AI platform.
|
||||
type OAuthProvider interface {
|
||||
AuthURL(state string) string
|
||||
ExchangeCode(ctx context.Context, code string) (*OAuthToken, error)
|
||||
RefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation Requirements
|
||||
|
||||
Each task must include appropriate documentation:
|
||||
|
||||
### Code-Level Documentation
|
||||
- Every exported Go function must have a doc comment
|
||||
- Every exported type must have a doc comment
|
||||
- Every package must have a `package` doc comment
|
||||
|
||||
Example:
|
||||
```go
|
||||
// Package provider defines the Provider interface for upstream AI API backends.
|
||||
//
|
||||
// Each provider (OpenAI, Anthropic, Gemini) implements this interface to
|
||||
// support chat completions, streaming, and other capabilities through
|
||||
// a unified abstraction.
|
||||
package provider
|
||||
```
|
||||
|
||||
### API Documentation
|
||||
When adding a new handler, document the endpoint in the handler file:
|
||||
|
||||
```go
|
||||
// POST /v1/chat/completions
|
||||
//
|
||||
// Creates a model response for the given chat conversation.
|
||||
// Supports both streaming (SSE) and non-streaming responses.
|
||||
//
|
||||
// Request: ChatRequest
|
||||
// Response (non-streaming): ChatResponse
|
||||
// Response (streaming): SSE events of StreamChoice
|
||||
//
|
||||
// Errors:
|
||||
// 400 - Invalid request body
|
||||
// 401 - Missing or invalid API key
|
||||
// 429 - Rate limited
|
||||
// 500 - Upstream error
|
||||
```
|
||||
|
||||
### User-Facing Documentation
|
||||
Update the relevant doc files under `docs/` when adding significant functionality:
|
||||
- `docs/deployment.md` — Environment variables, Docker setup
|
||||
- `docs/operations.md` — Log format, health checks, troubleshooting
|
||||
- `docs/management-api.md` — Admin API reference
|
||||
- `docs/architecture.md` — Architecture overview (create if missing)
|
||||
|
||||
---
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
Every handler must have a test:
|
||||
|
||||
```go
|
||||
func TestChatHandler_NonStreaming(t *testing.T) {
|
||||
// Setup mock server for upstream
|
||||
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(openai.ChatResponse{...})
|
||||
}))
|
||||
defer upstream.Close()
|
||||
|
||||
// Setup handler with mock upstream URL
|
||||
handler := NewChatHandler(&Config{UpstreamBaseURL: upstream.URL})
|
||||
|
||||
// Create request
|
||||
body := `{"model":"gpt-4","messages":[{"role":"user","content":"hi"}]}`
|
||||
req := httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(body))
|
||||
req.Header.Set("Authorization", "Bearer test-token")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
// Serve
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
// Assert
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
var resp ChatResponse
|
||||
json.Unmarshal(rec.Body.Bytes(), &resp)
|
||||
assert.Equal(t, "gpt-4", resp.Model)
|
||||
}
|
||||
```
|
||||
|
||||
### Test Coverage Targets
|
||||
- Provider packages: 80%+ (mock HTTP server)
|
||||
- Handler packages: 80%+ (httptest)
|
||||
- Transformer packages: 90%+ (pure function tests)
|
||||
- Utility packages: 90%+
|
||||
- Overall project: 70%+
|
||||
|
||||
---
|
||||
|
||||
## Provider Implementation Priority
|
||||
|
||||
| Provider | Priority | Complexity | When |
|
||||
|----------|----------|-----------|------|
|
||||
| OpenAI (Chat, Models) | P0 | Medium | Phase 2-A |
|
||||
| OpenAI (Images, Embeddings, Audio, Files) | P0 | Low-Medium | Phase 2-A |
|
||||
| Anthropic Messages | P0 | High (different auth, streaming format) | Phase 2-B |
|
||||
| Google Gemini | P1 | High (different API structure) | Phase 2-C |
|
||||
| OneAPI / NewAPI | P1 | Low (OpenAI-compatible) | Phase 3 |
|
||||
| Claude Platform | P1 | Medium | Phase 3 |
|
||||
| Codex | P2 | Medium | Phase 4 |
|
||||
| VoAPI / RIX / Super | P2 | Low | Phase 4 |
|
||||
| Veloera / Sub2 / DoneHub | P2 | Low | Phase 4 |
|
||||
|
||||
---
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
# Build
|
||||
CGO_ENABLED=0 go build -o boosapi ./cmd/boosapi/
|
||||
|
||||
# Run
|
||||
AUTH_TOKEN=admin PROXY_TOKEN=proxy ./boosapi
|
||||
|
||||
# Test
|
||||
go test ./... -v -count=1
|
||||
|
||||
# Vet
|
||||
go vet ./...
|
||||
|
||||
# Docker
|
||||
docker build -f docker/Dockerfile -t boosapi:latest .
|
||||
docker run -p 4000:4000 -e AUTH_TOKEN=admin -e PROXY_TOKEN=proxy boosapi:latest
|
||||
```
|
||||
|
||||
## Before Committing
|
||||
|
||||
1. `go vet ./...` — zero warnings
|
||||
2. `go test ./...` — all tests pass
|
||||
3. `gofmt -s -w .` — code is formatted
|
||||
4. `git diff --cached` — review for secrets (no real IPs, tokens, passwords)
|
||||
5. Commit message follows convention: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
|
||||
6. Sign off: `Signed-off-by: Boos4721 <boos4721@icloud.com>`
|
||||
Reference in New Issue
Block a user