SAPI 提供管理端、用户端和 OpenAI/Anthropic/Responses/Gemini 兼容 API 转发。
相关文档:
npm install
npm run build
cd backend
go run .默认地址:
- 首页:
http://localhost:3000 - 登录:
http://localhost:3000/#login - 用户注册:
http://localhost:3000/#register - 用户控制台:
http://localhost:3000/#portal - 管理后台:
http://localhost:3000/#admin - Swagger:
http://localhost:3000/swagger
默认账号:
username: admin
password: sapi-admin
生产环境在 .env 中修改:
SAPI_ADMIN_USER=admin
SAPI_ADMIN_PASSWORD=change-this-password
SAPI_PUBLIC_BASE_URL=https://sapi.example.com登录接口:
curl http://localhost:3000/api/admin/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"change-this-password"}'响应包含 token。管理端 API 使用:
Authorization: Bearer <admin-jwt>
管理员可在管理后台注册 Passkey,之后登录页可用系统 Passkey 登录。
要求:
- 必须使用 HTTPS 域名,或本地
localhost。 SAPI_PUBLIC_BASE_URL必须配置为真实访问域名。- 首次注册 Passkey 需要先用管理员密码登录。
Passkey 登录接口:
curl http://localhost:3000/api/admin/passkeys/login/options \
-H "Content-Type: application/json" \
-d '{}'前端会调用浏览器 WebAuthn API 完成 finish,普通 curl 不能直接模拟完整 Passkey 登录。
在管理后台配置 Provider:
- 名称
- Base URL
- API Key
- 启用状态
- 上游格式
- 模型列表
- 模型映射
- 优先级
- 故障阈值
Base URL 必须是 http 或 https,不能包含用户信息、空字节、回车或换行。
获取上游模型:
curl http://localhost:3000/api/admin/providers/models \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"baseUrl":"https://api.openai.com/v1","apiKey":"sk-..."}'Provider 的 upstreamFormat 支持:
| 值 | 行为 |
|---|---|
auto |
根据 Provider 名称和 Base URL 推断 OpenAI、Anthropic 或 Gemini。 |
openai |
强制 OpenAI 兼容格式。 |
anthropic |
强制把 OpenAI Chat 转为 Anthropic Messages。 |
gemini |
强制把 OpenAI Chat 转为 Gemini generateContent。 |
当上游模型实际可用,但应用层因为请求体格式报错时,优先在 Provider 里手动选择 anthropic 或 gemini,不要只依赖 auto 推断。
用户注册入口:
http://localhost:3000/#register
注册要求:
- 用户名 3 到 64 位,只能包含字母、数字、点、下划线、短横线。
- 密码至少 8 位。
- 邮箱验证码 6 位。
- 非
.edu.cn邮箱需要邀请码。
用户登录入口:
http://localhost:3000/#login
如果管理员配置了 GitHub OAuth,登录页会显示 GitHub 登录入口。首次通过 GitHub 登录会自动创建用户账号。若配置 SAPI_GITHUB_REQUIRED_FOLLOW_TARGET=EterUltimate,首次 GitHub 注册或绑定账号必须关注目标 GitHub 用户;已绑定用户可继续登录。
用户控制台接口使用:
Authorization: Bearer <user-jwt>
普通邮箱新用户默认订阅分组为 email,默认 1 RPM。邀请码和 GitHub 注册默认 lite,实际 10 RPM;教育邮箱注册默认 base,实际 30 RPM。
订阅分组:
| 分组 | 默认 RPM | 默认价格 | 说明 |
|---|---|---|---|
email |
1 | 免费 | 普通邮箱默认分组。 |
lite |
10 | 免费 | 轻量体验分组。 |
base |
30 | 9.90 CNY | 日常使用。 |
pro |
50 | 29.90 CNY | 高频调用。 |
ultra |
100 | 69.90 CNY | 大额度调用。 |
MAX |
不限速 | 免费 | 管理员授予的最高分组。 |
规则:
- 管理员 API Key 不限速。
- 用户 API Key 默认跟随用户订阅。
- 管理员可修改套餐 RPM、价格、入账额度、时长和启用状态。
- 管理员可给单个用户切换订阅。
- 管理员可一键切换所有用户订阅。
- 管理员可一键恢复默认订阅: 普通邮箱回
email,.edu.cn回base,GitHub 绑定用户回lite。 - 单个 API Key 可设置更低 RPM 作为额外限制。
- API Key 的显式 RPM 不能超过用户订阅 RPM。
- 用户前台
计费套餐会显示账户余额、近 365 天额度消耗、当前套餐 RPM 和最近订单。
全局切换所有用户到 base:
curl -X PUT http://localhost:3000/api/admin/subscriptions/global-tier \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"subscriptionTier":"base"}'恢复所有用户默认订阅:
curl -X PUT http://localhost:3000/api/admin/subscriptions/global-tier \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"restoreDefaults":true}'切换单个用户到 pro:
curl -X PUT http://localhost:3000/api/admin/users/usr_id \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"subscriptionTier":"pro"}'管理后台 总设置 包含三块计费设置:
订阅套餐: 修改每个套餐的 RPM、价格、入账额度、有效天数和启用状态。计费与模型价格: 从https://models.dev/api.json同步模型价格,也可手动覆盖单个模型价格。易支付: 配置 Submit 提交 URL、MAPI 提交 URL、商户 ID、商户密钥、软件通讯密钥、回调 URL 和支付方式。
额度消耗按请求日志里的 token 用量计算:
成本(CNY) = models.dev USD/1M token 价格 * token 数 / 1_000_000 * USD/CNY 汇率 * 加价倍率
回调 URL:
https://<domain>/api/payments/ezfpy/notify
https://<domain>/api/payments/ezfpy/return
用户购买付费套餐后,支付成功回调会把订单置为 paid,更新用户订阅分组、套餐到期时间,并把套餐额度加入账户余额。管理员虚拟用户拥有用户端全部功能且不限 RPM,不需要购买套餐。
用户控制台创建自己的 API Key,管理端也可以创建和管理 API Key。
API Key 调用转发接口时使用任一 Header:
Authorization: Bearer sk-sapi-...
X-API-Key: sk-sapi-...
用户创建 API Key:
curl http://localhost:3000/api/user/api-key \
-H "Authorization: Bearer <user-jwt>" \
-H "Content-Type: application/json" \
-d '{"name":"default","allowedModels":["gpt-4o-mini"]}'管理员可在管理后台一键封禁或解封用户 API Key、管理员 API Key。
封禁用户 Key:
curl -X PUT http://localhost:3000/api/admin/users/usr_id/api-keys/key_id \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"banned":true}'解封用户 Key:
curl -X PUT http://localhost:3000/api/admin/users/usr_id/api-keys/key_id \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"banned":false}'自动封禁:
- 同一 API Key 在 1 小时内超过 20 次提交不合规代理请求体,会自动封禁 1 小时。
- 封禁期间返回
429 api_key_banned。 - 响应包含
Retry-After。
curl http://localhost:3000/v1/models \
-H "Authorization: Bearer sk-sapi-..."也可调用:
curl http://localhost:3000/models \
-H "Authorization: Bearer sk-sapi-..."返回模型受 Provider 启用状态、模型映射、API Key 模型白名单影响。
查询单个模型:
curl http://localhost:3000/v1/models/gpt-4o-mini \
-H "Authorization: Bearer sk-sapi-..."模型 ID 如果包含 /,调用方应按 URL path 规则编码。
公开接口:
curl http://localhost:3000/api/health/models特性:
- 展示模型当前可用性。
- 服务端数据 TTL 为 5 分钟。
- 管理后台 Provider 变更后会刷新模型可用性。
- 可用于判断上游是否因为故障阈值、禁用状态或模型映射导致不可用。
全局 usage:
curl "http://localhost:3000/api/admin/usage?days=30" \
-H "Authorization: Bearer <admin-jwt>"用户 usage:
curl "http://localhost:3000/api/admin/users/usr_id/usage?days=365" \
-H "Authorization: Bearer <admin-jwt>"导出用户请求日志:
curl "http://localhost:3000/api/admin/users/usr_id/request-logs/export?days=7" \
-H "Authorization: Bearer <admin-jwt>" \
-o user-request-logs.tar.gz导出全局请求日志:
curl "http://localhost:3000/api/admin/request-logs/export?days=7&includeContent=true" \
-H "Authorization: Bearer <admin-jwt>" \
-o request-logs.tar.gz管理后台首页和 请求与用量 页提供 审计导出 入口,可分别下载详细请求体或 IP/设备信息。
导出文件是 tar.gz,包含:
metadata.jsonrequest-logs.jsonl
管理后台热力图基于 usage 中的按小时和按天聚合数据生成。
管理后台 服务器中控 调用:
curl http://localhost:3000/api/admin/server-status \
-H "Authorization: Bearer <admin-jwt>"返回:
fastfetch.availablefastfetch.modulesgoVersiongoroutinesmemorystore
服务端安装 fastfetch 时展示主机、CPU、内存、磁盘等模块;未安装时仍返回 Go runtime 和 store health。前端可设置自动刷新频率。
用户控制台首页提供 Base URL 到 /api/health 的浏览器侧测速。结果包含最佳、平均和最近状态。测速只发起健康检查,不携带 API Key。
用户控制台:
站内 Chat: Responses/OpenAI 兼容聊天,支持附件、Markdown/HTML 渲染和下载。生图工坊: Images API 和 Responses 图像工具调用。
API 来源:
SAPI Key: 使用当前站点 API Key。自有 API: 填写自有 OpenAI 兼容 Base URL 和 API Key。
模型列表:
- 通过当前来源的
/v1/models动态获取。 - SAPI 来源获取失败时回退到站点公开模型配置。
- 自有 API Key 只在当前 WebSocket 请求中使用,不写入 SAPI 状态。
AstrBot 使用 OpenAI 兼容提供商接入 SAPI。
在 AstrBot 管理面板进入 服务提供商,新增提供商时选择 OpenAI:
- API Base URL:
https://sapi.eterultimate.asia/v1 - API Key: 用户控制台或管理员创建的
sk-sapi-... - 模型: 点击获取模型列表后选择需要启用的模型
本地开发环境可把 API Base URL 改为:
http://localhost:3000/v1
不要把 API Base URL 填成 /v1/chat/completions。AstrBot 会自己拼接 OpenAI Chat Completions 路径,并会通过 GET /v1/models 获取模型列表。SAPI 同时兼容 GET /v1/models/{model},用于 OpenAI SDK 或 AstrBot 侧的单模型探测。
curl http://localhost:3000/v1/chat/completions \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'根路径兼容:
curl http://localhost:3000/chat/completions \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'curl http://localhost:3000/v1/completions \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-3.5-turbo-instruct","prompt":"hello"}'curl http://localhost:3000/v1/embeddings \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-d '{"model":"text-embedding-3-small","input":"hello"}'curl http://localhost:3000/responses \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","input":"hello"}'兼容路径:
curl http://localhost:3000/v1/responses \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","input":"hello"}'curl http://localhost:3000/v1/messages \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-3-5-sonnet-latest","max_tokens":128,"messages":[{"role":"user","content":"hello"}]}'根路径兼容:
curl http://localhost:3000/messages \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-3-5-sonnet-latest","max_tokens":128,"messages":[{"role":"user","content":"hello"}]}'curl http://localhost:3000/v1/messages/count_tokens \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-3-5-sonnet-latest","messages":[{"role":"user","content":"hello"}]}'根路径兼容:
curl http://localhost:3000/messages/count_tokens \
-H "Authorization: Bearer sk-sapi-..." \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-3-5-sonnet-latest","messages":[{"role":"user","content":"hello"}]}'curl http://localhost:3000/api/health
curl http://localhost:3000/api/ready
curl http://localhost:3000/api/public/config
curl http://localhost:3000/api/maintenance
curl http://localhost:3000/api/banner
curl http://localhost:3000/api/announcements
curl http://localhost:3000/api/health/providers
curl http://localhost:3000/api/health/models校验 API Key 并返回服务配置:
curl http://localhost:3000/api/public/key \
-H "Authorization: Bearer sk-sapi-..."提交建议:
curl http://localhost:3000/api/suggestions \
-H "Content-Type: application/json" \
-d '{"title":"问题标题","content":"问题内容"}'模型转发请求会把用户提交的请求 JSON 内容写入请求日志,保留 7 天:
- JSON 存储模式: 主状态只存摘要,完整内容写入
*.request-logs.jsonl。 - PostgreSQL 存储模式: 写入
sapi_request_logs.request_contentJSONB 字段。 - 响应正文不会被持久化保存。
用户控制台不会显示 IP、设备信息或请求 JSON。管理后台可查看完整请求 JSON,并可导出 tar.gz。7 天前日志会先归档到 request-log-archives/*.tar.gz 再清理。
统一错误响应:
{"error":{"message":"Invalid or disabled SAPI API key.","type":"invalid_api_key","code":"invalid_api_key"}}常见状态:
| 状态码 | code | 场景 |
|---|---|---|
400 |
invalid_json |
请求体不是单个 JSON 对象。 |
400 |
invalid_model |
模型字段不合法。 |
401 |
missing_api_key |
转发接口缺少 API Key。 |
401 |
invalid_api_key |
API Key 不存在、禁用或无效。 |
401 |
unauthorized |
控制面 JWT 缺失或无效。 |
403 |
user_disabled |
用户被禁用。 |
403 |
model_not_allowed |
API Key 不允许调用该模型。 |
413 |
request_too_large |
请求体超过配置限制。 |
429 |
login_rate_limited |
登录失败次数过多。 |
429 |
api_key_rate_limited |
API Key 失败尝试过多。 |
429 |
api_key_banned |
API Key 被手动或自动封禁。 |
429 |
rate_limit_exceeded |
API Key RPM 超限。 |
503 |
maintenance_mode |
站点维护中。 |
503 |
no_provider |
没有可用上游 Provider。 |
502 |
upstream_request_failed |
上游请求失败。 |