Files
Nexus/docs/design/plans/2026-06-02-editor-refactor.md
T

3.3 KiB
Raw Blame History

编辑器重构实施计划(P0

日期: 2026-06-02
设计文档: docs/design/specs/2026-06-02-editor-refactor-design.md


涉及文件清单

文件 变更类型 说明
server/api/schemas.py 修改 FileRead 响应无变化;FileWrite 加 `etag: str
server/api/sync_v2.py 修改 read_file 加 mtime/etag/eolwrite_file 加 etag 校验
frontend/src/utils/filePreload.ts 修改 readRemoteFileContent 返回 RemoteFileReadResult
frontend/src/composables/files/useFilesEditor.ts 修改 editFile/previewFile 接收新返回类型;saveActive 传 etag
frontend/src/components/FileEditorWorkbench.vue 修改 per-tab modelEOL 保留;409 冲突弹窗

实施步骤

Step 1:后端 schemas.py — FileWrite 加 etag

class FileWrite(BaseModel):
    ...
    etag: str | None = Field(None, max_length=64)  # sha256hex

Step 2:后端 sync_v2.py — read_file 加 mtime/etag/eol

  • stat -c '%s %Y' → size + mtime(已有,扩展解析 mtime
  • 新增 sha256sum path_qtimeout=5
  • 返回 {"content":..., "size":..., "path":..., "mtime": int, "etag": str, "eol": "LF"|"CRLF"}

Step 3:后端 sync_v2.py — write_file 加 etag 校验

  • payload.etag 非空,先 sha256sum 校验
  • 不一致 → 409 {"detail": "文件已被外部修改", "code": "ETAG_MISMATCH"}
  • sha256sum 命令失败(权限等)→ 跳过校验(降级,不报错)

Step 4:前端 filePreload.ts — 返回类型扩展

  • 新增 interface RemoteFileReadResult { content, mtime, etag, eol }
  • readRemoteFileContent 签名改为返回 Promise<RemoteFileReadResult>
  • 缓存存 RemoteFileReadResult(非纯 string
  • 更新 invalidateCachedFileisPreloadEligible 调用方

Step 5:前端 useFilesEditor.ts — 适配新返回类型

  • editFile:接收 RemoteFileReadResult,传给 editorWorkbench.openFile
  • previewFile:只取 .content,其余不变
  • 新增 onConflict 回调签名

Step 6:前端 FileEditorWorkbench.vue — per-tab model + EOL + 409

  • EditorTabStateeol / etag / mtime / model / viewState
  • openFilemonaco.editor.createModel with EOL 匹配,不 normalizeEol
  • applyTabToEditor:改为 editor.setModel(tab.model) + restoreViewState
  • persistEditorToActiveTab:加 tab.viewState = editor.saveViewState()
  • removeTab:加 tab.model?.dispose()
  • destroyEditor:不 dispose tab models(改由 removeTab/closePanel 负责)
  • saveActive:请求体加 etag: tab.etag,捕获 409 → showConflictDialog
  • 新增 ConflictDialog(覆盖/放弃两个按钮)

Step 7:测试

  • tests/test_editor_write_conflict.py(新增)
    • 写入后 etag 变更 → 409
    • etag=null → 无校验直接写入
    • sha256sum 失败 → 降级通过

依赖与配置变更

  • 无新依赖
  • 后端:仅 schema + API handler 变更,无 DB migration

回滚方式

  • 前端:git revert 对应 commit,重新 build+deploy
  • 后端:git revert + supervisorctl restart nexus

测试要点

  1. 双标签撤销互不影响
  2. CRLF 文件保存后不变为 LF
  3. etag 不一致时收到 409
  4. etag=null(首次打开未保存过)写入正常