Files
claude-api-proxy/_bmad-output/implementation-artifacts/tech-spec-claude-api-proxy.md
D8D Developer fafef0e034 feat: 实现 Claude API 透传代理服务
实现完整的 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>
2026-03-20 06:47:46 +00:00

243 lines
8.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 'Claude API 透传代理服务'
slug: 'claude-api-proxy'
created: '2026-03-20T04:30:00.000Z'
status: 'completed'
stepsCompleted: [1, 2, 3, 4, 5, 6]
tech_stack: ['Node.js', 'Express', 'TypeScript', 'axios', 'Docker']
files_to_modify:
- 'package.json'
- 'tsconfig.json'
- 'src/index.ts'
- 'src/proxy.ts'
- 'src/health.ts'
- 'src/logger.ts'
- '.env.example'
- 'Dockerfile'
- '.dockerignore'
- '.gitea/workflows/release.yaml'
code_patterns:
- '流式响应: axios stream + pipe'
- '错误处理: axios error + 统一 JSON 格式'
- '日志: JSONL 格式存储到 /tmp/proxy.jsonl'
- 'Docker: 多阶段构建 + 生产依赖分离'
- 'CI/CD: Gitea Actions + 阿里云镜像仓库'
test_patterns: []
---
# 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.ts` Express 入口,监听端口 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-version` headers
- 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 timeoutAI 请求可能耗时很长
- **日志清理**存储在 /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: 低影响或过度优化