
Novu多租户如何用Contexts隔离不同组织的通知【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu如果你的产品是 SaaS 或多组织multi-org形态同一个用户可能同时属于 Acme、Globex 等多个组织而你在 Novu 中又只有一个项目、一套 workflow。直接给每个组织复制一套 workflow、或者用组织名前缀区分 subscriberId会让维护成本快速失控。Novu 提供的做法是用 Contexts 中的tenant上下文作为组织边界在触发 workflow 时带上租户 context再在Inbox /组件上传入相同的 context让每个用户只看到自己所在组织的通知。这条路径来自官方文档 Multi-tenancy、Inbox with Context 与 Manage contexts前提是你已经在 Novu 中有一个可触发的工作流和可用的 secret key前端使用novu/react渲染Inbox /。先明确两个限制避免后面踩坑在动手之前文档给出了两条硬性约束直接影响 tenant context 的写法一次 workflow trigger 最多传5 个 context 键每个 context 的data对象序列化后不能超过64KB。tenant context 每个键支持两种写法二选一// 简单写法只有租户 ID context: { tenant: acme-corp } // 富对象写法ID 可用于模板渲染的元数据 context: { tenant: { id: acme-corp, data: { name: Acme Corporation, logo: https://cdn.acme.com/logo.png } } }data里的公司名、logo、套餐类型等元数据用于模板个性化它不参与Inbox 的过滤匹配——匹配只按 context 的 type 和 id 进行。这一点决定了后面所有验证方式值得记住。第 1 步定义 tenant context可跳过Novu 会自动创建 context所以这一步严格来说不是必须第一次在 trigger 或Inbox /中引用某个tenant:id时Novu 会自动创建just-in-time。但如果你希望元数据有一份明确的初始来源可以在两个地方预创建方式 ANovu dashboard。登录 Novu dashboard在侧边栏点Contexts新建时填写三个字段见 Manage contextsIdentifier该类型下的唯一标识例如acme-corpContext type类别这里填tenantCustom data (JSON)可选如{ name: Acme Corporation, plan: enterprise }。点Create context保存。方式 BAPI。用 cURL 直接创建NOVU_SECRET_KEY替换为你环境中的 secret keycurl -L -g -X POST https://api.novu.co/v2/contexts \ -H Content-Type: application/json \ -H Accept: application/json \ -H Authorization: ApiKey NOVU_SECRET_KEY \ -d { type: tenant, id: acme-corp, data: { name: Acme Corporation, plan: enterprise } }注意如果相同的type:id组合这里是tenant:acme-corp已存在API 创建请求会失败这可以作为“该租户已注册”的判断依据。元数据更新规则来自 Manage contextsworkflow trigger 中带data的富对象会整体替换存储的data不是合并行为类似 subscriber upserttrigger 中只传字符串 id 时复用已有 context不修改dataInbox /等面向 subscriber 的入口只会查找或创建 context永不更新已有data不要依赖 Inbox 来刷新租户元数据context 的type和id不可变只有data可更新且更新时整个data被替换。第 2 步触发 workflow 时带上 tenant contextworkflow 触发时 Novu 会先检查该 context 是否已存在不存在就自动创建存在且你传了data就更新只传字符串 id 则原样复用。Node.js SDK 示例YOUR_SECRET_KEY_HERE、workflowId、user-123均替换为你自己的值workflowId是 Novu 中目标 workflow 的 IDimport { Novu } from novu/api const novu new Novu({ secretKey: YOUR_SECRET_KEY_HERE }); await novu.trigger({ workflowId: workflowId, to: { subscriberId: user-123 }, payload: { amount: $250, plan: Pro, }, context: { tenant: { id: acme-corp, data: { name: Acme Corporation, plan: enterprise, }, }, }, });不想装 SDK 时可以用 cURL 打 trigger 接口效果一致name字段即 workflow 标识NOVU_SECRET_KEY换成你的 secret keycurl -L -g -X POST https://api.novu.co/v1/events/trigger \ -H Content-Type: application/json \ -H Accept: application/json \ -H Authorization: ApiKey NOVU_SECRET_KEY \ -d { name: workflowId, to: { subscriberId: user-123 }, payload: { amount: $250, plan: Pro }, context: { tenant: { id: acme-corp, data: { name: Acme Corporation, plan: enterprise } } } }多组织应用里同一个 subscriber如user-123可以分别以tenant: acme-corp和tenant: globex触发同一个 workflow得到的就是两条属于不同租户的通知不需要为每个组织复制 workflow。第 3 步给Inbox /传入相同的 tenant context隔离的另一半在展示端。把触发时使用的 tenant context 传给Inbox /的contextpropAPPLICATION_IDENTIFIER、SUBSCRIBER_ID替换为你应用中的应用标识和 subscriberimport { Inbox } from novu/react; Inbox applicationIdentifierAPPLICATION_IDENTIFIER subscriberSUBSCRIBER_ID context{{ tenant: { id: acme-corp, data: { name: Acme Corporation, plan: enterprise, }, }, }} /过滤按type id 精确匹配Inbox 中每个 context 条目解析成tenant:acme-corp这样的键只显示触发时使用了相同键集合的通知。官方文档给出的匹配组合如下Workflow ContextInbox Context是否显示{ tenant: acme }{ tenant: acme }是{ tenant: { id: acme, data: { name: Acme } } }{ tenant: { id: acme } }是{ tenant: acme }{ tenant: { id: acme } }是{}{ tenant: acme }否{ tenant: acme }{ tenant: globex }否{ tenant: acme }{}否{ tenant: acme, app: first }{ tenant: acme }否从上表可以推出几条实用结论trigger 带 context、Inbox 不带或带了不同的 context时通知不会出现在该 Inbox 会话中反过来 trigger 不带而 Inbox 带 context同样不显示。嵌套data不需要两边一致例如 Inbox 只传{ tenant: { id: acme } }就能匹配带data的 trigger。键集合必须完全一致trigger 用了tenantapp两个键Inbox 只传tenant就匹配不上。用户在应用内切换组织时用新的 context 重新渲染Inbox /Novu 会自动重新拉取通知并切换 WebSocket 订阅。第 4 步验证隔离是否生效文档提供了三个可以互相印证的检查点Dashboard 的 Contexts 区。自动创建和手动创建的 context 都会出现在侧边栏Contexts列表中确认tenant: acme-corp存在且data符合预期。Activity Feed 按 context 检索运行记录见 Applying context进入Activity Feed→Workflow Runs标签在搜索栏点Context以type:id格式如tenant:acme-corp搜索应能找到该租户相关的所有执行。API Traces 查看解析后的 context。从 Activity Feed 的Requests列表选择对应运行进入API Traces标签可以看到 Novu 实际收到并解析的完整 context 对象用于确认data是否按预期写入。前端侧的验证就是行为本身Acme 的 Inbox 会话只显示以tenant: acme-corp触发的通知其他租户的通知被自动排除。通知“发送成功”但 Inbox 里看不到时怎么排查这是官方 FAQ 明确列出的典型现象Inbox with Contextin-app 任务在 activity feed 中状态为 Success但通知不出现在 Inbox 中。原因是 Inbox 按 context 的 type/id 过滤任务成功只代表消息写入了对应 context 的作用域不代表当前 Inbox 会话能看到它。官方给出的检查顺序trigger 带了 contextInbox /就必须带上相同 type/id的 contexttrigger 不带 context就不要给Inbox /传contextprop用户切换租户后确认已用新 context 重新渲染Inbox /。逐项对照上面的匹配表基本都能定位是哪一种不匹配。可选增强按租户定制通知内容context 的data会注入到所有模板编辑器in-app、email、SMS、push中通过{{context}}访问器读取例如pWelcome, new user from {{context.tenant.data.name}}!/p pYour account is on the {{context.tenant.data.plan}} plan./p也可以在步骤的Step conditions里用 context 做条件分支比如仅在context.tenant.data.plan为enterprise时发送企业版专属邮件。这样一套 workflow 定义就能服务所有租户不必为每个租户复制模板。详见 Applying context。生产环境用 contextHash 防止客户端篡改 contextcontextprop 在客户端设置恶意用户可以修改它去窥探其他租户的通知。生产环境必须从你的服务端获取 context 详情与contextHash一并传给Inbox /。开启 HMAC 后subscriberHash始终必需只要给Inbox /传了contextcontextHash也必需不传context则只需要subscriberHash。计算方式对传给组件的同一个context 对象做规范化后再 HMAC-SHA256NOVU_SECRET_KEY为你的 secret keyimport { createHmac } from crypto; import { canonicalize } from tufjs/canonical-json; const context { tenant: { id: acme-corp, data: { name: Acme Corporation, plan: enterprise, }, }, }; const contextHash createHmac(sha256, NOVU_SECRET_KEY) .update(canonicalize(context)) .digest(hex);contextHash与通知匹配是两回事它校验的是你传给Inbox /的精确 context 对象含data字段所以要 hash 的正是组件收到的那个对象。组件侧再把它与subscriberHash一起传入Inbox applicationIdentifierYOUR_APPLICATION_IDENTIFIER subscriberYOUR_SUBSCRIBER_ID subscriberHash{subscriberHash} context{context} contextHash{contextHash} /HMAC 的完整配置见 Prepare for Production。小结与边界隔离的两侧必须对齐trigger 与Inbox /使用相同 type/id 的 tenant context键集合一致、data可以不一致context 自动创建、trigger 带data时整体替换元数据、Inbox 永不更新data元数据的更新要走 API/dashboard 或服务端 trigger单次 trigger 最多 5 个 context 键单个data上限 64KB删除 context 不可恢复清理前确认没有仍依赖它的 workflowManage contexts。后续如果需要按 context 管理更多租户元数据或做 provider 级路由从 Contexts API 和 Manage contexts 继续深入即可。【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考