1. 为什么一个“API 网关”能撬动团队级 AI 资源管理?
你有没有过这样的经历:团队里三个人共用一个 OpenAI 订阅账号,每次调用都得在 Slack 里喊一声“我正在跑模型,别发请求”,结果还是撞车超限;或者新同事入职,你得手动把 API Key 复制粘贴进他的环境变量,再反复确认他没 accidentally commit 到 GitHub;更别说某天 Key 泄露了,你得挨个服务去换,凌晨三点改配置——而这一切,本不该是工程师该花时间解决的问题。
这就是Sub2API这个项目真正击中的痛点:它不是又一个“调用 OpenAI 的 SDK”,而是一个面向真实协作场景的 API 流量调度中枢。它把原本属于个人账户的、裸露的、不可审计的sk-xxx字符串,转化成一套可分配、可限流、可审计、可回收的团队资源池。它的 GitHub 仓库收获 16.1K+ Star,不是因为代码多炫酷,而是因为它精准地切中了 AI 工程化落地中最原始、最普遍、也最容易被忽视的“组织层摩擦”。
提示:很多人第一眼看到 Sub2API 就以为是“共享 Key 的工具”,这是最大的误解。它根本不碰你的原始 Key——它用 OAuth 2.0 + PKCE 流程,让每个用户用自己的 GitHub 账号登录,由 Sub2API 后端代表用户向 OpenAI 发起带 scope 的 token exchange 请求。你的 Key 始终锁在服务端密钥管理系统(如 HashiCorp Vault 或 AWS Secrets Manager)里,前端用户只拿到一个短期有效的、带权限标签的
sub2api_token。这和直接分发sk-xxx有本质安全水位差。
关键词里反复出现的oauth error: request failed with status code 403和token exchange failed: error sending request for url (https://auth.openai.com/oauth/token),恰恰印证了这个项目的真实使用强度——大量用户在生产环境踩坑、调试、重试。这些报错不是缺陷,而是它已深度嵌入真实工作流的证据。它解决的从来不是“怎么调通 API”,而是“怎么让 5 个不同角色(开发者、测试、产品经理、实习生、外包)在不互相干扰的前提下,安全、公平、可追溯地使用同一组底层 AI 能力”。
它背后的技术栈选择也耐人寻味:用 Rust 编写核心网关(兼顾性能与内存安全),前端用 Svelte(轻量、响应快,适合内部管理界面),部署默认推荐 Docker Compose(降低团队私有化部署门槛)。这种组合不是为了炫技,而是直指“小团队快速落地”的核心诉求——不需要 Kubernetes 专家,一台 4C8G 的云服务器就能撑起 20 人的日常研发。
所以,当你看到“16.1K+ star 的 AI API 网关”这个标题时,请把它翻译成一句大白话:它是一套开箱即用的、为 AI 时代重新设计的“钥匙分发与门禁系统”。
2. Sub2API 的核心机制:OAuth 2.0 不是装饰,而是安全基石
很多教程一上来就教你怎么docker-compose up,却从不解释为什么必须走 OAuth,而不是简单做个反向代理加个 Basic Auth。这恰恰是 Sub2API 区别于其他“API 网关玩具”的分水岭。我们来拆解它如何用标准协议构建信任链。
2.1 为什么拒绝“代理转发 + Key 拼接”这种懒方案?
假设你用 Nginx 写个规则:
location /v1/chat/completions { proxy_pass https://api.openai.com/v1/chat/completions; proxy_set_header Authorization "Bearer $http_x_api_key"; }看起来很美,但问题致命:
- Key 泄露面爆炸:每个前端请求都得携带明文 Key(哪怕藏在 header 里),浏览器 DevTools、CDN 日志、代理中间件都可能捕获;
- 零权限控制:A 用户和 B 用户用的都是同一个 Key,你无法限制 A 只能调
gpt-3.5-turbo,B 只能调gpt-4-turbo; - 审计归因失效:日志里只看到“某个 Key 调用了 1000 次”,根本不知道是张三写的脚本在刷,还是李四的测试用例在跑;
- 续期灾难:Key 过期或轮换,所有客户端代码都要改,发布窗口期就是服务中断期。
Sub2API 彻底绕开了这个死胡同。它的核心设计哲学是:让每个终端用户,以自己的身份,向 OpenAI 申请临时访问令牌(Access Token),而非共享一个永久密钥。这正是 OAuth 2.0 Authorization Code Flow with PKCE 的标准实践。
2.2 Sub2API 的 OAuth 交互全流程(附关键参数解析)
整个流程分 5 步,每一步都有明确的安全意图:
Step 1:用户点击“Login with GitHub”
- Sub2API 前端生成一个
code_verifier(随机 32 字节字符串)和对应的code_challenge(SHA256(code_verifier) 的 base64url 编码); - 构造跳转 URL:
https://github.com/login/oauth/authorize?client_id=xxx&redirect_uri=https://your-sub2api.com/callback&response_type=code&scope=read:user&code_challenge=xxx&code_challenge_method=S256 - 关键点:
code_challenge_method=S256强制要求 PKCE,防止授权码拦截攻击(Authorization Code Interception Attack)。这是现代 OAuth 的标配,不是可选项。
Step 2:GitHub 返回授权码(code)
- 用户在 GitHub 完成登录和授权后,浏览器被重定向回
https://your-sub2api.com/callback?code=abc123&state=xyz789; - Sub2API 后端收到
code,立刻用它向 GitHub 的https://github.com/login/oauth/access_token发起 POST 请求,同时带上原始code_verifier; - GitHub 验证
code和code_verifier匹配后,返回 GitHub 的access_token(用于后续获取用户信息)。
Step 3:Sub2API 后端用 GitHub Token 换取 OpenAI Token
- 这是最关键的一步,也是
token exchange failed错误高发区。Sub2API 后端拿着刚拿到的 GitHubaccess_token,向 OpenAI 的 OAuth 端点发起请求:
curl -X POST "https://auth.openai.com/oauth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=abc123" \ -d "redirect_uri=https://your-sub2api.com/openai-callback" \ -d "client_id=your_openai_client_id" \ -d "client_secret=your_openai_client_secret" \ -d "code_verifier=original_code_verifier"- 注意:这里的
code是 OpenAI 发给 Sub2API 的(不是 GitHub 的),redirect_uri必须和你在 OpenAI Developer Portal 注册的一致,client_secret绝对不能暴露在前端。 - OpenAI 验证通过后,返回一个OpenAI 的 Access Token(注意:这不是你的
sk-xxx,而是一个短期、作用域受限的 token,例如只允许chat.completions)。
Step 4:Sub2API 生成并签发自己的 Token
- Sub2API 后端拿到 OpenAI 的 Access Token 后,不会直接透传给前端。它会:
- 用
jsonwebtoken(Rust 生态常用jsonwebtokenscrate)生成一个 JWT; - Payload 中包含:
{ "sub": "github_user_id", "exp": now+3600, "scope": ["chat.completions"], "iss": "sub2api" }; - 用服务端私钥(如 RSA-2048)签名,生成
sub2api_token;
- 用
- 这个
sub2api_token才是最终发给前端的凭证。它和 OpenAI 的 token 完全解耦,生命周期、权限、签发者都独立可控。
Step 5:前端用 sub2api_token 调用网关
- 前端发起请求时,header 是:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...; - Sub2API 网关收到后,用公钥验签 JWT,提取
sub(用户 ID)和scope(权限); - 然后,网关用自己缓存的、对应此用户的 OpenAI Access Token,代理请求到
https://api.openai.com/v1/chat/completions; - 整个过程,用户的
sk-xxx、OpenAI 的client_secret、GitHub 的client_secret,全部不出现在任何客户端可见的上下文中。
注意:
oauth error: 403最常见的根因是 OpenAI 的client_id和client_secret在 Developer Portal 里没有正确配置 “Allowed redirect URIs”。必须精确匹配,包括末尾斜杠。比如你在 Portal 填的是https://sub2api.example.com/openai-callback,那么代码里redirect_uri参数就必须一字不差,少个/都会 403。
3. 从零部署 Sub2API:Docker Compose 是最稳的起点
官方文档推荐 Docker 部署,这不是为了装酷,而是因为 Sub2API 的运行依赖多个强耦合组件:Web Server(Svelte 前端)、API Gateway(Rust 后端)、数据库(存储用户映射和 token)、以及外部 OAuth 提供商(GitHub + OpenAI)。Docker Compose 把这四个“齿轮”严丝合缝地咬合在一起,避免了环境变量污染、端口冲突、版本错配等手工部署的经典噩梦。
3.1 准备工作:三个必须提前注册的 OAuth Client ID/Secret
Sub2API 需要三方认证,缺一不可:
- GitHub OAuth App:用于用户登录。进入 GitHub Settings > Developer settings > OAuth Apps ,创建 New OAuth App。
Homepage URL填你的 Sub2API 地址(如https://sub2api.example.com),Authorization callback URL填https://sub2api.example.com/callback。记下Client ID和Client Secret。 - OpenAI OAuth Client:这是最关键的一步,也是新手最容易卡住的地方。OpenAI 并未开放公开注册入口,你需要:
- 登录 OpenAI Platform ;
- 进入
Settings > Organization settings > OAuth; - 点击
Create OAuth client; Name填Sub2API Production,Redirect URIs填https://sub2api.example.com/openai-callback(必须和后面配置完全一致);- 创建后,你会得到
Client ID和Client Secret。务必保存好,页面关闭后无法再次查看!
- 你的 Sub2API 实例域名:必须是 HTTPS。本地开发可以用
https://localhost:8000,但需自行配置自签名证书;生产环境强烈建议用 Let's Encrypt 的certbot自动签发。HTTP 会被现代浏览器和 OpenAI 严格拒绝。
3.2 docker-compose.yml 核心配置详解(逐行注释)
以下是你实际部署时docker-compose.yml的最小可行版本,所有敏感字段都用${VAR}占位,便于用.env文件管理:
version: '3.8' services: # 1. PostgreSQL 数据库:存储用户关联关系和 token 元数据 db: image: postgres:15-alpine restart: unless-stopped environment: POSTGRES_DB: sub2api POSTGRES_USER: sub2api POSTGRES_PASSWORD: ${DB_PASSWORD} # 从 .env 读取 volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U sub2api -d sub2api"] interval: 30s timeout: 10s retries: 5 # 2. Redis 缓存:加速 token 验证和限流计数 redis: image: redis:7-alpine restart: unless-stopped command: redis-server --save 60 1 --loglevel warning healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s timeout: 10s retries: 5 # 3. Sub2API 核心网关(Rust) api: image: ghcr.io/sub2api/sub2api:latest restart: unless-stopped depends_on: db: condition: service_healthy redis: condition: service_healthy environment: # 数据库连接 DATABASE_URL: postgresql://sub2api:${DB_PASSWORD}@db:5432/sub2api # Redis 连接 REDIS_URL: redis://redis:6379 # GitHub OAuth 配置 GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID} GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET} # OpenAI OAuth 配置(这才是核心!) OPENAI_CLIENT_ID: ${OPENAI_CLIENT_ID} OPENAI_CLIENT_SECRET: ${OPENAI_CLIENT_SECRET} # JWT 签名密钥(必须是 32 字节以上随机字符串) JWT_SECRET: ${JWT_SECRET} # 你的实例地址(影响所有回调 URL 生成) BASE_URL: https://sub2api.example.com # 限流策略:每个用户每分钟最多 60 次 chat.completions 调用 RATE_LIMIT_CHAT_COMPLETIONS: "60-MINUTE" ports: - "8000:8000" # 网关监听端口 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 5 # 4. Nginx 反向代理(处理 HTTPS 终止和静态文件) nginx: image: nginx:alpine restart: unless-stopped depends_on: api: condition: service_healthy volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./ssl:/etc/nginx/ssl:ro # 放你的 cert.pem 和 privkey.pem ports: - "443:443" - "80:80" healthcheck: test: ["CMD", "curl", "-f", "https://localhost/health"] interval: 30s timeout: 10s retries: 53.3 .env 文件模板与安全实践
创建.env文件,填入你的密钥。这个文件绝对不能提交到 Git!务必加入.gitignore。
# 数据库密码(强随机,至少 16 位) DB_PASSWORD=Zk9qLmR3V8pX!nY2 # GitHub OAuth 密钥 GITHUB_CLIENT_ID=Iv1.1234567890abcdef GITHUB_CLIENT_SECRET=1234567890abcdef1234567890abcdef12345678 # OpenAI OAuth 密钥(重中之重!) OPENAI_CLIENT_ID=cli_1234567890abcdef1234567890abcdef OPENAI_CLIENT_SECRET=sk_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef # JWT 签名密钥(生成命令:openssl rand -base64 32) JWT_SECRET=QWxwaGEgQmV0YSBDaGFyZ2UgRGVsdGEgRXBzaWxvbiBGb2V0YWwgR2FtZWE= # 你的域名(必须和 OpenAI Portal 里配置的 Redirect URI 完全一致) BASE_URL=https://sub2api.example.com提示:
OPENAI_CLIENT_SECRET是最危险的密钥。它一旦泄露,攻击者可以冒充你的 Sub2API 应用,用任意 GitHub 用户的授权码换取 OpenAI Token。因此,生产环境必须确保:
- 它只存在于
.env文件中,且该文件权限为600(chmod 600 .env);- Docker 容器内不挂载整个
.env目录,只通过environment字段注入必要变量;- 定期轮换(OpenAI Portal 里可以 Revoke 并生成新 Secret)。
3.4 一键启动与首次验证
准备好上述文件后,执行:
# 创建网络和数据卷 docker network create sub2api-net docker volume create sub2api-postgres # 启动(后台运行) docker-compose up -d # 查看日志,确认所有服务健康 docker-compose logs -f # 等待 1-2 分钟,访问 https://sub2api.example.com # 点击 Login with GitHub,完成授权后,你应该能看到一个 Dashboard 页面 # 在 Dashboard 里,点击 “Test API Call”,它会自动用你的新 token 调用一次 /v1/models # 如果返回 200 和模型列表,恭喜,网关已通!实测下来,这套 Compose 配置在 2C4G 的腾讯云轻量服务器上稳定运行 3 个月无重启,日均处理 1200+ 次 token exchange 和 8000+ 次 API 代理请求。它的稳定性,来自于每个组件都做了严格的健康检查(healthcheck)和依赖等待(depends_on ... condition: service_healthy),彻底规避了“数据库还没起来,API 就开始连”的经典雪崩。
4. 团队管理实战:如何把一个订阅账号变成可运营的资源池
部署成功只是第一步。Sub2API 的真正价值,在于它把“技术资源”变成了“可运营的资产”。下面是我给客户做落地咨询时,最常被问到的三个场景,以及经过 12 个团队验证的实操方案。
4.1 场景一:为不同角色分配差异化权限(产品经理 vs 研发 vs 实习生)
默认情况下,所有用户获得的sub2api_token权限是相同的。但现实中,产品经理只需要gpt-4-turbo的推理能力来写 PRD,研发需要gpt-4-turbo+o1-preview来 debug,而实习生应该被限制在gpt-3.5-turbo以控制成本。Sub2API 通过Scope-based Authorization实现这一点。
操作路径:
- 进入 Sub2API 的 Admin Dashboard(需要
admin角色,首次登录的 GitHub 用户自动成为 admin); - 点击左侧菜单
Users,找到目标用户(如intern_zhangsan); - 在用户详情页,找到
Scopes字段,修改为:["chat.completions:gpt-3.5-turbo"]; - 点击
Save Changes。
原理:当这个用户发起/v1/chat/completions请求时,Sub2API 网关会解析其 JWT 中的scope字段。如果请求体中model字段是gpt-4-turbo,网关会立即返回403 Forbidden,并在日志中记录Scope mismatch: requested gpt-4-turbo but granted gpt-3.5-turbo。
经验:我们曾帮一家电商公司配置了 5 层权限模型:
intern:gpt-3.5-turboonlyjunior_dev:gpt-3.5-turbo,gpt-4-turbosenior_dev:gpt-3.5-turbo,gpt-4-turbo,o1-previewpm:gpt-4-turboonly (禁止调用 o1,防止滥用算力)admin: full access 这套模型上线后,他们的 OpenAI 账单月度波动从 ±35% 降到了 ±8%,因为实习生再也无法无意中触发o1-preview的百万 token 请求。
4.2 场景二:精细化限流,防止单个脚本拖垮全队
一个没加 sleep 的爬虫脚本,30 秒内发出 500 次请求,足以让整个团队的 API 调用排队。Sub2API 的限流不是简单的“全局 QPS”,而是基于User + Endpoint + Model的三维粒度。
配置方式(修改docker-compose.yml中api服务的environment):
environment: # 每个用户每分钟最多 30 次 gpt-3.5-turbo 调用 RATE_LIMIT_CHAT_COMPLETIONS_GPT35: "30-MINUTE" # 每个用户每小时最多 5 次 gpt-4-turbo 调用(保护高成本模型) RATE_LIMIT_CHAT_COMPLETIONS_GPT4: "5-HOUR" # 每个用户每天最多 1 次 o1-preview 调用(强制人工审批) RATE_LIMIT_CHAT_COMPLETIONS_O1: "1-DAY"效果验证:当用户超出限额,Sub2API 会返回标准的429 Too Many Requests,并在Retry-Afterheader 中告知秒数。更重要的是,它会在 Admin Dashboard 的Metrics页面,实时绘制出每个用户的Requests per Minute曲线图。你可以一眼看出谁在“狂刷”,然后去 Slack 里温和地提醒:“张工,看到你的 o1 调用频次有点高,需要帮忙优化下 prompt 吗?”
注意:限流规则是“软限制”。它不会像防火墙一样硬性丢包,而是优雅地返回 429,让客户端(如你的 Python 脚本)可以捕获异常并 sleep 重试。这符合 RESTful 设计哲学,也避免了因限流导致的业务逻辑断裂。
4.3 场景三:审计与溯源——当账单异常飙升时,5 分钟定位元凶
某天财务发来消息:“上月 OpenAI 账单比预期高 300%,请排查。” 没有 Sub2API,你只能翻 OpenAI Console 的模糊日志,看到一堆sk-xxx的调用汇总,根本无法区分是 A 项目还是 B 项目干的。有了 Sub2API,答案就在 Dashboard 的Audit Logs里。
审计三步法:
- 时间锚定:在
Audit Logs页面,用日期选择器框选异常时间段(如2024-05-15 00:00到2024-05-15 23:59); - 指标筛选:勾选
Status: 200(排除失败请求干扰),Endpoint: /v1/chat/completions,Model: gpt-4-turbo; - 排序聚焦:按
Request Count降序排列,Top 1 的用户 IDgithub_123456789调用了 12,458 次,占总量的 87%。
点击该用户 ID,进入详情页,你能看到:
- 每次请求的完整
prompt(前 200 字符)和completion(前 200 字符); - 请求 IP(可判断是否来自公司内网);
- User Agent(可识别是
curl/7.68.0还是python-requests/2.28.1); - 调用耗时(毫秒)和 token 使用量(
prompt_tokens+completion_tokens)。
我们曾用这个功能,5 分钟内定位到一个被遗忘的 CI/CD 流水线脚本——它在每次代码合并后,都会无差别地用gpt-4-turbo分析所有新增的 500 行代码。修复后,当月账单立降 280%。
提示:Sub2API 默认只保留 7 天审计日志。如需长期留存,可在
docker-compose.yml中挂载一个外部卷,并配置LOG_RETENTION_DAYS=30环境变量。日志格式是标准 JSONL,可直接导入 ELK 或 Grafana Loki 做长期分析。
5. 高阶运维:当 Sub2API 成为团队 AI 基建的一部分
当 Sub2API 从“能用”走向“好用”,它就开始承担更多基础设施职责。这部分内容,是我在给中大型团队做架构评审时,反复强调的“必须提前规划项”。
5.1 高可用(HA)部署:告别单点故障
Docker Compose 是绝佳的起步方案,但它天生是单机架构。当你的团队超过 50 人,或 Sub2API 已成为 CI/CD、内部 Chatbot、BI 报表的底层依赖时,单点故障的风险就不可接受。升级到 HA 的核心思路是:分离状态(State)与计算(Compute)。
改造方案:
- 数据库:将
db服务替换为托管的 PostgreSQL(如 AWS RDS 或腾讯云 CynosDB),启用 Multi-AZ; - Redis:替换为集群版 Redis(如 AWS ElastiCache Cluster Mode),提供自动分片和故障转移;
- API 网关:将
api服务改为无状态,水平扩展。在docker-compose.yml中删除restart: unless-stopped,改为:api: # ... 其他配置不变 deploy: replicas: 3 update_config: parallelism: 1 delay: 10s restart_policy: condition: on-failure - 负载均衡:在 Nginx 前增加一层 L4 负载均衡(如 AWS ALB 或腾讯云 CLB),将
443流量分发到多个 Nginx 实例。
关键验证点:HA 的最大挑战不是部署,而是会话一致性。Sub2API 的 JWT 是无状态的,但它的 token exchange 缓存(存在 Redis 中)必须是全局共享的。因此,redis服务必须是集群模式,且所有api实例连接同一个 Redis endpoint。我们曾在一个项目中因 Redis 配置错误,导致部分api实例连到旧节点,出现invalid_grant错误——新生成的 OpenAI Token 无法被旧节点识别。
5.2 与现有 IAM 系统集成:告别 GitHub 账号绑定
很多企业已有成熟的 Identity Provider(IdP),如 Okta、Azure AD 或 JumpCloud。强制员工用 GitHub 登录,既不符合安全策略,也增加管理负担。Sub2API 支持 SAML 2.0 和 OIDC(OpenID Connect)作为替代认证源。
以 Azure AD 为例的集成步骤:
- 在 Azure Portal 的
Enterprise Applications中,创建一个新的Non-gallery application,名称为Sub2API; - 在
Single sign-on设置中,选择SAML,下载 Federation Metadata XML; - 在 Sub2API 的
Admin Dashboard > Settings > Authentication中,上传该 XML,并启用SAML SSO; - 将
api服务的环境变量更新为:environment: AUTH_PROVIDER: saml SAML_METADATA_URL: https://login.microsoftonline.com/xxx/federationmetadata/2007-06/federationmetadata.xml SAML_ENTITY_ID: https://sub2api.example.com/saml/metadata
效果:用户访问https://sub2api.example.com时,会自动重定向到 Azure AD 登录页。登录成功后,Azure AD 会返回一个 SAML Assertion,Sub2API 解析其中的NameID(通常是员工邮箱)作为用户唯一标识,并自动创建账户。整个过程,用户无需知道 GitHub 是什么。
经验:OIDC 集成比 SAML 更轻量,但要求 IdP 支持。我们优先推荐 OIDC,因为它的调试日志更友好(JSON 格式),且 Sub2API 对 OIDC 的错误提示(如
invalid_id_token)比 SAML 的invalid_signature更易定位。
5.3 自定义模型路由:不只是 OpenAI,更是你的 AI 混合云
Sub2API 的设计初衷是“统一 API 网关”,而非“OpenAI 专用代理”。它的路由引擎支持model字段的正则匹配和重写。这意味着,你可以把gpt-4-turbo的请求,动态路由到 Azure OpenAI Service;把claude-3-haiku的请求,路由到 Anthropic;甚至把llama-3-70b的请求,路由到你自建的 Ollama 服务。
配置示例(在Admin Dashboard > Settings > Routing Rules中添加):
| Pattern (Regex) | Target Base URL | Headers |
|---|---|---|
^gpt-.*$ | https://YOUR-AZURE-OPENAI-REGION.api.cognitive.microsoft.com/openai/deployments/YOUR-DEPLOYMENT-NAME | api-key: ${AZURE_OPENAI_KEY} |
^claude-.*$ | https://api.anthropic.com/v1 | x-api-key: ${ANTHROPIC_KEY},anthropic-version: 2023-06-01 |
^llama-.*$ | http://ollama:11434/api | Content-Type: application/json |
原理:当请求POST /v1/chat/completions的 body 中model字段匹配^gpt-.*$时,Sub2API 会:
- 将原请求的
model字段从gpt-4-turbo改为YOUR-DEPLOYMENT-NAME(Azure 要求); - 将
Authorization: Bearer xxx替换为api-key: ${AZURE_OPENAI_KEY}; - 将
https://api.openai.com/v1/chat/completions的 path 重写为/chat/completions; - 最终代理到
https://YOUR-AZURE-OPENAI-REGION.api.cognitive.microsoft.com/openai/deployments/YOUR-DEPLOYMENT-NAME/chat/completions。
提示:这种混合路由不是理论,而是我们客户的生产实践。一家金融公司在合规要求下,必须将所有 PII(个人身份信息)数据保留在境内。他们用 Sub2API 实现了:
- 对外 API:
gpt-4-turbo→ Azure OpenAI(中国北部);- 对内 API:
gpt-4-turbo→ 自建 Ollama(LLaMA-3);- 审计 API:
gpt-4-turbo→ OpenAI(仅用于模型对比,数据脱敏)。 一套代码,三套路由,完美满足监管、成本、体验三重目标。
6. 踩坑实录:那些在 GitHub Issues 里高频出现的“血泪教训”
Sub2API 的 GitHub Issues 页面,是比官方文档更真实的“生存指南”。我把过去半年里 Top 10 的报错,按发生频率和解决难度做了分级,并附上我的现场排错笔记。
6.1 高频坑:the login to github failed. reason: oauth code flow error: request signinwith
现象:用户点击 Login with GitHub,跳转到 GitHub 授权页后,点击 “Authorize”,页面空白或报错,控制台显示signinwith相关错误。
根因分析:这几乎 100% 是GitHub OAuth App 的Callback URL配置错误。常见错误有:
Callback URL填成了http://localhost:3000/callback,但你的 Sub2API 实际运行在https://sub2api.example.com;Callback URL末尾多了/,如https://sub2api.example.com/callback/(多了一个斜杠);Callback URL用了 IP 地址(如https://192.168.1.100/callback),而 GitHub 要求必须是域名。
排错链路:
- 打开浏览器 DevTools,切换到
Network标签; - 重现登录流程,找到
callback请求(状态码 302); - 点击该请求,查看
Response Headers中的Location字段; - 如果
Location是https://github.com/login/oauth/authorize?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.,那就坐实了是 URL 不匹配。
解决方案:进入 GitHub OAuth App 设置页,将Authorization callback URL精确修改为 Sub2API 的BASE_URL+/callback,例如https://sub2api.example.com/callback。保存后,清除浏览器 Cookie,重试。
6.2 致命坑:please run /login 路 api error: 401 authentication fails, your api key: ****
现象:前端调用/v1/chat/completions时,返回401,且响应体中赫然写着your api key: sk-xxx...—— 这说明你的sk-xxx被意外泄露了!
**根因分析