跳转至

赞助版(Sponsor Edition)实现说明

本文面向开发与运维人员,全面梳理赞助版的设计、实现、接口、数据结构、前端交互、容错机制与运维要点。当前文档基于项目现状整理,优先描述“已实现”的行为。


1. 背景与目标

  • 社区版默认限制:总密钥数 200、批量生成最多 20。
  • 赞助版目标:通过远程服务器验证许可证,解锁更高或无限的额度,并具备断线容错、切换设备等能力。
  • 远程验证服务器:https://license.zhoujie218.top/(已写死)。
  • 体验与安全:双层校验(本地缓存 + 远程验证)、宽限期、设备切换、错误透明反馈、日志留痕。

2. 总体架构

  • 计算节点(本服务)
  • 生成稳定的服务器机器码(绑定设备)。
  • 与远程服务器进行激活与心跳校验。
  • 将最近一次成功校验结果、本地状态、远程错误信息等写入本地 SQLite。
  • 提供后台接口给前端页面:激活、状态、心跳、设备切换、清除许可证等。
  • 定时任务:每日 3-5 点随机一次心跳。
  • 远程验证服务器
  • 接口:POST /api/license/verifyPOST /api/license/switch-device
  • 返回许可证详情(状态、设备数、过期时间、扩展字段等)。

3. 关键能力

  • 双层校验
  • 远程校验成功后写入本地;页面默认读取本地状态,避免频繁远程调用。
  • 手动刷新/激活时才主动远程校验。
  • 宽限策略
  • 完全信任期(trustDays):默认 7 天。
  • 最长宽限期(graceDays):默认 15 天。
  • 已激活但验证失败时,进入宽限展示与倒计时提示;倒计时基于最后一次远程失败时间计算(到期后回退为社区版)。
  • 动态额度
  • 远程 customField1 → 总密钥数限制(为空则视为无限)。
  • 远程 customField2 → 批量生成限制(为空则视为无限)。
  • 社区版兜底:总数 200、批量 20。
  • 设备切换
  • 允许从旧设备迁移到新设备,向远程发起切换请求,并在本地记录切换窗口状态。
  • 错误透明
  • 远程错误会下沉到后台与前端显示(含 HTTP 状态、业务消息、时间戳)。
  • 一键清除
  • 提供“清除许可证”接口与前端按钮,回退到社区版。

4. 数据模型(SQLite)

表:sponsor_verification

字段(核心): - license_key:赞助版许可证密钥(唯一)。 - machine_id:本机机器码。 - verification_token:远程返回 token(如有)。 - status:本地评估状态(active/grace/grace_limited/expired/pending/switching)。 - remote_verified_atlocal_verified_at:时间戳。 - grace_period_until:保留字段(当前主要基于最后失败时间 + graceDays 动态计算)。 - consecutive_failures:连续失败次数。 - last_remote_error_messagelast_remote_error_http_statuslast_remote_error_time:最近远程错误详情。 - last_license_type_name:最近一次成功校验的版本名(如:终生赞助版)。 - limit_total_licenses:动态总量限制(来自 customField1)。 - limit_batch_count:动态批量限制(来自 customField2)。 - device_switch_requested_atold_machine_id:设备切换相关。

表结构与字段在 config/database.js 的初始化/迁移逻辑中维护,启动时会自动补齐缺失列。


5. 服务器机器码

文件:utils/sponsorManager.js

思路:尽量使用稳定的硬件/环境指纹,容器内也相对稳定;采集并排序后进行 SHA-256 哈希,前缀 server_。 - 关键要素:platformcpus.lengthtotalmem、CPU 型号、OS 信息、架构、hostname、容器标识、网络接口签名等。


6. 后端接口

路由前缀:/api/sponsor

  • POST /activate
  • 入参:{ licenseKey }
  • 行为:调用远程 /api/license/verify,成功则写库并返回 license 全量信息。
  • 失败:返回 success:false,包含 remoteError(HTTP 状态与业务消息)。
  • GET /status
  • 行为:仅读取本地缓存状态(不触发远程)。
  • 返回:isSponsoredstatuseditionNamelicenseKeylastRemoteAtlastLocalAtlastRemoteError{message,httpStatus,time}graceDaystrustDaysdynamicLimits{totalLicenses,batchCount}
  • POST /heartbeat
  • 行为:基于本地 license_key 调远程校验;成功 200,失败 502 并附带 remoteError
  • GET /machine-id
  • 返回:本机机器码。
  • POST /device-switch-request
  • 入参:{ newMachineId, reason? }
  • 行为:向远程发起设备切换申请,并在本地记录状态。
  • POST /clear
  • 行为:清除 sponsor_verification 全部记录,回退社区版。

远程路径已写死:VERIFY_URL = https://license.zhoujie218.top/api/license/verifyDEVICE_SWITCH_URL = https://license.zhoujie218.top/api/license/switch-device


7. 管理端与额度控制

文件:routes/admin.js

  • 总量限制检查:
  • 若赞助版生效(active/grace/grace_limited),优先使用 dynamicLimits.totalLicenses;为空则视为无限;否则回退 MAX_LICENSES(默认 200)。
  • 批量生成限制(POST /generate-licenses):
  • 赞助版生效时用 dynamicLimits.batchCount;为空则不限。
  • 社区版:默认限制 20。
  • 验证规则更新为 count 1-1000(前端校验自适应)。

8. 前端交互(public/index.html + public/app.js

页面“赞助”下的卡片: - 版本状态卡 - 机器码、当前版本(来自 editionName,如:终生赞助版/社区版)。 - 当前许可证(有则展示密钥 + “清除许可证”按钮,无则显示“无”)。 - 赞助版状态(中文徽标:已激活/未激活)。 - 宽限政策(仅在“已激活但验证失败”时显示,提示倒计时:还剩 N 天)。 - 最近一次刷新反馈(成功/失败 + HTTP 状态 + 时间)。 - 激活表单 - 仅在提交时触发远程校验成功与否,成功会立即渲染远程 license 详情。 - 标签右侧提供“发电获取许可证”链接(外开、红色加粗): - 地址:https://afdian.com/item/26cd965e751911f086f352540025c377 - 手动刷新 - 触发远程心跳校验;成功/失败信息直观显示;页面状态使用本地缓存刷新,避免覆盖刚展示的远程详情。 - 远程详情块 - 展示 licenseKeyapplicationNamelicenseTypeName、设备/使用次数、激活/过期时间等。


9. 定时任务与运行策略

  • 定时心跳:在 server.js 内,服务启动后注册每日 3-5 点随机时间执行一次 heartbeat();执行完自动排程到次日。
  • 前端普通进入“赞助”页面不会主动远程校验,只读取本地状态,减少远程压力。

10. 错误处理与容错

  • 远程调用包装:
  • 网络/超时错误、HTTP 错误、业务错误统一封装为 remoteError(包含 httpStatusdatamessage)。
  • 本地降级:
  • 激活/心跳失败时不会立刻降为社区版;若之前激活过,则进入宽限展示,直至宽限到期。
  • 日志:
  • 成功/失败都会记录,最近一次消息会同步到 DB 并在前端展示。

11. 配置项(.env

  • SPONSOR_HEARTBEAT_INTERVAL_HOURS:保留(默认 24);当前采用每日随机时刻策略。
  • SPONSOR_GRACE_TRUST_DAYS:完全信任期(默认 7)。
  • SPONSOR_GRACE_MAX_DAYS:最长宽限期(默认 15)。
  • SPONSOR_MAX_CONSECUTIVE_FAILURES:连续失败阈值(默认 10)。
  • SPONSOR_DEVICE_SWITCH_WINDOW_HOURS:设备切换窗口(默认 24)。
  • SPONSOR_ALLOW_OFFLINE_TRUST:允许离线信任(默认 true)。

远程 URL 已硬编码,不再通过环境变量配置。


12. 安全与反绕过

  • 稳定机器码(容器友好)+ 设备切换申请。
  • 本地缓存 + 倒计时宽限(防止拔网线永久绕过)。
  • 动态额度来自远程票据,前端仅展示、后台严格校验。
  • 错误透明化,便于审计与快速处理异常。

13. 运维与故障排查

  • 查看本地状态:GET /api/sponsor/status
  • 手动心跳:POST /api/sponsor/heartbeat
  • 清除许可证:POST /api/sponsor/clear
  • 常见现象:
  • 前端按钮“手动刷新”后,若远程失败,会在“最近一次刷新反馈”显示详细原因(如:密钥暂停、密钥不存在、403/404等)。
  • 远程失败但已激活过:在“赞助版状态”下展示宽限倒计时提示。

14. 参考链接


15. 变更摘录(关键实现点)

  • 写死远程地址与接口路径;解析远程响应 license 字段;错误结构统一。
  • 状态接口仅返回本地缓存;心跳失败返回 502 给前端;前端展示不再“闪退”。
  • 添加“当前许可证”与“清除许可证”按钮;修复 ID 冲突导致的激活报错。
  • 动态额度落库并在管理接口生效;社区版默认 300/20。
  • 每日 3-5 点随机心跳,减少远程负载。