1flowbase
← 文档

工程手册

Console Route Registry 路由注册指南

本文说明 1flowbase 后台 console 路由以后应该怎么注册。结论先说清楚:

  • 导航和可见性由后端 Console Route Registry / Surface Registry 统一决定。
  • 前端不再维护一份本地导航菜单,也不根据权限自行推导菜单。
  • API 权限仍由后端真实 route、service 或 action 保护;console registry 只决定“当前用户应该看到哪些入口”。
  • RouteDefinitionNavigationItemPermissionBinding 是三件事,不要合并成一个对象。

三个对象

RouteDefinition

描述一个 console surface 的技术路由。

字段 含义
route_id 全局唯一 route id。系统内置可用短名;HostExtension 必须使用扩展拥有的命名空间。
surface_key surface 业务 key。settings 页面会从 /settings/<segment> 推导 section key,推导失败时才使用它。
path 前端可访问路径,例如 /settings/model-providers
surface_kind systemdynamic_pagehost_extension

描述这个 route 放到哪个导航槽位、用什么文案、排序如何。

字段 含义
item_id 全局唯一 navigation item id。
route_id 指向一个 RouteDefinition.route_id
parent_item_id 父导航项。settings 子项当前使用 settings。顶层 primary/secondary 为 null
label_key i18n key,例如 auto.model_providers
navigation_slot primarysecondarysettings
order 同一 slot 下排序,数值越小越靠前。

PermissionBinding

描述当前用户是否能看到这个 route。

字段 含义
binding_id 全局唯一 binding id。常用 <route_id>.access
route_id 指向一个 RouteDefinition.route_id
requirement authenticatedany_permission
permission_codes any_permission 时必须非空;authenticated 时必须为空。

裁剪规则:

  • authenticated:登录用户可见。
  • any_permission:用户拥有 permission_codes 中任一权限即可见。
  • 子项只有在自身 route 可见且父项可见时才返回。
  • GET /api/console/navigation 只返回当前用户可访问的 registry 结果。

注册入口怎么选

1. 系统内置 console 入口

适用于 1flowbase 自带页面,例如工作台、模板、设置里的模型供应商、用户管理等。

后端入口:

api/crates/access-control/src/navigation.rs

新增或调整 SYSTEM_CONSOLE_ROUTES 中的 ConsoleRouteSpec

示例:

ConsoleRouteSpec {
    route_id: "model-providers",
    surface_key: "model-providers",
    path: "/settings/model-providers",
    label_key: "auto.model_providers",
    navigation_slot: ConsoleNavigationSlot::Settings,
    parent_item_id: Some("settings"),
    order: 900,
    permission_codes: STATE_MODEL_PERMISSIONS,
    requirement: ConsolePermissionRequirement::AnyPermission,
}

如果这是一个新的 settings 内置页面,还要补前端页面内容:

  • web/app/src/features/settings/lib/settings-sections.tsx
    • 增加 SettingsSectionKey
    • 增加 section 定义,用于已知内置 section 的本地页面 body。
  • web/app/src/features/settings/pages/settings-page/SettingsSectionBody.tsx
    • 把新的 section key 映射到真实 React 页面或组件。
  • i18n
    • label_key 对应的 settings/appShell 文案。

注意:前端这里是页面内容映射,不是导航真值。导航能不能显示仍以后端 /api/console/navigation 为准。

2. HostExtension 设置入口

适用于可信 HostExtension 想在后台 settings 下贡献一个受控入口。

manifest 入口:

console_surfaces:
  route_definitions:
    - route_id: file-security.settings
      surface_key: file-security.settings
      path: /settings/file-security
      surface_kind: host_extension
  navigation_items:
    - item_id: file-security.settings
      route_id: file-security.settings
      parent_item_id: settings
      label_key: auto.file_security
      navigation_slot: settings
      order: 1300
  permission_bindings:
    - binding_id: file-security.settings.access
      route_id: file-security.settings
      requirement: any_permission
      permission_codes:
        - plugin_config.view.all

当前 validator 的硬规则:

  • route_iditem_idbinding_id 必须属于 extension 命名空间:
    • 等于 extension_id,或
    • <extension_id>. 开头。
  • path 必须以 /settings/ 开头。
  • surface_kind 必须是 host_extension
  • navigation_slot 必须是 settings
  • parent_item_id 必须是 settings,或属于 extension 自己的命名空间。
  • 同一个 manifest 内的 route_idpathitem_idbinding_id 不能重复。
  • navigation_items[].route_idpermission_bindings[].route_id 必须引用同一个 manifest 里的 route_definitions[].route_id
  • requirement: any_permissionpermission_codes 必须非空。
  • requirement: authenticatedpermission_codes 必须为空。

启动注册规则:

  • HostExtension manifest 解析和 validator 先执行。
  • Console surface 注册进入 ConsoleSurfaceRegistry
  • 如果与内置或已注册贡献发生 route_idpathitem_idbinding_id 冲突,注册失败。
  • loader 会把该 HostExtension 标记为 LoadFailed,不会静默忽略。

当前限制:

  • 这只注册导航入口和 route surface。
  • 远程前端 bundle 加载不在本阶段。
  • 如果没有宿主侧页面 renderer,未知 /settings/<sectionKey> 可以出现在导航里,但页面 body 不会自动变成插件前端。

3. 用户动态页面

surface_kind: dynamic_page 已经是 registry contract 的一部分,但当前还没有动态路由 CRUD 和持久化 records。

后续实现动态页面时,应遵守同一模型:

  • 动态页面 records 落到后端,后端生成 RouteDefinitionNavigationItemPermissionBinding
  • 前端仍只消费 /api/console/navigation
  • 不在前端本地拼菜单、补权限或做字段别名兼容。

如果实现需要数据库 migration、历史数据迁移或页面内容 runtime,需要先回到 issue/design gate。

API 和前端消费链

后端接口:

GET /api/console/navigation

返回结构:

{
  "route_definitions": [],
  "navigation_items": [],
  "permission_bindings": []
}

前端消费链:

web/packages/api-client/src/console-navigation.ts
  -> web/app/src/features/settings/api/console-navigation.ts
  -> app-shell Navigation / SettingsChromeMenu / SettingsPage

当前前端状态机:

  • loading:registry 还没返回,显示加载状态。
  • error:registry 请求失败,显示错误状态。
  • ready:registry 返回后,才按后端裁剪结果渲染导航。

只有 ready 后返回的空数组,才能表示“后端裁剪后当前用户没有可见入口”。不要把 loading/error 映射成空菜单或空页面。

APP_ROUTES 还要不要改

web/app/src/routes/route-config.ts 现在不是导航菜单真值层。它保留的是:

  • shell route 的选中态 matcher。
  • RouteGuard 所需的 route id。
  • 登录态 guard。

一般规则:

  • 新增 settings 子入口时,优先只改后端 registry。
  • settings 已有 /settings/$sectionKey 壳路由,很多 settings 子入口不需要新增 TanStack route。
  • 新增一个真正的顶层 shell 页面、详情页或需要特殊 selected matcher 的路径时,才改 APP_ROUTES 和 router。
  • 即使改了 APP_ROUTES,导航是否展示仍以后端 registry 为准。

选择 order 的建议

系统内置 settings 当前大致按 100 递增:

100  docs
200  api-key-authentication
300  auth-center
400  system-runtime
500  host-infrastructure
600  memory-observation
700  files
800  data-models
900  model-providers
1000 mcp-management
1100 members
1200 roles

HostExtension 建议从 1300 之后选择自己的区间,并为同一个 extension 留出间隔。

注册后必须补的测试

后端:

cargo test -p access-control navigation_tests
cargo test -p plugin-framework host_extension_contribution_tests
cargo test -p api-server console_navigation_route
cargo test -p api-server host_extension_loader_tests

前端:

pnpm --dir web/app exec ../../scripts/node/cli/exec-with-real-node.sh ../../scripts/node/cli/run-frontend-vitest.js run \
  src/features/settings/api/_tests/console-navigation-api.test.ts \
  src/app-shell/_tests/navigation.test.tsx \
  src/app-shell/_tests/settings-chrome-menu.test.tsx \
  src/features/settings/_tests/settings-page.test.tsx \
  src/routes/_tests/section-shell-routing.test.tsx

如果改了 app shell、settings 页面或样式边界:

pnpm --dir web/app build
node scripts/node/check-style-boundary/cli.js page page.settings
node scripts/node/tooling.js i18n-hygiene --max-findings 30

常见错误

  • RouteDefinitionNavigationItemPermissionBinding 合成一个对象。
  • 前端根据 me.permissions 自己推导导航。
  • registry 请求失败时回退到本地静态菜单。
  • registry loading/error 时显示普通空态。
  • 为展示文案新增接口字段别名。
  • HostExtension 使用不属于自己的 id。
  • any_permission 没有 permission codes。
  • 只注册了 navigation item,没有对应 route definition 或 permission binding。
  • 以为注册 console_surfaces 就自动获得远程前端 bundle。

最小 checklist

新增入口前先确认:

  • 我是在注册系统内置、HostExtension,还是未来动态页面?
  • route_iditem_idbinding_id 是否全局唯一?
  • path 是否已有壳路由或动态壳路由承接?
  • label_key 是否有 i18n 文案?
  • PermissionBinding 是否表达了后端真实权限语义?
  • 页面 API 本身是否仍有后端权限保护?
  • registry loading/error 是否有显式 UI?
  • 定向后端和前端测试是否覆盖了裁剪、排序、错误态和不回退本地静态菜单?