Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 161 additions & 11 deletions docs/guide/usage/cliproxy-egress-proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,169 @@ outline: deep

# CLIProxyAPI 出站代理配置

::: warning 撰写中
此文档目前为占位,正文尚未填充。完整撰写进度跟踪见 [issue #167](https://github.com/g1331/AutoRouter/issues/167)。
:::
在受限网络环境(GFW、企业出口白名单)里,CLIProxyAPI(下称 CPA)访问 Codex / Claude / Gemini 的登录端点和模型 API 往往必须经一层 HTTP/SOCKS 代理。AutoRouter 自身**不参与**这一层代理——出站代理由 CPA 容器自己消费,AutoRouter 只在「为某个账号设置覆盖」这一点上有管理 API 入口。本页讲清楚两种粒度的代理是怎么生效的,何时该用哪个,以及怎么验证。

## 计划覆盖的内容
## 责任划分

受限网络下 `CLIPROXY_PROXY_URL` 的使用、生效方式、验证流程。
```
┌───────────────────────────────────────────────────────────────┐
│ AutoRouter(Next.js / proxy-client.ts) │
│ 不读 HTTP_PROXY / HTTPS_PROXY / ALL_PROXY │
│ 不为 fetch 注入 dispatcher / agent │
│ AR → 上游(含 CPA 上游)的请求不走任何出站代理 │
└──────────────────────────┬────────────────────────────────────┘
│ 仅当上游是 CPA 池/单账号上游
┌───────────────────────────────────────────────────────────────┐
│ CLIProxyAPI 容器 │
│ 全局:CLIPROXY_PROXY_URL(env → config.yaml proxy-url) │
│ 账号:每个 auth-file 的 proxy_url 字段覆盖全局 │
│ CPA → Codex / Claude / Gemini 的请求走这两层代理 │
└───────────────────────────────────────────────────────────────┘
```

## 在正文就绪前的临时建议
`src/lib/services/proxy-client.ts:12-21` 的 `UpstreamForProxy` 接口字段里只有 `id` / `name` / `providerType` / `baseUrl` / `apiKey` / `timeout`,没有任何代理字段;同文件 `:139` 的 fetch 调用是 Node.js 原生 `fetch`,没有传 `dispatcher` 也没有读 `process.env.HTTP_PROXY`。这意味着即使在宿主机设了 `HTTPS_PROXY`,AutoRouter 的转发也不会走代理(Node `fetch` 默认行为)。

在该文档正文上线之前,可以参考以下材料获取等价信息
也就是说

- 项目仓库根目录的 [README.md](https://github.com/g1331/AutoRouter/blob/master/README.md)
- 现有长篇 [`docs/cliproxy-deployment.md`](/cliproxy-deployment)
- 现有长篇 [`docs/circuit-breaker.md`](/circuit-breaker)
- 项目 [Issue 列表](https://github.com/g1331/AutoRouter/issues) 与 [OpenSpec 提案](https://github.com/g1331/AutoRouter/tree/master/openspec)
- AutoRouter 自身要出网的场景(向上游发请求)**不能配代理**。如果 AR 部署在受限网络里需要直接访问 OpenAI/Anthropic,要么换部署位置,要么在容器外用 transparent proxy 解决。
- CPA 上游链路(AR → CPA → 模型 API)里,AR → CPA 这一段在同一网络内不需要代理;CPA → 模型 API 这一段由 CPA 自己消费代理配置。

## 全局代理 `CLIPROXY_PROXY_URL`

适用范围:CPA 容器内**所有** auth-file 默认使用的出站代理。

### 配置链路

只对 `managed`(sidecar)模式有效。配置链路:

1. `.env` 文件中的 `CLIPROXY_PROXY_URL`(`.env.example:131-133`)。
2. `docker-compose.cliproxy.yml` 把该值注入 CPA 容器 environment:
```yaml
environment:
- CLIPROXY_PROXY_URL=${CLIPROXY_PROXY_URL:-}
```
(`docker-compose.cliproxy.yml:25`)
3. CPA 容器 `docker-entrypoint.sh` 将 env 渲染进 `config.yaml`:
```bash
export CLIPROXY_PROXY_URL="${CLIPROXY_PROXY_URL:-}"
...
CONTENT=$(render_literal "$CONTENT" '${CLIPROXY_PROXY_URL}' "$CLIPROXY_PROXY_URL")
```
(`cliproxy/docker-entrypoint.sh:34, 67`)
4. `cliproxy/config.yaml.template:32-34` 的 `proxy-url: "${CLIPROXY_PROXY_URL}"` 被替换为真实值。
5. CPA 启动时读取 `proxy-url`,对所有 auth-file 默认应用。

### 支持的格式

| 协议 | 示例 |
| -------- | --------------------------------------------- |
| `http` | `http://proxy-host:8080` |
| `https` | `https://proxy-host:8443` |
| `socks5` | `socks5://proxy-host:1080` |
| 留空 | 不使用代理(`.env` 中 `CLIPROXY_PROXY_URL=`) |

### 生效时机

环境变量在 `docker-compose` 配置中是**容器启动期注入**——修改 `.env` 后必须**重建容器**,CPA 才会读到新值。注意:`docker compose restart cliproxyapi` **不行**,它只是重启已有容器,仍然使用容器创建时捕获的旧 env。正确做法是用叠加文件再次 `up -d`(compose 会检测到 env 变化并自动重建受影响的容器),或者显式 `--force-recreate`:

```
# 推荐:compose 自动判断需要重建哪些容器
docker compose -f docker-compose.yml -f docker-compose.cliproxy.yml up -d

# 或显式强制重建 CPA 容器
docker compose -f docker-compose.yml -f docker-compose.cliproxy.yml up -d --force-recreate cliproxyapi
```

运行中改 `.env` 不会自动生效。

### 外部模式(`external`)下

`external` 模式下 CPA 不由 AutoRouter 的 docker-compose 拉起,`CLIPROXY_PROXY_URL` 这条注入链路完全不存在。要给外部 CPA 配出站代理,必须在 CPA 自己的运行环境(systemd unit、docker-compose、Kubernetes manifest 等)里设置 `proxy-url`。AutoRouter `.env` 里的 `CLIPROXY_PROXY_URL` 在 `external` 模式下是无效字段。

## 账号粒度 `proxy_url`

适用范围:**单个 auth-file**,覆盖全局 `proxy-url`。

### 何时需要

- 不同 OAuth 账号属于不同地理区域(例如美区/欧区账号要走不同跳板);
- 部分账号要直连、其他账号走代理;
- 同一台 CPA 服务多个团队,每个团队的账号通过各自代理出网。

如果没有以上需求,**只设全局 `CLIPROXY_PROXY_URL` 即可**,账号粒度字段留空。

### 设置入口

管理后台账号列表的「编辑账号」对话框会写这个字段,对应 API:

```
PATCH /api/admin/cliproxy/instances/:id/auth-accounts/:accountName
Body: { "proxy_url": "socks5://team-a-proxy:1080", ... }
```

字段约束(`src/app/api/admin/cliproxy/instances/[id]/auth-accounts/[accountName]/route.ts:14-23`):`proxy_url` 是可选 string,trim 后最长 512 字符。

### 下发链路

1. PATCH 路由把 `proxy_url` 转给 `updateCliproxyAuthAccountFields`(`src/lib/services/cliproxy-auth-account-service.ts:238-244`)。
2. 服务层调用 `patchAuthFileFields`,向 CPA 管理 API 发:
```
PATCH <management_url>/v0/management/auth-files/fields
Body: { name, prefix, proxy_url, priority, note }
```
(`src/lib/services/cliproxy-management-client.ts:212-221`)
3. CPA 收到后更新内部 auth-file 配置,**立即生效**——不需要重启容器(与全局 `CLIPROXY_PROXY_URL` 的重启要求不同)。
4. AutoRouter **不缓存** `proxy_url`:本地 `cliproxy_auth_accounts` 表的字段列里没有 `proxy_url`(`src/lib/db/schema-pg.ts:744-770`),实际值始终以 CPA 侧为准。下次想看某个账号当前的 `proxy_url`,要从 CPA 的 `/v0/management/auth-files` 列表读。

### 与全局的优先级

CPA 侧的行为:账号粒度 `proxy_url` 非空时覆盖全局;为空(或字段不存在)时使用全局 `CLIPROXY_PROXY_URL`;都为空则不使用代理。

注意:把账号粒度 `proxy_url` 设回空串与「未设置」在 CPA 不同版本里可能行为略有差异。最稳妥的「回到全局」做法是通过 CPA 管理 API 把字段删掉(AutoRouter 当前 PATCH 接口的 `proxy_url` 是 optional,不传该字段表示不更新,传空串需要 CPA 侧明确支持「空串 = 清除」语义)。如果发现「设了空串后仍然走老代理」,可以把账号删掉重建,或者直接到 CPA 侧改配置。

## 何时用哪个

| 场景 | 全局 `CLIPROXY_PROXY_URL` | 账号 `proxy_url` |
| ---------------------------------- | ---------------------------- | ---------------- |
| 整台 CPA 都要走同一个代理 | ✓ | |
| 90% 账号走代理 A、几个特例走代理 B | ✓(代理 A) | ✓(代理 B) |
| 每个账号都不同代理 | | ✓ |
| 部分账号直连、其他账号走代理 | | ✓ |
| 全部账号直连 | | |
| 仅 `external` 模式 CPA | (AR 无法设置,到 CPA 自配) | ✓ |

## 校验方法

### 全局代理是否生效

1. 改完 `.env` 并 `docker compose -f docker-compose.yml -f docker-compose.cliproxy.yml up -d`(容器会被重建,参见上一节)后,检查容器 env:
```
docker compose exec cliproxyapi env | grep CLIPROXY_PROXY_URL
```
2. 检查渲染后的 config.yaml(默认路径 `/CLIProxyAPI/config.yaml`,由 `cliproxy/docker-entrypoint.sh:12` 的 `CLIPROXY_CONFIG_TARGET` 决定;若覆盖了该变量按实际值看):
```
docker compose exec cliproxyapi cat /CLIProxyAPI/config.yaml | grep proxy-url
```
3. 触发一次实际调用(管理后台列出账号 / 客户端发请求),CPA 日志中应能看到代理握手。

### 账号代理是否覆盖

通过管理后台编辑账号、保存 `proxy_url`,再调用 CPA `/v0/management/auth-files` 列表确认字段已更新。**不要**通过 AutoRouter 本地 `cliproxy_auth_accounts` 表来校验——该表无 `proxy_url` 字段,是缓存表,不反映 CPA 侧真实值。

### 常见症状速查

| 症状 | 排查方向 |
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
| 改了 `.env` 但 CPA 不走代理 | 没重启 CPA 容器;或当前是 `external` 模式(AR `.env` 不会注入到外部 CPA) |
| AR 本身访问上游被墙 | AR 不支持出站代理,不可配;考虑在 AR 容器外解决,或换部署位置 |
| 账号 `proxy_url` 设了但 CPA 还在用全局 | 检查 CPA 版本对「空串 vs 未设置」的处理;或直接到 CPA 侧 `/v0/management/auth-files` 看真实值 |
| OAuth 登录卡住 | 出站代理本身不通;先用宿主 `curl -x ... https://...` 自测代理可达 |
| 管理后台显示账号正常但调用失败 | 账号粒度 `proxy_url` 写错;从 CPA 侧(不是 AR 缓存)取真实值核对 |

## 不在本页范围内

- AR 容器在受限网络下访问数据库 / 监控 / DNS 等其它出站需求:与本页无关,按基础设施层方案处理。
- CPA `proxy-url` 的更高级配置(按目标域名分流、TLS 验证等):见 CPA 自身文档。
- CPA 实例的 `mode` 字段差异:见 [CLIProxyAPI 外部 vs sidecar 选择](./cliproxy-modes)。
- 全部 sidecar 部署变量:见 [环境变量参考](../deployment/env-reference) 与 [CI 部署后追加 CLIProxyAPI sidecar](../deployment/cliproxy-sidecar)。
20 changes: 10 additions & 10 deletions docs/guide/usage/cliproxy-first-time.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ outline: deep

实例字段对应数据库表 `cliproxy_instances`(`src/lib/db/schema-pg.ts:718`),表单字段如下:

| 字段 | 必填 | 含义 |
| ---------------- | ---------------------- | ----------------------------------------------------------------------------------------- |
| `name` | 是,唯一,最长 64 字符 | 实例名称,仅用于管理后台显示 |
| `mode` | 是,默认 `managed` | `managed`(sidecar,与 AutoRouter 同 Docker 网络)/ `external`(独立运行的远端 CPA 服务) |
| `base_url` | 是 | 客户端代理转发的基础地址。后续创建池上游时会拼接 provider 后缀作为上游 `base_url` |
| `management_url` | 是 | 管理 API 基础地址。AutoRouter 调用 `/v0/management/*` 拉取账号、发起 OAuth 等都走这里 |
| `client_api_key` | 创建必填,编辑可留空 | 转发流量时注入到 `Authorization` 头的密钥;DB 中以 Fernet 加密存(`schema-pg.ts:727`) |
| `management_key` | 创建必填,编辑可留空 | 管理 API 鉴权密钥;DB 中同样 Fernet 加密 |
| `enabled` | 否,默认 true | 关闭后所有依赖该实例的池上游不可用 |
| `description` | 否,最长 512 字符 | 备注 |
| 字段 | 必填 | 含义 |
| ---------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | 是,唯一,最长 64 字符 | 实例名称,仅用于管理后台显示 |
| `mode` | 是,默认 `managed` | `managed`(sidecar,与 AutoRouter 同 Docker 网络)/ `external`(独立运行的远端 CPA 服务) |
| `base_url` | 是 | 客户端代理转发的基础地址。后续创建池上游时会拼接 provider 后缀作为上游 `base_url` |
| `management_url` | 是 | 管理 API 基础地址。AutoRouter 调用 `/v0/management/*` 拉取账号、发起 OAuth 等都走这里 |
| `client_api_key` | 创建必填,编辑可留空 | 转发流量时注入到 `Authorization` 头的密钥;DB 中以 Fernet 加密存(`schema-pg.ts:727`) |
| `management_key` | 创建必填,编辑可留空 | 管理 API 鉴权密钥;DB 中同样 Fernet 加密 |
| `enabled` | 否,默认 true | 仅作为管理后台的 enabled / disabled 标识,**不参与路由 / 调度**——把它设为 false 不会让依赖该实例的池上游自动停止接流量;要停流量请在上游层 `is_active = false`。详见 [外部 vs sidecar 选择](./cliproxy-modes) |
| `description` | 否,最长 512 字符 | 备注 |

### sidecar 拓扑下的 base_url 与 management_url 怎么填

Expand Down
Loading
Loading