diff --git a/docs/device-session-management-design.html b/docs/device-session-management-design.html index ddf6337..731b14f 100644 --- a/docs/device-session-management-design.html +++ b/docs/device-session-management-design.html @@ -141,15 +141,74 @@ INDEX (user_id), INDEX (device_id), UNIQUE (refresh_jti) -
devices 表加列(同迁移)devices 表(完整字段)既有 7 列(000001 建表 + 000015 加 dp_uuid)+ 本方案新增 2 列(000016)。完整 schema:
++devices( + id INTEGER PK AUTOINCREMENT, -- 000001 内部自增主键 + uuid TEXT NOT NULL UNIQUE, -- 000001 客户端生成的稳定设备 UUID(API 对外用它) + user_id NOT NULL, -- 000001 FK users.id(设备归属用户) + name TEXT NOT NULL, -- 000001 设备名(主机名/机型,≤64 runes) + platform TEXT NOT NULL CHECK(...), -- 000001 ios|android|windows|macos|linux * + last_seen DATETIME NULL, -- 000001 数据面最后活跃(connect + ReportUsage touch)→ 在线判定 + created_at DATETIME NOT NULL, -- 000001 设备首次注册时间 + dp_uuid CHAR(36) NULL UNIQUE, -- 000015 每设备数据面凭证(sing-box VLESS user uuid) + client_version TEXT NULL, -- 000016 最近上报的客户端版本(列表展示) ← 新增 + totp_trusted_until DATETIME NULL -- 000016 预留:2FA「信任设备」过期点;清除登录时清空 ← 新增 +) +INDEX (user_id) -- 000001 +UNIQUE INDEX (dp_uuid) -- 000015(NULL 不冲突,存量待回填) +
| 列 | 类型 | 用途 | |
|---|---|---|---|
| 列 | 类型 | 来源 | 用途 |
client_version | TEXT NULL | 该设备最近上报的客户端版本,列表展示 | |
totp_trusted_until | DATETIME NULL | 预留:未来 2FA「信任设备」过期点;清除登录信息时一并清空 | |
id | INTEGER PK | 000001 | 内部自增主键(sessions/usage_device_daily 用 device_id 引用它) |
uuid | TEXT UNIQUE | 000001 | 客户端生成的稳定设备 UUID;/v1/me/devices/{uuid} 路径参数即它 |
user_id | FK users.id | 000001 | 设备归属用户 |
name | TEXT | 000001 | 设备名(主机名/机型,normalizeName 截断 ≤64 runes) |
platform | TEXT CHECK | 000001 | 平台枚举。*现有 CHECK 仅 4 值,需 000016 放宽加 linux |
last_seen | DATETIME NULL | 000001 | 数据面最后活跃;<3min=在线。由 connect + ReportUsage touch |
created_at | DATETIME | 000001 | 设备首次注册时间(≠最后登录,后者取 session.created_at) |
dp_uuid | CHAR(36) NULL UNIQUE | 000015 | 每设备数据面凭证;EnsureDeviceDpUUID 首连铸入,作 sing-box VLESS user uuid |
client_version | TEXT NULL | 000016 新增 | 该设备最近上报的客户端版本,列表展示 |
totp_trusted_until | DATETIME NULL | 000016 新增 | 预留:未来 2FA「信任设备」过期点;清除登录信息时一并清空 |
既有列:uuid / user_id / name / platform / last_seen / created_at / dp_uuid(000015)。设备「最后登录时间」= 该设备最近一条 session.created_at(不复用 last_seen,后者是数据面活跃度)。
*platform CHECK 放宽:000001 的约束是 CHECK(platform IN ('ios','android','windows','macos')),不含 linux,但桌面客户端支持 Linux。000016 需重建该约束加入 'linux'(SQLite 改 CHECK 要走「建新表→拷数据→换名」;MySQL 同步改)。
name 怎么来:由客户端在登录/注册时填好上报(不是服务端造)。客户端用 device_info_plus 按平台取一个可读名:
+| 平台 | 取值 | 示例 |
|---|---|---|
| macOS / Linux | 主机名(Platform.localHostname) | Wangjia-MacBook-Pro |
| Windows | 计算机名(WindowsDeviceInfo.computerName) | DESKTOP-7F3K |
| iOS | 设备名/机型(IosDeviceInfo.name 或 .utsname.machine) | iPhone 15 |
| Android | 厂商 + 机型(manufacturer + model) | Xiaomi 13 |
RegisterIfAbsent 经 normalizeName 截断 ≤64 runes 存库。后续可允许用户在「我的设备」里改名(PATCH,二期可选)。
+created_at(devices)= 设备首次注册时间,只写一次。last_seen(devices)= 数据面最后活跃,即「此刻是不是正连着节点走流量」。由 connect + 每条 ReportUsage(连着时 ~60s 一次)touch;now - last_seen < 3min ⇒ 「在线」。sessions.created_at(auth 层,与是否正在走流量无关)。列表上「在线」红绿点看 last_seen,「最后登录 N 天前」看 session。(user_id, devices.uuid)。uuid 由客户端生成、持久化在 flutter_secure_storage,同一台机器重装 app 内仍稳定 → 一台真实设备恒等于一行 devices,不会因重登重复计数。RegisterIfAbsent(userID, deviceUUID, name, platform, maxDevices) 在注册新设备时已会按套餐上限校验设备数(maxDevices 来自计划:免费 1 / PRO 5,对应 Me.devicesMax)。超限时拒绝注册新设备(返回「设备数已达上限」),即「按设备控制用户能登录的设备数」。sessions(未撤销)或 last_seen 在线数为准,限「并发」而非「累计」。sessions 表 + devices.id 同时支持这两种统计。usage_device_daily(已存在,本方案使其真正写入)