From 763fbb77a80ede0b847301074ccb46db917a5767 Mon Sep 17 00:00:00 2001 From: Tianyang Date: Sat, 1 Aug 2026 00:22:16 +0800 Subject: [PATCH] Add DocHub design spec --- .../specs/2026-08-01-dochub-design.md | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-01-dochub-design.md diff --git a/docs/superpowers/specs/2026-08-01-dochub-design.md b/docs/superpowers/specs/2026-08-01-dochub-design.md new file mode 100644 index 0000000..e18cb60 --- /dev/null +++ b/docs/superpowers/specs/2026-08-01-dochub-design.md @@ -0,0 +1,153 @@ +# DocHub 设计文档 + +## 背景与目标 + +小团队(10 人以下)需要一个基于 MinIO 的文档管理系统,支持: +- 浏览 MinIO 中存储的文档(主要是 Markdown / HTML,图片为次要内容) +- 全文内容检索(不只是文件名搜索) +- 在线编辑文档内容并保存 +- 上传新文档 + +无需用户登录和权限管理(内部工具,信任团队内所有成员)。 + +## 技术栈 + +| 层 | 选型 | 理由 | +|----|------|------| +| 前端 | React + Vite + Monaco Editor | Monaco 提供代码级编辑体验(语法高亮、查找替换),适合 Markdown/HTML 源码编辑 | +| 后端 | Python + FastAPI | 团队熟悉度高,异步 IO 足够应对 MinIO/Meilisearch 网络调用瓶颈,生态利于后续扩展(文档解析、AI 摘要等) | +| 存储 | MinIO(独立部署) | S3 兼容对象存储,文档内容和元数据(大小/时间/路径)都存在这里,不引入额外数据库 | +| 检索 | Meilisearch | 轻量、内置中文分词、MIT 协议、Docker 一条命令部署,检索延迟低 | + +明确不引入的组件: +- 关系型数据库(内容和元数据用 MinIO 承载,索引用 Meilisearch 承载,当前需求下够用) +- 用户认证/权限系统(团队内部工具,暂不需要) + +## 架构 + +``` + ┌─────────────┐ + HTTPS │ nginx │ + ───────────▶ │ (反向代理) │ + └──────┬──────┘ + │ + ┌────────────┼────────────┐ + ▼ ▼ ▼ + app.domain api.domain minio.domain(可选) + │ │ + ┌───────▼───────┐ ┌──▼──────────────┐ + │ React 前端 │ │ FastAPI 后端 │ + │ Vite + Monaco│ │ (Python) │ + └───────────────┘ └───┬──────────┬───┘ + │ │ + S3 API │ │ REST API + ▼ ▼ + ┌─────────────┐ ┌──────────────┐ + │ MinIO │ │ Meilisearch │ + │ (独立部署) │ │ (与后端同机) │ + └─────────────┘ └──────────────┘ +``` + +**部署形态**:两套独立的 docker-compose,各自独立生命周期: +- `minio/docker-compose.yml` — 仅 MinIO,独立服务器或独立栈,长期稳定运行,可被其他项目复用 +- `app/docker-compose.yml` — frontend + backend + meilisearch,可整体重启/升级而不影响已存储的数据 + +两套 compose 之间通过共享 Docker 网络或宿主机 IP 通信(后端配置 MinIO endpoint 即可,代码不感知部署形态差异,因为走的是标准 S3 API)。 + +**域名与端口**:通过 nginx 反向代理 + 子域名区分服务,无需在域名上绑定端口: +``` +app.domain → React 前端 +api.domain → FastAPI 后端 +minio.domain → MinIO 控制台(可选对外) +``` +Meilisearch 仅供后端内网访问,不对外暴露子域名。 + +## 数据流 + +**上传文档** +``` +前端上传文件 → 后端 PUT 到 MinIO + → 后端解析内容(Markdown/HTML 转纯文本) + → 推送索引到 Meilisearch + → 返回成功 +``` + +**浏览文件列表** +``` +前端请求文件树 → 后端调用 MinIO ListObjects → 返回文件夹/文件结构 +``` + +**查看/编辑文档** +``` +点击文件 → 后端从 MinIO GetObject 取内容 → 前端 Monaco Editor 加载展示 +保存编辑 → 后端覆盖写入 MinIO → 同步更新 Meilisearch 索引 +``` + +**全局搜索** +``` +输入关键词 → 后端转发查询到 Meilisearch + → 返回匹配文档(含高亮片段) + → 前端展示结果列表,点击跳转到对应文档 +``` + +**删除文档** +``` +后端从 MinIO 删除对象 → 同步从 Meilisearch 删除对应索引 +``` + +**关键决策:索引更新采用后端同步处理**,即写完 MinIO 后立即在同一请求中更新 Meilisearch 索引,不引入 MinIO webhook 异步机制。理由:团队规模小、写入量低,同步更新逻辑更简单、更容易保证一致性,避免维护额外的 webhook 消费者服务。 + +## 模块设计 + +### 前端(React + Vite + Monaco Editor) + +| 组件 | 职责 | +|------|------| +| `FileExplorer` | 左侧文件树,展示文件夹/文件,支持点击导航 | +| `DocumentViewer/Editor` | 主区域,用 Monaco 加载文档内容,支持编辑与保存 | +| `SearchBar` | 顶部全局搜索框,输入即查(debounce) | +| `SearchResults` | 搜索结果列表,展示高亮匹配片段,点击跳转对应文档 | +| `UploadButton` | 拖拽或选择文件上传 | +| `apiClient` | 封装对后端的 fetch 调用(documents CRUD + search),是前端与后端之间的唯一边界 | + +### 后端(FastAPI) + +API 层: + +| 接口 | 方法 | 职责 | +|------|------|------| +| `/api/documents` | GET | 列出文件树(基于 MinIO ListObjects) | +| `/api/documents/{key}` | GET | 读取单个文档内容 | +| `/api/documents/{key}` | PUT | 创建/覆盖文档(写 MinIO + 更新索引) | +| `/api/documents/{key}` | DELETE | 删除文档(删 MinIO + 删索引) | +| `/api/upload` | POST | 处理文件上传(multipart) | +| `/api/search` | GET | 转发查询到 Meilisearch,返回结果 | + +内部模块划分(各模块单一职责,可独立测试): +- `minio_client.py` — 封装 MinIO 操作(list/get/put/delete),只负责与 MinIO 交互,不含业务逻辑 +- `search_client.py` — 封装 Meilisearch 操作(index/update/delete/search),只负责与 Meilisearch 交互 +- `document_service.py` — 业务逻辑层,协调 `minio_client` 和 `search_client`(保存文档先写 MinIO 再更新索引;删除时顺序相反),并负责 Markdown/HTML 转纯文本供索引使用 +- `routes/documents.py`、`routes/search.py` — API 路由层,只做参数校验和调用 service,不直接触碰 MinIO/Meilisearch 客户端 + +依赖方向:`routes` → `document_service` → `minio_client` / `search_client`。上层不了解下层实现细节,替换 MinIO 为其他 S3 兼容存储或替换 Meilisearch 为其他搜索引擎时,只需改动对应的 client 模块。 + +## 错误处理 + +- MinIO 写入成功但索引更新失败:记录日志,不阻塞用户操作(文档已保存成功),后续可手动触发全量重建索引 +- 请求的文档不存在:返回 404 +- 上传文件类型/大小校验:在后端 `document_service` 层做校验(限制非文档类型或超大文件),拒绝时返回明确错误信息 + +## 测试策略 + +- 后端:pytest 测试 `document_service` 业务逻辑,mock `minio_client` 和 `search_client`,覆盖保存/删除时序、索引失败降级等场景 +- 前端:暂不强制要求自动化测试覆盖(小型内部工具,手动验证优先级更高) + +## 部署资源参考 + +学习/小团队场景下,DocHub 各服务的大致资源占用(供部署规划参考,非强制要求): +- MinIO:约 256-512MB 内存 +- Meilisearch:约 512MB-1GB 内存(随索引文档量增长) +- FastAPI 后端:约 256-512MB 内存 +- React/nginx 前端:约 128-256MB 内存 + +4 核 / 4GB 内存足以运行 DocHub 全部服务;若与其他自建服务(如 Gitea、数据库)共享同一台机器,建议预留合计 8GB 以上内存。