# token-hub **Repository Path**: freecpu/token-hub ## Basic Information - **Project Name**: token-hub - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-29 - **Last Updated**: 2026-09-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 量子枢纽因循再造工程项目 — 套壳平台 > 依据《量子枢纽因循再造工程项目需求书-v2.0.md》(v2.0-r2) 改造落地的可运行套壳平台: > 前端登录 + **远程 MySQL 5.7.44(Alembic 版本化迁移)** + 真实上游对接(token.wxkjwlw.com / New API) > + **独立 nginx 反代**(8001 HTTP / 8002 HTTPS,不使用宿主 80/443)。 一套「轻量套壳 + 分销代理」的最小闭环平台:**注册 → 充值 → 自动发 Key → 看报表**, 用户体系与上游完全隔离,所有终端用户共享一个上游母账号,赚差价。 --- ## 1. 技术选型 | 层 | 选型 | 说明 | | --- | --- | --- | | 后端 | **Python 3.12 + FastAPI + Uvicorn** | 异步 IO,适合对接场景 | | 数据库 | **远程 MySQL 5.7.44**(SQLAlchemy 2.0 + PyMySQL) | 连接参数经 `.env` 注入;quota → BIGINT、金额 → DECIMAL(12,2) | | 数据库迁移 | **Alembic** | 版本化建表/演进,保证独立、可复制、可迁移;应用启动自动 `upgrade head` | | 反向代理 | **独立 nginx 容器**(docker compose 编排) | 监听 8001(HTTP)/8002(HTTPS),证书挂载 | | 前端 | **原生 SPA**(HTML/CSS/JS,零构建) | 后端直接托管,无需 npm | | 上游对接 | requests 会话客户端 | 复刻 PRD §5 UpstreamClient | | 定时任务 | APScheduler | 余额/用量/明细/定价同步 + 可用性探测 | | 安全 | bcrypt + JWT(python-jose) + AES-GCM(cryptography) | 密码哈希、登录态、完整 Key 加密存储 | | 支付 | 微信支付 Native **Mock** | 完整复刻下单→回调→幂等时序,生产替换点已标注 | ## 2. 快速开始 先建库(一次)并配置 `.env`(不入 git): ```bash mysql -h -P -u root -p < scripts/init_mysql.sql # 执行前替换脚本内密码占位符 cp .env.example .env # 填写 DB_* / UPSTREAM_PASSWORD 等 ``` 再安装依赖并启动: ```powershell pip install -r requirements.txt python run.py # 打开 http://127.0.0.1:8000 ``` - 启动时自动执行 Alembic `upgrade head` 建表/升级;日志打印连接目标(host:port/db,无密码)。 - **首个注册账号自动成为管理员**(可进管理端)。 - JWT 密钥、AES 主密钥自动生成在 `data/` 目录(不入 git)。 ### 自测 ```powershell python tests\test_e2e.py # 端到端:注册→充值→多Key/额度上限→报表计费→控制台→管理端→清理 ``` > 测试强制使用 `TEST_DB_NAME`(默认 `token_hub_test`,库名必须以 `_test` 结尾,防止误清业务库), > 启动时自动重建测试表(Alembic);会真实调用上游创建 Key,**结束后自动删除测试 token**,不在你账号留残留。 > 本地连远程 MySQL 跑测试:先开隧道 `ssh -N -L 13306:127.0.0.1:13306 root@`,再设 `DB_HOST=127.0.0.1 DB_PORT=13306`。 ### Docker 部署(应用 + 独立 nginx) ```powershell docker compose up -d --build # 构建并后台启动 docker compose logs -f # 看日志 docker compose ps # 状态(含健康检查) docker compose down # 停止(./data 密钥卷保留;业务数据在 MySQL) ``` - 对外端口仅 nginx 的 **8001(HTTP)/ 8002(HTTPS)**;应用 8000 仅在容器网络内,由 nginx 反代。 - HTTPS 证书:在 `.env` 设 `CERT_DIR`(服务器证书目录)→ 挂载为容器内 `/etc/nginx/certs`。 - 应用启动时自动执行 Alembic 迁移建表;凭据/参数全部经 `.env`(`env_file`)注入容器。 ## 3. 目录结构 ``` token-hub/ ├── app/ │ ├── config.py # 配置(env 优先,密钥落盘 data/) │ ├── db.py # MySQL 引擎/会话(PyMySQL + pool_pre_ping/pool_recycle) │ ├── migrate.py # Alembic 编程接口(启动自动 upgrade head) │ ├── models.py # ORM:User/Order/KeyMapping/UsageLog/UpstreamSnapshot/PricingCache/Setting/AlertLog │ ├── security.py # bcrypt / JWT / AES-GCM / Key 脱敏 │ ├── schemas.py # Pydantic 模型 │ ├── deps.py # 鉴权依赖 + 登录限流 │ ├── scheduler.py # APScheduler 定时同步 + 可用性探测 │ ├── main.py # FastAPI 入口 + 前端托管 │ ├── upstream/client.py # UpstreamClient(AuthManager+KeyService+ReportService+RateLimiter) │ ├── services/ │ │ ├── store.py # 运行时设置 + quota/元换算 + base_url 解析 │ │ ├── payment.py # 模拟微信支付 + 幂等回调 │ │ ├── provision.py # 支付成功自动发 Key / 续费 / 重置 │ │ └── sync.py # 余额/用量/明细/定价同步 + 告警 │ └── routers/ # auth.py / user.py / admin.py ├── alembic/ # 数据库迁移(env.py + versions/0001_initial.py) ├── alembic.ini # Alembic 配置(连接串由 env 注入,不写死) ├── nginx/conf.d/token-hub.conf # 独立 nginx 反代(8001 HTTP / 8002 HTTPS) ├── scripts/init_mysql.sql # 建库 + 建账号(业务库/测试库,仅执行一次) ├── web/ # 前端 SPA(index.html + css + js) ├── tests/test_e2e.py # 端到端自测(MySQL 测试库 + Alembic 重建) ├── run.py # 启动入口 ├── docker-compose.yml # 应用 + 独立 nginx 编排 ├── Dockerfile # 应用镜像 ├── .env.example # 配置模板(复制为 .env 填写敏感信息) └── requirements.txt ``` ## 4. 已实现功能(对齐 PRD) ### 用户端 - **FR-U1 注册/登录**:私有用户体系、bcrypt、JWT、登录 IP 限流(60s/10 次) - **FR-U2 充值**:Mock 微信 Native 下单 → 扫码 → 回调;订单状态机 pending→paid→credited; **回调幂等**;首充赠额(默认 10%);到账计入**账户余额**(不自动建 Key) - **FR-U3 Key 管理(多 Key + 额度上限)**: - 一个账号可创建**多个 Key**(名称在用户内唯一); - 每个 Key 独立设置:**额度上限(remain_quota)、分组(group)、模型白名单(model_limits)、IP 白名单(allow_ips)、过期时间(expired_time)、启停(status)**; - **额度上限不检查/不扣账户余额**:设定上限只是限制该 Key 最大可用额,与余额无关; - 完整 Key **AES-GCM 加密入库**,列表脱敏展示;创建/重置/启用时展示完整值,列表行内「复制」可随时按需解密取回;重置与启用均删旧建新(旧 Key 立即失效); - base_url 按 proxy_mode 下发;Python/Node/curl 接入示例 - **账户余额模型**:余额 = 累计充值 - 累计消耗;消耗只在 token 真实消费后,由上游报表(`/api/log/self`) 同步入库后按**零售价(上游消耗 × MARKUP_RATIO)**计费结算扣减(`sync.bill_pending`,幂等); 「我的 Key」列表同屏展示**「已用(扣费)」与「上游」双口径**(扣费=上游成本×加价系数,与余额/明细一致); - **计费口径公式**(对账用,全站统一): - 汇率:1 元 = `QUOTA_PER_USD / UPSTREAM_USD_RATE` ≈ **71428.57 quota**(上游 500000 quota = $1,$1 = ¥7); - 充值到账:`quota = round((amount + amount × BONUS_RATIO) × 71428.57)`(赠额按**每笔**充值,默认 10%); - 逐行扣费:`retail_quota = round_half_up(上游该行 quota × MARKUP_RATIO)`(逐行独立四舍五入为整数); - 余额:`wallet = Σ充值到账 − Σretail_quota`;页面金额 = `quota ÷ 71428.57`(用量明细保留 4 位小数) - **欠费自动停用 / 充值自动恢复**:结算后余额 ≤ 0 → 自动冻结其全部启用 Key(上游 remain_quota 置 0, 上游立即拒调;**Key 字符串不变**,冻结前上限记入 `frozen_remain`),并写告警;充值到账余额 > 0 → 自动恢复停前上限; 欠费期间创建新 Key / 调额 / 手动启用均被拒(防绕过);开关:设置 `auto_stop_arrears`(默认 1,置 0 关闭); - **FR-U4 控制台/报表**:余额卡片、用量明细表(时间/模型/上游成本/扣费,按天筛选分页)、近 7/30 天趋势图; **数据实时性**:打开控制台/「我的 Key」/用量页时**节流直刷上游**(同名同步 ≥15s 一次、并发去重、失败静默回退本地数据), 定时任务 3 分钟兜底;明细同步**翻页追平 + 回填追平**(最多 10 页;上游对进行中的请求先记空行、完成后回填 quota/类型/Key 名,已入库行会比对更新并补计费,防漏账) - **FR-U5 接入文档**:模型与价格表(上游同步 × 我方加价系数)、base_url、SDK 示例 - **FR-U6 账户设置**:改密 ### 管理端 - **FR-A1 用户管理**:列表/搜索/禁用(禁用=删除其上游 Key) - **FR-A2 订单管理**:列表 + 人工补单(回调丢失恢复,幂等) - **FR-A3 上游余额监控**:实时母账号余额 + 多级告警(<¥500 预警 / <¥100 紧急) - **FR-A4 定价管理**:加价系数、赠额比例、proxy_mode、欠费自动停用开关(auto_stop_arrears)、阈值等运行时可配 - **FR-A5 风控**:session 失效告警、连续登录失败告警、可用性探测 P0 告警 ### API 代理层(PRD §4) - PoC 以 `proxy_mode` 配置项落地:模式 A(直连上游)/ B、C(下发我方域名 base_url) - 管理端可实时切换 proxy_mode,用户端 base_url 与文档随之变化 - Nginx 独立容器配置随项目提供(`nginx/conf.d/token-hub.conf`,监听 8001/8002) ## 5. 上游接口契约(已实测验证) 所有 session 接口需带请求头 `New-Api-User: `。 | 用途 | 方法 / 路径 | | --- | --- | | 登录 | `POST /api/user/login` `{username,password}` → `data.id` + `session` cookie | | 账户 | `GET /api/user/self` → `quota / used_quota` | | Key 列表 | `GET /api/token/?p=0&size=N` → `data.items[]` | | 创建 Key | `POST /api/token/` `{name,remain_quota,expired_time,unlimited_quota,group,model_limits_enabled,model_limits,allow_ips}` | | 取完整 Key | `POST /api/token/batch/keys` `{ids:[...]}` → `data.keys{id:rawkey}` | | 改额度/分组/限制 | `PUT /api/token/`(提交完整 token 对象) | | 删除 Key | `DELETE /api/token/:id` | | 明细 | `GET /api/log/self?p=&page_size=` | | 定价 | `GET /api/pricing` → `data[]`(model_ratio/model_price/completion_ratio/quota_type) | - 计费单位:上游 quota 以**美元**计(`QUOTA_PER_USD=500000` quota = $1),上游站按 `UPSTREAM_USD_RATE`(默认 7.0)折人民币展示/下单, 故 **1 元人民币 = 500000 / 7 ≈ 71428.57 quota**(全站统一换算,见 `store.quota_per_yuan`) - 下发给用户的 Key 加 `sk-` 前缀(`KEY_PREFIX` 可配) - **上游 `PUT /api/token/` 忽略 `status` 与过去式 `expired_time`**(实测不变),故本 PoC 的「停用」=上游吊销+本地标记,「启用」=重建上游 token(新 Key、额度不变)。 ## 6. 重要说明 / 已知边界 1. **上游母账号余额为 0 / 不足时**(freecpu): - 平台侧「注册→充值→发 Key→报表」闭环**完全可用**(Key 会被真实创建、额度被真实写入 token); - 但用终端 Key **实际调用模型会因母账号无余额而失败**。要跑通真实调用,请先给上游母账号充值。 - 管理端「上游余额监控」会如实显示 ¥0 并触发**紧急告警**——这正是 FR-A3 的预期行为。 2. **支付为 Mock**:完整复刻微信 Native 时序与幂等,`app/services/payment.py` 中标注了生产替换点 (换真实商户号/APIv3 验签即可,订单状态机与发 Key 逻辑无需改动)。 3. **完整 Key 安全**:AES-GCM 加密入库,主密钥 `data/master.key`(勿泄露/勿入 git); 列表脱敏展示,仅在创建/重置响应及用户主动「复制」(按需解密接口)时返回明文。 4. **凭据隔离**:上游账号密码仅存于服务端配置,禁止入前端、禁止入日志。 5. **时区**:应用容器统一 `Asia/Shanghai`(Dockerfile + compose 双保险);若不设,页面时间与入库时间会慢 8 小时(容器默认 UTC)。 ## 7. 配置项(`.env` 或环境变量) 见 `.env.example`(完整模板,仅占位符;`.env` 不入 git)。常用: `DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME`(远程 MySQL)、`CERT_DIR`(nginx 证书目录)、 `PROXY_MODE`、`OUR_API_DOMAIN`、`BONUS_RATIO`、`MARKUP_RATIO`、`UPSTREAM_USERNAME`、 `UPSTREAM_PASSWORD`、`ENABLE_SCHEDULER`。 其中 proxy_mode / 加价 / 赠额 / 告警阈值也可在**管理端运行时修改**(存 Setting 表,优先于 env)。