Chapter 02 · Backend
后端设计
Go 模块化单体,满足前端全部功能即可、不做过度设计。API 契约完整继承 design/server/ARCHITECTURE.md §3 蓝本,唯一语义修订:数据面从 WireGuard 换为 sing-box(REALITY / Hysteria2),connect 改为下发用户凭证 + 节点连接参数。
§1服务形态与模块划分
单体起步、按模块分包(实现顺序即蓝本 §7):
HTTP APIchi · /v1
→
auth验证码/注册/JWT
codes激活码/兑换/webhook
devices设备/订阅校验
nodes目录/connect/调度
usage用量/额度
admin管理端(独立监听)
gRPC server(mTLS)节点 agent:注册 / 心跳 / 凭证下发与回收 / 用量上报
scheduler探针汇聚 · 判封 · 节点生命周期驱动(见 04 章)
→
MySQL主数据
Redis验证码/限流/实时负载
一个二进制三个监听:公网 API(CDN 后)· agent gRPC(mTLS 双向认证)· 管理端(内网/白名单)
- 每模块完成定义(蓝本约定):单测 + OpenAPI 同步 + 错误文案双语(
{code, message_zh, message_en})+ 符合脱敏口径。 - 管理端绝不与公网 API 共用监听端口;部署时仅绑内网地址,前置白名单 + 2FA(见 06 章)。
§2API 契约 v1(继承蓝本 §3,正式版用 OpenAPI 定义)
# 认证(无需 JWT)
POST /v1/auth/code {email} # 发 6 位验证码(Redis 10min,限 1/min)
POST /v1/auth/register {email, code, password} # 建号 + 7 天试用 + JWT
POST /v1/auth/login {email, password} # JWT(access 15min + refresh 30d)
POST /v1/auth/refresh {refresh_token}
# 账户
GET /v1/me # 账户 + 订阅 + 用量摘要
GET /v1/me/devices
DELETE /v1/me/devices/:id # 移除设备(同步回收节点侧凭证)
# 商业闭环(App 内无支付,只有兑换)
POST /v1/redeem {code} # 兑换激活码(幂等 + 审计)
POST /v1/ads/unlock {device_id, ad_token} # 免费版激励视频解锁当日时长(验 SDK 回执)
GET /v1/plans # 套餐目录(数字与 design/CLAUDE.md §7 一致)
# 节点(数据面入口)
GET /v1/nodes ?if_version=N # 节点目录(按套餐过滤,304 支持)
POST /v1/nodes/:id/connect {device_id} # 下发连接凭证(见 §3,语义已适配 sing-box)
POST /v1/nodes/:id/disconnect {device_id}
GET /v1/usage ?days=7 # 用量曲线(统计页)
GET /v1/notices # 公告(亦发布为多镜像签名静态 JSON)
- 除 auth 外全部接口要求 JWT;限流用 Redis 滑动窗口(按 IP + 用户双维度)。
- 错误体统一
{code, message_zh, message_en};文案遵守铁律 13 脱敏口径。 - 节点目录响应带
version;客户端if_version命中返回 304——这是节点秒级灰度的基础(见 04 章)。
§3connect 语义适配(相对蓝本 v0.1 的唯一修订)
修订理由蓝本 v0.1 的数据面是 WireGuard(connect = 下发 WG peer)。WG 协议特征明显,在目标网络环境会被快速识别阻断;plan/ phase-0~3 已选定 REALITY 主线 + Hysteria2 备线。API 形状不变,仅返回体语义调整。
3.1 连接流程
客户端POST /nodes/:id/connect
→
API 校验订阅有效 · 设备数 ≤ 上限 · 免费版校验 ad_unlocked_at + 剩余分钟
→
gRPC → agent确保该用户 UUID 已在节点 sing-box 用户表
→
返回连接参数server, port, uuid, flow, reality pbk/sid/sni · hy2 备用
免费版凭证带 TTL = 当日剩余分钟,到时 agent 自动从用户表移除;付费版 TTL 24h 在线自动续期
3.2 凭证模型
- 每用户一个数据面 UUID(与账号 ID 解耦,可轮换):节点侧只见 UUID,零账号信息——单节点被抄不泄露用户库。
- 凭证轮换:用户改密 / 管理端封禁 / 定期轮换时生成新 UUID,gRPC 广播到所有在册节点替换;旧 UUID 给 5 分钟宽限期平滑重连。
- 订阅过期、设备被移除、套餐降级 → scheduler 驱动 agent 即时回收对应 UUID。
- Hysteria2 凭证同源派生(同一 UUID 作 auth password),客户端按网络状况自动选协议。
§4关键业务流程
4.1 注册与试用(对齐客户端 UI:邮箱 → 验证码 → 设密码)
POST /auth/code:风控(IP + 邮箱限频、一次性邮箱域黑名单)→ 发码(Redis 10min)。POST /auth/register:验码 → 建号 → 自动写入 7 天 PRO 试用(subscriptions(plan=pro, expires_at=now()+7d, source='trial'),一邮箱仅一次)→ 返回 JWT。- 试用到期回落 free:仅 tier=free 节点、每日 10 分钟、每日首连前需激励视频解锁。
4.2 激活码生命周期
发卡店售出webhook(HMAC 签名)
人工渠道TG/LINE/邮箱 · 管理端批量生成 batch
→
codes 入库status=unused · 库存 hash
→
POST /redeem事务:redeemed + 订阅顺延(叠加非覆盖)
→
audit_log必记
- 码格式:Crockford Base32,16 位含校验位;明文只出现一次(生成响应 / 发卡店),库内存 hash。
- 兑换幂等:同一用户重复提交同一码返回首次结果;风控:单用户失败 5 次/小时锁 1 小时。
4.3 免费版广告解锁
- 客户端播完激励视频 →
POST /v1/ads/unlock携带广告 SDK 回执 → 服务端向 AdMob/Unity 校验回执真伪 → 记usage_daily.ad_unlocked_at。 connect对 free 用户强制校验当日ad_unlocked_at与剩余分钟;分钟数由 agent 上报的会话时长累计,服务端为权威。
§5后端安全要点(详见 06 章总纲)
- 密码 argon2id;JWT RS256,签名密钥定期轮换;全站 TLS 1.3。
- 验证码 / 兑换 / 登录接口限流 + 防一次性邮箱 + 失败锁定;所有敏感操作进
audit_log。 - 无日志口径(写进隐私政策并据实执行):不记录目的地址 / DNS 查询 / 流量内容;仅
usage_daily字节数与分钟数。 - agent gRPC 双向 mTLS:节点证书由内部 CA 签发、与节点 ID 绑定,节点被回收即吊销。
- CI:lint + 单测 + OpenAPI 校验 + 镜像构建;OpenAPI 即 API 文档与客户端 SDK 生成源。