NocoBase 嵌入外部系统指南:@nocobase/plugin-embed 插件原理与实战
NocoBase 嵌入外部系统指南nocobase/plugin-embed 插件原理与实战【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase本文面向希望把 NocoBase 搭建的业务系统无缝嵌入到自有门户、管理后台或第三方页面中的开发者。围绕内置插件nocobase/plugin-embed本文将系统讲解嵌入链接的生成方式、/embed路由的渲染机制、会话与 Token 的隔离管理、访问控制与鉴权流程并结合仓库源码给出可验证的实现依据帮助读者理解嵌入的完整链路并正确配置使用。插件概览定位与元数据nocobase/plugin-embed是 NocoBase 内置builtIn: true的免费isFree: true插件其官方定位是将 NocoBase 嵌入外部系统或页面中使其成为该系统或页面的一部分见 插件文档。从插件清单的元数据frontmatter与 package.json 中可以确认以下关键事实字段值说明packageNamenocobase/plugin-embed插件包名displayName嵌入 NocoBase / Embed NocoBase插件显示名称supportedVersions1.x、2.x支持 NocoBase 1.x 与 2.xisFreetrue免费插件builtIntrue随发行版内置defaultEnabledfalse默认不启用需在插件管理器中手动启用editionLevel0基础版即可使用licenseApache-2.0开源协议插件在运行时区分为服务端与客户端两部分服务端 server/index.ts 目前仅是一个空的PluginEmbedServer继承类说明嵌入能力全部集中在前端实现真正的工作由src/clientv1 客户端与src/client-v2v2 客户端承担。核心机制一/embed路由与页面渲染嵌入功能的核心是提供一套独立的/embed路由前缀使嵌入页面可以脱离 NocoBase 自身的导航框架、顶栏与侧边栏只渲染业务页面本身。v2 客户端的路由注册在 client-v2/plugin.tsx 中插件通过布局管理器注册了一个名为embed的布局const EMBED_ROUTE_PREFIX /embed; this.app.layoutManager.registerLayout({ routeName: embed, routePath: EMBED_ROUTE_PREFIX, uid: EMBED_LAYOUT_MODEL_UID, // embed-layout-model layoutModelClass: EMBED_LAYOUT_MODEL_CLASS, // EmbedLayoutModelV2 authCheck: false, });同时注册了对应的布局模型加载器将EmbedLayoutModelV2类与embed-layout-modelUID 关联见 EmbedLayoutModel.tsx。EmbedLayoutModelV2继承自BaseLayoutModel其render()方法返回 EmbedLayoutComponent。嵌入路径的判定client-v2/route.ts 中的isEmbedRoutePathname()用于判定当前路径是否属于嵌入路由const embedPathname normalizeRootPath(relativePathname).replace(/\/$/, ); if (embedPathname /embed) return true; return embedPathname.startsWith(/embed/);这段代码会先剥离应用自身的basename通过router.getBasename()或publicPath再判断相对路径是否为/embed或以/embed/开头。也就是说无论 NocoBase 部署在根路径还是子路径如/nocobase下只要访问${basePath}/embed/...就会进入嵌入模式。v1 客户端的路由注册v1 客户端client/index.tsx采用router.add方式注册了一系列嵌入路由均设置skipAuthCheck: true鉴权由嵌入层自行处理this.router.add(embed, { path: /embed, Component: EmbedLayout, skipAuthCheck: true }); this.router.add(embed.page, { path: /embed/:name, Component: EmbedPage, skipAuthCheck: true }); this.router.add(embed.page.tab, { path: /embed/:name/tabs/:tabUid, Component: PageTabs, skipAuthCheck: true }); this.router.add(embed.page.flowTab, { path: /embed/:name/tab/:tabUid, Component: EmbedPage, skipAuthCheck: true }); this.router.add(embed.page.view, { path: /embed/:name/view/*, Component: EmbedPage, skipAuthCheck: true }); this.router.add(embed.page.flowTabView, { path: /embed/:name/tab/:tabUid/view/*, Component: EmbedPage, skipAuthCheck: true });从路由表可以推断嵌入模式下不仅支持普通页面:name还支持标签页tabs/:tabUid、流程页面tab/:tabUid以及详情视图view/*等多种页面形态。页面渲染普通页与流程页client/EmbedLayout.tsx 中的EmbedPage组件根据页面类型分两条渲染路径普通页面通过RemoteSchemaComponent远程加载页面 schema并用KeepAlive保持页面状态、以CurrentPageUidContext提供当前页面 UID 上下文流程页面NocoBaseDesktopRouteType.flowPage走EmbedFlowPage通过getEmbedLayoutModel()获取EmbedLayoutModelV2模型再用FlowModelRenderer配合FlowRoute渲染流程页面。嵌入布局在视觉上完全隐藏了 NocoBase 自身的头部导航——EmbedAdminLayout使用--nb-header-height: 0px将头部高度置零见 EmbedLayout.tsxv2 的 EmbedLayoutComponent 同样设置了--nb-header-height: 0px并针对移动端断点Grid.useBreakpoint()中screens.md false或窗口宽度小于 768px自动切换为移动端布局。核心机制二一键复制嵌入链接插件为页面配置提供了复制嵌入链接的能力使用者无需手写 URL可直接从页面菜单一键获取可嵌入的链接。v2注册为页面菜单项client-v2/copyEmbedLinkFlow.tsx 通过RootPageModel.registerExtraMenuItems将复制嵌入链接注册到页面公共操作菜单中RootPageModel.registerExtraMenuItems({ keyPrefix: COPY_EMBED_LINK_KEY, // embed.copyEmbeddedLink group: common-actions, sort: -100, matcher: (model) !!getRoutePageUid(model), items: (model, t) [ /* 菜单项点击后调用 copy(buildEmbedLink(model)) */ ], });链接的生成逻辑buildEmbedLink()const pageUid getRoutePageUid(model); // 取 schemaUid / uid / id 等页面标识 const pathname getEmbedRoutePath(app, /embed/${pageUid}); return new URL(pathname, window.location.origin).toString();它从当前路由模型currentRoute.schemaUid、parentId、uid等解析出页面 UID拼接出{origin}{basePath}/embed/{pageUid}形式的完整链接。v1页面设置菜单项v1 客户端在PageSettings中注册了同名菜单项client/index.tsx其点击逻辑EmbedLayout.tsx通过字符串替换生成链接const url window.location.href .replace(/admin, /embed) // 将管理端路径替换为嵌入路径 .replace(pageUid, fieldSchema[x-uid]) // 用当前块的 uid 替换页面 uid .replace(window.location.search || , ); // 去掉查询参数 copy(url);这段逻辑从源码结构看属于 v1 的简化实现把当前访问地址中的/admin前缀替换为/embed并将页面 UID 替换为当前 schema 节点的x-uid从而得到可嵌入到任意 iframe 或新窗口中的链接。核心机制三嵌入会话隔离与 Token 管理嵌入场景中最棘手的问题是外部宿主页面本身可能就是一个已登录的 NocoBase 页面或者同一浏览器中同时存在多个嵌入实例。插件通过embedSession实现了会话级的隔离核心代码在 client-v2/embedSession.tsx。存储前缀的哈希隔离getEmbedStoragePrefix()以应用作用域 窗口作用域的组合生成隔离的存储前缀const appScope app.router?.getBasename?.() || app.getPublicPath?.() || window.location.origin; const frameScope getEmbedWindowName(); return ${EMBED_STORAGE_PREFIX}_${hashStorageSegment(appScope)}_${hashStorageSegment(frameScope)}_;其中getEmbedWindowName()会为当前窗口分配一个以__nocobase_embed_开头的唯一window.name。这样即使同一浏览器中嵌套了多个 NocoBase 嵌入实例各自的 Token、认证器等sessionStorage键也不会互相覆盖。会话激活与 Token 注入activateEmbedSession()在进入嵌入路由时被调用它完成三件事保存当前应用原始的storage与storagePrefix快照用于退出嵌入后恢复将apiClient.storage切换为sessionStorage并应用隔离前缀从 URL 查询参数中读取token与authenticator若存在则注入到认证上下文中const searchParams getSearchParams(search); const token searchParams.get(token); const authenticator searchParams.get(authenticator); if (authenticator) { session.authenticator authenticator; app.apiClient.auth.setAuthenticator?.(authenticator); } if (token) { session.token token; app.apiClient.auth.setToken(token); }这意味着宿主系统完全可以通过https://your-nocobase.com/embed/{pageUid}?tokenxxxauthenticatorxxx的方式在 URL 中携带已签发的 Token 实现免登录嵌入。新 Token 的自动同步registerEmbedSessionTokenSync()在 axios 响应拦截器中监听x-new-token响应头一旦服务端在响应中下发了新 Token例如刷新后的 Token便会自动更新会话中的 Token保证嵌入会话长期可用responseInterceptor.use((response) { syncEmbedSessionTokenFromHeaders(app, response.headers); // 读取 x-new-token return response; });会话退出与恢复restoreEmbedSessionToken()当接口返回 401 时把会话中保存的 Token 重新写回存储实现一次会话内重试restoreEmbedSession()离开/embed路径后恢复应用最初的存储配置并触发auth:tokenChanged事件通知上层刷新认证状态。EmbedSessionProvider作为应用级 Provider通过providers.unshift插入到最前监听路由变化自动在激活嵌入会话与恢复普通会话之间切换embedSession.tsx。核心机制四嵌入访问控制与鉴权嵌入页面绕过了 NocoBase 常规的路由鉴权skipAuthCheck: true/authCheck: false因此插件必须自行完成用户校验、权限检查与页面可达性判断。v2EmbedAccessGuardclient-v2/EmbedAccessGuard.tsx 是 v2 的访问守卫组件其鉴权流程如下校验当前用户请求auth:check接口skipAuth: true不携带旧凭证若未登录则重置 ACL 并渲染 403 页面加载嵌入运行时确保数据源dataSourceManager与路由仓库的可访问路由routeRepository.ensureAccessibleLoaded已加载页面可达性判断canAccessEmbedPage()通过routeRepository.getRouteBySchemaUid(pageUid)判断目标页面是否存在于当前用户可访问的路由集合中不存在则直接渲染 403加载 ACL请求roles:check接口将返回的角色、权限片段snippets写入应用级 ACL 存储并同步pluginSettingsManager.setAclSnippets()与apiClient.auth.setRole()若用户没有ui.*权限片段还会调用flowEngine.flowSettings.disable()禁用流程设置能力渲染上下文将CurrentUserContext与ACLContext注入子树供嵌入页面内的区块、按钮按权限渲染。v1auth check 拦截器v1 客户端的 client/embedAuth.ts 采用 axios 响应拦截器方式处理嵌入鉴权。当任意请求返回 401 时若当前不在/embed路径下则原样抛出在嵌入路径下先调用restoreEmbedSessionToken()尝试用会话 Token 恢复若失败的请求恰好是auth:check则构造一个status: 200的未授权用户响应用户 ID 为__nocobase_embed_unauthorized__且带__nocobaseEmbedUnauthorized标记让上层感知已登录但无权限而不是直接崩溃。EmbedLayout组件client/EmbedLayout.tsx通过isEmbedUnauthorizedUser()识别这类特殊用户并渲染 403 页面NotAuthorized否则渲染AdminProvider EmbedAdminLayout。源码结构与测试验证插件源码位于 packages/plugins/nocobase/plugin-embed主要文件与职责如下路径职责src/server/index.ts服务端插件入口空实现src/client/index.tsxv1 客户端路由注册、PageSettings 菜单项src/client/EmbedLayout.tsxv1 嵌入布局、页面渲染、复制链接src/client/embedAuth.tsv1 嵌入鉴权拦截器src/client-v2/plugin.tsxv2 客户端布局注册、Provider、菜单src/client-v2/embedSession.tsx会话激活、Token 同步与恢复src/client-v2/EmbedAccessGuard.tsxv2 访问控制守卫src/client-v2/EmbedLayoutComponent.tsxv2 嵌入布局 UIsrc/client-v2/copyEmbedLinkFlow.tsx复制嵌入链接src/client-v2/route.ts/embed路径判定与链接拼装src/locale/zh-CN.json中文本地化文案仓库同时提供了较完整的测试覆盖可作为行为契约参考client/e2e/popup.test.ts 与 client/e2e/templates.ts端到端验证嵌入弹窗场景client/tests/EmbedPage.test.tsx 与 client/tests/plugin.test.tsv1 页面渲染与插件加载测试client-v2/tests/包含EmbedAccessGuard、EmbedLayoutComponent、copyEmbedLinkFlow、route与插件本身的单元测试覆盖鉴权守卫、布局组件、复制链接流程与路径判定逻辑。安装与启用由于插件defaultEnabled: false需要手动启用。启用方式与 NocoBase 通用插件管理流程一致以管理员身份登录 NocoBase进入「插件管理」插件市场页面搜索nocobase/plugin-embed显示名嵌入 NocoBase点击安装/启用。启用后即可在任意页面的菜单v2或页面设置v1中找到「复制嵌入链接」入口将生成的${origin}/embed/{pageUid}链接放入宿主系统的 iframe、弹窗或新窗口中使用。若宿主侧已具备登录态也可在链接后追加?tokenxxxauthenticatorxxx实现免登录直入。使用场景与注意事项基于上述机制该插件适合以下场景门户集成将 NocoBase 页面嵌入企业门户或第三方管理后台页面只展示业务内容不带 NocoBase 全局导航流程审批嵌入通过embed.page.flowTab/embed.page.flowTabView路由嵌入流程页面与流程详情视图在外部系统内完成审批操作多实例并行借助window.name 哈希前缀的存储隔离同一浏览器中可同时运行多个嵌入实例而互不串号。实际使用时需注意嵌入链接会暴露页面 UID权限完全由EmbedAccessGuard的页面可达性判断与 ACL 加载逻辑兜底请确保目标页面已配置合适的角色权限Token 以查询参数形式传递会出现在浏览器历史与日志中生产环境建议通过宿主页预先换取短期 Token 或采用同域 Cookie 方案服务端插件本身不提供额外接口所有能力均位于客户端升级客户端版本时需同步验证嵌入路由与会话逻辑的兼容性插件声明支持 1.x 与 2.x。小结nocobase/plugin-embed以一套/embed路由前缀为核心串联起链接生成 → 会话隔离 → Token 注入与同步 → 权限校验 → 页面渲染的完整嵌入链路copyEmbedLinkFlow负责产出可复用的嵌入链接embedSession通过存储前缀哈希与x-new-token拦截保证会话独立与长期可用EmbedAccessGuard/embedAuth在绕过常规路由鉴权后自行完成用户与 ACL 校验EmbedLayout(Component)最终以无头部导航的形态渲染出可无缝融入宿主系统的业务页面。理解了这条链路即可在自有系统中安全、稳定地完成 NocoBase 的嵌入式集成。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考