-
Notifications
You must be signed in to change notification settings - Fork 1
Architecture
KS-OTO edited this page Sep 21, 2026
·
1 revision
页面挂在边缘云上,而部分平台按节点生存时间计费 —— 端点数直接决定唤醒次数与单次存活时长。 因此所有读数合并成 3 个接口(此前是 13 个):
| 接口 | 何时调用 | 说明 |
|---|---|---|
GET /api/status |
首屏一次 | 只回配置状态(哪些平台配了几个 Key、缺哪一半、站点自定义),0 次上游调用,因此极快 |
GET /api/usage |
每次刷新 | 10 家平台的读数合并成一封信封,见下 |
GET /api/volc/inference-usage |
按需(改模型过滤时) | 火山推理用量。故意不合并:它是唯一带用户输入的读数接口,并入会让「改一个模型过滤」退化成「全量重拉 10 家」 |
① 每个平台是独立切片,一家的失败不会污染其余九家(也不会让整封信封变成 500)。
未配置走 NOT_CONFIGURED,前端渲染成中性空态而不是错误墙。未配置的 Key 不影响其他平台出卡。
服务端每个子查询各自容错:同一账号下的多个子接口不共用 Promise.all,
否则任一子接口 500 就把该账号其余数据全部丢掉(这是修过的真实 bug)。
② 服务端有 TTL 缓存 + 单飞(single-flight):命中缓存时上游调用为 0, 并发刷新共享同一次上游请求。TTL 按数据变化节奏分两档:
| 档位 | TTL | 覆盖 |
|---|---|---|
| 余额 / 用量类 | 60 秒 | 余额、窗口用量、代金券 |
| 结构 / 权益类 | 300 秒 | 资源包、套餐、座席 |
默认刷新间隔 180 秒,所以按天计的那几项大约每两次自动刷新才真正打一次上游。
手动点「刷新」会绕过缓存(?refresh=1),因此手动刷新永远拿得到实时数据。
| 平台 | 鉴权方式 |
|---|---|
| 火山方舟 | 火山 v4 签名(Web Crypto 手写,见 sign.ts) |
| 阿里云(BSS / ROA) | RPC/ROA 签名 + ACS3-HMAC-SHA256 |
| 百度千帆 | BCE AK/SK 签名 |
| 百炼个人版 | 控制台会话 Cookie(或 AK/SK 换 access token) |
| 模力方舟代金券 | 会话 Cookie |
| 其余 | Bearer Token / API Key |
两处语义坑:
-
New API token 要系统访问令牌,不是
sk-模型调用密钥; - 上游业务码比 HTTP 码信息量大;账号被停用也是 401,但令牌已被认出 → 这种情况不回落账单接口(否则会把「账号停用」误报成「权限不足」)。
server/ 平台无关 API 核心 + Bun 入口
app.ts createAppHandler(env):3 个接口(/api/status + /api/usage + /api/volc/inference-usage)
cache.ts 上游查询的 TTL 缓存 + 单飞
index.ts Bun 入口:Bun.serve + dist/ 静态服务
env-vars.ts 环境变量的单一事实来源(SERVER_ENV_VARS)
multi.ts 多账号凭据读取(PREFIX / PREFIX_2 / ...)
sign.ts 火山引擎 v4 签名(Web Crypto)
volc.ts 方舟管控面 API 客户端
deepseek.ts DeepSeek 余额客户端
zhipu.ts 智谱客户端(Coding Plan 额度 / 账户余额 / 资源包)
gitee.ts 模力方舟(Gitee AI)资源包余额客户端
aliyun.ts 阿里云 RPC/ROA 签名 + BSS 资源包客户端
aliyun-console.ts 百炼控制台网关(Token Plan 个人版用量 + ACS3 签名)
tokenplan.ts 阿里云 Model Studio Token Plan 客户端(组织/座席/共享包)
opencode.ts OpenCode Go 订阅额度客户端
newapi.ts New API 客户端:管理接口优先、账单接口回落,订阅/钱包模式判定
balances.ts OpenRouter 余额客户端
plans.ts Kimi / MiniMax Token Plan 客户端
src/ Vue 3 前端(TDesign Vue Next + Pinia)
api.ts 前端 API 客户端(错误信封手写收窄;不引 Zod —— 它整库进客户端)
tdesign.ts TDesign 组件显式注册表(整库默认导出不可摇树,实测多 43% 体积)
stores/ Pinia stores(dashboard 数据编排 / theme 暗色主题)
types.ts 共享类型(多账号 AccountEnvelope 判别联合 + AccountDetail 统一详情模型)
detail.ts 详情模型的构件库(field/metric/table/notice/link/windowQuota/cardsOf …)
utils.ts 展示格式化工具 + 列策略谓词 + 平台卡排序
modelDocs.ts 平台名 → 官方「可用模型」文档地址
format.ts 数值精度的唯一入口(渲染层禁 toFixed / Math.round)
components/ 各平台区块组件(AccountSection 统一外壳)
ui/ 共享渲染骨架(AccountCard / AccountCardBody / AccountDetailPanel /
DetailSection / DetailFields / DetailTable / MetricTile / UsageBar)
*Detail.ts 每个平台一个适配器:平台原始响应 → AccountDetail
assets/layout.css 全站唯一布局层(语义化网格原语 + 断点;组件不写断点)
assets/theme.css 全站唯一配色入口(覆盖 TDesign 的 --td-*,亮/暗各一套)
e2e/ Playwright E2E 冒烟
worker/ Cloudflare Workers 入口(复用 server/app.ts)
cloud-functions/ EdgeOne Makers 云函数(/api/* 全捕获 + /api/diag 自诊断)
docs/ design-baseline.md(设计系统规格)+ 供应商接口调研
.github/ CI(四道门禁)+ issue / PR 模板 + Dependabot
详情视图统一建模成 AccountDetail:字段恒存在的扁平对象,失败时取中性空值。
平台差异收敛在各自的 *Detail.ts 适配器里,Section 组件只做
cardsOf(data.accounts, toXxxDetail) + <AccountCard>,模板里不认平台专属字段。
cardFace 是「卡面 vs 弹窗」的唯一开关 —— 别在组件里挑字段。现有两种用法:
同一账号的另一个订阅、只对部分模型生效的限额(火山日额度就是后者)。
细节见 设计系统
与仓库内的
docs/design-baseline.md。
{ "providers": { "deepseek": { "data": { "accounts": [] } }, // 成功 "newapi": { "error": "未配置 …", "code": "NOT_CONFIGURED" }, // 未配置 "zhipu": { "error": "…", "code": "Zhipu_HTTP_500" }, // 真实失败 }, "fetchedAt": 1789616585319, }