用户 API 密钥与外部接口
I10 已提供默认关闭的个人 API 密钥模块。用户在设置页创建、一次性查看、改名和撤销密钥,外部脚本可调用实际文件与异步任务接口。生产 Worker、独立本地 PostgreSQL、Queue / R2 和浏览器已验收;真实云端部署、第三方调用与负载仍待独立验证。它不是站主配置的模型、邮件、支付或数据库密钥。
开启与数据库升级
packages/core/website.ts 的公开构建配置:
apiKeys: {
enabled: false,
maxActiveKeys: 10,
requestsPerMinute: 120,
maxExpirationDays: 365,
},| 字段 | 默认 | 范围与行为 |
|---|---|---|
enabled | false | 关闭时不显示设置卡片,所有已定义密钥管理 / 外部接口返回 404;不删除历史密钥 |
maxActiveKeys | 10 | 1—50;每人同时启用且未过期的密钥数量,创建时检查 |
requestsPerMinute | 120 | 1—1000;新密钥的插件请求窗口上限,签发时保存;改变配置不重写旧密钥 |
maxExpirationDays | 365 | 1—365;新密钥最长有效期,不改变旧密钥到期时间 |
密钥属于个人账号,不读取当前团队的套餐或成员身份。至少使用已验证邮箱、非匿名、未封禁账号;改变密钥要求实际存在且创建不足 24 小时的登录会话。不会根据注册顺序或团队角色授予管理权。
新增 0013_big_thunderbolts.sql:apikey 使用安装的 @better-auth/api-key@1.7.4 数据库字段及哈希,api_key_issuance 保存签发请求的 UUID、参数摘要和密钥 ID。后者不保存明文,也不级联到密钥记录:插件清理已过期密钥后,旧创建请求仍不能重新签发。账号删除会清理其密钥及签发记录。ID 分别使用 key_ / kis_ 加 16 位 CUID2。
按增量迁移教程核对目标数据库并升级历史,再一起重建前后端。不开外部 API 的站点可保持关闭。本任务主预览仍只有 0012,0013 只在独立一次性数据库验收,尚未应用到主数据库;主预览保持关闭,不会查询新表。维护命令核对全部迁移历史,升级代码后在迁移未齐的数据库会拒绝初始化管理员,不能跳过检查。
本模块没有新增 .env 密钥或云资源。文件操作需要原有 STORAGE;提交 / 重试任务需要原有 TASK_QUEUE 和任务开关。开启 security.turnstile.enabled 后,API 的上传、提交、重试也需要相应动作的 X-Turnstile-Token;密钥不绕过验证码,自动化脚本的使用方式需要先考虑这个配置。
设置页流程
- 进入
/settings,刷新密钥列表。列表每页 20 条,仅显示名称、短前缀、权限、状态和到期时间。 - 填写名称、有效天数和实际需要的权限,先复核再确认。取消复核不写入。
- 创建成功后立即保存明文;关闭展示或离开页面后不能再次查看。它只保留在当前组件内存,不进入查询 / 变更缓存或本地存储。
- 可以改名和撤销;不提供重新启用、续期或增加权限。需要新权限时签发新密钥,并撤销旧密钥。
创建 UUID 与签发在同一事务提交,同一账号 / UUID / 参数只签发一次;同 UUID 换参数返回 409。若服务端已成功但响应丢失,再发同一请求只返回 { id, key: null, replayed: true },不重发明文,不额外创建密钥。先刷新列表,未保存时撤销对应密钥、以新 UUID 再创建。页面保留原确认用于手动重试,没有自动重试。
关闭展示清除的是页面可见状态,不承诺擦除浏览器开发工具、历史网络响应或已复制到其他位置的凭据。数据库只保存插件的不可恢复哈希;管理列表、审计、邮件和正常应用日志不输出完整密钥。站主也不能从数据库取回明文。
鉴权与作用域
外部接口固定在 /api/v1,仅接受 Authorization: Bearer <用户密钥>。密钥前缀为 abk_;不接收 URL 参数中的密钥,不回退到 Cookie,也不创建模拟会话。密钥不能登录后台、购买 / 修改订阅、修改账号或管理其他密钥。
| 权限 | 允许的接口 |
|---|---|
files:read | 文件列表、单条元数据、私有文件内容下载 |
files:write | 上传和删除自己的文件 |
tasks:read | 任务列表、报价 / 余额、单条任务状态 |
tasks:write | 提交、取消和有版本条件的重试 |
每次读取实际密钥、当前账号和作用域。过期、撤销、邮箱不再验证、匿名或封禁会拒绝后续请求;已接受的在途请求及任务仍按原执行 / 扣费 / 退款规则完成,不宣称撤销能中断全部已接受工作。每把密钥有插件请求限制;所有密钥与浏览器进一步共用原有每账号文件 / 任务限制、容量、配额和余额,增加密钥数量不能扩大这些业务额度。
文件接口
| 方法与路径 | 结果 |
|---|---|
GET /api/v1/files | { items, nextCursor };下一页用 cursor 传 JSON 编码的服务端游标 |
GET /api/v1/files/:id | 自己文件的元数据 |
GET /api/v1/files/:id/content | 实际文件字节,附件下载、禁止缓存;结果文件同样走此接口 |
POST /api/v1/files | 原始二进制正文,Content-Type 声明类型,X-File-Name 为编码后的文件名;201 返回文件元数据 |
DELETE /api/v1/files/:id | 原删除规则;任务仍保留的输入不能强删 |
上传继续验证实际字节、类型、大小及个人配额,不提供公开 R2 地址。上传没有请求 UUID 去重;响应不确定先查询文件列表,直接重发可能生成第二个文件。删除不会绕过任务输入保留或结果结算。
下面是调用示例,环境变量由调用者在自己的终端设置;不要把真实密钥提交进代码、教程或共享日志。
curl "$SITE_ORIGIN/api/v1/files" \
-H "Authorization: Bearer $USER_API_KEY" \
-H 'Content-Type: text/plain' \
-H 'X-File-Name: input.txt' \
--data-binary @input.txt报价、任务和重试
| 方法与路径 | 结果 |
|---|---|
GET /api/v1/tasks/quote | 原 text-normalize 报价、版本、个人余额与支付审核限制;按原规则首次初始化欢迎积分 |
GET /api/v1/tasks | 个人任务分页列表 |
GET /api/v1/tasks/:id | 个人任务、结果文件 ID 和当前 revision |
POST /api/v1/tasks | 相同原任务参数、稳定 requestKey UUID 和已确认报价;201 表示提交已记录,不代表执行成功 |
POST /api/v1/tasks/:id/cancel | 原取消规则;重复取消不会重复退款,运行中可能只是取消请求已记录 |
POST /api/v1/tasks/:id/retry | 必须携带 If-Match: "<revision>",复用原尝试预算和预扣,不重复扣费 |
提交正文示例;文件 ID、UUID 和报价版本必须来自当前站点:
{
"requestKey": "替换为本次稳定UUID",
"inputFileId": "替换为上传返回的fil_ID",
"processorId": "text-normalize",
"processorVersion": 1,
"parameters": { "trimTrailingWhitespace": true },
"acceptedQuote": { "credits": 0, "version": "从报价接口读取" }
}同账号相同 requestKey 与输入,API 与浏览器共用一条任务和一笔原账本预扣;改变内容沿用 UUID 返回 REQUEST_CONFLICT。改价后旧报价返回 QUOTE_CHANGED,必须重新复核。实际执行、Queue 重发、R2 结果、取消返还与故障核对继续使用原任务生命周期,不另建收费路径。
重试前读单条任务的 revision,按 HTTP 引号格式发送 If-Match: "2"。服务在原任务事务锁内比较版本;重试或其他派发改变版本后,旧请求返回 STALE_REVISION,不能在下一次失败后重放旧请求再多执行一次。响应不确定先读取任务当前状态;重新重试是一次新的明确操作,重新读取版本,不能把 409 当成可自动无限重试。重试仍可能因原尝试预算、退款、失效文件、容量或支付审核而拒绝。
错误与排查
外部接口错误形如 { "error": { "code": "…", "message": "…" } };浏览器密钥管理保留认证客户端的 { code, message } 格式。
| 状态 | 处理 |
|---|---|
| 400 / 413 | 检查严格输入、大小、文件名及重试版本头;不修改后盲目重发 |
| 401 | 缺少、无效、过期、撤销或作用域不足;密钥不能当浏览器会话 |
| 403 | 当前账号不可用;管理变更可能要求重新登录或正确 Origin |
| 404 | 模块关闭、未开放路径,或资源不属于当前账号 / 不存在 |
| 409 | UUID 内容冲突、任务版本 / 报价变化、容量 / 积分 / 状态限制;先读现状 |
| 429 | 密钥或账号额度用尽;参考 Retry-After: 60,它不是精确的剩余窗口承诺 |
| 503 | 所需资源 / 验证未配置或操作未确认;读取现状后手动恢复,不能当作未写入 |
密钥管理每账号每分钟最多 30 次,列表和创建重放也计入。SESSION_NOT_FRESH 的确认流程会先实际退出,再重新登录;退出失败不清空当前会话。上线前让调用方明确提交 UUID、上传响应丢失和重试版本规则。
本地验收与维护
bun keys:validate
bun run test -- --run
bun typecheck
bun lint
bun db:check
bun config:checkkeys:validate 只接受 development 和 loopback PostgreSQL,创建 api_key_acceptance_… 一次性数据库,应用全部迁移,并只在独立 Worker bundle 中开启密钥及三积分任务。使用实际生产入口、真实 Queue / R2、真实插件及 PostgreSQL;外部 HTTP 被拒绝,邮件进入独立本地收件箱。8 并发签发只有一把明文、其余返回已处理;文件字节、单账本扣费、浏览器服务重放、结果下载、取消退款、版本重试、并发限流、作用域、过期、封禁和撤销都有断言。测试结束销毁临时数据库 / Worker / 资源,不升级当前配置数据库或改动源配置。
bun keys:validate --ui 额外构建独立工作台并暂停供浏览器检查,使用脚本中的一次性测试账号,不涉及真实账号或支付。验收创建 / 撤销后创建 .local/api-key-ui-stop 文件,让脚本核对落库状态并清理;暂停最多 12 分钟。不要将此验收服务器作为长期预览或生产部署。
实现契约参考当前安装包与插件官方文档。插件升级时重新核对哈希、字段、默认端点和限流行为;不能直接开放其全部管理操作或启用 API 密钥模拟会话。