主题
Sub2API 自建部署实录
把 Claude / OpenAI / Gemini / Grok 的订阅账号转成标准 API 接口的开源网关,自带多账号调度、按 token 计费和 Web 管理后台。项目地址 Wei-Shaw/sub2api,LGPL-3.0。
本文是一次完整自建的实录,只记录实测确认过的内容,包含若干官方文档没写、踩过才知道的坑。凡属推断的地方都会标注。
先读这个
项目 README 自己写明「使用此项目可能违反 Anthropic 等上游提供商的服务条款」。首次进后台会强制要求管理员签署《部署与运营合规承诺》,其中第四条第 3 款明确约束「在缺乏必要授权、资质、备案、许可的情况下向公众提供 API 中转、账号额度分发、付费调用」,第 4 款约束中国大陆境内提供生成式 AI 服务的备案义务。
自己单人使用不涉及这些。要开放注册、发卡密、收费之前,请自行评估。签署页会记录你的账号、IP、User-Agent 和版本号。
本文对应的版本
实测于 Sub2API 0.1.169(commit 26d894ef,built 2026-07-31)。
上游约两天发一个版本,第九、十节那些扒出来的字段名和路由是版本相关的。照本文操作前先核对自己的版本,方法和升级步骤见第十三节。
一、环境与资源
实测三个容器的真实占用,比想象中小得多:
| 容器 | 镜像 | 内存实测 |
|---|---|---|
| sub2api | weishaw/sub2api:latest | 48.5 MB |
| sub2api-postgres | postgres:18-alpine | 59.5 MB |
| sub2api-redis | redis:8-alpine | 12.8 MB |
合计约 121 MB。一台 2 GB 内存、20 GB 磁盘的小机器完全跑得动。
选机器时真正要看的是出口
sub2api 要去调 Anthropic / OpenAI,服务器出口能不能直连上游比配置重要得多。境内机器基本没戏,除非配代理(见第七节)。
二、部署
2.1 先读脚本再跑
官方给的是 curl | bash 一把梭。别直接跑,先下下来读一遍:
bash
mkdir -p /opt/sub2api && cd /opt/sub2api
curl -sSL -o docker-deploy.sh \
https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh
cat docker-deploy.sh读下来是干净的:只下载 docker-compose.yml 和 .env.example、用 openssl rand -hex 32 生成三个密钥、建数据目录。不启动任何东西、不装 nginx、不改系统。确认无误再执行:
bash
bash docker-deploy.sh为什么特别在意 nginx
如果宿主机上跑着 Caddy,任何自带 nginx 的 compose 都会来抢 80 端口把 Caddy 顶掉。实测 sub2api 的 compose 只有三个 service,没有 nginx,Postgres 和 Redis 也都不映射端口到宿主机,只走内部网络。这点它做得很干净。
2.2 必须改的 .env
默认连接池会撑爆 Postgres
.env.example 里这几项是照着大机器写的,直接跑必然报 too many connections:
ini
DATABASE_MAX_OPEN_CONNS=256 # Postgres 默认 max_connections 只有 100
DATABASE_MAX_IDLE_CONNS=128 # 128 条常驻空闲连接,每条几 MB 白烧内存
REDIS_POOL_SIZE=4096
REDIS_MIN_IDLE_CONNS=256小机器改成:
ini
DATABASE_MAX_OPEN_CONNS=25
DATABASE_MAX_IDLE_CONNS=5
REDIS_POOL_SIZE=32
REDIS_MIN_IDLE_CONNS=4这几项是死变量,改了没用
.env.example 里还有 POSTGRES_SHARED_BUFFERS=1GB、POSTGRES_EFFECTIVE_CACHE_SIZE=4GB、POSTGRES_MAX_CONNECTIONS=1024、REDIS_MAXCLIENTS=50000。
这些在 docker-compose.local.yml 里根本没有被引用(postgres 服务块没有对应的 command: 参数),属于另一个部署变体的遗留。Postgres 18 会走自己的默认值 shared_buffers=128MB,在小机器上反而是合适的。不用管它们。
其余要设的:
ini
BIND_HOST=127.0.0.1 # 默认 0.0.0.0,除非直接暴露端口,否则改成回环
SERVER_PORT=8080
ADMIN_EMAIL=admin@你的域名
ADMIN_PASSWORD=<自己设一个强密码>
TZ=Asia/ShanghaiADMIN_PASSWORD 留空的话会自动生成并打在首次启动日志里。
2.3 起服务
bash
docker compose up -d
docker compose ps三个容器都应该是 healthy。看初始化日志确认:
Database initialized successfully
Admin user created: admin@你的域名
database connection pool configured {"max_open": 25, "max_idle": 5}
[Pricing] Downloaded 217 models successfully
Server started on 0.0.0.0:8080connection pool configured 那行会回显你改的值,是补丁生效的直接证据。
三、反向代理与 HTTPS
DNS 加一条 A 记录指向服务器,如果用 Cloudflare 必须走灰云(proxied: false),否则 Caddy 拿不到 HTTP-01 挑战。
Caddyfile 追加:
caddy
sub.你的域名 {
import common_log
encode gzip
reverse_proxy 127.0.0.1:8080
}bash
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
systemctl reload caddyCaddy 不需要 nginx 那个下划线设置
官方文档提到 nginx 需要 underscores_in_headers on 才能做会话路由。Caddy 默认不过滤下划线 header,不用额外配置。(此条为推断,未单独构造用例验证;实测常规请求转发正常。)
四、初次进后台
浏览器打开 https://sub.你的域名,用 .env 里的管理员账号登录。
第一件事:改掉初始密码,尤其是脚本自动生成、明文躺在 .env 和日志里的那个。
第二件事:签合规声明。在此之前所有 admin API 都会返回:
json
{"code":"ADMIN_COMPLIANCE_ACK_REQUIRED",
"message":"administrator compliance acknowledgement is required"}签署记录会写进 settings 表的 admin_compliance_acknowledgement:<用户ID>,包含版本号、时间、你的 IP 和 User-Agent。
注册默认就是关的
出厂 registration_enabled = false,POST /api/v1/auth/register 返回 403 REGISTRATION_DISABLED,不用担心开了域名就被陌生人注册。
注意一个容易误判的地方:如果你发一个字段不全的注册请求(比如空密码),拿到的是 400 字段校验失败——这是 Gin 的请求体绑定先于业务逻辑执行,它不能证明注册是开着的。要判断真实状态,得发一个字段完整的请求,或者直接查 registration_enabled 设置项。
五、邮件(验证码 / 密码重置)
5.1 端口必须用 465
smtp_use_tls 是隐式 TLS,不是 STARTTLS
填 587 会报:
tls dial: tls: first record does not look like a TLS handshake因为 587 是明文起步 + STARTTLS 升级,而 sub2api 的 smtp_use_tls=true 会直接发起 TLS 握手。
必须用 465(SMTPS 隐式 TLS)。
正确配置:
| 项 | 值 |
|---|---|
smtp_host | 你的邮件服务器主机名 |
smtp_port | 465 |
smtp_use_tls | true |
smtp_username / smtp_password | 专用发信账号 |
smtp_from_email / smtp_from_name | 发件人 |
frontend_url | https://sub.你的域名(密码重置链接靠它,不是 api_base_url) |
容器出网与证书
如果邮件服务器就在同一台宿主机上,别填 Docker 网桥网关地址(172.x.0.1)——宿主服务通常只监听回环,容器连不上,而且 TLS 证书主机名也对不上。直接填公网域名,容器绕一圈出去再回来,证书能验证通过。
5.2 验证是不是真发出去了
配置保存成功 ≠ 发得出去。发验证码的路由是:
POST /api/v1/auth/send-verify-code
{"email":"...","scene":"register"}别猜这个路由
/api/v1/auth/email/send-code、/api/v1/auth/send-email-code、/api/v1/admin/settings/test-email 全是 404。sub2api 没有独立的「测试发信」按钮式接口,只能走真实业务流。同一地址有 60 秒限流,重复调会返回 VERIFY_CODE_TOO_FREQUENT。
成功的样子(应用侧 + 邮件服务器侧两边对上才算数):
sub2api: [EmailQueue] Worker 2 sent verify code to xxx@...
maddy: submission: incoming message {"sender":"...","username":"..."}
maddy: submission: RCPT ok
maddy: submission: accepted如果只看到 RCPT error / user doesn't exists,那是收件人信箱不存在,SMTP 配置本身是好的。
六、四层模型:账号 / 分组 / 渠道 / 密钥
这是 sub2api 里最容易绕晕的部分,先把关系理清:
上游账号 (accounts) ← 真正持有额度的东西:订阅 OAuth 或 API Key
│ 多对多,中间表 account_groups
▼
分组 (groups) ← 平台隔离 + 计费倍率 + 限额,调度在这一层做
│
▼
渠道 (channels) ← 对外呈现,绑定一组 group
│
▼
网关密钥 (api_keys) ← 用户拿去调用的 sk-xxx,绑定一个 group分组是按平台隔离的
groups.platform 取值 anthropic / openai / gemini / antigravity / grok。一个 anthropic 的账号进不了 openai 的分组。新装会自带一个 default 分组(platform=anthropic)。
调用时的实际流转(来自真实请求日志):
security_audit.gateway_check_done decision="allow"
sticky.session_hash_generated session_hash=...
sticky.account_selected selected_account_id=2 account_name="..."
[Forward] Using account: ID=2 Platform=anthropic Type=apikey Proxy=七、IP 管理 / 添加代理是什么
一句话
这是给「上游账号」配出站代理的——sub2api 去调 Anthropic / OpenAI 时从哪个 IP 出去。不是给整个服务挂代理,也跟你访问后台、用户调你的网关都没有关系。
7.1 数据结构上的证据
proxies 表: id, name, protocol, host, port, username, password,
status, expires_at, fallback_mode, backup_proxy_id, expiry_warn_days
accounts 表: proxy_id → FOREIGN KEY REFERENCES proxies(id)
proxy_fallback_origin_id代理挂在账号上,不是挂在服务上。每次转发的日志里会按账号打印用了哪个代理:
[Forward] Using account: ID=2 Name=... Platform=anthropic Type=apikey Proxy=
^^^^^^ 空 = 直连7.2 字段含义
| 字段 | 说明 |
|---|---|
protocol | http / https / socks |
host / port | 代理地址 |
username / password | 代理认证,没有就留空 |
fallback_mode | none(不回退,代理挂了就失败) / proxy(切到备用代理) / direct(回退直连) |
backup_proxy_id | fallback_mode=proxy 时的备用代理 |
expires_at + expiry_warn_days | 买的代理有有效期,到期前提醒 |
7.3 什么时候需要它
这是它最实在的用途:
上游按 IP 风控。订阅账号(尤其是 OAuth 类型)从机房 IP 调用,比从住宅 IP 更容易触发风控。给账号绑一个住宅代理能缓解。
服务器出口被上游挡住。典型症状是 curl 上游域名返回 403,且响应头带:
cf-mitigated: challenge server: cloudflare这个 403 要会读
cf-mitigated: challenge是 Cloudflare 人机挑战,不是封 IP。curl 永远过不了 JS 挑战,换 User-Agent 也没用。所以它不能证明你的 API 调用一定会被挡——真实带 token 的 API 请求走的路径和浏览器首页的 CF 规则可能完全不同。要判断到底通不通,只能拿真实凭据打一次。挂代理是这种情况下的备选方案,不是必然需要。
多账号分散出口。多个订阅账号全从同一个 IP 出去,容易被识别成同一主体。一账号一代理可以分开。
境内服务器。上游根本不可直连时,代理是唯一出路。
7.4 不需要它的情况
如果你的服务器能直连上游(比如海外机器 + api.openai.com / api.anthropic.com 这类纯 API 域名,通常不设 CF 挑战),那就别配,直连更快更稳。代理是解决问题的工具,不是必装项。
八、没有订阅账号,怎么完整体验一遍
订阅号不好搞,但整条流水线(调度 / 计费 / 用量统计 / 仪表盘 / 多用户)其实可以用任何一个 OpenAI 或 Anthropic 兼容的 API 端点当上游点亮——比如你自己已有的 New API / one-api 中转站,或者任何一把可用的官方 key。
建一个 type=apikey 的账号指向它即可。这样跑出来的计费、token 统计、调度日志都是真实的,只是上游不是订阅号而已。
TIP
这跟「把 sub2api 挂到 New API 后面当上游」是相反方向,纯粹当测试台架用。体验完删掉即可。
完整步骤见下一节的 API 速查。
九、API 速查(全部实测)
sub2api 没有 Swagger
/swagger/doc.json 和 /openapi.json 都返回 200,但内容是前端 SPA 的 index.html——那是 catch-all 路由,不是 API 文档。
要查路由只能扒二进制:
bash
docker exec sub2api sh -c "grep -aoE '/api/v1/[a-z0-9/:{}-]+' /app/sub2api | sort -u"要查请求体字段,发一个空 body,让 Gin 的校验错误把必填字段名吐出来:
json
{"code":400,"message":"Invalid request: Key: 'CreateAccountRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag\n..."}填一个非法值则会吐 oneof 错。枚举值可以直接扒:
bash
docker exec sub2api sh -c "grep -aoE 'oneof=[a-z_ |]+' /app/sub2api | sort -u"这招有代价
字段填对的那一刻它就会真的建出对象。 探测时务必用可识别的临时名字,探完清理干净。
9.1 两种认证方式
| 方式 | 请求头 | 适用 |
|---|---|---|
| 管理员 API Key | X-API-Key: admin-xxxx | /api/v1/admin/* |
| 用户 JWT | Authorization: Bearer <access_token> | /api/v1/admin/* 和 /api/v1/keys |
两个反直觉的点
- 管理员 API Key 用
X-API-Key,用Authorization: Bearer会401 INVALID_TOKEN,用X-Admin-Key会401 UNAUTHORIZED。 - 管理员 API Key 不能用于
/api/v1/keys(那是用户自己的密钥管理,只认 JWT),也不能当推理网关 key 用(401 INVALID_API_KEY)。
取 JWT:
bash
curl -s -X POST http://127.0.0.1:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@你的域名","password":"..."}'9.2 建上游账号
POST /api/v1/admin/accountsjson
{
"name": "上游账号名",
"platform": "anthropic",
"type": "apikey",
"credentials": {
"api_key": "sk-xxxx",
"base_url": "https://你的上游"
}
}| 字段 | 说明 |
|---|---|
platform | anthropic / openai / gemini / antigravity / grok |
type | apikey(写 api_key 会 oneof 报错)或 oauth |
credentials.base_url | 不要带 /v1 |
api_key 加密存储,GET 不回显,只返回 credentials_status.has_api_key: true。
9.3 账号绑分组
PUT /api/v1/admin/accounts/<id>
{"group_ids": [1]}账号的 GET 不返回分组字段
绑完再 GET 账号,你看不到 group_ids——这不代表没绑上。要确认就查中间表:
sql
SELECT * FROM account_groups;9.4 建分组
POST /api/v1/admin/groups
{"name":"...","platform":"anthropic","rate_multiplier":1}rate_multiplier 必须 > 0
漏了会返回 500 internal error,响应体不说原因。真实原因只在容器日志里:
[ERROR] POST /api/v1/admin/groups Error: rate_multiplier must be > 09.5 建渠道
POST /api/v1/admin/channels
{"name":"...","group_ids":[1]}9.6 发网关密钥
POST /api/v1/keys ← 注意:不是 /admin/api-keys,也不是 /user/api-keys
Authorization: Bearer <JWT>
{"name":"...","group_id":1}/api/v1/admin/api-keys 和 /api/v1/user/api-keys 都是 404。响应里直接给明文 key(sk-...),只有这一次能拿到。
9.7 调余额
POST /api/v1/admin/users/<id>/balance
{"operation":"set", "balance": 20, "remark":"..."}operation ∈ set / add;字段是 balance 不是 amount。
余额为 0 时请求根本不出门
网关直接返回 INSUFFICIENT_BALANCE,不会调上游、不会产生任何用量记录。测试前记得先充。
9.8 调用网关
bash
curl https://sub.你的域名/v1/messages \
-H "x-api-key: sk-你的网关密钥" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"claude-sonnet-4-6","max_tokens":60,
"messages":[{"role":"user","content":"你好"}]}'验收要看三处,缺一不算通:
sql
-- 用量落库
SELECT id,user_id,account_id,model,input_tokens,output_tokens
FROM usage_logs ORDER BY id DESC LIMIT 3;
-- 密钥被用过
SELECT id,name,last_used_at FROM api_keys;外加余额确实被扣。
十、settings API 的两个性质
PUT 是全量替换,不是局部合并
只发想改的几个字段,其余字段会被写成零值。正确姿势是先 GET 全量、在结果上覆盖、再整体 PUT 回去。
改之前先备份:
bash
curl -s -H "X-API-Key: $KEY" http://127.0.0.1:8080/api/v1/admin/settings \
> settings-backup-$(date +%Y%m%d-%H%M%S).jsonsmtp_password 是只写字段
GET 只返回 smtp_password_configured: true/false,不返回密码本身。做「GET 全量 → 整体 PUT」时如果不显式把 smtp_password 补回去,会被清空。
审计日志里的 changed 不等于「被改坏了」
第一次 PUT 之后,审计日志的 changed 列表可能有二三十项,settings 表行数从 37 涨到 122。
这是代码里的默认值被固化成了数据库行,值本身没变。看到列表别急着下结论,先把值读回来对一遍。
十一、排错速查
| 症状 | 原因 | 处理 |
|---|---|---|
ADMIN_COMPLIANCE_ACK_REQUIRED | 管理员没签合规声明 | Web 后台签署,无法通过 API 绕过 |
tls: first record does not look like a TLS handshake | SMTP 端口填了 587 | 改 465 |
too many connections | DATABASE_MAX_OPEN_CONNS 超过 Postgres max_connections | 改成 25 |
建分组 500 internal error | 漏了 rate_multiplier | 补上,且必须 > 0 |
INSUFFICIENT_BALANCE | 用户余额为 0 | 先充值 |
REGISTRATION_DISABLED | 注册开关是关的(默认) | 后台打开 |
VERIFY_CODE_TOO_FREQUENT | 同地址 60 秒限流 | 等一分钟或换地址 |
上游 403 + cf-mitigated: challenge | Cloudflare 人机挑战 | 见第七节,先别急着断定被封 |
| 绑完分组账号 GET 里看不到 | 该字段本就不在账号表示里 | 查 account_groups 表 |
十二、备份与迁移
12.1 热备数据库(不停服,升级前必做)
Postgres 容器里自带 pg_dump(实测 /usr/local/bin/pg_dump,PostgreSQL 18.4),不用停服务:
bash
docker exec sub2api-postgres pg_dump -U sub2api -d sub2api -Fc \
> /opt/sub2api/db-$(date +%Y%m%d-%H%M%S).dump-Fc 是自定义压缩格式,恢复用 pg_restore:
bash
# 恢复到一个空库(--clean 会先删同名对象,确认过再用)
docker exec -i sub2api-postgres pg_restore -U sub2api -d sub2api --clean --if-exists \
< db-20260802-213000.dump没演练过的备份不算备份
上面这条恢复命令我没有在生产库上实跑过——--clean 会删对象,在跑着的库上验证代价太大。
真要确认可恢复,正确做法是另起一个空 Postgres 容器导进去,确认表数和关键行数对得上(实测当前是 85 张表、settings 246 行),再把临时容器删掉。在没做过这一步之前,别把这份备份当成可靠退路。
12.2 整目录冷备与迁移
整个部署目录自包含,打包即可搬走:
bash
cd /opt && docker compose -f sub2api/docker-compose.yml down
tar czf sub2api-backup-$(date +%Y%m%d).tar.gz sub2api/包含 docker-compose.yml、.env、data/、postgres_data/、redis_data/。新机器解压后 docker compose up -d 即可。
体量很小,实测三个数据目录合计约 106 MB(postgres_data 77 MB、redis_data 20 MB、data 8.6 MB),压缩后更小,随手备份没有负担。
WARNING
.env 和 settings 表里都有明文凭据(含 smtp_password),备份文件按敏感数据对待。
十三、版本与升级
镜像标签和二进制自报的版本对不上
实测同一个镜像(weishaw/sub2api:latest,digest sha256:85d29bfc…)给出两个不同答案:
| 来源 | 版本 | commit | 时间 |
|---|---|---|---|
镜像 OCI 标签 org.opencontainers.image.version | 0.1.168 | 99c8e4bf… | 镜像 Created 2026-07-29 |
容器内二进制 --version | 0.1.169 | 26d894ef… | built 2026-07-31T09:07:54Z |
对照上游 GitHub Releases:v0.1.168 发布于 07-29、v0.1.169 发布于 07-31——二进制是对的,镜像标签落后一个版本。
(成因未验证。推测是 CI 打标签那步取了上一个 release 号,且 BuildKit 重写了镜像的 Created 时间戳。标注为推断。)
判断版本只认这一条命令:
bash
docker exec sub2api /app/sub2api --version没有版本查询接口,别去找了
/version 返回 200,但内容是前端 SPA 的 index.html——和第九节 /swagger/doc.json 是同一个 catch-all 陷阱。/api/v1/version 直接 404。
前端注入的 window.__APP_CONFIG__ 里确实有 version 字段,但实测是空字符串。
13.1 latest 漂得很快
上游发布节奏(GitHub Releases 实测):
| 版本 | 发布日 |
|---|---|
| v0.1.170 | 2026-08-02 |
| v0.1.169 | 2026-07-31 |
| v0.1.168 | 2026-07-29 |
| v0.1.166 | 2026-07-27 |
| v0.1.165 | 2026-07-25 |
| v0.1.164 | 2026-07-23 |
约两天一个版本。 而 docker-compose.yml 里写死的是 image: weishaw/sub2api:latest,任何一次 docker compose pull 都可能跨好几个版本,本文第九、十节扒出来的字段名和路由不保证还成立。
要固定版本就把 compose 改成具体 tag:
yaml
image: weishaw/sub2api:0.1.169镜像 tag 没有 Release 名字里的 v 前缀,改之前先 docker manifest inspect weishaw/sub2api:0.1.169 确认这个 tag 真的存在。
13.2 数据库迁移是自动的,回滚不是
实测库里有 Atlas 的迁移记录表:
schema_migrations
atlas_schema_revisions
auth_identity_migration_reports容器启动时自动向前迁移(首装那次的日志就是 Initializing database... → Database initialized successfully)。当前共 85 张表。
升级容易,降级不容易
Atlas 只自动向前迁移,不会自动回滚 schema。新版本一旦改了表结构,你把镜像 tag 换回旧版并不能把库改回去,旧代码大概率起不来。
升级前先做 12.1 的数据库热备——这是唯一可靠的退路。
13.3 升级步骤
bash
cd /opt/sub2api
# 1. 记下当前版本,出事好定位
docker exec sub2api /app/sub2api --version
# 2. 热备数据库(见 12.1)
docker exec sub2api-postgres pg_dump -U sub2api -d sub2api -Fc \
> db-before-upgrade-$(date +%Y%m%d-%H%M%S).dump
# 3. 备份 settings 全量(见第十节,PUT 是全量替换那条)
# curl -s -H "X-API-Key: $KEY" http://127.0.0.1:8080/api/v1/admin/settings > settings-backup-....json
# 4. 拉新镜像重启
docker compose pull && docker compose up -d
# 5. 验收
docker compose ps # 三个都要 healthy
curl -s http://127.0.0.1:8080/health # 期望 {"status":"ok"}
docker exec sub2api /app/sub2api --version
docker compose logs --tail=50 sub2api # 看迁移有没有报错有个现成的健康检查端点
GET /health 返回 {"status":"ok"},不需要认证。compose 里三个服务都配了 healthcheck(sub2api 打 /health,Postgres 用 pg_isready,Redis 用 redis-cli ping),且都是 restart: unless-stopped。接外部监控直接打 /health 就行。
settings 行数会随版本增长,别拿它当异常指标
第十节提到首次 PUT 之后 settings 表从 37 行涨到 122 行。实测在 0.1.169 上已经是 246 行——新版本会持续把新的默认配置项固化进库。
行数变化是正常的,不能用来判断配置有没有被改坏。要判断只能把值读回来对。
十四、本文还没覆盖的部分
写在这里是为了避免误导——以下都是没验证过的,不是"验证过没问题":
| 缺口 | 说明 |
|---|---|
| 订阅账号(OAuth)接入 | 全文只跑通了 type=apikey。OAuth 怎么授权、回调怎么配、token 过期怎么刷新,一概没测。这是项目的核心卖点,也是第七节住宅代理防风控真正要保护的对象——防线写了,被防的东西还没接上 |
| 流式响应(SSE) | 第 9.8 节的 curl 是非流式的。真实客户端基本都走 stream: true,这条链路没验证过,Caddy 那段 encode gzip 对流式输出的影响也没测 |
| 并发与压力 | 第一节 121 MB 是空载数字。没有并发下的内存、连接数实测,"2 GB 机器跑得动"只对单人低频使用成立 |
| 备份恢复演练 | 见 12.1 的红框,pg_restore 没实跑过 |
| 运营侧操作 | 第六节讲清了账号/分组/渠道/密钥的数据模型,但计费倍率怎么定、卡密/充值怎么发、限额怎么设、启动日志里那 217 个模型的定价表在哪改,都没写 |
| 日志轮转与告警 | 第十一节排错表覆盖的是配置期错误。运行期的日志轮转、磁盘增长、容器 unhealthy 时的排查路径,没有 |
凭据统一存放位置见凭据库,本文一律不写具体 IP、域名和密码。