实现完整的 Claude API 代理服务,支持: - 透传 /v1/* 路径到上游 API - 流式 (SSE) 和非流式响应 - JSONL 格式请求日志 - /health 健康检查端点 - Docker 多阶段构建 - Gitea Actions CI/CD 工作流 安全加固: - 容器以非 root 用户运行 - 添加 Docker HEALTHCHECK - 实现优雅关闭处理 (SIGTERM/SIGINT) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
8.9 KiB
8.9 KiB
title, slug, created, status, stepsCompleted, tech_stack, files_to_modify, code_patterns, test_patterns
| title | slug | created | status | stepsCompleted | tech_stack | files_to_modify | code_patterns | test_patterns | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Claude API 透传代理服务 | claude-api-proxy | 2026-03-20T04:30:00.000Z | completed |
|
|
|
|
Tech-Spec: Claude API 透传代理服务
Created: 2026-03-20T04:30:00.000Z
Overview
Problem Statement
D8D 项目有一个 API Key 池,目前直接请求 Claude 官方 API。但官方 API 有 IP 并发限制,单个 IP 的并发数有限。为了绕过这个限制,需要启动多个代理容器实例,每个实例有不同的出口 IP,从而分散请求。
Solution
创建一个纯透传的 Claude API 代理服务:
- 使用 Express.js 构建轻量级代理服务
- 接收什么就转发什么,不做任何业务逻辑处理
- 支持流式(streaming)和非流式两种输出模式
- 构建为 Docker 镜像,部署到阿里云 ECI (t5-lc2m1.nano: 0.5 CPU, 1G 内存)
- 通过 K8S 管理 Pod 和 Ingress 配置
Scope
In Scope:
- Express.js 代理服务(透传 /v1/messages 端点)
- 支持流式和非流式响应
- Dockerfile 构建配置
- Gitea workflow 自动构建镜像
- K8S 部署配置参考
Out of Scope:
- 用户认证和授权
- 余额检查和计费
- API Key 池管理(由 D8D 主项目处理)
- 复杂的日志和监控
Context for Development
Codebase Patterns
参考项目:/mnt/code-fast/d8d-worktrees/merge-epics
代理转发参考:
src/server/api/ai/claude/v1/messages/post.ts- Claude API 调用实现- 使用 axios 进行 HTTP 请求
- 流式响应使用 TransformStream 处理
K8S 部署参考:
src/server/utils/k8s.ts- K8S 客户端封装- 使用阿里云 ECI (virtual-kubelet)
- 创建 Deployment、Service、Ingress
Docker 构建参考:
Dockerfile- 多阶段构建.gitea/workflows/release.yaml- Gitea Actions 构建流程
Files to Reference
| File | Purpose |
|---|---|
/mnt/code-fast/d8d-worktrees/merge-epics/src/server/api/ai/claude/v1/messages/post.ts |
Claude API 转发逻辑参考 |
/mnt/code-fast/d8d-worktrees/merge-epics/src/server/utils/k8s.ts |
K8S 客户端实现参考 |
/mnt/code-fast/d8d-worktrees/merge-epics/Dockerfile |
Docker 构建参考 |
/mnt/code-fast/d8d-worktrees/merge-epics/.gitea/workflows/release.yaml |
Gitea workflow 参考 |
Technical Decisions
- 框架选择: Express.js(用户指定,轻量简单)
- 语言: TypeScript(类型安全)
- HTTP 客户端: axios(支持流式响应)
- 容器规格: 阿里云 ECI t5-lc2m1.nano (0.5 CPU, 1G 内存)
- 镜像仓库: registry.cn-beijing.aliyuncs.com/d8dcloud/
- 端口: 3000
- API URL: 环境变量
ANTHROPIC_BASE_URL,本地读取.env,容器通过 K8S 注入 - 智谱端点:
https://open.bigmodel.cn/api/anthropic - 路径透传: 完全透传
/v1/*路径到上游 API - 健康检查: 添加
/health端点供 K8S 探针使用 - Headers 透传:
x-api-key、authorization、anthropic-version、content-type、accept - 超时设置: 不设置 axios timeout,让上游 API 自己处理
- 日志格式: JSONL 格式存储到
/tmp/proxy.jsonl,容器重启自动清理 - 日志内容: 只记录元信息(time, method, path, status, duration, stream, error),不记录请求/响应 body
- 请求体大小: Express body-parser 设置
limit: '50mb' - 包管理器: pnpm(与参考项目一致)
- 镜像名称:
registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:${VERSION} - HTTPS: 依赖 K8S Ingress 处理 TLS,代理本身不需要 HTTPS
- 流式错误处理: 流式传输中若上游报错,关闭客户端连接,记录错误日志
- 启动命令:
node dist/index.js
Implementation Plan
Tasks
Task 1: 项目初始化
- 创建
package.json配置 Express、axios、TypeScript 依赖 - 创建
tsconfig.json配置 TypeScript 编译选项 - 创建
.env.example环境变量示例
Task 2: 核心代理服务
- 创建
src/index.tsExpress 入口,监听端口 3000 - 创建
src/proxy.ts代理逻辑:- 捕获所有
/v1/*路由 - 透传 headers (x-api-key, authorization, anthropic-version, content-type, accept)
- 透传 body 到
ANTHROPIC_BASE_URL - 流式响应:设置 SSE headers,使用 axios stream + pipe
- 非流式响应:直接转发 JSON
- 不设置 axios timeout,让上游 API 自己处理
- 监听
req.on('close')处理客户端断开 - 流式错误处理:上游流式报错时关闭客户端连接,记录日志
- 捕获所有
- 设置 Express body-parser
limit: '50mb'
Task 3: 日志服务
- 创建
src/logger.ts日志工具 - JSONL 格式写入
/tmp/proxy.jsonl - 记录字段:time, method, path, status, duration, stream?, error?
- 不记录请求/响应 body(避免敏感数据泄露)
Task 4: 健康检查
- 创建
src/health.ts添加/health端点 - 返回
{ status: 'ok', timestamp: ISO时间 }
Task 5: Docker 构建配置
- 创建
Dockerfile多阶段构建 - 构建阶段:pnpm 安装依赖 + TypeScript 编译
- 运行阶段:只包含生产依赖 + 编译产物
- 暴露端口 3000
- 启动命令:
CMD ["node", "dist/index.js"] - 创建
.dockerignore排除 node_modules、.git、.env 等
Task 6: CI/CD 工作流
- 创建
.gitea/workflows/release.yaml - 触发条件:push 到
release/*分支 - 步骤:提取版本号 → 登录阿里云镜像仓库 → 构建推送镜像
- 镜像名称:
registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:${VERSION}
Acceptance Criteria
AC1: 非流式请求转发
- Given: 客户端发送 POST
/v1/messages请求 (stream: false) - When: 代理收到请求
- Then: 请求被转发到
ANTHROPIC_BASE_URL/v1/messages,响应完整返回
AC2: 流式请求转发
- Given: 客户端发送 POST
/v1/messages请求 (stream: true) - When: 代理收到请求
- Then: 设置 SSE headers,流式转发响应数据
- And: 若流式过程中上游报错,关闭客户端连接并记录日志
AC3: 健康检查
- Given: 客户端请求 GET
/health - When: 代理收到请求
- Then: 返回
{ status: 'ok', timestamp: '...' }状态码 200
AC4: Docker 镜像构建
- Given: 执行
docker build - When: 构建完成
- Then: 镜像大小 < 200MB,能正常启动并监听 3000 端口
AC5: Headers 透传
- Given: 请求包含
x-api-key、authorization、anthropic-versionheaders - When: 代理转发请求
- Then: 这些 headers 被完整传递到上游 API
AC6: 错误透传
- Given: 上游 API 返回错误响应(如 400/401/429/500)
- When: 代理收到错误响应
- Then: 透传错误状态码和响应 body 给客户端
Additional Context
Dependencies
生产依赖:
- express - Web 框架
- axios - HTTP 客户端(支持流式)
- dotenv - 环境变量加载
开发依赖:
- typescript - TypeScript 编译器
- @types/express - Express 类型定义
- @types/node - Node.js 类型定义
- tsx - TypeScript 执行器(支持热重载)
- tsc-alias - TypeScript 路径别名处理
Testing Strategy
- 手动测试流式和非流式响应
- 使用 curl 测试代理功能
Notes
- 代理服务需要完全透传,包括 headers 和 body
- 流式响应需要正确处理 SSE (Server-Sent Events) 格式
- API Key 通过请求头 x-api-key 或 Authorization 传递
- 超时策略:不设置 axios timeout,AI 请求可能耗时很长
- 日志清理:存储在 /tmp 目录,容器重启自动清空,不会累积
- 安全考虑:日志不记录请求/响应 body,避免敏感数据泄露
- HTTPS/TLS:代理本身不处理 HTTPS,完全依赖 K8S Ingress 处理 TLS
- 流式错误处理:流式传输中上游报错时,直接关闭客户端连接
- 镜像命名:
registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:${VERSION}
Review Notes
- 对抗性审查已完成 (2026-03-20)
- 发现总数: 12 个 (2 严重, 4 高, 4 中, 2 低)
- 修复数量: 3 个 (F1, F3, F4)
- 跳过数量: 9 个 (经专家讨论确认合理)
- 解决方案: 派对模式专家讨论 (Winston, Amelia, Barry, Quinn)
已修复:
- F1: Docker 添加 USER node 和 HEALTHCHECK
- F3: 添加 SIGTERM/SIGINT 优雅关闭处理
- F4: 流式错误处理添加 writableEnded 检查
已跳过 (合理原因):
- F2: 同步日志对轻量代理可接受
- F5: 透传代理不应验证请求体
- F6-F12: 低影响或过度优化