1 changed files with 188 additions and 0 deletions
@ -0,0 +1,188 @@ |
|||||
|
# Person Panel:多用户个人数据中心与 RSS 收件箱 — 设计说明 |
||||
|
|
||||
|
**日期**:2026-04-18 |
||||
|
**状态**:已评审(待实现计划) |
||||
|
**范围**:在现有 Nuxt 4 + Nitro + SQLite + Drizzle + `@nuxt/ui` 仓库上扩展,不引入分布式队列/Redis(单实例)。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 1. 背景与目标 |
||||
|
|
||||
|
### 1.1 目标 |
||||
|
|
||||
|
构建 **多用户个人数据中心**:每位用户独立管理 **个人资料/生平叙事**、**Markdown 文章**、**时光机(时间线)**、**RSS 阅读收件箱**;支持 **条目级可见性**(公开 / 私密 / 仅链接);实例级 **管理员** 负责账号生命周期(邀请制:管理员创建账号)。定时任务在 **单实例进程内** 周期性拉取 RSS。 |
||||
|
|
||||
|
### 1.2 非目标(第一期不做) |
||||
|
|
||||
|
- **空间协作**:多账号共编同一主页(owner/editor 等)不做。 |
||||
|
- **RSS 订阅源列表对外公开**:订阅源仅本人可见。 |
||||
|
- **通用第三方 API 插件平台**:先做 RSS/Atom(及可解析的变体);扩展点可在代码层预留接口,不产品化。 |
||||
|
- **分布式任务队列 / 多实例 SQLite 水平扩展**:不纳入本期;若未来需要,再评估「外部 HTTPS 触发同步」或独立 worker。 |
||||
|
|
||||
|
### 1.3 已确认需求摘要 |
||||
|
|
||||
|
| 维度 | 选择 | |
||||
|
|------|------| |
||||
|
| 用户模型 | 多用户,数据按 `userId` 隔离 | |
||||
|
| 注册 | 邀请制:管理员创建账号 | |
||||
|
| 可见性策略 | 混合:部分可对公网展示 | |
||||
|
| 可见性粒度 | **条目级**(每篇文章、每条时间线事件、每条 RSS 条目可单独设置) | |
||||
|
| RSS 源列表 | 永远仅本人可见 | |
||||
|
| RSS 新条目默认 | **私密** | |
||||
|
| 角色 | **实例级**:`admin`(开号/禁用)与 `user` | |
||||
|
| 内容主线 | **自写 Markdown 为主**;RSS 为阅读收件箱,可与时间线等合并展示(UI 层) | |
||||
|
| 定时任务 | **进程内调度**(A) | |
||||
|
| 部署 | **单实例** | |
||||
|
|
||||
|
### 1.4 架构方案(选定) |
||||
|
|
||||
|
**单仓 Nuxt/Nitro 单体 + 领域分包 + SQLite**:在现有仓库内划分 `auth/admin`、`profile`、`posts`、`timeline`、`rss`、`jobs`;定时器在 Nitro `ready` 钩子注册;抓取逻辑异步分批、每源超时,避免阻塞事件循环。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 2. 功能模块 |
||||
|
|
||||
|
1. **Auth**:延续 Cookie 会话;`users` 表扩展 `role`、`status`、`publicSlug` 等。 |
||||
|
2. **Admin**:创建用户、禁用/启用;首个管理员通过 **bootstrap 环境变量** 创建(见 §5.2)。 |
||||
|
3. **Profile**:简介、头像、生平(Markdown)、社交链接等;展示受条目/字段可见性约束(与 §3 一致)。 |
||||
|
4. **Posts**:Markdown 文章;`slug` 在单用户内唯一;元数据含标题、摘要、封面、标签、发布时间。 |
||||
|
5. **Timeline**:时间线事件表;可与 Posts / RSS 在 UI 上合并展示;第一期以 **独立事件表 + 可选手动关联** 为主,自动聚合为二期。 |
||||
|
6. **RSS**:订阅源、抓取状态、条目存储、去重;每用户独立。 |
||||
|
7. **Jobs**:进程内调度,周期性同步到期订阅源;启动后短延迟补跑一次。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 3. 数据模型与可见性 |
||||
|
|
||||
|
### 3.1 多租户隔离 |
||||
|
|
||||
|
所有业务表含 **`userId`**。`me/*` API 必须从会话解析 `userId`,**禁止**由客户端指定任意 `userId` 访问他人数据。`admin/*` 在显式校验 `role=admin` 后操作账号维度字段。 |
||||
|
|
||||
|
### 3.2 表(概念) |
||||
|
|
||||
|
| 域 | 表 | 要点 | |
||||
|
|----|-----|------| |
||||
|
| 账号 | `users`(扩展) | `role`: `admin \| user`;`status`: `active \| disabled`;`publicSlug` 唯一(公开主页) | |
||||
|
| 文章 | `posts` | Markdown 正文、元数据、`visibility`、`userId`;`slug` 用户内唯一 | |
||||
|
| 时光机 | `timeline_events` | 日期/时间段、标题、正文、`visibility`、`userId` | |
||||
|
| RSS | `rss_feeds` | `feedUrl`、`userId`、拉取状态(`lastFetchedAt`、`lastError` 等)、可选 `pollIntervalMinutes` | |
||||
|
| RSS | `rss_items` | `feedId`、`userId`(冗余便于查询)、`guid`、`canonicalUrl`、标题、摘要、正文片段、`publishedAt`;默认 `visibility=private` | |
||||
|
|
||||
|
**去重**:优先 `guid`,否则规范化后的 `canonicalUrl`。 |
||||
|
|
||||
|
### 3.3 可见性枚举(统一) |
||||
|
|
||||
|
- **`private`**:仅所有者可读(第一期建议:**管理员默认不读取用户私密正文**,仅管理账号;若需审计再单列策略)。 |
||||
|
- **`unlisted`**:凭 **不可猜测链接** 访问;**不出现在**公开聚合列表;**不进**站点公共 feed(若未来有)。 |
||||
|
- **`public`**:出现在该用户 **公开主页** 的聚合列表中。 |
||||
|
|
||||
|
**仅链接 URL(U1,已定稿)**:采用 **用户可读 `publicSlug` + 短随机 `shareToken`** 的路径形态(例如 `/p/:publicSlug/t/:shareToken`),避免仅依赖可枚举 id。 |
||||
|
|
||||
|
### 3.4 公开主页 URL(实现定稿) |
||||
|
|
||||
|
采用 **`/@:publicSlug`** 作为公开主页路径(若实现时发现与路由冲突,可退化为 `/u/:publicSlug`,但仓库内应 **只保留一种** canonical 形式)。 |
||||
|
|
||||
|
### 3.5 禁用用户后的公开行为(实现定稿) |
||||
|
|
||||
|
用户 `status=disabled` 时,其 **公开主页返回 404**;已登录会话在后续请求中拒绝。不在第一期做「禁用后仍展示历史公开快照」。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 4. API 与路由分层 |
||||
|
|
||||
|
- **`server/api/public/*`**:未登录可访问;仅聚合 **`visibility=public`** 的内容。`unlisted` 通过专用详情路由(§3.3)。 |
||||
|
- **`server/api/me/*`**:登录用户读写自有数据。 |
||||
|
- **`server/api/admin/*`**:仅 `admin`;用户 CRUD、可选实例级健康统计(注意隐私与脱敏)。 |
||||
|
|
||||
|
中间件:公开白名单 + 会话保护;`admin` 额外校验 `role`。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 5. 管理员与账号流程 |
||||
|
|
||||
|
### 5.1 创建与禁用 |
||||
|
|
||||
|
- 管理员创建用户:`username`、初始密码、可选 `email`;`role=user`,`status=active`。 |
||||
|
- 禁用:`status=disabled`,使会话失效策略与中间件一致(见 §3.5)。 |
||||
|
|
||||
|
### 5.2 首个管理员(Bootstrap) |
||||
|
|
||||
|
通过一次性环境变量创建,例如: |
||||
|
|
||||
|
- `BOOTSTRAP_ADMIN_USERNAME` |
||||
|
- `BOOTSTRAP_ADMIN_PASSWORD` |
||||
|
|
||||
|
在 **迁移/启动脚本** 中若检测到不存在任何 `admin` 用户则创建;成功后运维侧应 **移除或清空** 上述变量。具体执行点(`seed` vs 启动插件)在实现计划中敲定,但必须保证 **不可重复无意提权**。 |
||||
|
|
||||
|
### 5.3 自助注册 |
||||
|
|
||||
|
关闭或隐藏面向公众的注册;与现有 `register` 路由的兼容策略在实现计划中明确(例如仅 dev 开放或完全移除)。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 6. Nuxt UI 信息架构 |
||||
|
|
||||
|
- **登录**:Nuxt UI 表单组件;无自助注册入口(或仅管理员可见)。 |
||||
|
- **站内(登录后)**:侧栏 — 概览、文章、时光机、RSS、资料、设置;`admin` 额外「用户管理」。 |
||||
|
- **RSS**:左侧订阅源(本人)、右侧条目流;支持 **手动触发同步**;条目上切换可见性。 |
||||
|
- **公开站**:独立简洁布局,以阅读为主。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 7. 定时任务(进程内) |
||||
|
|
||||
|
- Nitro **`ready`** 注册间隔任务(如每 N 分钟扫描到期 `rss_feeds`)。 |
||||
|
- **启动补跑**:短延迟触发一轮到期同步。 |
||||
|
- **并发**:小并发池 + **每源超时**;失败写入 `lastError`;可选对连续失败源做退避。 |
||||
|
- **手动同步**:`me/rss/sync` 类接口,仍受 SSRF 与超时约束。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 8. 安全基线 |
||||
|
|
||||
|
- **RSS SSRF**:仅允许 `http/https`;限制重定向;解析 IP 时阻断私网段(实现细节遵循所选 HTTP 客户端能力);响应体大小上限。 |
||||
|
- **管理员接口**:内存级速率限制或反向代理层限制(实现计划二选一)。 |
||||
|
- **Cookie**:延续 `HttpOnly`、`SameSite` 等与现有 `cookie` 服务一致的做法。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 9. 错误处理与观测 |
||||
|
|
||||
|
- **API 错误**:统一 JSON 错误体(沿用现有 `handler`/响应工具);区分 **401/403/404/422/429/500**。 |
||||
|
- **RSS 抓取错误**:写入 `rss_feeds.lastError`(及时间);用户可在 UI 查看 **自己的** 源状态;不向访客泄露内部堆栈。 |
||||
|
- **日志**:抓取失败、SSRF 拒绝、管理员操作可记审计日志(级别与脱敏在实现计划中定)。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 10. 测试策略 |
||||
|
|
||||
|
- **单元测试**:RSS URL 校验、去重键生成、可见性判定纯函数。 |
||||
|
- **集成测试**:`me` 与 `public` 路由在 **多用户数据并存** 下无串租;`admin` 仅管理员可访问。 |
||||
|
- **手动清单**:禁用用户 404、unlisted 链接不可枚举进列表、新 RSS 条目默认私密。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 11. 配置项(建议) |
||||
|
|
||||
|
| 变量 | 含义 | |
||||
|
|------|------| |
||||
|
| `RSS_SYNC_INTERVAL_MINUTES` | 全局默认轮询间隔(可被单源覆盖) | |
||||
|
| `RSS_FETCH_TIMEOUT_MS` | 单源超时 | |
||||
|
| `RSS_MAX_CONCURRENT_FEEDS` | 同步并发上限 | |
||||
|
| `RSS_MAX_RESPONSE_BYTES` | 响应体上限 | |
||||
|
| `BOOTSTRAP_ADMIN_*` | 仅首次创建管理员(§5.2) | |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 12. 自检记录(spec review) |
||||
|
|
||||
|
- **占位符**:无 TBD;bootstrap 执行点标为「实现计划敲定」属合理边界。 |
||||
|
- **一致性**:多用户隔离、条目级可见性、RSS 默认私密、单实例进程内任务彼此一致。 |
||||
|
- **范围**:第一期不引入队列/多实例;协作与 OPML 等未承诺。 |
||||
|
- **歧义**:公开路径已定 `/@:publicSlug`;禁用用户公开 404;unlisted 为 U1。 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 13. 后续步骤 |
||||
|
|
||||
|
实现前在单独会话使用 **writing-plans** 技能生成实现计划;本设计文档经作者与维护者确认后再进入开发。 |
||||
Loading…
Reference in new issue