总体架构

OpenProxy 采用三层结构:

层级组件技术栈职责
代理层apps/apiRust + Axum请求转发、鉴权、用量写库
业务层apps/serverBun + Elysia用户、密钥、模型、供应商、计费
前端层apps/webReact + Vite租户仪表盘 + 管理后台

apps/apiapps/server 共用同一个 PostgreSQL 数据库。代理层在每次请求时直接从数据库读取供应商/模型/密钥配置,结合进程内缓存加速解密。

请求流程

Client

apps/api  (Rust, :5060)
  ├─ 1. 鉴权中间件
  │      校验 Bearer Token 是否存在于 ai_api_keys 表
  │      检查额度、请求次数、过期时间、模型访问权限
  ├─ 2. 供应商选择
  │      查询该模型绑定的 ai_providers + ai_provider_api_keys
  │      按权重随机排序(weight 字段)
  ├─ 3. Key 轮换
  │      读取近期使用缓存:该 API Key 最近 N 次命中的(供应商, Key)组合
  │      N = min(10, 供应商×Key 组合总数)
  │      未近期使用的组合排在前面,近期使用的排在后面作为兜底
  ├─ 4. 上游转发
  │      按序尝试每个(供应商, Key)组合
  │      连接错误或非 2xx → 记录警告,尝试下一个组合
  │      成功 → 将本次命中的组合写入近期使用缓存
  └─ 5. 用量回写
         向数据库写入 tokens(prompt + completion)、费用、延迟、provider_id

供应商选择 — 权重随机

每条 ai_provider 记录有一个整型 weight 字段。请求到达时,所有绑定该模型的供应商按权重随机打乱顺序,权重越高的供应商被选中的概率越大,同时保留一定随机性以避免完全集中到单一供应商。

多 Key 轮换

每个供应商可在 ai_provider_api_keys 中配置多个 API Key。轮换逻辑以用户 API Key(api_key_id)为粒度运行:

  1. 统计当前请求中该模型可用的 (供应商, api_key) 组合总数。
  2. window = min(10, 组合总数)
  3. 维护一个每用户的进程内环形缓冲区(容量 window),记录最近 window 次成功请求命中的 (provider_id, api_key_hash) 对。
  4. 每次新请求时,将全部组合拆分为:
    • 非近期:未在环形缓冲区中 → 优先尝试
    • 近期:已在环形缓冲区中 → 作为兜底(所有非近期组合失败后才尝试)
  5. 成功响应后,将本次命中的 (provider_id, api_key_hash) 推入环形缓冲区(超出容量时丢弃最旧的记录)。

这一机制确保同一用户的连续请求尽量分散到不同 Key 和供应商,降低单 Key 触发限速的概率,且不需要额外的数据库操作。

供应商 API Key 缓存

供应商 API Key 以 RSA 加密形式存储在数据库中,运行时通过 tokio::task::spawn_blocking 异步解密。解密结果缓存在进程全局内存中:

  • 数据结构OnceLock<RwLock<HashMap<加密密文, 解密明文>>>
  • TTL:1 小时(整体清除,到期后所有条目一次性失效)
  • 缓存键:数据库中的原始加密密文字符串

第一次请求(或缓存过期后首次请求)需要进行 RSA 解密;TTL 范围内后续请求直接读取内存。

故障切换行为

所有 (供应商, Key) 组合按轮换顺序依次尝试:

  • 连接错误(网络不通、超时):记录警告,尝试下一个组合
  • HTTP 非 2xx(如 429 限速、500 内部错误):记录警告,尝试下一个组合
  • 最后一个组合失败:将上游的错误响应(或连接失败时的 502 Bad Gateway)原样返回给客户端

对调用方完全透明,单次请求可能在多个上游之间回退,最终要么成功返回,要么返回最后一次的错误。

数据模型概览

ai_models                  — 注册的模型(名称、定价、是否公开)
  └─ ai_providers          — 模型对应的后端(base_url、weight)
       └─ ai_provider_api_keys  — 每个供应商的 API Key(加密存储)

ai_api_keys                — 用户持有的 API Key(额度、次数、过期)
  └─ ai_api_key_models     — 每个 Key 的模型级访问控制

usage_logs                 — 每次请求的 tokens、费用、延迟、供应商
orders / teams / users     — 计费与多租户

认证体系

用户侧认证(登录、OAuth、会话管理)完全由 apps/server 中的 better-auth 负责。支持方式:

  • 邮箱 + 密码
  • 魔法链接(邮件)
  • 手机 OTP
  • GitHub OAuth
  • Google OAuth

调用 apps/api 时使用的 Bearer Token 是独立的长效 API Key(存储于 ai_api_keys),与用户会话 Token 无关。

共享包

用途
packages/schema前后端共享的 Zod/TypeBox 校验 Schema
packages/payment-provider支付网关抽象(支持 ZPay stub 与正式版)
packages/phone-auth手机 OTP 供应商抽象
packages/ui共享 React UI 组件