348 lines
23 KiB
Markdown
348 lines
23 KiB
Markdown
# NewAPI 接口范围
|
||
|
||
> 计费平台在 NewAPI 中可用的接口参考:每个接口的鉴权档位、请求与返回结构、字段语义、实测行为与对平台的用途——**平台所有上游调用都出自这份文档**。
|
||
>
|
||
> 依据:NewAPI 源码考证 + 接口实测 · 实测环境:NewAPI 实例 · 上游仓库:https://github.com/QuantumNous/new-api
|
||
|
||
---
|
||
|
||
## 0. 通用说明
|
||
|
||
### 认证方式
|
||
|
||
所有需要凭证的接口均使用 API key 鉴权,在请求头中携带:
|
||
|
||
```
|
||
Authorization: Bearer sk-xxxxxxxxxxxx
|
||
```
|
||
|
||
**不需要**账号登录态(access token / session)——这是仅凭 sk-key 即可查询的关键前提。BaseURL 即 NewAPI 站点地址,路径直接拼接、无多余前缀。
|
||
|
||
### 关键开关:`DisplayTokenStatEnabled`
|
||
|
||
- **billing 接口(⑤⑥)受影响**:开启 → 返回 token 级额度;关闭 → 回退账号级额度(会暴露整个账号余额)
|
||
- `/api/usage/token`、`/api/log/token` **不受影响**,恒返回 token 级数据
|
||
- **实测已确认当前站点开关为开启状态**;开关位于后台设置 → 计费设置(Billing)→ 货币与显示(Currency & Display)→ `Display Token Statistics`,仅管理员可改
|
||
|
||
### 换算参数:`QuotaPerUnit`
|
||
|
||
- 默认 `500000`(源码 `common/constants.go`,锚点 `$0.002 / 1K tokens`),**管理员可改且改后立即全站生效**(详见 ④)
|
||
- 实现时必须从 `GET /api/status` 实时读取,**不得硬编码**
|
||
- 实测交叉验证:`/api/usage/token` 的 `total_used = 673,419,457` ÷ 500000 = **1346.84 美元**;`/v1/dashboard/billing/usage` 的 `total_usage = 134,683.8914` 美分 ÷ 100 = **1346.84 美元**——完全吻合
|
||
|
||
## 1. 鉴权三态总览
|
||
|
||
本项目对接的接口按鉴权中间件分三档:
|
||
|
||
| 鉴权档 | 中间件 | 语义 | 本项目的请求方式 |
|
||
|--------|--------|------|----------------|
|
||
| **公开**(none) | 无 / `HeaderNavModulePublicOrUserAuth` | 不持有任何凭证即可访问 | 直接请求,无需 key |
|
||
| **严格**(strict) | `TokenAuth` | 校验 key 有效、未过期、有剩余额度;无效直接被拒 | 携带 sk-key 请求 |
|
||
| **宽松**(lenient) | `TokenAuthReadOnly` | 只验 key 存在且用户未封禁;**过期/额度耗尽仍可查**,但**显式禁用(Disabled)会被 401 拒绝**(`middleware/auth.go` 注释原文:only explicitly disabled tokens are denied) | 携带 sk-key 请求;**本项目主要依赖此档** |
|
||
|
||
> 失败状态码全集(2026-09-13 源码复核):**401** = key 不存在(`MsgTokenInvalid`)或 token 被显式禁用(`MsgTokenStatusUnavailable`);**403** = 账号被封禁;**500** = 数据库错误。→ 摘除计数只认 401;403 / 500 视为其他故障,不计入。宽松档**允许** key 过期(Expired)与额度耗尽(Exhausted)状态查询。
|
||
|
||
## 2. 接口清单
|
||
|
||
### 公开接口组(无需 sk-key)
|
||
|
||
#### ① `GET /api/user/groups` — 用户分组与倍率
|
||
|
||
- **返回**:站点配置的用户分组(default / vip 等)与倍率;**未启用分组时 `data` 为空对象**(当前站点实测即此状态)
|
||
- **用途**:解释「同一模型为什么计费不同」——分组倍率影响同一模型的实际消耗
|
||
- **本项目用途**:低优先级;仅当站点启用分组倍率后才有内容
|
||
|
||
#### ② `GET /api/perf-metrics/summary` — 全站模型服务质量
|
||
|
||
- **参数**:`hours`(统计窗口小时数,默认 24)
|
||
- **返回**:每个模型的 `avg_latency_ms`(平均延迟)、`success_rate`(成功率 %)、`avg_tps`(每秒 token 数)、`recent_success_series`(按小时采样的成功率时间序列);明细版 `/api/perf-metrics?model=xxx`(model 必填,另支持 group/hours)
|
||
- **鉴权细节**:`HeaderNavModulePublicOrUserAuth("pricing")`——默认公开,管理员可为 pricing 模块开启 requireAuth
|
||
- **实测**:HTTP 200,含站点真实模型的服务质量数据
|
||
- **本项目用途**:**高**——本工具独有视角。单个 sk-key 日志看不到全局服务质量;使用者反馈「慢/报错多」时,此接口能定位是模型问题还是自身问题。技术设计时可作为「模型健康度」功能的数据源
|
||
|
||
#### ③ `GET /api/rankings` — 全站模型用量排行
|
||
|
||
- **参数**:`period`(统计周期,默认 week)
|
||
- **返回**:每个模型的 `rank`(名次)、`previous_rank`(上期名次)、`total_tokens`(总用量)、`share`(份额)、`growth_pct`(环比增长 %)
|
||
- **实测**:HTTP 200,14KB 数据
|
||
- **本项目用途**:中——项目作者选「主推模型」(便宜且稳定)的数据依据;配合②的成功率可识别衰退模型
|
||
|
||
#### ④ `GET /api/status` — 站点配置与计费参数
|
||
|
||
- **返回**:60+ 字段,与本项目相关的计费字段:`quota_per_unit`(**换算比率当前生效值,管理员可改**)、`quota_display_type`(USD/CNY/TOKENS,影响 billing 接口语义)、`usd_exchange_rate`、`price` 等
|
||
- **本项目用途**:**关键**——换算比率的唯一权威来源,实现时必须实时读取、不得硬编码;同时感知 `quota_display_type` 切换(若切到 CNY/TOKENS,billing 接口数字语义会变)。实测当前站点:`quota_per_unit = 500000`、`quota_display_type = USD`、`usd_exchange_rate = 6.8`
|
||
- **`quota_per_unit` 的行为事实(2026-09-13 源码评估)**:由全局变量承载,改设置是「先写库、再热更新内存」(`model/option.go` 的 `UpdateOption` → `updateOptionMap`)——改后**立即全站生效、无需重启**。三条关键行为:
|
||
- **无历史**:`options` 表一 key 一行、改即覆盖——任何接口都读不到过去的取值;管理审计只记"键名被改过"、**不记值**
|
||
- **无编辑入口**:后台设置页该字段默认隐藏;当显示类型为 TOKENS 或取值非默认(≠500000)时才显示,且显示时仍为只读(`disabled`)——源码 `web/src/features/system-settings/general/pricing-section.tsx`(2026-09-29 main 分支复核);修改只能走 API(`PUT /api/option/`,Root 权限,热更新内存立即生效)
|
||
- **解读跟随当前值**:一切对外的金额换算(历史记录解读、billing 接口、界面显示)都在读取时实时使用当前值——改它 = 全站金额表达整体平移(数据库里的 quota 整数不受影响)
|
||
|
||
### 严格鉴权组(TokenAuth,sk-key)
|
||
|
||
#### ⑤ `GET /v1/dashboard/billing/subscription` — 总额度上限(美元)
|
||
|
||
- **返回结构**(源码 `controller/billing.go:13` → `GetSubscription`):
|
||
|
||
```json
|
||
{
|
||
"object": "billing_subscription",
|
||
"has_payment_method": true,
|
||
"soft_limit_usd": 100000000,
|
||
"hard_limit_usd": 100000000,
|
||
"system_hard_limit_usd": 100000000,
|
||
"access_until": 0
|
||
}
|
||
```
|
||
|
||
- **字段**:`hard_limit_usd`(总额度上限,美元;三个 limit 字段同值)、`access_until`(token 过期时间,Unix 秒,0 = 永不过期)
|
||
- **注意**:无限额度 key 返回 `100000000`(一亿占位值,**无参考意义**);受 `DisplayTokenStatEnabled` 与 `quota_display_type` 影响(USD 模式直接是美元,CNY 模式做货币转换,Tokens 模式是原始 token 数)
|
||
- **路径变体**:`/dashboard/billing/subscription`(不带 `/v1` 前缀)是同一接口,为兼容不同客户端写法——测一组即可
|
||
- **本项目用途**:中——按 key 统计的「累计剩余」列候选数据源;与⑥相减可得剩余额度
|
||
|
||
#### ⑥ `GET /v1/dashboard/billing/usage` — 累计已用(美分)
|
||
|
||
- **返回结构**(源码 `controller/billing.go:75` → `GetUsage`):
|
||
|
||
```json
|
||
{
|
||
"object": "list",
|
||
"total_usage": 134683.8914
|
||
}
|
||
```
|
||
|
||
- **字段**:`total_usage`(**累计已用,单位美分**,÷100 = 美元;从 key 创建至今累计,非当月)
|
||
- **注意**:受 `DisplayTokenStatEnabled` 与 `quota_display_type` 影响(USD 模式才是美元);`hard_limit_usd`(⑤)− `total_usage ÷ 100`(⑥)= 剩余额度(仅有限额 key 有意义);可与⑧的 `total_used ÷ quota_per_unit` 交叉对账(不一致 = 站点改过比率,见④)
|
||
- **路径变体**:`/dashboard/billing/usage` 同⑥
|
||
- **本项目用途**:中——对账与校验用途
|
||
|
||
#### ⑦ `GET /v1/models` — 可用模型列表
|
||
|
||
- **返回**:当前 key 可用的模型列表,OpenAI 兼容格式(`data[].id` 为模型名)
|
||
- **变体**(与用量统计无关,列出仅为完整覆盖):`/v1/models/:model` 单模型详情、`/v1beta/models`(Gemini 格式)、`/v1beta/openai/models`(OpenAI 格式,Gemini 路由下)
|
||
- **已知限制**:`/v1/models` 查数据库 `abilities` 表(渠道实际配置),而 `/v1/models/:model` 查代码内硬编码的静态模型列表(`openAIModelsMap`)——渠道配了新模型或自定义模型名但代码未收录时,会出现"列表里有但单查返回 `model_not_found`"的现象;不影响实际模型调用
|
||
- **本项目用途**:低——调试期验证渠道配置用
|
||
|
||
### 宽松鉴权组(TokenAuthReadOnly,sk-key)
|
||
|
||
#### ⑧ `GET /api/usage/token` — token 级用量汇总(原始 quota)
|
||
|
||
- **返回结构**(源码 `controller/token.go` → `GetTokenUsage`):
|
||
|
||
```json
|
||
{
|
||
"code": true,
|
||
"message": "ok",
|
||
"data": {
|
||
"object": "token_usage",
|
||
"name": "令牌名称",
|
||
"total_granted": 6012,
|
||
"total_used": 673419457,
|
||
"total_available": -673413445,
|
||
"unlimited_quota": true,
|
||
"model_limits": {},
|
||
"model_limits_enabled": false,
|
||
"expires_at": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
- **字段**:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `name` | string | 令牌名称 |
|
||
| `total_granted` | int | 总额度 = RemainQuota + UsedQuota(原始 quota 单位) |
|
||
| `total_used` | int | **累计已用**(原始 quota 单位)。从 key 创建以来累计,非当月;换算美元 ÷ `quota_per_unit` |
|
||
| `total_available` | int | 剩余 = granted − used。无限额度时为负数,无实际意义 |
|
||
| `unlimited_quota` | bool | 是否无限额度。true 时 granted/available 无实际意义 |
|
||
| `model_limits` / `model_limits_enabled` | object / bool | 模型限制 |
|
||
| `expires_at` | int | 过期时间(Unix 秒),0 = 永不过期 |
|
||
|
||
- **注意**:额度单位是**原始 quota 整数**,需自行换算美元;禁用再解禁不会重置 `total_used`(源码验证:`Update()` 的 `.Select(...)` 列表不含 `used_quota`);key 耗尽后仍可查(用完也能对账)
|
||
- **本项目用途**:**高**——按 key 统计「全周期累计(已用/剩余)」的直接数据源
|
||
|
||
#### ⑨ `GET /api/log/token` — 最近 1000 条调用日志
|
||
|
||
- **返回结构**(源码 `controller/log.go:62` → `GetLogByKey`):
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "",
|
||
"data": [
|
||
{
|
||
"id": 123456, "user_id": 12, "created_at": 1751328000, "type": 2,
|
||
"content": "模型倍率 1.0,分组倍率 1.0", "username": "用户名", "token_name": "令牌名",
|
||
"model_name": "gpt-4o", "quota": 2500, "prompt_tokens": 1500, "completion_tokens": 500,
|
||
"use_time": 2, "is_stream": true, "channel": 0, "channel_name": "", "token_id": 100,
|
||
"group": "default", "ip": "",
|
||
"request_id": "20260911113652…",
|
||
"other": "{\"billing_source\":\"subscription\",\"cache_tokens\":29056,\"frt\":3931,\"model_ratio\":0.075,\"request_path\":\"/v1/messages\",\"stream_status\":{\"status\":\"ok\"},…}"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
- **字段说明**(共 21 个字段;按实测响应 + 上游源码校正,2026-09-11):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `id` | int | 展示序号,**每次从 1 重编**——跨请求无意义,绝不能用于去重 |
|
||
| `user_id` | int | 账号 ID(同一实例内恒定) |
|
||
| `created_at` | int | 调用时间(**UTC Unix 秒**,三字段交叉实测) |
|
||
| `type` | int | 日志类型(取值见下);token 日志实见 2=消费、5=错误 |
|
||
| `content` | string | 消费记录为空串;错误记录为错误详情文本 |
|
||
| `username` / `token_name` | string | 账号用户名 / 令牌名 |
|
||
| `model_name` | string | 调用的模型名 |
|
||
| `quota` | int | 本次消耗的 quota(换算美元 ÷ `quota_per_unit`);错误记录为 0 |
|
||
| `prompt_tokens` / `completion_tokens` | int | 输入 / 输出 token 数;错误记录为 0 |
|
||
| `use_time` | int | 耗时(秒);错误记录为 0 |
|
||
| `is_stream` | bool | 是否流式 |
|
||
| `channel` / `channel_name` | int / string | 渠道 ID / 渠道名(普通用户视角后者为空串) |
|
||
| `token_id` | int | 上游 token ID(对账用) |
|
||
| `group` | string | 分组名(默认 `default`) |
|
||
| `ip` | string | 请求 IP(普通用户视角为空串) |
|
||
| `request_id` | string | 请求 ID(`yyyyMMddHHmmss` 前缀 + 随机串);源码写入有兜底保证非空——**去重唯一依据**。**补充实测(2026-09-15)**:错误场景下**不保证行级唯一**——1000 条实测样本中 8 个 `request_id` 各对应 3 条 `type=5` 错误记录(同一请求的多次失败 / 重试,`content` 的 `status_code` 可不同);按 `request_id` 去重会把它们合并为 1 条(消费类 `type=2` 样本中未观察到重复) |
|
||
| `upstream_request_id` | string | 上游请求 ID;JSON tag 为 `omitempty`——**空值时不出现、有值时才出现**,消费方需按「可选字段」处理 |
|
||
| `other` | string | JSON 字符串,扩展信息(结构见下) |
|
||
|
||
- **`type` 完整取值**(上游源码 `model/log.go`;常量注释明确「刻意不用 iota,以保证枚举值永不改变」):
|
||
|
||
| 值 | 常量 | 含义 | 可能出现在 token 日志? |
|
||
|----|------|------|------------------------|
|
||
| 0 | `LogTypeUnknown` | 未知 | 兜底值 |
|
||
| 1 | `LogTypeTopup` | 充值 | 否(账号级,无 token_id) |
|
||
| 2 | `LogTypeConsume` | 消费 | **是**(实测) |
|
||
| 3 | `LogTypeManage` | 管理操作 | 否(审计类) |
|
||
| 4 | `LogTypeSystem` | 系统 | 否 |
|
||
| 5 | `LogTypeError` | 错误 | **是**(实测) |
|
||
| 6 | `LogTypeRefund` | 退款 | 可能(任务计费链路带 token_id) |
|
||
| 7 | `LogTypeLogin` | 登录 | 否(账号级) |
|
||
|
||
> 本接口按 `token_id` 过滤、不按 `type` 过滤——凡是「带 token_id 写入」的类型都可能返回。
|
||
|
||
- **`other` 字段**(JSON 字符串,内容随 `type` 变化,实测 2026-09-11):
|
||
- 消费记录(`type=2`):`billing_source`、`cache_ratio`、`cache_tokens`、`completion_ratio`、`frt`、`group_ratio`、`model_price`、`model_ratio`、`reasoning_effort`、`request_conversion`、`request_path`、`stream_status`(对象)、`subscription_*`(订阅维度,共 8 个)、`user_group_ratio`、`wallet_quota_deducted`
|
||
- 错误记录(`type=5`):`error_code`、`error_type`、`request_path`、`status_code`
|
||
- 注:普通用户视角下,服务端会先对 `other` 做「可见性过滤」再返回——上表为过滤后实际可见的键
|
||
|
||
- **关键限制**:
|
||
- **硬上限 1000 条**:固定返回最近 1000 条(`Limit(common.MaxRecentItems)`,`MaxRecentItems = 1000`,`common/constants.go`);**不支持任何请求参数**(源码不读任何 `c.Query()`,无分页 / limit / 时间过滤);固定按 `id desc` 排序(最新 1000 条)
|
||
- **超过 1000 条的旧记录无法获取**:无法翻页、无法扩大 limit;实测当前站点日志速率下 1000 条 ≈ 3 天窗口
|
||
- **大响应**:pretty-print 后 2~4MB——同步引擎需直面的单次响应体积
|
||
- **本项目用途**:**核心**——增量同步引擎的唯一数据源;本地攒全量后,任意时间范围的用量/明细/模型统计都是本地聚合查询
|
||
|
||
## 3. 换算与 quota 计算
|
||
|
||
### 核心换算公式
|
||
|
||
```
|
||
原始 quota ÷ quota_per_unit(默认 500000) = 美元
|
||
```
|
||
|
||
### 消费记录的 `quota` 是怎么算出来的(实测反推,2026-09-13)
|
||
|
||
按 tokens 计费的模型,`quota` 在**请求时**由「倍率 × 用量」算出并固化进日志(此后不再变化):
|
||
|
||
```
|
||
quota = ( (prompt_tokens − cache_tokens) ← 提示 tokens(未命中缓存部分)
|
||
+ cache_tokens × cache_ratio ← 缓存命中部分按折扣
|
||
+ completion_tokens × completion_ratio ) ← 补全 tokens 按倍率
|
||
× model_ratio ← 模型倍率
|
||
× group_ratio ← 分组倍率
|
||
```
|
||
|
||
- 验证:6/6 条实测日志逐条吻合(差值仅为四舍五入)
|
||
- 按次计费模型走 `model_price` 分支:`quota = model_price × QuotaPerUnit × group_ratio`(源码 `relay/helper/price.go`)
|
||
- **公式中没有 `QuotaPerUnit`**:倍率与美元的锚定(`model_ratio = 1` ⇔ `$0.002 / 1K tokens`)已隐含它——倍率制下改 `QuotaPerUnit` 等于全站重定标;美元定价路径(`model_price` / 表达式 `$/1M`)才显式乘 `QuotaPerUnit`
|
||
|
||
## 4. 范围之外(sk-key 不可达)
|
||
|
||
### 4.1 不存在的接口(404)
|
||
|
||
| 路径 | 状态 |
|
||
|------|------|
|
||
| `/v1/usage`、`/v1/quota`、`/api/token/self` | 不存在 |
|
||
|
||
### 4.2 存在但需要登录态的接口(sk-key 访问 401)
|
||
|
||
支持时间范围查询与过滤,但**必须用账号登录态**(access token + `New-Api-User` header):
|
||
|
||
| 路径 | 鉴权 | 能力 |
|
||
|------|------|------|
|
||
| `/api/log/self` | `UserAuth()` | 支持 `start_timestamp`、`end_timestamp`、`token_name` 过滤,返回日志明细 |
|
||
| `/api/log/self/stat` | `UserAuth()` | **支持时间范围 + token_name,返回 quota 汇总**(调 `SumUsedQuota`)——NewAPI 中最适合算"当月用量"的接口,是「按时间段统计用量」无法纯 sk-key 实现的**核心障碍** |
|
||
| `/api/data/self` | `UserAuth()` | 按日聚合的看板数据 |
|
||
| `/api/token/` (GET/POST/PUT/DELETE) | `UserAuth()` | token 的增删改查 |
|
||
| `/api/user/self` | `UserAuth()` | 用户自身信息 |
|
||
|
||
### 4.3 订阅信息
|
||
|
||
- **存在完整列表接口,但鉴权是 `UserAuth()`(账号登录态),不是 sk-key**(源码 `router/api-router.go` · `controller/subscription.go:GetSubscriptionSelf`,2026-10-08 核对):
|
||
|
||
| 路径 | 鉴权 | 能力 |
|
||
|------|------|------|
|
||
| `GET /api/subscription/self` | `UserAuth()` | 返回 `all_subscriptions`(含已过期):`id`、`start_time`/`end_time`(Unix 秒)、`status`、`amount_total`/`amount_used`、`created_at` 等;另有 `subscriptions`、`billing_preference` |
|
||
| `GET /api/subscription/plans` | `UserAuth()` | 套餐列表 |
|
||
| `/api/subscription/admin/*` | `AdminAuth()` | 管理端绑定/作废等,与本项目无关 |
|
||
|
||
- **sk-key(`TokenAuth` / `TokenAuthReadOnly`)挂不上这些路由**——钱包页在 F12 里看到的 JSON 即 `GET /api/subscription/self`,需网页登录 Cookie/会话,不是 `Authorization: Bearer sk-…`
|
||
- 「没有免 auth 的接口」结论仍成立;**并非漏查**,而是查询面从未对 sk-key 开放。UI 只显示过期时间,是前端展示裁剪,底层数据在上述接口里
|
||
- 订阅 `id` 与日志 `other.subscription_id` 同体系(作者 JSON 中 `id: 44/22/6` 可与日志对账)
|
||
- 对应设计:订阅信息由购买者手工录入或**辅助登录选用**(`subscriptions表字段设计`;密码不落库、token 会话内);登记时可粘贴 `all_subscriptions` JSON 预填;**不做后台账号自动同步**
|
||
|
||
### 4.4 参数被忽略的情况(实测)
|
||
|
||
| 请求 | 实际行为 |
|
||
|------|---------|
|
||
| `/api/log/token?p=2` | 参数被忽略,仍返回最近 1000 条 |
|
||
| `/api/log/token?limit=5000` | 参数被忽略,仍返回最近 1000 条 |
|
||
| `/api/log/token?start_timestamp=1751328000` | 参数被忽略,仍返回最近 1000 条 |
|
||
| `/v1/dashboard/billing/usage?start_date=2025-07-01` | 参数被忽略 |
|
||
|
||
### 4.5 充值记录(2026-10-09 实测)
|
||
|
||
- **`GET /api/user/topup/self?p=1&page_size=10`**——分页充值单;路径属 `/api/user/*`,**预期 `UserAuth()`**(账号登录态,非 sk-key)
|
||
- 响应 `{ data: { page, page_size, total, items }, success }`;单条字段与样本:
|
||
|
||
| 字段 | 样本 | 含义 |
|
||
|------|------|------|
|
||
| `id` | `17` | 上游充值单 ID |
|
||
| `user_id` | `6` | 账号 |
|
||
| `amount` | `1` | **平台计费金额**(充值记账额;与 `money` 之间为管理员约定汇率,不标币种) |
|
||
| `money` | `2.04` | **实付金额** |
|
||
| `trade_no` | `USR6…` | 交易号 |
|
||
| `payment_method` / `payment_provider` | `alipay` / `epay` | 渠道 |
|
||
| `create_time` / `complete_time` | Unix 秒 | 发起 / **入账完成** |
|
||
| `status` | `success` | 单据状态 |
|
||
|
||
- **无 `quota_granted` 字段**——换得额度为派生(`amount × quota_per_unit`);登记不固化该列(`topups表字段设计.md`)
|
||
- **展示时刻 = `complete_time`**(平台列表即显示它);`create_time` 仅发起时刻——设计只存一条事件时间 `paid_at` = `complete_time`
|
||
- **辅助登录代拉**(与订阅同模式):`UserAuth` access_token、密码不落库;**仅 `p` / `page_size` 分页、无时间参数**——前端传统分页、可翻至尽头(不设页数硬上限);不做后台定时同步
|
||
|
||
## 5. 行为事实:写权限与 key 生命周期
|
||
|
||
- **sk-key 没有任何写接口**:没有 POST/PUT/DELETE 能改 token 自身;无法通过 sk-key 重置 `used_quota`(没有"重置用量"接口)
|
||
- **禁用 token 只改 `status` 字段**:不重置 `used_quota`、不清零 `remain_quota`、不丢历史日志;解禁后三者原封不动
|
||
- **唯一让 `used_quota` 归零的途径**:删除 token 后新建(产生全新 Id/Key,旧日志按旧 token_id 关联,新 token 查不到旧日志)
|
||
- **解禁保护**:额度耗尽(`Exhausted`)且 `RemainQuota ≤ 0` 且非无限额度的 token 不能直接解禁,需先充值;已过期的 token 也不能直接解禁,需改过期时间
|
||
|
||
## 6. 源码参考
|
||
|
||
| 关注点 | 文件路径 | 关键函数/位置 |
|
||
|--------|---------|--------------|
|
||
| 路由注册 | `router/api-router.go` | usageRoute、logRoute 组 |
|
||
| 路由注册 | `router/dashboard.go:14-23` | billing 路由组 |
|
||
| 路由注册 | `router/relay-router.go` | models 路由 |
|
||
| API key 鉴权 | `middleware/auth.go:171` | `TokenAuthReadOnly()` |
|
||
| API key 鉴权 | `middleware/auth.go` | `TokenAuth()` / `SetupContextForToken` |
|
||
| token 用量查询 | `controller/token.go` | `GetTokenUsage` |
|
||
| 日志查询 | `controller/log.go:62` | `GetLogByKey` |
|
||
| 日志查询(model) | `model/log.go` | `GetLogByTokenId`(Limit 1000) |
|
||
| billing 查询 | `controller/billing.go:13 / :75` | `GetSubscription` / `GetUsage` |
|
||
| 显示开关 | `common/constants.go` | `DisplayTokenStatEnabled` |
|
||
| 日志上限 | `common/constants.go` | `MaxRecentItems = 1000` |
|
||
| 配额单位 | `common/constants.go` | `QuotaPerUnit`(默认 500000) |
|
||
| token 更新 | `model/token.go` | `Update()`(Select 不含 used_quota) |
|
||
| 额度增减 | `model/token.go` | `increaseTokenQuota` / `decreaseTokenQuota` |
|
||
| 日志类型常量 | `model/log.go` | `LogTypeConsume = 2`、`LogTypeError = 5` 等(刻意不用 iota) |
|