Files
curriculum-project-hub/.omo/文件库-开工计划.md
T

115 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 文件库 · 开工计划(v1.0
> 配套文档:《文件库-接口契约.md》(v0.1,仓库根目录)。本计划已吸收规划顾问评审意见,包含开工前必须先冻结的**架构收口决策 D11–D19**(回写契约 v0.2 时并入)。
> 工期按 1 人全栈估算;前端从 Phase 2 后可并行。
## 开工判定
**可以开工。** 四个外部依赖(版本包 / Group / 审计 / 平台身份)全部有契约可依、可 mock 并行开发;无阻塞项。唯一前置:本文件的 D11–D19 决策在写第一行 schema 前过一遍(半天,自查即可,有异议再升级)。
## 架构收口决策(新增,先冻结再动工)
| 编号 | 决策 | 理由 |
|---|---|---|
| D11 | `creator` 是 Node 的**不可变列**,创建时同时落一条 Manage grant;独立权限开关关闭时,**creator 的 Manage 仍生效**,其余项目级 grant 冻结不参与计算 | 否则 creator 可能被自己关掉的开关锁死 |
| D12 | 移动节点授权 = **本节点 Manage + 目标父 Edit+**;移动在事务 + Postgres 咨询锁内完成,防并发成环 | 预检环检测在并发下不安全 |
| D13 | Group resolve 失败返回 **503**(不伪装 404/403);每次 HTTP 请求 fresh resolve,不跨请求缓存;单请求内多次校验合并为一次调用(此点与 Group 团队确认,OPEN-9) | 依赖故障 ≠ 无权限;契约 G4 要求实时 |
| D14 | 命名规则:NFC 归一化、trim、≤128 字符、禁 `/` 与控制字符;**活跃兄弟节点大小写不敏感唯一**;根节点全局唯一 | 缺命名规则必有脏数据 |
| D15 | 删除 = 只给被删节点打标,后代**不递归打标**,靠"任一祖先已删"过滤;所有查询链路强制此过滤(含递归 SQL,不只靠 Prisma middleware) | 递归打标的写放大与恢复复杂度不可控 |
| D16 | `baseVersion` 为**文件级版本**GET file 返回的 version);无关文件的提交不产生冲突 | 与 C1 `head(dir, filePath)` 语义一致 |
| D17 | breadcrumb 只暴露用户有 View 的祖先名称;无权限祖先显示占位符 `…`,不暴露名字 | 404 不泄露语义(D8)延伸到面包屑 |
| D18 | v1 **单副本部署**(git 仓库在本地磁盘);多副本需共享存储或分片,后置 | 不解决不存在的扩展问题 |
| D19 | 网站管理员**不隐式读内容**force_adjust 凭 node id 操作(id 从审计或用户上报获得) | C4 已定的最小权限原则 |
## 阶段计划
### Phase 0 · 骨架与身份先行(1–2 天)
- [ ] 过 D11D19,回写契约 v0.2
- [ ] repo 骨架 `server/` + `web/`Postgres docker-composeCIlint / typecheck / vitest / 集成测试用真实 Postgres service container
- [ ] **JWT 中间件 + 本地 JWKS 测试服务 + 签名 token fixtures**(有效/过期/错 issuer/错 audience/错算法/未知 kid/轮换)——身份先行,不做"宽松 mock 身份"
- [ ] 4 个 port 接口定义 + mock`VersionStore``GroupResolver``AuditSink`(直写 outbox 表的实现就是真的,mock 的是对端服务)、`ExportAdapter`
- [ ] fastify-swagger 接入,OpenAPI 骨架——**OpenAPI 随每个端点同步产出,不留到最后**
- **验收**:骨架可 `pnpm dev` 起服务;本地 JWKS 六类 token fixture(有效/过期/错 issuer/错 audience/错算法/未知 kid)中间件行为全对;CI 流水线绿
### Phase 1 · 数据模型与权限引擎(2–3 天)⚑ 心脏
- [ ] Prisma schema`Node`parentId **权威** + id 编码的 materialized path 派生列,name 不入 path)、`Grant``ProjectIndependentSettings``OutboxEvent`(含 attempts/nextAttemptAt/lease/lastErrorrelay 字段一次到位)、`ExportJob`
- [ ] 数据库约束:kind 枚举、项目无子节点、活跃兄弟名唯一(部分唯一索引,root 的 NULL parent 特殊处理)、活跃 grant 按 (nodeId, principalType, principalId) 唯一、独立设置仅项目
- [ ] **纯权限 reducer**:输入已解析的 grants + 祖先链 + 用户组集合,输出 role|null;不碰 DB/网络。fast-check 属性测试:max 单调、加 grant 只升不降、祖先继承、toggle 行为、顺序无关、软删过滤
- [ ] 数据获取层 + `effective(user, node)` 组装
- [ ] 树操作服务:createcreator 自动 Manage、root 仅管理员且必带 ≥1 Manage)、rename、moveD12 事务+咨询锁)、soft deleteD15)、breadcrumbD17
- **验收**:并发对移(A→B 与 B→A)、移动+并发建子、property tests、删除祖先过滤,真实 Postgres 全绿
### Phase 2 · 树与授权 API12 天)
- [ ] nodes 端点组 + grants 端点组 + independent-permission 开关 + effective-permission 自查
- [ ] 授权矩阵校验(契约 8.1creator-only 授 Manage、Manage 不可动 creator、force_adjust 独立通道)
- [ ] 全部写操作落 outbox 事件(C3 词汇表)
- **验收**:契约 8.1 矩阵逐格 API 测试;D8 的 404/403/503 三分语义测试
### Phase 3 · 仓库生命周期与文件 API(2 天)
- [ ] 项目 provisioning 状态机 `PROVISIONING → READY | FAILED`:先建 DB 行(PROVISIONING)→ 调 `VersionStore.init`(幂等)→ 置 READY;失败可重试;孤儿仓库对账 job
- [ ] 每项目写锁(Postgres 咨询锁,跨进程安全)——不假设版本包能跨进程串行
- [ ] 路径安全:canonical 后必须落在项目根内、拒绝 `.git`、禁 symlink 逃逸、路径长度上限
- [ ] 上传限额(单文件 10MB / 单项目配额,数值 OPEN-5)+ 流式接收
- [ ] files 端点组全量(list/read/upload/commits→409/diff/history/delete+ 双客户端冲突 e2e
- **验收**DB/git 故障注入(init 失败、提交后崩溃)→ 对账能收敛;路径穿越语料库测试全拒
### Phase 4 · 外部集成(12 天)
- [ ] `GroupResolver` HTTP client:超时/5xx/429/畸形 JSON/万级 group 的故障注入测试;失败 → 503(D13)
- [ ] Audit relay worker:租约领取、指数退避、eventId 幂等、重启恢复、积压告警
- [ ] 导出 job:状态机 + 轮询 + 下载权限校验;`ExportAdapter` 桩(参数 OPEN-6 未定前**不计入完成标准**)
- **验收**:relay 重启不丢不重(对端幂等);group 故障时写操作 503、读操作按 D13
### Phase 5 · 前端(35 天,Phase 2 后并行启动)
- [ ] 登录(平台 SSO 占位 + 本地 JWKS 直通开关)
- [ ] 树浏览(懒加载 + 分页)、breadcrumb(D17 占位符)、创建对话框(类型 + 批量授权选择器,Group search 接 C2
- [ ] 权限管理面板(按 8.1 矩阵控制可选项,creator 标识)
- [ ] 文件页:查看/上传/下载/历史列表(编辑 UI 不归我方,留对接位)
- [ ] 导出按钮 + job 轮询
- **验收**:无权限节点全链路不可见;授权面板不会送出矩阵禁止的组合
### Phase 6 · 硬化与交付(12 天)
- [ ] 404/403/503 全端点核对;并发与重试幂等抽查
- [ ] 备份脚本(DB + git 仓库一致性快照)、健康检查(DB/JWKS/磁盘/Group)、磁盘水位告警
- [ ] OpenAPI 终稿 + 部署脚本 + 一页运维手册
## 里程碑
| 里程碑 | 内容 | 累计工期 |
|---|---|---|
| M1 | Phase 0–1:骨架 + 权限引擎 + 树操作可跑 | ~4 天 |
| M2 | Phase 2–3:后端 API 全量(含文件冲突流) | ~7 天 |
| M3 | Phase 4:外部集成(导出不计) | ~9 天 |
| M4 | Phase 56:前端 + 硬化,可交付 | ~14–18 天 |
## Mock 保真红线(mock 可以顶,但不许骗)
| Mock | 不许掩盖的事 | 真身到位后的契约测试 |
|---|---|---|
| VersionStore | 磁盘延迟、半初始化、锁残留、跨进程并发 | 真包跑临时仓库:并发 commit/init 重试/遍历与 symlink 语料 |
| GroupResolver | 超时、5xx、畸形响应、大规模组集 | 故障注入 HTTP 桩全过;我方绝不自己推祖先 |
| GroupSearch(前端选择器) | 与 resolve 是**两个独立端点**,不可用 resolve mock 顶替 | C2 `/groups/search` 真身到位后跑分页/空结果/超时 |
| Audit 对端 | 重复投递、乱序、长时间不可用 | relay 重启恢复 + 对端幂等 |
| JWT | issuer/audience/算法/轮换 | 本地 JWKS 全用例 |
| ExportAdapter | 参数、幂等、耗时 | 参数定了再写,之前不算完成 |
## Top 5 风险
1. **DB/git 分裂**(崩溃导致元数据、仓库、审计三边不一致)→ provisioning 状态机 + 对账 jobPhase 3 前置
2. **移动子树改变整树权限** → D12 事务+锁+源目标双授权,并发测试
3. **creator/独立权限开关交互** → D11 先冻结
4. **外部契约缺口**(导出参数、请求内 resolve 合并)→ OPEN 清单跟踪,mock 保真红线不许掩盖
5. **存储滥用/路径逃逸** → 路径安全 + 配额进 Phase 3 验收,不拖到硬化
## OPEN 清单(在契约文档基础上增补)
- OPEN-9:单请求内合并多次 resolve 是否符合 G4"实时"语义(找 Group 团队确认)
- OPEN-10:恶意软件扫描归属(我方/平台/不做)
- 其余 OPEN-1~8 见《文件库-接口契约.md》第 10 节