COOL SaaS 模块微服务起始项目

这是一个可复制到 cool-midway-biz/microservices/<service-name> 的干净起始项目。 它只包含托管 SaaS 模块微服务运行所需的基础结构,不包含 Demo 商品业务、Pilot、 Examples、测试 Fixture 或任何既有模块的生成契约。

初始化

  1. 将本目录的项目文件复制到 cool-midway-biz/microservices/<service-name>,不要复制模板 仓库自身的 .gitscaffold 工具也必须移除模板 origin再由业务项目重新初始化。

  2. 修改 package.json 中的包名,并按需修改 src/config/app.default.ts 中的本地默认端口 7101

  3. 在开发环境的模块管理页输入基础版本(例如 1.0.0)并锁定 Candidate平台会分配 Build Revision、1.0.0-rc.<revision> 候选版本和同名 release 分支。

  4. 安装依赖后,用平台给出的精确 Revision 生成并启动开发实例:

    pnpm install
    pnpm exec cool saas-model generate --module <module-code> --revision <build-revision>
    pnpm dev
    

首次生成会初始化 Git如果需要、要求配置 origin,并切换到平台指定的 release/<candidate-version> 分支。CLI 会在项目根目录写入可提交的 .cool-saas-candidate.json其中只有模块、Revision、版本、分支与 checksum不包含 控制面 token、Runtime ticket 或其他 Secret。

src/config/app.default.ts 会直接导入 src/saas/generated。因此全新模板在首次生成 完成前不能编译,这是为了避免用占位契约冒充平台锁定的 Build Revision。

start:local 会显式开启仅限 local/test 的 Runtime bootstrap fallback本地控制面 启动命令也必须开启同一个 flag。两端未另设 token 时只使用仓库内公开的开发常量, 生产环境始终拒绝这条路径。

CLI 生命周期

在微服务根目录执行:

pnpm exec cool saas-model generate `
  --module <module-code> `
  --revision <build-revision>

pnpm build

管理端的“生成到本地”仍只负责选择目录并写入生成文件,不会初始化 Git、修改 remote 或切换分支;完整 Candidate 开发流程应使用上面的 CLI generate 命令建立这些 Git 约束。

使用默认输出目录时CLI 会写入 src/saas/generated/<module-code>,并自动维护 src/saas/generated/index.ts 稳定导出入口。

生成后可随时执行 pnpm exec cool saas-model verify 检查当前分支、候选锁、生成文件与 控制面的精确 Candidate 是否一致。pnpm dev 会先执行同一校验,再调用 package.jsoncoolSaas.lifecycle.dev 的非递归命令构建并启动原有开发宿主;因此 配置里不要写回 pnpm dev。项目接入自己的 watch/reload 工具后,可将该命令替换为对应 的开发启动命令。

CLI 生成的 models.tscontracts.tssaas-actions.tssaas-events.tsmodule.tsindex.ts 和锁文件属于同一个不可变 Build请整体保留且不要手工修改。

固化 Candidate

开发联调完成后,先提交并将当前 release 分支推送到 origin,然后执行:

pnpm run publish

该脚本只调用 cool saas-model finalize。CLI 从 package.jsoncoolSaas.lifecycle 读取并依次执行 testbuild(本模板分别为 typecheck 和原有 build确认工作区仍然 干净且当前提交已推送后,创建并推送 v<candidate-version> Tag最后向控制面幂等提交 freeze 证据。生命周期子命令不得调用 pnpm devpnpm publish 或再次调用 cool saas-model dev/finalize,以免递归。

freeze 成功后,原来的 development 实例会因身份不再匹配而停止参与路由。再次执行 pnpm devCLI 会读取候选的 Frozen Artifact只允许从完全一致且干净的 Git commit 以 runtimeMode=frozen 启动。请用这个固化实例完成最终验收;验收通过后才能在管理端 晋升 Stable。accepted 状态也可以用同一命令重启精确固化实例,但已晋升、拒绝、替代 或放弃的候选会被拒绝。

控制面地址与凭证继续使用 COOL_SAAS_CONTROL_PLANE_URLCOOL_SAAS_TOKEN,或 CLI 的 --endpoint--token 参数。CLI token 只用于校验/freeze 请求,不会写入候选锁,也 不会传给测试、构建或开发宿主子进程。freeze 调用 /internal/saas/module_release/freeze,只发送 Bearer 凭证,不依赖浏览器 Cookie。

依赖与发布

@cool-midway/core@cool-midway/module-runtime@cool-midway/rpc 使用 https://npm.blendercg.art/ 中已发布的精确版本。安装前通过环境变量提供私服凭证, 不要把 token 写入仓库:

$env:NODE_AUTH_TOKEN='<registry-token>'
pnpm install

本地环境可以使用 Runtime 提供的开发配置。应用开发者在部署描述中只需要关注:

PORT=7101
SAAS_CONTROL_PLANE_URL=https://control-plane.example.com

PORT 是进程在容器内监听的健康检查端口,不是 Action RPC 的入口。同一镜像的所有 副本都可以监听 7101。宿主机可分别映射为 17101:710117102:7101,也可以完全 不发布端口;无论选择哪种方式,容器内配置都保持不变。

仓库当前网关与模块宿主默认连接 Redis 6397。部署环境如果使用其他 Redis 服务端口, 由编排器同时覆盖双方的 REDIS_PORT,不能只修改一侧。独立模块宿主默认采用 RPC_RUNTIME_MODE=all,即同一进程同时接收 Action 并运行托管 Outbox/Inbox Worker 只有明确拆成 rpcworker 两类副本时才覆盖该值,不能用兼容旧名称 server 冒充完整托管宿主。

生产环境由受信部署编排器在应用配置之外完成以下工作:

  • 签发绑定精确 Build/Release 的 bootstrap ticket并注入 BUILD_SHA、Redis 和 RPC HMAC 材料Release 身份及摘要由控制面权威响应回填,不要求手工填写。
  • 为每个副本分别挂载短期、一次性且绑定模块/Build/Release/制品的 SAAS_RUNTIME_BOOTSTRAP_TOKEN_FILE,不能让两个副本共用一个票据。票据签发结果会 同时返回并绑定该副本的 instanceId
  • 为每个副本提供独立、可写的 SAAS_RUNTIME_BOOTSTRAP_STATE_FILE;其中的实例 UUID、 Runtime Node ID、租约及 fencing 状态由 Runtime 管理,开发者不要手工填写。
  • 使用票据签发结果中的 instanceId 生成对应的 SAAS_TENANT_CREDENTIALS_FILE,再与 票据一起挂载给该副本。源码和 Compose 文件都不能保存 Secret 值。

生产及其他严格环境不接受手工配置 RPC_INSTANCE_IDSAAS_RUNTIME_NODE_IDSAAS_SERVICE_TOKEN_FILE 来绕过 bootstrap。进程只能通过一次性 ticket 建立新会话, 或恢复 Runtime 已验真且仍有效的独占 state静态三元组仅作为 local/test 的迁移兼容, 不能用于部署。

bootstrap.js 会先执行 Runtime pre-bootstrap再校验精确 Release 环境,最后启动 Midway。GET /health/ 读取 rpcHostReadiness 的真实快照bootstrap、Node ID 租约或 RPC 注册任一未就绪时都会返回 HTTP 503,只有全部就绪才返回 200

仓库根目录的 docker-compose.microservices.yml 是同镜像双副本示例。两个副本容器内 均使用 7101,并分别映射到宿主机 1710117102,每个副本有独立的 bootstrap ticket、tenant credential Secret 和 Runtime state volume。生产环境若只需容器网络 内的 readiness probe可以用部署 override 清空 portsAction RPC 不受影响:

services:
  demo-stable-a:
    ports: []
  demo-stable-b:
    ports: []

Compose 顶层 Secret 的 ${..._FILE} 是编排器生成的宿主机文件路径,不是应用配置。 这些源文件在 Linux 上必须限制为服务账户私有权限(例如 0600),且不能提交到 仓库。生产平台可等价替换为 Kubernetes/Swarm external Secret容器内的固定挂载路径 和 *_FILE 环境变量无需改变。

SAAS_CONTROL_PLANE_URL 必须是容器自身能够访问的地址;容器中的 127.0.0.1 指向 容器本身,不能用来表示宿主机或另一个控制面容器。生产环境必须使用 HTTPS。

Description
No description provided
Readme 319 KiB
Languages
JavaScript 61.9%
TypeScript 38.1%