16 KiB
许可证服务接入说明
本文档只说明当前项目与 license-server 的真实接入方式。
1. 必填环境变量
主应用至少需要:
LICENSE_SERVER_URL=https://your-license-server.example.com
LICENSE_PROJECT_CODE=flnxi-sucai
LICENSE_REQUEST_SIGNING_SECRET=replace-with-same-secret-on-license-server
LICENSE_PUBLIC_KEY=<license-server-rsa-public-key>
LOCAL_ENCRYPTION_KEY=<generate-a-32-char-or-longer-local-key>
含义:
LICENSE_SERVER_URL许可证服务地址LICENSE_PROJECT_CODE当前主应用所属项目编码,必须和license-server主库中的projects.code一致LICENSE_REQUEST_SIGNING_SECRET主应用和许可证服务共享的 HMAC 签名密钥,至少 32 字符,两边必须一致;不要写入 Git 或对外文档LICENSE_PUBLIC_KEY许可证服务签发许可证时使用的 RSA 公钥,主应用本地验签和缓存恢复会用到LOCAL_ENCRYPTION_KEY主应用本地加密保存许可证缓存时使用的密钥,至少 32 字符,不能使用示例值
Node Agent 如果需要版本检查,也必须配置同样两项:
LICENSE_SERVER_URL=https://your-license-server.example.com
LICENSE_PROJECT_CODE=flnxi-sucai
Go Agent 目前走的是另一条现状链路,文档和脚本还没有和 Node Agent 完全统一;接入时不要默认它也已经接好了同一套 LICENSE_SERVER_URL / LICENSE_PROJECT_CODE 版本检查流程。
license-server 自身还需要配置授权模块的镜像站位置:
MODULE_PULL_TOKEN_SECRET=replace-with-32-byte-or-longer-module-pull-token-secret
PREMIUM_MODULE_REGISTRY_BASE=registry.example.com/flnxi/premium
PREMIUM_MODULE_VERSION=1.0.0
如需要单独指定付费模块镜像,可使用 PREMIUM_MODULE_PAID_CORE_IMAGE 覆盖;固定 digest 可使用 PREMIUM_MODULE_PAID_CORE_DIGEST。原则是:模块核心本体在镜像站,验证服务器只发授权清单、实例绑定凭证和短期拉包 token。
2. 联调入口规则
历史文档里曾记录过内部联调 IP、测试机连通性和临时域名状态。那些信息属于一次性现场快照,不是当前项目默认事实来源,也不应写入客户交付环境。
正式部署或联调时,必须重新确认:
- 当前真实
LICENSE_SERVER_URL - 当前真实
LICENSE_PROJECT_CODE - 主应用和许可证服务是否使用同一个
LICENSE_REQUEST_SIGNING_SECRET license-server后台是否存在对应项目- 主应用所在机器到许可证服务的网络连通性
- 证书、域名、反向代理是否匹配当前入口
3. 当前公开接口规则
现在所有真正可用的 license-server 公开接口都必须带项目编码:
POST /api/license/{projectCode}/activate
POST /api/license/{projectCode}/verify
POST /api/license/{projectCode}/heartbeat
POST /api/license/{projectCode}/register-free
POST /api/license/{projectCode}/premium-modules
POST /api/license/{projectCode}/premium-modules/{moduleId}
POST /api/license/{projectCode}/premium-modules/{moduleId}/pull-token
POST /api/license/{projectCode}/generate
GET /api/registry/auth
GET /api/version/{projectCode}/check
GET /api/version/{projectCode}/agent
首次部署验证服务时,docker compose up -d 只会启动服务,不会自动创建数据库表、超管和项目登记。最短初始化顺序是:
docker compose exec -T license-server npm run db:push
docker compose exec -T license-server npm run init-admin -- admin
docker compose exec -T license-server npm run init-project -- flnxi-sucai
init-project 会把 flnxi-sucai 写入验证服务主库的项目表,默认指向同一个 Compose PostgreSQL 的 public schema。没有这一步,素材系统请求 /api/license/flnxi-sucai/... 时会找不到项目。
旧的无项目接口:
/api/license/activate
/api/license/verify
/api/license/heartbeat
/api/license/register-free
/api/license/generate
/api/version/check
/api/version/agent
现在统一返回 410,不能再继续使用。
4. 主应用当前调用方式
主应用本地仍然暴露自己的代理接口:
/api/license/activate
/api/license/activate-free
/api/license/verify
/api/license/heartbeat
/api/license/check
/api/license/info
其中:
activate、activate-free、verify、heartbeat会根据LICENSE_PROJECT_CODE转发到远端项目维度接口。activate、activate-free、verify、heartbeat转发到远端前会加入timestamp、nonce和 HMACsignature;远端会拒绝签名缺失、签名错误、过期和重复nonce请求。activate、activate-free、verify、heartbeat返回的签名许可证必须包含projectCode、licenseKey、instanceId和fingerprintHash;主应用保存和读取本地许可证时会核对这些绑定字段,缺少绑定字段的旧许可证需要重新激活。activate、activate-free、verify、heartbeat在许可证保存或续证成功后会尝试同步premium-modules授权模块清单、对应授权凭证和短期镜像拉取凭证。FREE 清单为空,不拉额外模块;STANDARD/PRO 清单包含统一paid-core。FREE 人数/素材数上限由基础镜像读取本地已验签许可证执行。check用于在本地许可证缓存存在时恢复激活状态,会先读取并验签本地许可证。info用于读取当前许可证类型、用量和功能开关。
联网边界按下面理解:
- 必须联网:首次激活、免费版注册、续期心跳、验证服务器授权变更同步、获取短期镜像拉包 token、拉取授权模块镜像、升级版本。
- 不要求每次实时联网:用户登录后的日常上传、创建用户、素材浏览、模板使用等操作,优先使用本地已验签许可证缓存、已缓存模块授权凭证和 Agent 回写的本机模块运行状态。
- 断网期间如果本地许可证仍在有效租约内,且需要的模块容器仍为
running并通过/health和/authorize,对应受控能力可以继续使用;一旦许可证过期、模块被吊销、需要重新拉包或本地模块不可用,就必须重新连验证服务器恢复。
主应用内部已经不会再直接访问无项目的 license-server 路径,而是会根据 LICENSE_PROJECT_CODE 转发到:
${LICENSE_SERVER_URL}/api/license/${LICENSE_PROJECT_CODE}/...
所以主应用部署时,LICENSE_PROJECT_CODE 现在是必填项。
补充:
- 远端
POST /api/license/{projectCode}/generate是付费激活码生成接口,当前要求X-API-Key、timestamp和sign,不应被普通前端直接调用。 - 远端
POST /api/license/{projectCode}/premium-modules是授权模块清单接口,要求同一套 HMAC 请求签名;清单来源是LicenseModuleGrant授权台账。默认规则是:FREE 清单为空,STANDARD/PRO 返回统一paid-core;禁用、过期、吊销或硬件指纹明显不匹配会被拒绝。 - 远端
POST /api/license/{projectCode}/premium-modules/{moduleId}是单个授权模块凭证接口,要求premium-module-downloadHMAC 请求签名,并复查许可证状态、硬件指纹和模块授权台账。 - 远端
POST /api/license/{projectCode}/premium-modules/{moduleId}/pull-token是短期镜像拉取凭证接口,要求premium-module-pull-tokenHMAC 请求签名;返回registryHost、repository、tag、digest、用户名flnxi-module、15 分钟有效的pullToken和expiresAt。 - 远端
GET /api/registry/auth是给 Registry 前置代理使用的鉴权接口,用来校验客户拉取请求中的短期 token、token 审计台账、仓库路径、清单 digest、许可证状态、激活实例和模块授权状态。它不是普通前端接口。 premium-modules返回签名 manifest(模块清单),每个模块会带镜像站上的模块本体地址、实例绑定信息、授权凭证大小和 SHA256;主应用随后获取实例绑定的小型授权凭证和短期拉包凭证,校验大小、SHA256、镜像地址和过期时间后再本地加密缓存。- 授权模块核心本体应放在镜像站或等价的私有制品仓库,验证服务器只负责授权判断、签名清单和授权凭证,不承担大文件下载站职责。
- 主应用的人数/素材数上限、自定义模板和内网穿透服务端入口已经依赖本地已验签 manifest 和对应授权凭证;许可证功能开关或本地数量字段被篡改但没有有效授权凭证时,服务端仍会拒绝。
activate / verify / heartbeat / register-free / premium-modules / premium-modules/{moduleId} / premium-modules/{moduleId}/pull-token当前会校验请求 HMAC 签名;返回的许可证、授权模块清单、授权凭证和短期拉包凭证必须通过签名、hash、大小、租约、实例、许可证类型、镜像地址和过期时间检查后才能缓存或放行。
5. 付费模块拉取规则
当前采用“两层凭证”:
- 模块授权凭证:证明这个客户、这台机器、这张许可证允许使用某个模块。
- 镜像拉取凭证:15 分钟短期 token,只允许拉取 token 内声明的模块仓库和 tag;镜像清单 digest 和客户侧 Agent 的整镜像 digest 校验用于防止模块版本被替换。
镜像站推荐由 Nginx / Registry 代理做私有拉取鉴权:发布账号仍只用于内部推送镜像;客户侧拉付费模块时使用用户名 flnxi-module,密码使用验证系统签发的短期 pullToken。代理把 Registry 主机、仓库、请求类型、digest 和 token 转发给 GET /api/registry/auth 校验,通过后才放行拉取。Registry 原始后端不要直接对客户开放。
主应用不会直接挂 Docker Socket,也不会自己启动模块容器。当前 Go Agent 已提供 POST /api/modules/sync 和 GET /api/modules/status 入口,用来接收主应用准备好的短期拉取凭证,负责拉取模块镜像、校验 digest、启动模块容器、健康检查和失败回滚。模块运行状态变化后,Agent 可通过 POST /api/license/premium-modules/runtime-status 回写主应用;主应用会把回写状态持久化,并把运行态纳入付费能力判断。
当前模块源码入口是:
premium-modules/paid-core
paid-core 承接 STANDARD/PRO 的统一付费能力运行门禁;主应用会同时检查模块授权凭证、Agent 回写的 running 状态、模块 /health 和 /authorize,任一环节不可用都会拒绝对应受控能力。自定义模板字段保存前校验和格式化已迁入 paid-core 的 /templates/validate-fields,模板保存请求整体标准化已迁入 /templates/normalize-save,模板删除前使用中拦截规划已迁入 /templates/plan-delete,素材提交的自定义字段内容校验已迁入 /materials/validate-template-fields,素材 metadata 准备和大小检查已迁入 /materials/prepare-metadata,已有素材模板的结构变更风险判断已迁入 /templates/detect-structure-changes,模板迁移批次大小、安全确认、预览模式和分批执行规划已迁入 /templates/plan-migration-batch,模板迁移目标字段投影、兼容性判断、metadata 准备和旧字段备份计算已迁入 /templates/prepare-migration-metadata,迁移预览和备份字段影响统计已迁入 /templates/build-migration-preview,公开模板列表、上传页模板列表和素材类型 tag 的展示投影已迁入 /templates/project-public-options,后台模板详情字段填写率统计已迁入 /templates/calculate-field-stats,素材列表搜索/筛选查询计划已迁入 /materials/plan-list-query,素材列表自定义字段搜索规划已迁入 /materials/plan-custom-search,首页筛选字段统计来源规划已迁入 /materials/plan-home-filter-fields,部门隔离范围规划已迁入 /departments/plan-scope,部门结构创建/重命名/删除联动计划已迁入 /departments/plan-structure-change,API 访问预留能力也已纳入 /authorize 的 apiAccess:use 门禁。当前标准版和专业版默认仍未把 apiAccess 作为客户功能出售;如果未来开启,也必须同时通过本地许可证、模块凭证、模块运行状态和 paid-core 授权。这些接口都是本地模块入口,不是每次保存都联网请求验证服务器;/templates/normalize-save 负责模板名称、状态、素材类型、素材大类、字段组、展示配置和字段列表标准化,/templates/plan-delete 负责删除模板前按使用素材数量生成放行或拦截计划,/materials/validate-template-fields 负责上传/编辑素材时的自定义字段可见性、必填和规则校验,/materials/prepare-metadata 负责自定义字段写入 metadata 前的整理、压缩和大小检查,/templates/plan-migration-batch 负责迁移批次大小、安全确认、预览模式和分批执行规划,/templates/prepare-migration-metadata 负责迁移时从旧素材字段中选出目标模板字段、判断目标模板必填字段是否可自动补齐、生成目标 metadata、计算丢弃字段备份并拦截超限,/templates/build-migration-preview 负责把丢弃字段清单转换成迁移预览、备份统计和受影响样例,/templates/project-public-options 负责把主应用查到的数据库快照转换成公开展示 DTO,/templates/calculate-field-stats 负责从主应用传入的素材 metadata 快照中计算字段填写率、低填写率字段和排序结果,/materials/plan-list-query 负责从主应用传入的搜索字段、筛选字段和启用字段快照中生成素材列表查询计划,/materials/plan-custom-search 负责从主应用传入的可搜索字段快照中挑出应查询 metadata 的自定义字段 key,/materials/plan-home-filter-fields 负责从主应用传入的首页筛选字段快照中生成标准列或 metadata 统计来源计划,/departments/plan-scope 负责从主应用传入的部门快照中计算管理范围,/departments/plan-structure-change 负责根据部门和用户快照生成创建、重命名、删除的联动计划。FREE 人数/素材数上限不走额外模块,内置模板基础校验也仍在基础镜像内执行。注意:paid-core 承接的是付费能力的规则、计划、校验和展示投影;主应用继续保留登录、权限、Prisma 查询、SQL 执行、数据库事务和最终写入,这是当前商业化安全边界。
6. 后台管理入口
license-server 后台现在按项目维度工作:
/admin/select-project
/admin/{projectCode}
/admin/{projectCode}/licenses
/admin/{projectCode}/alerts
/admin/{projectCode}/versions
/admin/{projectCode}/customers
/admin/{projectCode}/api-keys
说明:
- 普通管理员只能进入被分配的项目
- 超级管理员可以进入所有项目
- 超级管理员还可以在项目维度管理该项目的 API Key
旧的无项目后台页现在只做跳转或废弃提示,不再承载真实管理逻辑。
7. 接入前检查
上线前至少确认这 5 项:
license-server主库里已存在目标项目,且projects.code = LICENSE_PROJECT_CODE- 该项目对应的项目库连接配置正确
- 主应用
.env已配置LICENSE_SERVER_URL、LICENSE_PROJECT_CODE、LICENSE_REQUEST_SIGNING_SECRET、LICENSE_PUBLIC_KEY和LOCAL_ENCRYPTION_KEY license-server已配置MODULE_PULL_TOKEN_SECRET、PREMIUM_MODULE_REGISTRY_BASE、模块版本和需要固定的模块 digest- Registry 前置代理已能把客户拉取请求转发到
GET /api/registry/auth - Node Agent 如启用版本检查,也已配置相同的远端通信两项
- 不再调用任何旧的无项目公开接口
8. 推荐联调方式
可以按这个顺序联调:
- 先在
license-server后台进入/admin/select-project - 选中目标项目,确认项目后台数据正常
- 再从主应用激活页走一次激活
- 最后检查主应用的验证与心跳是否正常
如果主应用激活失败,优先检查:
LICENSE_PROJECT_CODE是否写错LICENSE_REQUEST_SIGNING_SECRET是否和许可证服务配置一致- 目标项目是否已禁用
license-server是否仍被旧地址或旧接口调用