e51666c52e
- 新增 web_sessions 表(sqlc):refresh_token 以 AES-256-GCM 落盘,密钥取自 CDROP_SESSION_SECRET(env、不在库内);cookie 仅存不透明随机串,表 id 存其 SHA-256,故 DB 单独泄露既换不出可用 cookie、也解不出 token - /auth/exchange 建 session 并下发 HttpOnly; Secure; SameSite=Lax; Path=/api/auth cookie,响应体不再回传 refresh_token - /auth/refresh 改为 cookie 驱动(7 天滑动失活),同时即开机静默免重登; 新增 /auth/logout(删 session + 清 cookie)、/auth/device(写设备名入 session) - device_name 随 session 存,开机 refresh 带回前端,抗 iOS PWA 存储清除; setup / 改名时 syncWebDeviceName 推送服务端 - striped per-session 锁串行化同会话并发 refresh,防一次性 refresh_token 被 花两次而把用户从所有 tab 登出 - 前端 store 移除 refreshToken 字段(长寿命凭据彻底不进 JS);main.tsx 首屏前 bootstrapAuth 用 cookie 静默续期,命中直接进已登录态、避免登录页闪现 - config:prod 强制 CDROP_SESSION_SECRET,缺失拒启动 - 桌面端不受影响(自有 loopback flow + Go keyring,不碰这两个端点)
70 lines
2.7 KiB
SQL
70 lines
2.7 KiB
SQL
-- cdrop MVP schema (PROJECT_BRIEF.md §4)
|
||
-- 以 IF NOT EXISTS 形式由 internal/db/bootstrap.go 启动期单事务执行。
|
||
|
||
CREATE TABLE IF NOT EXISTS devices (
|
||
user_id TEXT NOT NULL,
|
||
name TEXT NOT NULL,
|
||
type TEXT NOT NULL,
|
||
last_seen INTEGER NOT NULL,
|
||
PRIMARY KEY (user_id, name)
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS shortcut_tokens (
|
||
jti TEXT PRIMARY KEY,
|
||
user_id TEXT NOT NULL,
|
||
label TEXT NOT NULL,
|
||
scopes TEXT NOT NULL,
|
||
created_at INTEGER NOT NULL,
|
||
expires_at INTEGER NOT NULL,
|
||
last_used_at INTEGER,
|
||
revoked INTEGER NOT NULL DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS transfer_sessions (
|
||
id TEXT PRIMARY KEY,
|
||
user_id TEXT NOT NULL,
|
||
sender_name TEXT NOT NULL,
|
||
receiver_name TEXT NOT NULL,
|
||
state TEXT NOT NULL,
|
||
mode TEXT,
|
||
file_name TEXT NOT NULL,
|
||
file_size INTEGER NOT NULL,
|
||
file_sha256 TEXT,
|
||
bytes_transferred INTEGER NOT NULL DEFAULT 0,
|
||
created_at INTEGER NOT NULL,
|
||
finished_at INTEGER,
|
||
fail_reason TEXT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS clipboard_state (
|
||
user_id TEXT PRIMARY KEY,
|
||
content_type TEXT NOT NULL,
|
||
content TEXT,
|
||
source_device TEXT,
|
||
updated_at INTEGER NOT NULL,
|
||
-- version 是每用户严格单调递增的版本号(每次写 +1),变更探测 / ETag 用;
|
||
-- updated_at 仍是墙钟(TTL / 展示)。既有库由 bootstrap 的幂等 ALTER 补列。
|
||
version INTEGER NOT NULL DEFAULT 0,
|
||
-- origin_ts 是内容在源设备被复制的时刻(ms)。冲突收敛按它做 LWW——写入仅在
|
||
-- origin_ts 严格大于现存时被接受,故延迟/乱序到达的旧复制不会盖掉新的。
|
||
origin_ts INTEGER NOT NULL DEFAULT 0
|
||
);
|
||
|
||
-- web_sessions:浏览器端「免重登」。cookie 里只放不透明随机串,本表 id 存其
|
||
-- SHA-256,故 DB 单独泄露也换不出可用 cookie;refresh_token 以 AES-256-GCM 落盘
|
||
-- (密钥在容器环境、不在库内)。仅浏览器使用——桌面端 refresh_token 由 Go keyring
|
||
-- 持有、走自有 loopback flow,绝不入此表。device_name 随会话存,使 PWA 存储被清后
|
||
-- 开机仍能恢复本机设备名。
|
||
CREATE TABLE IF NOT EXISTS web_sessions (
|
||
id TEXT PRIMARY KEY,
|
||
user_id TEXT NOT NULL,
|
||
refresh_token TEXT NOT NULL,
|
||
device_name TEXT NOT NULL DEFAULT '',
|
||
user_agent TEXT NOT NULL DEFAULT '',
|
||
created_at INTEGER NOT NULL,
|
||
last_used_at INTEGER NOT NULL,
|
||
expires_at INTEGER NOT NULL
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_web_sessions_expires ON web_sessions (expires_at);
|