studynote/docs/superpowers/specs/2026-05-05-user-auth-cloud-sync-design.md

7.0 KiB
Raw Blame History

用户认证 + 云端同步 + 评论系统设计文档

日期: 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

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 天,存储在 localStoragekey: mikivl_token
  • 无 refresh token30 天足够,过期重新登录)
  • 密码使用 bcrypt hashcost 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 — 添加 currentUsersyncStatus state写操作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-ormbetter-sqlite3bcryptjsjsonwebtoken
  • 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 评论区)