Skip to content

fix(web): 补齐本地查看、普通编辑与独占编辑入口 - #50

Merged
opendev9 merged 4 commits into
devfrom
fix/local-file-modes
Oct 25, 2026
Merged

opendev9 merged 4 commits into
devfrom
fix/local-file-modes

Conversation

@servicememeai

@servicememeai servicememeai commented Oct 23, 2026 •

Copy link
Copy Markdown

合并前置要求(2026-10-23 owner 裁决)

保持 Draft,当前方案未达到合并要求。必须结合 Checkout/Checkin 需求统一检查,按评审结果修改并完成验证后,再评审合并。 补齐网页三种入口或现有测试通过,均不能单独作为合并依据。

联合评审需明确三模式与 Checkout/Checkin 的关系、签出/提交/签入的操作入口及责任边界、占用者与其他用户的行为,以及续租、撤权、断线和未知回执的恢复规则。本文后续现有方案描述是待复核实现,不代表上述需求已经确认。

  • 结合 Checkout/Checkin 需求完成统一方案评审。
  • 根据评审结论调整 Hub 与必要的关联实现、文档和测试。
  • 完成对应验证,再评审是否达到合并要求。

文件列表原入口固定签发 optimistic-edit,且依赖旧的 CF_AGENT_OPTIMISTIC_UPLOAD_READY 开关;文件预览页也缺少独占编辑入口。本 PR 为两处复用同一个“本地文件操作”窗口,显式选择 local-view / optimistic-edit / exclusive-edit,统一调用已有 openV2LocalFile。仅修改 Hub,不变更 Chrome/Agent2 协议、表或 Host。

权限与失败规则

  • 文件有效权限 r 仅查看;rw 且库可写才允许编辑,文件权限优先于父目录权限。
  • 独占仅在路径 lock-info 明确 checkout_supported=true 且 checked_out=false 时允许。能力未知/失败只禁用独占;已有 Guard 禁用新写任务并引导回已有 Agent2 任务,查看仍可用。
  • 本机保存与云端发布的区别直接说明:普通编辑自动发布,独占需 Commit/Checkin。
  • 模式拒绝、403/409/503、URI 不匹配、Native 回执未知均不自动重试、不降级 V1/普通编辑/直接下载;未知结果先查 Agent2 任务中心。防重复点击。
  • UI 是提示,Hub Authority 与原生 Server 继续做最终授权和 Guard/CAS 裁决。

验证

  • Hub 定向 Jest:3 suites / 30 tests 通过,覆盖三模式、只读/无权限/只读库、文件权限覆盖父目录、Guard 占用/不可用/失败、回调接线、防重入、拒绝与无自动降级。
  • Chrome dev node --test tests/open-uri.cjs tests/agent2-routing.cjs:19 项通过;无需扩展改动。
  • test_no_managed_boundary.py:11 项通过;三个修改入口 Babel 解析、git diff --check 通过。
  • 使用隔离 Jest/Babel 依赖运行,没有改依赖或锁文件;完整依赖环境的测试命令见 docs/local-file-modes-web.md。
  • UI 截图未取得:执行环境缺浏览器,下载返回损坏 ZIP。未做全量前端/Django/镜像/Rust 构建。

核验 dev:Hub df8c547,Chrome 0a66287,Agent2 3daea00。共享窗口沿用已有 src/cloudfile 协议/API 边界,分层例外与后续整体迁移说明见文档;本次不扩大目录重构。

关联与待验收

Related: eaopen/cloudfile-local-agent2#10, eaopen/cloudfile-local-agent2#1

两项 Issue 保持 Open;本 PR 不声明端到端通过,也不推 PROD。

详见 docs/local-file-modes-web.md。

@opendev9

Copy link
Copy Markdown

2026-10-24 状态

当前 head a2b6c986f6 与 dev 无冲突(mergeable/clean),static CI PASS。

按 2026-10-23 owner 裁决保持 Draft:需先结合 Checkout/Checkin 需求完成统一方案评审(三模式与签出/提交/签入边界、占用人行为、续租/撤权/断线/未知回执),再调整实现与验证后评审合并。本轮未改动该 PR,也未合并。

@opendev9

Copy link
Copy Markdown

2026-10-25 Checkout/Checkin 统一方案草案(供 owner 评审;本 PR 保持 Draft)

三模式语义(Hub 只签发浏览器范围短期 ticket,不做最终授权)

模式 用户意图 Hub 行为 依赖 Guard
local-view 只读打开 签 view 票据;不创建 Guard;不写回 否
optimistic-edit 普通编辑 编辑票据;Agent2 本机保存后自动条件发布;云端确认前不显示“已保存” 否
exclusive-edit 独占编辑 仅当 lock-info 明确 checkout_supported=true 且未被占用时签发;显式 Commit/Checkin 是(cf_edit_guard)

与 Checkout/Checkin 的关系

  • Checkout:独占入口创建/续期 cf_edit_guard 租约并打开工作副本;不是第二种锁表。
  • Checkin:显式条件发布 + 释放 Guard;Commit 仍由 Server CAS/权限终判。
  • 普通编辑不 Checkout;只看不写不建 Guard。
  • 状态机:无 → 占用中(generation/owner/expiry)→ 释放;续租只延长同一 generation;换 owner 必须先释放或管理员强制释放(写审计)。
  • 权限/会话:ticket 短 TTL,Server 在每次读/提交/Guard RPC 重新终判;撤权、停用、过期 ticket 拒绝,不降级普通编辑/直接下载。
  • 断线/未知回执:Agent2 不自动重试;UI 指向任务中心按 journal/guard 查询;提交用 generation+expected head 幂等重放。
  • 占用冲突:其他用户进入独占 → 409 + 指向现有任务;强制释放仅管理员且审计。
  • 责任边界:Hub=入口/权限/票据;Agent2=工作副本/journal;Server=最终权限+CAS+Guard。

实施顺序(评审通过后)

  1. 把上述矩阵落成 docs 决策记录(含 409/403/503 错误合同与无自动降级规则)。
  2. Hub:edit/v1/open/ 按 mode 分派、lock-info 能力探测、拒绝路径文案;补 Jest 用例(三模式、占用、撤权、Guard 不可用、未知回执)。
  3. Agent2:按模式消费 ticket;独占只认 Guard;普通编辑自动发布仍要求云端确认。联动 chore(v2): bring latest dev baseline into Guard-only edit feature #10(网页接线)与 feat(file-actions): add local software sessions #1(三端验收)。
  4. Server:确认现有 Guard/CAS 接口无需新增表;只补入口合同测试。
  5. 真实 DEV 三端验收(假定 CE 文件功能正常,只验修改部分)。

按 owner 要求,此文档评审通过前不合并本 PR。

Copy link
Copy Markdown
Author

Owner 确认 — 2026-10-25:Checkout 只支持 OFF / OPTIONAL(按库),不支持 REQUIRED

正式产品决策:

  • OFF(默认):普通库保持 Seafile 文件版本与权限行为,C/Go 不进入 Guard SQL / Guard Diff 热路径。
  • OPTIONAL(Repo UUID 白名单):仅这些库提供主动 Checkout → Commit(保留 Guard)→ Checkin(发布并释放);未签出的文件正常普通编辑。
  • 不做 REQUIRED,不按扩展名自动锁定,不增加 Lock/Intent/Session 表,不混入 cf_library_storage。

前置候选 PR(均保持 Draft,需联动部署):

  • Server #41:Go/C 原生提交与文件操作按 Repo 跳过或强制 Guard。
  • Hub #87:Repo 级能力判断、OFF 状态不查询 Guard。
  • Docker #48:CF_ENABLE_CHECKOUT + CF_CHECKOUT_REPOS,启动时同步 Hub/Go/C 配置。

本 PR #50 仍保持 Draft;网页矩阵更新要求:

  1. OFF 库:本地查看、普通编辑可用;不展示独占按钮,尽量避免不必要的 lock-info 请求。
  2. OPTIONAL 库:独占入口仅主动触发;他人占用禁用新编辑,但本人持锁必须可通过新票据恢复现有 Agent2 任务(现有对所有 checked_out 禁用按钮有阻断)。
  3. Guard 不可用时 OPTIONAL 库拒绝新不安全写入,OFF 不受影响。
  4. 清单变更只在处理完活动 Guard/待确认 Agent 工作后统一重启;不要用撤下白名单来强制释放锁。
  5. chore(v2): bring latest dev baseline into Guard-only edit feature #10/feat(file-actions): add local software sessions #1 的实机验收仍是交付前置,不以 Mock/CI 冒充 E2E。

上述三个候选 PR 未运行完整构建/真实 E2E,不能仅凭创建 PR 就视为合并条件满足。

@opendev9

Copy link
Copy Markdown

2026-10-25 对照最新 OFF/OPTIONAL 决策的检查

…known lock state

- use lock-info checked_out_by_me: the Guard owner gets "Resume exclusive
  editing" (same open/exclusive-edit path; Rust Agent verifies the original
  Guard) while another user's lock stays blocked
- an unavailable/unknown lock-info (503/network) now disables ordinary and
  exclusive editing instead of treating it as "not checked out"; local
  viewing remains available
- update the OFF/OPTIONAL policy matrix in docs, refresh Chinese/English
  strings, and sync with latest Hub dev
- Jest: 3 suites / 32 tests pass (dialog now 17)
@opendev9
opendev9 marked this pull request as ready for review October 25, 2026 04:21
@opendev9
opendev9 merged commit 0592fe7 into dev Oct 25, 2026
2 checks passed
@opendev9
opendev9 deleted the fix/local-file-modes branch October 25, 2026 04:21
mynamexyz pushed a commit that referenced this pull request Oct 26, 2026
`.eslintrc.json` puts `react*` into the `builtin` path group with
`position: before`, so `reactstrap` has to precede `prop-types`; the `@/`
imports are alphabetized. PR #50 added both files with the imports in the
wrong order, which fails the frontend build with:

  src/components/local-file-dialog/index.js
    Line 3:1: `reactstrap` import should occur before import of `prop-types`
    Line 5:1: `@/cloudfile/agent-local-edit` import should occur before
              import of `@/cloudfile/cloudfile-api`
  src/features/library-view/toolbar/selected-dirents-toolbar/index.js
    Line 3:1: `@/components/local-file-dialog` import should occur after
              import of `@/components/icon`

No logic change; `eslint` on both files now reports 0 errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants