diff --git a/API.en.md b/API.en.md index bf848ff..1c16cb3 100644 --- a/API.en.md +++ b/API.en.md @@ -27,7 +27,7 @@ This document describes the actual behavior of the current Go codebase. | Base URL | `http://localhost:5001` or your deployment domain | | Default Content-Type | `application/json` | | Health probes | `GET /healthz`, `GET /readyz` | -| CORS | Enabled (`Access-Control-Allow-Origin: *`) | +| CORS | Enabled (`Access-Control-Allow-Origin: *`, allows `Content-Type`, `Authorization`) | --- diff --git a/API.md b/API.md index 3a18f02..d173031 100644 --- a/API.md +++ b/API.md @@ -27,7 +27,7 @@ | Base URL | `http://localhost:5001` 或你的部署域名 | | 默认 Content-Type | `application/json` | | 健康检查 | `GET /healthz`、`GET /readyz` | -| CORS | 已启用(`Access-Control-Allow-Origin: *`) | +| CORS | 已启用(`Access-Control-Allow-Origin: *`,允许 `Content-Type`, `Authorization`) | --- diff --git a/CONTRIBUTING.en.md b/CONTRIBUTING.en.md index 5848593..baf5eae 100644 --- a/CONTRIBUTING.en.md +++ b/CONTRIBUTING.en.md @@ -107,7 +107,7 @@ ds2api/ │ │ └── claude/ # Claude adapter │ ├── admin/ # Admin API handlers │ ├── auth/ # Auth and JWT -│ ├── config/ # Config loading +│ ├── config/ # Config loading and hot-reload │ ├── deepseek/ # DeepSeek client, PoW WASM │ ├── server/ # HTTP routing (chi router) │ ├── sse/ # SSE parsing utilities diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 345c256..c75d450 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,7 +107,7 @@ ds2api/ │ │ └── claude/ # Claude 兼容适配器 │ ├── admin/ # Admin API handlers │ ├── auth/ # 鉴权与 JWT -│ ├── config/ # 配置加载 +│ ├── config/ # 配置加载与热更新 │ ├── deepseek/ # DeepSeek 客户端、PoW WASM │ ├── server/ # HTTP 路由(chi router) │ ├── sse/ # SSE 解析工具 diff --git a/DEPLOY.en.md b/DEPLOY.en.md index 15125ec..e69754a 100644 --- a/DEPLOY.en.md +++ b/DEPLOY.en.md @@ -182,6 +182,7 @@ healthcheck: | `VERCEL_TOKEN` | Vercel sync token | — | | `VERCEL_PROJECT_ID` | Vercel project ID | — | | `VERCEL_TEAM_ID` | Vercel team ID | — | +| `DS2API_VERCEL_PROTECTION_BYPASS` | Deployment protection bypass for internal Node→Go calls | — | ### 3.3 Vercel Architecture diff --git a/DEPLOY.md b/DEPLOY.md index b3b53d1..cf008cd 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -182,6 +182,7 @@ healthcheck: | `VERCEL_TOKEN` | Vercel 同步 token | — | | `VERCEL_PROJECT_ID` | Vercel 项目 ID | — | | `VERCEL_TEAM_ID` | Vercel 团队 ID | — | +| `DS2API_VERCEL_PROTECTION_BYPASS` | 部署保护绕过密钥(内部 Node→Go 调用) | — | ### 3.3 Vercel 架构说明 diff --git a/OPTIMIZATION_REPORT.md b/OPTIMIZATION_REPORT.md deleted file mode 100644 index ede8440..0000000 --- a/OPTIMIZATION_REPORT.md +++ /dev/null @@ -1,119 +0,0 @@ -# 🔍 DS2API 深度优化分析报告 - -本报告详细列出了对 `ds2api` 项目进行代码审计后发现的潜在问题与优化建议。按照 **优先级**(高、中、低)进行分类。 - ---- - -## 🚨 高优先级:安全、并发与核心性能 (Critical) - -这些问题可能直接导致生产环境崩溃、安全漏洞或严重的性能瓶颈,建议**立即修复**。 - -### 1. `Save()` 方法并发写不安全 -- **位置**: `internal/config/config.go` (L312) -- **问题**: `Save()` 方法使用了 `s.mu.RLock()`(读锁)来保护文件写入操作。读锁允许多个 goroutine 同时进入,这意味着如果有并发的保存请求,会发生**写竞争**,导致 `config.json` 文件损坏或内容错乱。 -- **即使目前没有高并发写场景,这也是一个严重的并发 Bug。** -- **建议实现**: - ```go - func (s *Store) Save() error { - s.mu.Lock() // 必须使用写锁 - defer s.mu.Unlock() - // ... 写入逻辑 - } - ``` - -### 2. WASM Runtime 频繁重复实例化 -- **位置**: `internal/deepseek/pow.go` -> `Compute` 方法 -- **问题**: 每次调用 `Compute`(即每次聊天请求)都会执行 `p.runtime.InstantiateModule(...)`。WASM 模块实例化是一个昂贵的操作(分配内存、初始化 Table 等)。 -- **影响**: 在高并发下,这会消耗大量 CPU 并导致显著延迟,甚至 OOM。 -- **建议实现**: - - 引入 `Instance` 池化机制,或在 `Client` 生命周期内复用 Instance(需注意 WASM 内存状态重置)。 - - 使用 `wazero` 的复用特性。 - -### 3. API Key 鉴权为线性查找 (O(n)) -- **位置**: `internal/config/config.go` (L247 `HasAPIKey`) -- **问题**: 每次 API 请求都会遍历所有 Keys 切片。当 Key 数量较多时(例如几百个),性能会线性下降。 -- **建议实现**: - - 在 `Store` 结构体中维护一个 `keyMap map[string]struct{}` 索引。 - - 在加载配置时同步更新此索引。 - - 查找复杂度降为 **O(1)**。 - -### 4. Admin 默认弱口令风险 -- **位置**: `internal/auth/admin.go`, `internal/admin/handler_vercel.go` -- **问题**: 如果环境变量未设置,系统默认回退到 `"admin"` 作为管理密钥。用户可能在无意中将不安全的实例暴露到公网。 -- **建议实现**: - - 如果未设置 `DS2API_ADMIN_KEY`,在启动日志中打印醒目的 **WARNING**。 - -### 5. 缺乏优雅停机 (Graceful Shutdown) -- **位置**: `cmd/ds2api/main.go` -- **问题**: 程序收到中断信号时直接 `os.Exit(1)`。 -- **影响**: 正在进行的流式对话会被强行切断,造成用户体验中断,且可能导致 `config.json` 写入不完整。 -- **建议实现**: - - 使用 `http.Server{}` 替代 `http.ListenAndServe`。 - - 监听 `os.Interrupt` 信号,调用 `server.Shutdown(ctx)` 等待现有请求完成(例如设置 5-10秒超时)。 - ---- - -## 🟠 中优先级:架构设计与可维护性 (Refactor) - -这些问题影响代码的长期维护性,存在“散弹式修改” (Shotgun Surgery) 的风险。 - -### 6. SSR/Stream 解析逻辑严重重复 (DRY) -- **位置**: - - `openai/handler.go` (`handleNonStream`, `handleStream`) - - `claude/handler.go` (`collectDeepSeek`, `handleClaudeStreamRealtime`) - - `admin/handler_accounts.go` (`testAccount`) - - `sse/parser.go` -- **问题**: 解析 DeepSeek SSE 流(Thinking/Text 分流、ToolCall 探测)的逻辑被复制粘贴了 **6 次以上**。 -- **影响**: 如果 DeepSeek 的 API 格式微调(例如前段时间的 `thinking_content` 变更),你需要同时修改所有文件,极易遗漏引发 Bug。 -- **建议实现**: - - 抽象一个 `DeepSeekStreamConsumer` 结构体或通用函数,统一处理流式读取、Thinking 分离和 ToolCall 探测。 - -### 7. 账号测试接口为串行执行 -- **位置**: `internal/admin/handler_accounts.go` (L142 `testAllAccounts`) -- **问题**: 代码使用 `time.Sleep(time.Second)` 强行间隔并串行测试。如果用户有 50 个账号,测试一次需要 50 秒以上,前端会超时。 -- **建议实现**: - - 使用 `Goroutines` + `Semaphore` (或 `errgroup`) 控制并发度(例如并发 5-10 个)。 - - 移除硬编码的 sleep 或大幅减小。 - -### 8. `FindAccount` 性能低效 -- **位置**: `internal/config/config.go` (L270) -- **问题**: `Identifier()` 方法会对 Token-Only 账号做 SHA256 运算。`FindAccount` 每次遍历都重新计算一次 Hash。 -- **建议实现**: - - 类似 API Key,建立 Account ID 索引 `accMap map[string]*Account`。 - -### 9. 工具函数重复定义 -- **位置**: 多个包中存在 `writeJSON`, `toBool`, `intFrom`。 -- **建议实现**: 统一移动到 `internal/util` 包。 - ---- - -## 🟡 低优先级:代码质量与微观优化 (Cleanup) - -### 10. 仓库包含无用大文件 -- **问题**: `tokenizer.json` (7.8MB) 和 `tokenizer_config.json` 存在于根目录,但 Go 代码并未引用(`go.mod` 中无 huggingface tokenizers 库)。 -- **建议**: 删除这些残留文件,减小镜像体积。 - -### 11. `itoa` 实现极其低效 -- **位置**: `internal/deepseek/pow.go` -- **问题**: 使用 `json.Marshal(n)` 来将 int 转 string。 -- **建议**: 使用标准库 `strconv.FormatInt(n, 10)`,性能快 10 倍以上。 - -### 12. Token 估算过于粗略 -- **位置**: `internal/util/messages.go` -- **问题**: `len/4` 算法对中文完全不准(中文通常 1 char ≈ 1-2 tokens)。 -- **建议**: 简单优化:如果由 ASCII 组成则 /4,非 ASCII 则 *1.5 或其他经验值。 - -### 13. CORS 配置矛盾 -- **位置**: `internal/server/router.go` -- **问题**: 同时设置 `Access-Control-Allow-Origin: *` 和 `Access-Control-Allow-Credentials: true` 是无效的(浏览器安全规范)。 -- **建议**: 若采用宽松模式,保持 `Access-Control-Allow-Origin: *`,并移除 `Access-Control-Allow-Credentials`。 - ---- - -## ✅ 建议执行路线图 - -为了稳健地优化项目,建议按照以下顺序执行: - -1. **Phase 1 (Fix Critical) ✅ 已完成:** ~~修复 `Save()` 锁问题、WASM 重复创建、Admin 默认密码警告、Graceful Shutdown。删除无用大文件。~~ 同时修复了 `itoa` 低效实现。 -2. **Phase 2 (Refactor) ✅ 已完成:** ~~统一 API Key/Account 的索引机制,重构 SSE 解析逻辑 (DRY),优化 `testAllAccounts` 并发。~~ 同时完成了重复工具函数的统一清理(`writeJSON`/`toBool`/`intFrom` → `internal/util`)。 -3. **Phase 3 (Cleanup) ✅ 已完成:** ~~优化 CORS,改进 Token 估算等微小性能点。~~ CORS 采用宽松模式(`Access-Control-Allow-Origin: *`,不启用 Credentials);Token 估算区分 ASCII/非 ASCII 字符。 diff --git a/README.MD b/README.MD index dae84b1..f94ee54 100644 --- a/README.MD +++ b/README.MD @@ -12,21 +12,38 @@ ## 架构概览 -```text -┌──────────────┐ ┌──────────────────────────────────────┐ -│ 客户端 │ │ DS2API │ -│ (OpenAI / │───▶│ ┌────────┐ ┌──────────┐ ┌──────┐ │ -│ Claude │ │ │鉴权中间件│─▶│适配器层 │─▶│DeepSeek│ -│ 兼容) │ │ └────────┘ │OpenAI/ │ │Client │ │ -│ │◀───│ │Claude │ └──────┘ │ -│ │ │ ┌────────┐ └──────────┘ │ -│ │ │ │Admin API│ ┌──────────┐ │ -│ │ │ └────────┘ │账号池/队列│ │ -│ │ │ ┌────────┐ └──────────┘ │ -│ │ │ │WebUI │ ┌──────────┐ │ -│ │ │ │(/admin)│ │PoW WASM │ │ -│ │ │ └────────┘ └──────────┘ │ -└──────────────┘ └──────────────────────────────────────┘ +```mermaid +flowchart LR + Client["🖥️ 客户端\n(OpenAI / Claude 兼容)"] + + subgraph DS2API["DS2API 服务"] + direction TB + CORS["CORS 中间件"] + Auth["🔐 鉴权中间件"] + + subgraph Adapters["适配器层"] + OA["OpenAI 适配器\n/v1/*"] + CA["Claude 适配器\n/anthropic/*"] + end + + subgraph Support["支撑模块"] + Pool["📦 账号池 / 并发队列"] + PoW["⚙️ PoW WASM\n(wazero)"] + end + + Admin["🛠️ Admin API\n/admin/*"] + WebUI["🌐 WebUI\n(/admin)"] + end + + DS["☁️ DeepSeek API"] + + Client -- "请求" --> CORS --> Auth + Auth --> OA & CA + OA & CA -- "调用" --> DS + Auth --> Admin + OA & CA -. "轮询选账号" .-> Pool + OA & CA -. "计算 PoW" .-> PoW + DS -- "响应" --> Client ``` - **后端**:Go(`cmd/ds2api/`、`api/`、`internal/`),不依赖 Python 运行时 @@ -185,6 +202,7 @@ cp config.example.json config.json | `VERCEL_TOKEN` | Vercel 同步 token | — | | `VERCEL_PROJECT_ID` | Vercel 项目 ID | — | | `VERCEL_TEAM_ID` | Vercel 团队 ID | — | +| `DS2API_VERCEL_PROTECTION_BYPASS` | Vercel 部署保护绕过密钥(内部 Node→Go 调用) | — | ## 鉴权模式 diff --git a/README.en.md b/README.en.md index ebf9781..304b117 100644 --- a/README.en.md +++ b/README.en.md @@ -12,21 +12,38 @@ DS2API converts DeepSeek Web chat capability into OpenAI-compatible and Claude-c ## Architecture Overview -```text -┌──────────────┐ ┌──────────────────────────────────────┐ -│ Clients │ │ DS2API │ -│ (OpenAI / │───▶│ ┌────────┐ ┌──────────┐ ┌──────┐ │ -│ Claude │ │ │Auth MW │─▶│Adapter │─▶│DeepSeek│ -│ compat) │ │ └────────┘ │OpenAI/ │ │Client │ │ -│ │◀───│ │Claude │ └──────┘ │ -│ │ │ ┌────────┐ └──────────┘ │ -│ │ │ │Admin API│ ┌──────────┐ │ -│ │ │ └────────┘ │Account │ │ -│ │ │ ┌────────┐ │Pool/Queue│ │ -│ │ │ │WebUI │ └──────────┘ │ -│ │ │ │(/admin)│ ┌──────────┐ │ -│ │ │ └────────┘ │PoW WASM │ │ -└──────────────┘ └──────────────────────────────────────┘ +```mermaid +flowchart LR + Client["🖥️ Clients\n(OpenAI / Claude compat)"] + + subgraph DS2API["DS2API Service"] + direction TB + CORS["CORS Middleware"] + Auth["🔐 Auth Middleware"] + + subgraph Adapters["Adapter Layer"] + OA["OpenAI Adapter\n/v1/*"] + CA["Claude Adapter\n/anthropic/*"] + end + + subgraph Support["Support Modules"] + Pool["📦 Account Pool / Queue"] + PoW["⚙️ PoW WASM\n(wazero)"] + end + + Admin["🛠️ Admin API\n/admin/*"] + WebUI["🌐 WebUI\n(/admin)"] + end + + DS["☁️ DeepSeek API"] + + Client -- "Request" --> CORS --> Auth + Auth --> OA & CA + OA & CA -- "Call" --> DS + Auth --> Admin + OA & CA -. "Rotate accounts" .-> Pool + OA & CA -. "Compute PoW" .-> PoW + DS -- "Response" --> Client ``` - **Backend**: Go (`cmd/ds2api/`, `api/`, `internal/`), no Python runtime @@ -185,6 +202,7 @@ cp config.example.json config.json | `VERCEL_TOKEN` | Vercel sync token | — | | `VERCEL_PROJECT_ID` | Vercel project ID | — | | `VERCEL_TEAM_ID` | Vercel team ID | — | +| `DS2API_VERCEL_PROTECTION_BYPASS` | Vercel deployment protection bypass for internal Node→Go calls | — | ## Authentication Modes diff --git a/version.txt b/version.txt deleted file mode 100644 index 42a0d46..0000000 --- a/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.6.11 \ No newline at end of file