docs: 用户认证+云同步+评论系统设计文档
This commit is contained in:
parent
005a608369
commit
0b21ff0644
238
docs/superpowers/specs/2026-05-05-user-auth-cloud-sync-design.md
Normal file
238
docs/superpowers/specs/2026-05-05-user-auth-cloud-sync-design.md
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
# 用户认证 + 云端同步 + 评论系统设计文档
|
||||||
|
|
||||||
|
**日期:** 2026-05-05
|
||||||
|
**作者:** MikiVL
|
||||||
|
**状态:** 已批准
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 背景与目标
|
||||||
|
|
||||||
|
当前笔记应用(www.mikivl.online/app)的所有数据存储在浏览器 IndexedDB 中,清除浏览器数据或换设备即丢失。本次扩展目标:
|
||||||
|
|
||||||
|
1. 添加用户账号系统(用户名+密码注册/登录)
|
||||||
|
2. 登录用户的笔记双存——本地 IndexedDB 继续作为缓存,服务端 SQLite 持久化
|
||||||
|
3. 云存储功能需邀请码激活(预生成 10 个)
|
||||||
|
4. 主页(www.mikivl.online)新增评论区,登录用户可发评论
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 架构总览
|
||||||
|
|
||||||
|
```
|
||||||
|
前端(React) 服务端(Hono + Node.js) 数据库(SQLite + Drizzle)
|
||||||
|
──────────────── ────────────────────── ───────────────────────────
|
||||||
|
IndexedDB(本地) ←→ /api/auth/* users
|
||||||
|
/api/notes/* ←──── notes
|
||||||
|
/api/folders/* ←──── folders
|
||||||
|
/api/comments/* ←──── comments
|
||||||
|
/api/ai/*(已有) invite_codes
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据库 Schema
|
||||||
|
|
||||||
|
### users
|
||||||
|
```sql
|
||||||
|
id TEXT PRIMARY KEY -- nanoid
|
||||||
|
username TEXT UNIQUE NOT NULL
|
||||||
|
passwordHash TEXT NOT NULL -- bcrypt hash
|
||||||
|
cloudEnabled INTEGER DEFAULT 0 -- 0=未激活,1=已激活(填写邀请码后)
|
||||||
|
createdAt INTEGER NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
### notes
|
||||||
|
```sql
|
||||||
|
id TEXT PRIMARY KEY
|
||||||
|
userId TEXT NOT NULL REFERENCES users(id)
|
||||||
|
title TEXT NOT NULL DEFAULT ''
|
||||||
|
content TEXT NOT NULL DEFAULT ''
|
||||||
|
folderId TEXT -- null = 根目录
|
||||||
|
tags TEXT NOT NULL DEFAULT '[]' -- JSON 数组
|
||||||
|
starred INTEGER DEFAULT 0
|
||||||
|
wordCount INTEGER DEFAULT 0
|
||||||
|
deletedAt INTEGER -- null = 未删除,软删除时间戳
|
||||||
|
createdAt INTEGER NOT NULL
|
||||||
|
updatedAt INTEGER NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
### folders
|
||||||
|
```sql
|
||||||
|
id TEXT PRIMARY KEY
|
||||||
|
userId TEXT NOT NULL REFERENCES users(id)
|
||||||
|
name TEXT NOT NULL
|
||||||
|
parentId TEXT -- null = 根目录
|
||||||
|
order INTEGER DEFAULT 0
|
||||||
|
createdAt INTEGER NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
### comments
|
||||||
|
```sql
|
||||||
|
id TEXT PRIMARY KEY
|
||||||
|
userId TEXT NOT NULL REFERENCES users(id)
|
||||||
|
content TEXT NOT NULL
|
||||||
|
createdAt INTEGER NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
### invite_codes
|
||||||
|
```sql
|
||||||
|
code TEXT PRIMARY KEY
|
||||||
|
usedByUserId TEXT -- null = 未使用
|
||||||
|
usedAt INTEGER -- null = 未使用
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 认证方案
|
||||||
|
|
||||||
|
- **JWT**,签发时有效期 30 天,存储在 `localStorage`(key: `mikivl_token`)
|
||||||
|
- 无 refresh token(30 天足够,过期重新登录)
|
||||||
|
- 密码使用 bcrypt hash(cost factor 10)
|
||||||
|
- 所有 `/api/notes/*`、`/api/folders/*` 接口需携带 `Authorization: Bearer <token>`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 同步策略
|
||||||
|
|
||||||
|
**本地优先,登录后同步:**
|
||||||
|
|
||||||
|
1. **未登录**:完全使用 IndexedDB,功能不受影响
|
||||||
|
2. **登录时**:执行全量同步——拉取云端数据与本地合并,以 `updatedAt` 较新的为准
|
||||||
|
3. **本地写操作**(create/update/delete):操作完成后立即异步推送到云端(fire-and-forget,失败静默)
|
||||||
|
4. **冲突处理**:同一条笔记本地和云端都有修改,取 `updatedAt` 较大的版本
|
||||||
|
|
||||||
|
**同步接口:**
|
||||||
|
|
||||||
|
- `GET /api/notes/sync` — 返回该用户全量笔记和文件夹
|
||||||
|
- `POST /api/notes/sync` — 批量 upsert 笔记和文件夹(幂等)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API 接口定义
|
||||||
|
|
||||||
|
### 认证
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/auth/register
|
||||||
|
Body: { username: string, password: string }
|
||||||
|
Response: { token: string, user: { id, username, cloudEnabled } }
|
||||||
|
|
||||||
|
POST /api/auth/login
|
||||||
|
Body: { username: string, password: string }
|
||||||
|
Response: { token: string, user: { id, username, cloudEnabled } }
|
||||||
|
|
||||||
|
POST /api/auth/activate
|
||||||
|
Header: Authorization: Bearer <token>
|
||||||
|
Body: { code: string }
|
||||||
|
Response: { success: true }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 同步
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/notes/sync
|
||||||
|
Header: Authorization: Bearer <token>
|
||||||
|
Response: { notes: Note[], folders: Folder[] }
|
||||||
|
|
||||||
|
POST /api/notes/sync
|
||||||
|
Header: Authorization: Bearer <token>
|
||||||
|
Body: { notes: Note[], folders: Folder[] }
|
||||||
|
Response: { synced: number }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 笔记 CRUD(单条操作,供实时同步用)
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT /api/notes/:id -- upsert 单条笔记
|
||||||
|
DELETE /api/notes/:id -- 软删除(设置 deletedAt)
|
||||||
|
PUT /api/folders/:id -- upsert 单条文件夹
|
||||||
|
DELETE /api/folders/:id -- 删除文件夹
|
||||||
|
```
|
||||||
|
|
||||||
|
### 评论
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/comments -- 获取所有评论(公开)
|
||||||
|
POST /api/comments -- 发布评论(需登录)
|
||||||
|
Header: Authorization: Bearer <token>
|
||||||
|
Body: { content: string }
|
||||||
|
Response: { id, username, content, createdAt }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前端改动
|
||||||
|
|
||||||
|
### 新增文件
|
||||||
|
- `src/lib/auth.ts` — JWT 存取、login/register/activate API 封装
|
||||||
|
- `src/lib/sync.ts` — 全量同步、单条推送、冲突合并逻辑
|
||||||
|
- `src/components/auth/LoginModal.tsx` — 登录/注册弹窗(Tab 切换),含邀请码激活入口
|
||||||
|
- `src/components/auth/UserMenu.tsx` — 侧边栏底部用户区域(头像、用户名、云同步状态、登出)
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
- `src/stores/appStore.ts` — 添加 `currentUser`、`syncStatus` state;写操作(createNote/updateNote/deleteNote/restoreNote/createFolder/updateFolder/deleteFolder)完成后触发 `sync.pushOne()`
|
||||||
|
- `src/components/sidebar/Sidebar.tsx` — 底部添加 `<UserMenu />`
|
||||||
|
- `homepage/index.html` — 底部新增评论区(读取 `/api/comments`,登录用户可发评论)
|
||||||
|
|
||||||
|
### appStore 新增 state
|
||||||
|
```typescript
|
||||||
|
currentUser: { id: string; username: string; cloudEnabled: boolean } | null
|
||||||
|
syncStatus: 'idle' | 'syncing' | 'error'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 服务端文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
server/
|
||||||
|
├── index.ts (已有,注册新路由)
|
||||||
|
├── db.ts (Drizzle 初始化,schema 定义,DB 文件路径:/opt/mikivl/data/app.db)
|
||||||
|
├── middleware/
|
||||||
|
│ └── auth.ts (JWT 验证,注入 c.set('userId', ...))
|
||||||
|
└── routes/
|
||||||
|
├── auth.ts (register/login/activate)
|
||||||
|
├── notes.ts (sync GET/POST + 单条 CRUD)
|
||||||
|
├── folders.ts (单条 CRUD)
|
||||||
|
└── comments.ts (list/create)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 邀请码
|
||||||
|
|
||||||
|
预生成 10 个,数据库初始化时写入:
|
||||||
|
|
||||||
|
```
|
||||||
|
MIKI-A7X2-KP9Q
|
||||||
|
MIKI-B3N8-WR4L
|
||||||
|
MIKI-C6M1-ZT7Y
|
||||||
|
MIKI-D9F5-HJ2V
|
||||||
|
MIKI-E4K3-QN6U
|
||||||
|
MIKI-F8R7-XG1S
|
||||||
|
MIKI-G2W4-LB5E
|
||||||
|
MIKI-H5T9-MC8D
|
||||||
|
MIKI-J1P6-VF3O
|
||||||
|
MIKI-K7Q2-YH4N
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 部署变更
|
||||||
|
|
||||||
|
- 服务端新增依赖:`drizzle-orm`、`better-sqlite3`、`bcryptjs`、`jsonwebtoken`
|
||||||
|
- SQLite 数据库文件:`/opt/mikivl/data/app.db`(首次启动自动创建)
|
||||||
|
- 服务器需创建目录:`mkdir -p /opt/mikivl/data`
|
||||||
|
- PM2 重启 hono-server 后生效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 分拆子项目顺序
|
||||||
|
|
||||||
|
由于子系统间有依赖关系,按以下顺序实现:
|
||||||
|
|
||||||
|
1. **服务端基础层**(db.ts + schema + auth 路由)
|
||||||
|
2. **前端认证**(LoginModal + UserMenu + auth.ts + appStore 扩展)
|
||||||
|
3. **云同步**(sync.ts + notes/folders 路由 + appStore 写操作触发同步)
|
||||||
|
4. **首页评论**(comments 路由 + homepage/index.html 评论区)
|
||||||
Loading…
x
Reference in New Issue
Block a user