7.0 KiB
7.0 KiB
用户认证 + 云端同步 + 评论系统设计文档
日期: 2026-05-05
作者: MikiVL
状态: 已批准
背景与目标
当前笔记应用(www.mikivl.online/app)的所有数据存储在浏览器 IndexedDB 中,清除浏览器数据或换设备即丢失。本次扩展目标:
- 添加用户账号系统(用户名+密码注册/登录)
- 登录用户的笔记双存——本地 IndexedDB 继续作为缓存,服务端 SQLite 持久化
- 云存储功能需邀请码激活(预生成 10 个)
- 主页(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
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
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
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
id TEXT PRIMARY KEY
userId TEXT NOT NULL REFERENCES users(id)
content TEXT NOT NULL
createdAt INTEGER NOT NULL
invite_codes
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>
同步策略
本地优先,登录后同步:
- 未登录:完全使用 IndexedDB,功能不受影响
- 登录时:执行全量同步——拉取云端数据与本地合并,以
updatedAt较新的为准 - 本地写操作(create/update/delete):操作完成后立即异步推送到云端(fire-and-forget,失败静默)
- 冲突处理:同一条笔记本地和云端都有修改,取
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、syncStatusstate;写操作(createNote/updateNote/deleteNote/restoreNote/createFolder/updateFolder/deleteFolder)完成后触发sync.pushOne()src/components/sidebar/Sidebar.tsx— 底部添加<UserMenu />homepage/index.html— 底部新增评论区(读取/api/comments,登录用户可发评论)
appStore 新增 state
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 后生效
分拆子项目顺序
由于子系统间有依赖关系,按以下顺序实现:
- 服务端基础层(db.ts + schema + auth 路由)
- 前端认证(LoginModal + UserMenu + auth.ts + appStore 扩展)
- 云同步(sync.ts + notes/folders 路由 + appStore 写操作触发同步)
- 首页评论(comments 路由 + homepage/index.html 评论区)