Files
mnote/design/05-editor-mainline/done/5-33-navigation-page-route-guard-checklist-v1.md
T
lix-2026 1882db7681 收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目
- 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线
- 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径

验证:
- cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1
- cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1
- git diff --check
- git diff --cached --check
- codegraph index . --force && codegraph status .
- codegraph sync . && codegraph status .
2026-06-01 09:29:12 +08:00

13 KiB
Raw Blame History

5-33 导航页与路由守卫执行清单 v1

状态:done

Owner05-editor-mainline / 03-rust-web / control-plane

创建时间:2026-05-27

背景:当前本地文件夹入口在没有明确页面时容易回退打开默认页面;未登录、删除页、失效页等路径也缺少统一页面级恢复策略。需要把“导航页作为主页”与“路由守卫”合并设计,避免用户停留在错误页或误打开无关页面。

0. 当前落地状态

  • Phase A 核心语义已落地:/ 与 local folder / folder scope 无明确页面时渲染导航页,不再自动打开 recent / first page。
  • Phase B recent 闭环已落地:control-plane SQLite recent 表、读写 API、SSR recent 展示、打开 folder/page 写入 recent。
  • Phase C 主路径已落地:导航页/星标文件夹进入 scoped 导航页;FileTree 普通文件夹点击只展开;Markdown 普通点击记录 recent 并打开文档页。
  • Phase D 验证已落地:补 Rust route/API 测试与 task500-navigation-page-route-guard-smoke.js browser smoke,覆盖未登录、删除页回退、recent 分组和导航页首屏无 editor bootstrap。
  • 后续增强已拆出:文档页 breadcrumb 文件夹段逐级回到导航页、所有 fetch 404/410 的全局浏览器兜底继续由 design/05-editor-mainline/process/5-35-navigation-breadcrumb-fetch-guard-followup-v1.md 跟踪;当前服务端 HTML shell 已覆盖 canonical route guard。

2026-06-01 归档说明:Phase A-D 主路径已有 Rust route/API 测试与 task500-navigation-page-route-guard-smoke.js 证据;本文归档到 done/,剩余增强不继续阻塞导航页主清单。

1. 目标

  • 本地文件夹和文件夹 scope 没有明确页面时显示导航页,不再自动打开 recent / first page 作为默认正文。
  • 导航页是主编辑区的主页状态;点击 Markdown 页面后,文档页覆盖该主页。
  • 点击文件夹后,行为对齐星标文件夹:切到 scoped Explorer,并以该文件夹为导航页上下文。
  • 导航页展示最近访问的文件夹和最近访问的页面,两组分开显示,并可直接访问。
  • 页面级路由守卫统一处理未登录、无权限、页面不存在、页面已删除等场景。
  • 首屏性能不因导航页退化:不构建 Page Aggregate,不启动 tiptap island,不额外扫描完整大目录。

2. 非目标

  • 不新增第二套树真相;导航页只消费 Rust projection / control-plane recent。
  • 不把 recent 写入 Markdown frontmatter 或本地文件夹 .mnote 作为长期事实。
  • 不把文件夹导航页做成真实 Markdown 页面。
  • 不改变 local-first Markdown 正文事实源。
  • 不在本阶段实现复杂最近访问排序模型,例如全文索引热度、跨设备文件内容同步或多设备路径重绑定。

3. 产品语义

3.1 导航页状态

  • 工作区导航页:/ 或默认 local workspace 入口,展示工作区级最近访问、打开本地文件夹入口和当前工作区概览。
  • 文件夹导航页:/?sourceKind=local_folder&rootUri=...&fileTreeScope=docs&treeView=filetree,展示 docs/ scope 的子文件夹、Markdown 页面和资源入口。
  • 导航页不是错误页;它是没有明确文档目标时的正常主页。

3.2 点击行为

  • 点击最近文件夹:进入该文件夹 scope,主区域显示文件夹导航页。
  • 点击最近页面:打开文档页并覆盖导航页。
  • 在导航页或 Explorer 中点击 Markdown:打开文档页并覆盖导航页。
  • 在文档页点击 breadcrumb 的文件夹段:回到对应文件夹导航页。
  • Ctrl / Meta / 中键点击页面:允许浏览器新标签页打开;普通点击优先走 MNote 当前主编辑区。

3.3 失效恢复

  • 未登录访问受保护页面:跳转 /auth?next=<encoded-current-url>
  • 登录后有 next:返回原始目标;如果目标已失效,则进入对应导航页。
  • Markdown 文件不存在、已删除或 document id 无法解析:回到当前 rootUri + fileTreeScope 的导航页,并显示轻量提示。
  • 文件夹 scope 不存在或无权限:回到 root 导航页或授权页,不停留在空白错误态。

4. 架构判断

  • 导航页属于 05-editor-mainline 的工作区/文档壳体验,数据入口由 03-rust-web 的 route 和 projection 提供。
  • Recent 持久化属于 control-plane 用户状态,不属于文件系统事实。
  • FileTree / PageTree 内容仍由 Rust projection 生成;前端只负责导航、局部渲染和交互。
  • 路由守卫 canonical 逻辑放在 Rust HTML shell route;前端 fetch guard 只做浏览器交互恢复。

5. 数据与协议

5.1 Recent 记录

  • 在 control-plane 增加用户 recent 记录表或复用已有偏好表能力,字段至少包含:
    • id
    • user_id
    • kind: folder | page
    • source_kind: MVP 先支持 local_folder
    • root_uri
    • relative_path
    • document_id
    • title
    • visited_at
    • status
  • Recent folder 使用 root_uri + relative_path 定位;root 文件夹 relative_path=""
  • Recent page 使用 root_uri + document_id 定位,并冗余保存 relative_path/title 方便展示。
  • 每个用户每类 recent 保留有限条数,MVP 建议 folder 10 条、page 20 条。
  • list recent 时做授权过滤;无权限、root 不可用、路径不存在的记录不作为可点击主项。
  • localStorage 里已有 recent local roots 只作为迁移/兜底读取,不作为长期 canonical。

5.2 API

  • 新增或扩展读取 API:返回当前用户 recent folders / recent pages。
  • 新增写入 API:打开 folder scope 或 Markdown page 后记录 recent。
  • API 写入只记录用户行为,不触碰 Markdown 或 .mnote 文件。
  • API 返回项包含可直接用于导航的 URL 参数:sourceKind/rootUri/fileTreeScope/documentId/workspaceId

6. Rust HTML Shell

6.1 导航页渲染

  • 新增导航页 view model,区分 workspacefolder 两种 scope。
  • 导航页首屏只加载 shallow file projection 和 recent 列表。
  • 导航页不调用 build_page_aggregate_snapshot
  • 导航页不注入 __MNOTE_PAGE_AGGREGATE__ / __MNOTE_EDITOR_BOOTSTRAP__,除非后续明确需要编辑器。
  • 导航页继续使用 PageLayout 和现有 workspace sidebar。
  • 文件夹导航页主区域展示:
    • 当前文件夹标题和路径面包屑
    • 子文件夹列表
    • 当前层 Markdown 页面列表
    • 当前层资源列表
    • 最近访问页面和最近访问文件夹

6.2 / 入口选择规则

  • / 未登录继续跳 /auth?next=/
  • /pageId 且无 fileTreeScope:显示工作区导航页。
  • /sourceKind=local_folder&rootUri=... 但无 pageId:显示对应 root 导航页。
  • /fileTreeScope:显示该 scope 文件夹导航页。
  • /pageId:尝试打开文档页;失败时回退导航页。
  • 移除或限制 recent_page_id -> first_page 的自动正文打开行为,避免文件夹入口误开默认页面。

6.3 /documents/{document_id} 守卫

  • 未登录访问 /documents/{document_id}303 /auth?next=<current-url>
  • local_folder 缺少 rootUri:跳 root/workspace 导航页,并保留错误提示参数。
  • local_markdown_not_found:跳 /?sourceKind=local_folder&rootUri=...&fileTreeScope=<parent>&missingPage=<document_id>
  • local_workspace_access_denied:跳授权说明页或 root 导航页提示无权限。
  • 其它不可恢复错误保留错误页,但必须带 trace id。

7. Browser Runtime

  • 抽出 openNavigationPageForFolder(rootUri, relativePath, workspaceId) helper,供最近文件夹、星标文件夹、breadcrumb 文件夹段复用。
  • 点击文件夹时不只展开侧栏;如果是导航动作,应更新 URL fileTreeScope 并渲染文件夹导航页。
  • 点击 Markdown 时记录 recent page,然后打开文档页覆盖导航页。
  • 点击导航页或星标 folder scope 时记录 recent folder,然后显示导航页;FileTree 普通文件夹点击只展开子项。
  • fetch 返回 401 且当前是浏览器交互:跳 /auth?next=<current-url>
  • fetch 返回 404/410 且目标是当前页面:跳当前文件夹导航页。
  • 保留普通业务错误 toast,不把所有 API 错误都变成导航跳转。

8. 性能约束

  • 导航页首屏不得扫描完整 workspace root 下所有 Markdown。
  • 文件夹导航页使用 scoped shallow FileTree projection。
  • PageTree scope 可继续给 sidebar 使用,但主导航页首屏不依赖完整 PageTree。
  • Recent list 查询必须限制条数。
  • 大目录 smoke 要验证打开 design/ scope 不显示 root 兄弟目录,不触发完整 root 渲染。
  • 导航页首屏不启动 tiptap island,避免无文档目标时加载编辑器 bundle。
  • 若需要最近页面标题刷新,只在列表展示时对有限条记录做存在性校验,不批量读取正文。

9. 验收标准

  • 登录后访问 / 显示导航页,而不是自动打开某个默认页面。
  • 打开本地文件夹 root 后显示该文件夹导航页。
  • 点击文件夹进入 scoped 导航页,Sidebar Explorer 与主区域 scope 一致。
  • 点击 Markdown 后打开文档页并覆盖导航页。
  • 最近文件夹和最近页面分组展示;点击最近文件夹进入导航页,点击最近页面打开文档。
  • 删除当前 Markdown 后刷新旧 /documents/...,进入父文件夹导航页并显示提示。
  • 未登录访问 /documents/.../auth?next=...,登录后能回到目标或导航页兜底。
  • 无权限访问本地 folder 不显示空白文档页。
  • 导航页没有注入页面 aggregate 和 editor bootstrap。
  • 大目录 scope 打开不退化为完整 root 扫描。

10. 测试计划

10.1 Rust 单元 / route 测试

  • root_entry_without_page_renders_navigation_home
  • root_entry_local_folder_scope_renders_folder_navigation
  • root_entry_does_not_auto_open_recent_page_for_folder_scope
  • document_shell_unauthenticated_redirects_to_auth_with_next
  • document_shell_missing_local_markdown_redirects_to_folder_navigation
  • recent_navigation_records_are_user_scoped
  • recent_navigation_list_filters_inaccessible_local_roots

10.2 Browser smoke

  • 新增 task5xx-navigation-home-local-folder-smoke.js
    • 登录测试账号
    • 打开本地 folder root
    • 断言主区域是导航页
    • 断言未打开默认 Markdown
  • 新增 task5xx-navigation-folder-scope-smoke.js
    • 点击文件夹
    • 断言 URL 含 fileTreeScope
    • 断言主区域标题和 Explorer scope 一致
  • 新增 task5xx-navigation-recent-smoke.js
    • 打开两个文件夹和一个 Markdown
    • 回到导航页
    • 断言 recent folders / recent pages 分组可见并可点击
  • 新增 task5xx-route-guard-auth-and-deleted-page-smoke.js
    • 未登录访问文档 URL 跳 auth
    • 登录后打开文档
    • 删除对应 .md
    • 刷新旧文档 URL 后回到文件夹导航页

10.3 回归测试

  • cargo test -p mnote-web root_entry -- --nocapture
  • cargo test -p mnote-web local_folder -- --nocapture
  • node scripts/task492-sidebar-starred-shortcuts-smoke.js
  • node scripts/task497-local-page-tree-filetree-open-performance-smoke.js
  • git diff --check
  • 修改代码后运行 codegraph sync .,提交前确认 CodeGraph 无 pending。

11. 分阶段执行建议

Phase A:守住语义

  • 新增导航页 SSR 壳和 route 测试。
  • / 入口选择规则,不再在 folder scope 下自动打开默认页面。
  • /documents/{document_id} 增加未登录和 missing local markdown 路由恢复。

Phase BRecent 闭环

  • control-plane 增加 recent 记录。
  • 导航页 SSR 展示 recent folders/pages。
  • 打开 folder/page 时写入 recent。

Phase C:浏览器交互对齐

  • 文件夹点击进入导航页 scope。
  • Markdown 点击覆盖导航页。
  • breadcrumb 文件夹段返回导航页。
  • fetch guard 处理 401/404/410

Phase D:性能与 smoke

  • 确认导航页首屏不构建 editor / aggregate。
  • 大目录 scoped smoke 验证不加载完整 root。
  • 删除页、未登录、recent 点击 smoke 通过。

12. 风险与边界

  • Recent 写入如果只做前端 localStorage,会和登录用户、授权过滤、SSR 首屏冲突;MVP 应优先 SQLite。
  • 文件夹导航页如果复用完整 PageTree 递归扫描,可能在大目录退化;首屏应以 shallow FileTree projection 为主。
  • mnote_recent_page_id 旧 cookie 不能继续作为文件夹入口自动打开正文的依据;最多用于工作区级“继续编辑”入口展示。
  • 路由守卫不能把所有错误都吞成导航页;不可恢复错误仍要保留 trace,方便排障。
  • Ctrl / Meta / 中键打开浏览器新标签页不能破坏普通点击覆盖主页的主路径。