Files
new-api-key-usage/README.md
T
skycurtain 0e9db439ba docs: 收尾(阶段 8)——部署文档 + 端到端走查 + 文档状态
- docs/delivery/部署文档.md:前置条件 / 环境变量 / 一条命令链 / systemd 与 Nginx 示例 / 首次运行检查 / 运行约束(后端单实例 · 3 天窗口 · 主密钥 · 401 摘除)/ 升级 / 排查
- 从零端到端走查:清库 → db:push → 启动(空转 + QPU 基线)→ 管理面登记 → 引擎立即补一轮(984 条)→ 查看面查询(未折算「—」)→ 补录购买记录 → 金额出现 → 换算参数时间线;无 pageerror
- 8 篇设计文档状态 → 已实现(2026-09-15)
- README:状态行 / 文档地图(含 delivery)/ 下一步(部署 + 待补验与裁决清单)
- AGENTS.md:项目速览更新
2026-09-15 01:10:55 +08:00

105 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# new-api-key-usage
> 针对 NewAPI 平台的 key 用量统计工具
定时用每个 sk-key 轮询网关日志接口,按 `request_id` 去重,攒全量日志到本地数据库;任意时间范围的用量查询,即一条本地聚合查询。
| 项目 | 内容 |
|------|------|
| 状态 | **已实现(2026-09-15,阶段 0–8 全部完成)**——前后端功能齐备、实测走查通过;真实上游联调待作者补验(见实施计划 §6) |
| 技术栈 | Bun + Hono + PostgreSQL · 前端 Vite + Vue 3 + naive-ui |
| 部署 | 实例运营方提供的服务器;交付方式为推送仓库 / 产物,由运营方执行部署 |
## 文档地图
| 文档 | 什么时候读 | 状态 |
|------|-----------|------|
| [`docs/design/api_keys表字段设计.md`](docs/design/api_keys表字段设计.md) | 数据模型第一张表:字段、语义与决策理由 | 已实现(2026-09-15;定稿 2026-09-11,09-13 修订 key 登记与表名、新增 `disabled_source`) |
| [`docs/design/usage_logs表字段设计.md`](docs/design/usage_logs表字段设计.md) | 数据模型第二张表:日志明细、去重与写入策略 | 已实现(2026-09-15;定稿 2026-09-11) |
| [`docs/design/purchases表字段设计.md`](docs/design/purchases表字段设计.md) | 数据模型第三张表:购买记录(实付人民币的唯一落点、折算链第二条) | 已实现(2026-09-15;定稿 2026-09-13) |
| [`docs/design/qpu_history表字段设计.md`](docs/design/qpu_history表字段设计.md) | 数据模型第四张表:换算参数时间线(自动采样、只读) | 已实现(2026-09-15;定稿 2026-09-13) |
| [`docs/design/交互设计.md`](docs/design/交互设计.md) | 界面设计:方向层 + 细节层(跨页共性、逐页细节、状态与文案) | 已实现(2026-09-15;定稿 2026-09-13) |
| [`docs/design/API契约.md`](docs/design/API契约.md) | 前端 ↔ 服务端接口契约:约定层 + 逐页端点(前端实现看这篇) | 已实现(2026-09-15;定稿 2026-09-13) |
| [`docs/design/同步引擎设计.md`](docs/design/同步引擎设计.md) | 采集侧引擎:调度、每轮动作、失败处理、可观测、部署初始化(后端实现看这篇) | 已实现(2026-09-15;定稿 2026-09-13) |
| [`docs/design/实施计划.md`](docs/design/实施计划.md) | 开发推进方案:loop 模式 + 八阶段验收标准 + 进度表(**含实现假设与待定问题清单**) | 已实现(2026-09-15;八阶段全部完成) |
| [`docs/delivery/部署文档.md`](docs/delivery/部署文档.md) | **给实例运营方**:环境变量、一条命令链、systemd / Nginx 示例、运行约束与排查 | 定稿(2026-09-15) |
| [`docs/upstream/NewAPI-sk-key接口文档.md`](docs/upstream/NewAPI-sk-key接口文档.md) | 查 sk-key 可用接口的字段级事实(源码实证,430 行) | 事实依据 |
| [`docs/upstream/NewAPI-接口范围.md`](docs/upstream/NewAPI-接口范围.md) | 本项目对接的接口范围(9 个接口的用途与鉴权) | 事实依据 |
- 命名与分区:**目录用英文、文档名用中文**;`docs/design/`(我们定的方案与决策)· `docs/upstream/`(NewAPI 的实证事实,不做设计决策)· `docs/delivery/`(给部署方看的部署文档)· `docs/deprecated/`(**已废弃的历史文档,只存档、不引用、不作依据**)
- 生命周期:设计文档定稿后不搬家、只改状态(草案 → 讨论中 → 待审阅 → 定稿(日期) → 已实现);被取代时删除(git 历史即考古),尚未入库的历史文档移入 `docs/deprecated/`
## 这是什么项目
- 名字直述用途:NewAPI 网关下按 key 查询用量(key usage)
- 全场景统一小写 kebab-case:仓库名 / 包名 / 文档标题一致,无大写变体
## 背景故事
- 本工具源于一个真实场景:[NewAPI](https://github.com/QuantumNous/new-api)(Go + GORM 的开源 LLM 网关)实例提供订阅服务,多个使用者通过分发 API key 共享同一份订阅额度
- 项目作者是该实例的**普通用户**:购买了订阅额度,创建多个 sk- API key 分发给多名使用者分摊使用成本——**非商业,纯成本分担**
- 关键身份约束:项目作者**只有 sk-key,没有实例的管理员账号**。因此:
- 一切数据获取只能走 key 级接口(账号级接口用 sk-key 访问会 401)
- 工具部署在**实例运营方提供的服务器**上,作者不直接接触服务器——**交付方式是推送仓库 / 产物,由运营方执行部署**
## 核心需求(v2 定稿)
| # | 需求 | 说明 |
|---|------|------|
| 1 | key 用量查询 | 输入 sk-key + 时间范围 → 查模型请求日志与用量。支持一次输入多个 key 合并查询(项目作者借此获得全员视角) |
| 2 | key 登记 | 管理通道手动登记:登记后入库并加入轮询;查看面凭已登记的 key 查询(纯只读) |
**明确不做的**(v1 → v2 的收缩边界):费用分摊计算(后议;折算基础已就绪——两张折算表设计见文档地图)、计费周期管理、多组支持、登录页、admin 角色、会话记忆。
## 架构的根本动因:为什么必须本地攒数据
这不是优化选项,是上游接口的物理约束决定的**唯一路径**:
1. sk-key 可用的接口(`/api/usage/token`、`/api/log/token` 等)返回的都是**累计值**或**最近 1000 条**,没有任何接口支持时间范围过滤
2. 真正适合算"当月用量"的接口(`/api/log/self/stat`,支持时间范围 + quota 汇总)需要账号登录态,sk-key 访问 401——这是"按时间段统计用量"无法纯客户端实现的**核心障碍**
3. `/api/log/token` 硬上限 1000 条、不支持任何参数(分页 / limit / 时间过滤全被忽略)
→ 结论:轮询同步 + `request_id` 去重入库 + 本地数据库。上游接口的完整实证(含源码出处)见 [`docs/upstream/NewAPI-sk-key接口文档.md`](docs/upstream/NewAPI-sk-key接口文档.md)。
## 已定技术决策
| # | 项 | 决策 | 备注 |
|---|----|------|------|
| 1 | 部署形态 | 实例运营方提供的服务器,标准前后端 | 弃 Cloudflare Workers(v1 方案),因运营方愿意提供服务器资源 |
| 2 | 运行时 / 框架 | **Bun + Hono** | 单进程部署、原生 TS;Hono 轻量路由 |
| 3 | 数据库 | **PostgreSQL** | 服务器现有实例 |
| 4 | 鉴权 | 无 admin、无登录页;查看面凭已登记的 key 查询;管理面由 env 口令 | 写操作全部收拢在管理面(key 管理 + 折算数据);key 失效由轮询 401 自动摘除、管理面手动启停补位 |
| 5 | 信任模型 | 实例运营方(= 网关管理员 = 服务器管理员)不是威胁对象——他们本就能看到一切;真正的防护对象是第三方拖库(sk-key 应加密存库,实现时落地) | 无法在对方控制的机器上对抗对方,接受此公理 |
| 6 | 计费口径 | 展示 quota + $ 参考换算(÷500000),并支持折算为实付人民币 | 折算机制(两层时间线)见文档地图中的两张折算表设计 |
| 7 | 代码组织 | 前后端分离 **monorepo**(Bun workspaces:`apps/web` · `apps/server` · `packages/shared`) | **前后端独立部署、分别更新**(2026-09-13 修订:前端静态产物独立托管;后端不托管前端产物);**后端单进程**(API + 同步引擎同进程、单实例);不引入 turbo 等编排工具 |
| 8 | 数据库访问 | **Drizzle ORM + drizzle-kit** | TS 定义 schema;迁移由 drizzle-kit 生成 |
| 9 | 前端 | **Vite + Vue 3** | 页面结构与 UI 组件库选型留技术稿定 |
| 10 | 人民币折算 | 两层时间线(`quota_per_unit` 自动采样 + 购买记录人工登记),**按购买时刻取尺** | 整笔订阅一把尺;历史永远用当时的尺子量;见文档地图中的两张折算表设计 |
| 11 | 管理通道 | `/manage/*` 独立路由 + env 口令;key 管理(登记 / 清单 / 启停)+ 购买记录的 CRUD + 换算参数时间线只读查看 | 本工具唯一写操作入口;查询域保持完全只读;见 [`docs/design/交互设计.md`](docs/design/交互设计.md) §0 |
## 关键事实清单(实测 / 源码确认,实现时不得违反)
> 出处均见 [`docs/upstream/NewAPI-sk-key接口文档.md`](docs/upstream/NewAPI-sk-key接口文档.md),此处仅索引。
1. **`QuotaPerUnit = 500000`**:quota ÷ 500000 = 额度美元(接口①与④交叉实测吻合)——**但它不是实付金额**:折算到实付人民币还需乘「购买汇率」(由运营方定价、可变,历史上调整过一次)。完整折算机制见文档地图中的两张折算表设计
2. **去重必须用 `request_id`**:`/api/log/token` 返回的 `id` 是每次从 1 重编的展示序号,绝不能用于去重
3. **`created_at` 是 UTC Unix 秒**(三字段交叉实测),北京时间输入需转换
4. **1000 条硬上限**:窗口只保留最近 1000 条——实测当前站点的日志速率下约等于 3 天;5 分钟级同步余量充分,但同步停摆超过约 3 天会永久丢数据
5. **`TokenAuthReadOnly`**:用量/日志接口用宽松鉴权,key 过期 / 额度耗尽仍可查询(用完也能对账);**显式禁用会被 401 拒绝**(2026-09-13 源码复核)
6. **`DisplayTokenStatEnabled` 已开启**:billing 接口返回 token 级额度
## 工程注记
- CORS 必须由服务端代理解决(浏览器直连 NewAPI 网关会被拦)——**指上游网关**;本项目前后端之间的连线见下条
- **前后端连线(2026-09-13 修订)**:**同域反代为主**(`/api/*` 反代到后端端口——浏览器视角同源,无 CORS、前端用相对路径)、**跨源直连为辅**(后端 CORS 白名单可配 + 前端运行时 API base;`X-Manage-Token` 自定义头会触发 OPTIONS 预检;凭证不进 Cookie,无需 credentials 模式)
- bun runtime 跑 vue-tsc 存在已知 bug([bun#4754](https://github.com/oven-sh/bun/issues/4754)),类型检查与构建需解耦
## 下一步
1. **部署**:按 [`docs/delivery/部署文档.md`](docs/delivery/部署文档.md) 执行(一条命令链;前后端独立 + **后端单实例**注记)
2. **待作者补验 / 裁决**(清单见 [`docs/design/实施计划.md`](docs/design/实施计划.md)):
- 真实 key 的真实链路(真实登记 / 采集轮次 / 401 摘除)——本地开发以样例快照验证(实施计划 §6)
- 实现假设 #1–#20 的逐条确认
- 待定问题 #5:错误行 `request_id` 不唯一(去重合并策略待裁决)
3. 后议功能:充值折算、多订阅实测等见两张折算表设计文档的「待定事项」