Files
mnote/bugs/05-editor-mainline/done/5-36-onlyoffice-local-edit-save-callback-contract-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

5.2 KiB
Raw Blame History

5-36 OnlyOffice local-folder 编辑保存 callback 契约

状态

  • 状态:done

目标

为 local-folder Office 编辑保存建立 callback 写回契约。P1 可以先落设计和测试骨架;若实现,必须覆盖 status 2/6 的下载写回和路径安全。

原因

ONLYOFFICE 官方保存链路要求 callback status === 2status === 6 时,集成后端下载 body.url 并写回原文件。此前 mnote-web callback 仍代理 legacy Nextlocal-folder 写回未闭环。本轮已补齐 local-folder callback 写回路径。

允许修改

  • rust/crates/mnote-web/src/routes/onlyoffice.rs
  • rust/crates/adapter-onlyoffice/src/lib.rs
  • 相关单测
  • 如需记录设计,可补充本 checklist

禁止事项

  • 不绕过 allowed roots 写任意路径。
  • 不在没有冲突保护时覆盖用户无关文件。
  • 不强行删除 legacy callback 代理。

Checklist

  • 明确 local asset callback 定位:callback URL 携带 rootUri / path / sessionId / token,并用 bridge session 校验 asset match。
  • status 非 2/6 时返回 { "error": 0 },不写文件。
  • status 2/6 时下载 rewritten body.url
  • 写回前校验目标路径属于当前 local root allowed roots。
  • 写回后写入原始本地文件;后续页面刷新由现有 local-folder watcher / projection refresh 感知。浏览器编辑保存端到端 smoke 另列后续验收,不作为本 bug 阻塞项。
  • 编辑入口仍保留 guard,不让用户误解为完整协作编辑能力已产品化。

验收

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_callback -- --test-threads=1
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_local_callback -- --test-threads=1
  • cargo test --manifest-path rust/Cargo.toml -p adapter-onlyoffice callback_preparation -- --test-threads=1
  • 后续浏览器编辑保存验收需单独设计,不纳入 P0。

本轮执行记录

  • 2026-06-01Codex 复核当前实现并补齐 callback 写回回归测试。

    • OnlyOfficeCallbackQuery 已携带 root_uri / path / session_id / token
    • buildCallbackUrl() 已把 local-folder Office asset 的 root/path/session/token 写入 callback query。
    • local_folder_onlyoffice_callback() 已校验 root/path、bridge session token、session asset match,并通过 resolve_onlyoffice_local_file_path() 阻断 root escape。
    • status 2/6 通过 prepare_callback() rewrite 后下载 body.url 并写回原始本地文件;非 2/6 返回成功且不写文件。
    • 新增/确认测试覆盖:
      • onlyoffice_local_callback_rejects_unauthenticated_local_write
      • onlyoffice_local_callback_writes_status_two_body_to_original_file
      • onlyoffice_local_callback_writes_status_six_body_to_original_file
      • onlyoffice_local_callback_rejects_root_escape_path
      • onlyoffice_local_callback_ignores_non_write_status
    • 验证通过:
      • cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_callback -- --test-threads=1
      • cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_local_callback -- --test-threads=1
      • cargo test --manifest-path rust/Cargo.toml -p adapter-onlyoffice callback_preparation -- --test-threads=1
  • 2026-05-21Reasonix worker C 分析完成。 结论:本轮不应实现完整写回。

    阻塞输入缺口:

    1. callback URL 当前只携带 assetIduserId,缺少 rootUripath
      • buildCallbackUrl()onlyoffice.rs 页脚 JS 中硬编码只传两个参数。
      • 对于 local-folder assetassetId local:asset:path/to/file.docx),无法从 assetId 推导 rootUri。
      • 没有 rootUri 就无法 resolve_onlyoffice_local_file_path → 无法安全写回。
    2. adapter-onlyofficeprepare_callback 不感知 local-folder 路径。
      • OnlyOfficeCallbackPreparationInput 没有 rootUri / path 字段。
      • prepare_callback 只做下载 URL rewrite 和 session 定位,不含路径校验。
    3. callback Rust handler 的 proxy_legacy_onlyoffice_json 目前代理 legacy Nextlocal-folder 无 legacy 时返回 NOT_IMPLEMENTED。

    实现写回所需的最小增量(下轮实现):

    • page() 页脚的 buildCallbackUrl():检测 local asset,追加 rootUripath query 参数。
    • OnlyOfficeCallbackQuery struct:增加 root_uri: Option<String>path: Option<String>
    • callback() handler:检测到 local asset 时(assetId 含 local: / local-file: 前缀),从 query 提取 rootUri 和 path,调用 resolve_onlyoffice_local_file_path 校验路径在 allowed roots 内。
    • adapter-onlyoffice:在 OnlyOfficeCallbackPreparationInputprepare_callback 增加 local path 字段。
    • status 2/6:下载 rewritten body.url,写回校验过的本地路径。
    • status 非 2/6:返回 {"error": 0},不写文件。

    当前 guard 状态:

    • Worker B 已在 layout.rs 中实现 withOfficeEditModeGuardconfirm 对话框 + data-mnote-last-office-edit-mode-requested / data-mnote-last-office-edit-mode-guard 数据属性。
    • 编辑入口在 local-folder writeback 未闭环时带 guard 提示,符合安全策略。