DocFlow 协同、权限与本地历史
解释权限、本地恢复、同步启动、awareness 与本地快照的实际责任。
本页整理自 2026-10-04 的 DocFlow 静态调查材料。原材料没有提供仓库 URL、分支或提交哈希;正文中的路径、行号、计数与依赖版本均指向该未锚定快照。本轮整理读取了归档报告,未取得原工作副本、启动产品或验证外部后端。下文的“复核”指原材料记载的复核,前端路径存在与运行可用性分别判断。
协同数据流
启动时序:权限 → 本地快照 → WS(原材料逐项复核)
/docs/[room]
│
├─ useDocumentPermission (useDocumentPermission.ts)
│ ① GET /api/v1/... GetDocumentPermissions(Number(documentId)) :39
│ ② permission === 'NONE' → 提前返回,不建文档 :59-63
│ ③ setDoc(new Y.Doc()) + 用权限响应构造 currentUser :65-71
│
├─ useCollaboration (useCollaboration.ts)
│ ④ new IndexeddbPersistence('tiptap-collaborative-<documentId>') :45
│ 注释:"Load local IndexedDB snapshot first so the WS
│ diff is minimal." :38
│ ⑤ persistence.on('synced') → isIndexedDBReady :47,54-56
│ ⑥ new HocuspocusProvider({ url: NEXT_PUBLIC_WEBSOCKET_URL,
│ name: documentId, document: doc,
│ token: getAuthToken() ← cookie 'auth_token' 原样 :70-74
│ onSynced, onAuthenticationFailed, onStateless }) :78-92
│
└─ page.tsx 组装
⑦ isCollaborationBootstrapReady = isIndexedDBReady && isServerSynced
│ (useCollaboration.ts:145)
⑧ 就绪后才挂 Collaboration(field 'content')/CollaborationCaret
page.tsx:102-108token 直接取 cookie 'auth_token'(utils/auth/cookie.ts:94-96)随 WS 握手发送,未见客户端额外签发或重签逻辑;已有 cookie token 是否带签名由仓库外的签发端决定。
服务端交互与只读下发
客户端监听 provider 的 onStateless,解析 JSON 中 type === 'server:permission' 的 readOnly 字段作为权威只读标志(优先于 HTTP 权限 VIEW/COMMENT 兜底,page.tsx:85-89);认证失败则 toast 提示"已使用本地缓存打开"(useCollaboration.ts:78-92)。这条消息是仓库内反推"Hocuspocus 服务端存在 onAuthenticate/onChange 拦截器"的唯一线索——服务端代码不在本仓库(README 自述的"通过拦截器机制实现权限控制与数据持久化"无法在仓库内核实)。
注记:COMMENT 权限等级与死代码评论功能的关系。协同功能盘点 的权限兜底提到 VIEW/COMMENT,而 编辑器功能盘点/未接线功能 #19 指出评论 mark 未注册——两个事实组合后的实际效果,调研材料只能回答一部分: (1) 权限等级枚举全集未知:代码显式处理的只有 NONE(
useDocumentPermission.ts:59-63,直接不建文档);VIEW/COMMENT 仅作为只读兜底出现在 isReadOnly 判定(page.tsx:85-89);是否存在 EDIT 等其他等级,材料未给出,本调查保留未知。 (2) 拿到 COMMENT 权限的用户在所查快照的构建配置下没有任何评论入口:评论 mark 与任何评论 UI 均不存在,其可见行为完全由服务端下发的 readOnly 映射决定——若 COMMENT 映射为只读,体验等同只读浏览;若映射为可写,体验等同普通编辑者(同样不能评论)。 (3) "COMMENT 等级是为未接线的评论功能预留"是合理推断而非代码证据;服务端映射逻辑不在本仓库,无法核实。
Awareness 在线状态
本地用户信息经 provider.awareness.setLocalStateField('user', currentUser) 广播(useCollaboration.ts:106-110);监听 'update' 遍历 getStates() 过滤自己得到 connectedUsers(:113-140);DocumentHeader 合并 currentUser + connectedUsers 渲染 CollaborationUsers 头像列表(DocumentHeader/index.tsx:27-31,51-53)。
"历史版本回溯":有 UI 但纯本地(原材料复核)
useEditorHistory(page.tsx:91-97 传入 autoSnapshot: true / interval 300000 / snapshotOnUnmount: true——复核勘误:参数在 page.tsx 而非 useEditorHistory.ts,后者 :30 有默认值):监听 doc 'update' 记录内容是否变化(state vector 哈希),定时器到期且内容变化才快照,组件卸载时补一次(useEditorHistory.ts:155-204)。snapshotService(services/snapshot/index.ts)用 Y.snapshot + encodeSnapshot 编码(:102-103,临时关 gc :98-99,128)存浏览器 IndexedDB 库 docflow-snapshots(:11),每文档上限 50 份自动清理(:247-254);恢复时 decodeSnapshot → createDocFromSnapshot → 取 content XmlFragment → 清空目标后逐子节点 clone 替换(:149-194)。整个服务只调 indexedDB.open,无任何 HTTP——换设备无法取得本机历史快照,所查前端没有接入服务端版本历史;后端是否另有该能力无法由这份材料确认。UI 侧 HistoryPanel 提供创建(带描述)/恢复/删除/清空及四个确认对话框(docs/_components/HistoryPanel/index.tsx:41-100)。
离线与冲突
未配置 WEBSOCKET_URL 时 provider 置 null 并直接 isServerSynced=true(useCollaboration.ts:60-65),编辑器以 IndexedDB 本地数据离线打开(page.tsx:253 以 isCollaborationBootstrapReady 为渲染门槛);重连增量同步依赖 HocuspocusProvider/Yjs 自身机制,仓库无自定义重连代码。冲突处理完全依赖 Yjs CRDT 自动合并——grep 'conflict' 全仓库仅命中 useDocumentEdit.ts:65-82 的 markConflicts,那是 AI 建议 agentSuggestion mark 的 schema excludes 检测(为冲突 mark 预留,协同无关)。
第二处协同:workflow 画布(演示级)
dashboard/workflow 用另一个 HocuspocusProvider 同步 ReactFlow:URL 硬编码 wss://flow.codecrack.cn、房间名固定 'flow-room'(canvas.tsx:131-144),用户名/颜色随机生成(:44-73),Y.Map('nodes')/Y.Map('edges')(:145-146),awareness 靠 setTimeout 100ms 延迟初始化(:152-193),他人光标渲染在 cursors-layer.tsx:20-53;无 IndexedDB 持久化。
最后更新于