# gm-libs：每个 Webanion 前端与后端起步所依托的软件包

Company: Webanion
Service: 附加服务
Period: 2026-06 - present
Tech: NestJS, TypeScript, Node.js, PostgreSQL, OpenFGA, Temporal, Docker, Software Architecture, SaaS, Verdaccio
Web: https://webanion.com
Canonical: https://webanion.com/zh/portfolio/gm-libs-reusable-nestjs-packages-for-auth-data-and-crud

一个带版本、与框架无关的软件包家族，Webanion 的每个前端和后端都以它为起点，服务端有一层 NestJS，客户端有 React、Vue 和 Expo 绑定：支持通行密钥和单点登录的身份认证、功能开关、审计日志、多租户数据、加密、文件、文档、消息、实时与离线、webhook、worker 和一套 UI 组件库。每个包都有自己的私有仓库和独立发布，从工作室的私有 npm registry 安装，工作室的控制台会显示哪个消费方在哪个版本上落后。

## 完整故事

为什么把鉴权、审计和功能开关一次做好、纳入版本管理并在各处复用，会让一家工作室的客户受益更多。

### 每个后端都曾从零开始

<p>客户付费开发的每个后端，在做任何客户真正关心的事情之前，都需要同样的基础：登录、会话、权限、配置、文件存储、邮件、PDF、后台任务和审计日志。如果每个项目都重新编写，这些部分要耗费数周时间，每个项目各不相同，还各自带着自己的 bug。</p><p>gm-libs 就是答案：把这些底层基础设施从早已验证过它们的项目中提取出来，加固成带有版本、文档和变更日志的包，并在工作室构建的各个前端和后端中复用。</p>

### 登录与权限，一次解决

<p>鉴权包提供 Argon2id 密码哈希、短时效的访问令牌配合轮换的刷新会话，以及基于 OpenFGA 的关系型权限，因此像“分支机构员工只能看到自己所在的分支机构”这样的规则，是一个声明式的模型，而不是散落在代码各处的检查。它还支持带账号关联的社交登录和 OIDC 登录、无密码登录、多因素认证、通行密钥和单点登录。新后端只需导入一个模块，就能获得全部这些能力。</p><p>对客户而言，这意味着安全机制已经在一个金融平台上经过加固和测试，而不是在截止日期的压力下重新发明。</p>

### 安全地开启功能

<p>功能开关包决定为每个租户、分支机构、角色或个人展示什么、运行什么，而且从不阻塞请求：开关从内存中读取，如果无法连接开关服务器，每个开关都会回退到安全的默认值，除非团队另有选择，否则默认关闭。它与授权配合使用，而不是取代授权，因此只有开关和权限都同意时，功能才会出现。</p><p>客户可以先把一个功能发布给一个分支机构或一个人，观察效果，再无需重新部署就扩大范围，Al-Insaf 团队就是这样管理自己的功能的。</p>

### 从不拖慢任何人的审计日志

<p>审计包会在每一次写操作时记录谁在何时、何地做了什么，按类别和严重程度分类，并把每条记录移出请求路径，再分批写入。审计存储变慢或出错时会被记录到日志中，但绝不会阻塞正在使用系统的人，正因如此，才能放心地审计一切重要操作。</p>

### 每个包，只做一件事

<p>除了 auth、flags 和 audit，这个家族还覆盖了前端和后端所需的其余部分，每个包只做一件事，接入方式也完全一致：底层是与框架无关的代码，服务端有一层 NestJS，包延伸到客户端的地方则提供 React、Vue 和 Expo 绑定。</p><ul><li><strong>基础与数据：</strong>core（错误、请求上下文、会隐去凭据的日志器、缺少密钥时立即报错的类型化配置、金额、分页、限流、幂等、健康检查）和 db（多租户 Postgres，每个租户独立一个数据库，附带迁移和模型）。</li><li><strong>身份与访问：</strong>auth、flags、audit，以及用于字段和密钥信封加密、密钥可轮换的 crypto。</li><li><strong>文件、文档与消息：</strong>用于存储和签名上传的 media、pdf，用于流式 CSV 和 XLSX 导出及带校验导入的 tabular，用于邮件、短信、推送和团队聊天的 notifications，以及用于实时对话的 chat。</li><li><strong>实时与离线：</strong>用于经过认证的按频道事件的 realtime，带有适用于 React、Vue 和 Expo 的自动重连客户端；以及 offline，为每个用户提供缓存和只发送一次的写入队列，可用于浏览器和 Expo 应用。</li><li><strong>集成与运行时：</strong>按 Standard Webhooks 规范签名的 webhooks，用于 Temporal 的 worker，提供加固的无头浏览器和拒绝内部地址的出站请求的 web，用于流式加密数据库备份的 datavault，以及用于构建 MCP 服务器的 mcp。</li><li><strong>界面：</strong>ui，从设计令牌到 React、Vue、Next.js 和 Expo 组件。</li></ul><p>此外，devkit 是每个库在发布前都要运行的检查。每个包都有自己的私有仓库和独立的发布版本，因此只有当客户的团队采用新版本时，客户的系统才会改变。</p>

### 一次修复，惠及所有系统

<p>由于工作室的前端和后端共享同样的基础，一次安全修复或改进只需提升版本号，就能到达每一个前端和后端，并附有说明改了什么、为什么改的变更日志。各次发布都严格遵守语义化版本，因此不会因为另一个客户需要新功能，就让某个客户的系统出问题，而控制台当天就能显示哪个消费方还没有采用新版本。</p><p>对客户来说，结果是更快的交付、更少的意外，以及一个由已在别处经过生产环境检验的组件构建的产品，其中包括<a href="/portfolio/al-insaf-cooperative-finance-platform-with-ledger-and-loans">Al-Insaf 的故事</a>中那个达到账本级严谨度的金融平台。</p>

### 每个包，已发布且受到监看

<p>每个包都从工作室的私有 npm registry 安装，工作室的控制台也像消费方一样读取这个 registry。它会 ping registry，读取每个包的每个版本及其发布时间、打包大小和完整性，并读取各消费方默认分支上的清单文件，判断每个依赖是否解析到最新发布。一个库家族的价值，取决于能否证明使用它的每个系统都是最新的，而这份证明就在这里。</p><p>在截图当天，也就是 2026 年 9 月 30 日，视图显示 registry 运行正常、每个包都已发布、registry 本身有一份最近的备份，并且除工作室自己的工具之外，每个消费方都在使用最新发布，那些工具仍停留在 core 和 pdf 的较早版本上。</p>

### 从 GitHub Release 到 npm install，经由工作室的 registry

<p>给某个库打上标签，就会在 GitHub 上运行它的发布流程，并在 Release 中附上一个打包文件。集群内的同步任务每五分钟发布一次这个完全相同的文件，因此 Release 和 registry 中保存的是相同的字节，每晚还会再核对一次。读取需要登录，公开域名拒绝一切写入。之所以没有采用 GitHub Packages，是因为这些库和它们的消费方位于不同的组织中。</p>
