v57:令牌升级为「设备会话」(24h 滑动续期 / 每账号 10 台 / 网页端远程退出)

用户 2026-10-17 定的模型:
  · 一次登录 = 一台设备一条会话;同一台设备重登,旧令牌立即作废
  · 24 小时滑动续期(一直联网就不用每天输密码);离线超 24h 只锁界面、不删数据
  · 网页端「个人设置 → 设备管理」能看见都有谁在登录,并能把丢了的那台踢掉
  · 每账号最多 10 台,超了让用户先踢旧的

服务端配套:
  · api_tokens 加 device_id / device_name / platform / app_version / last_ip
    + idx_api_device;历史令牌回填 legacy-<id>(不回填 = 踢不掉的幽灵会话)
  · ApiToken:issueDevice / listDevices / revokeDevice / revokeOthers / revokeCurrent
    + 滑动续期(剩余不足 4h 才写库);verify 同时返回数字键与关联键(三处调用方在用)
  · 新增 /api/v1/logout、/api/v1/devices;ping 报 features.device_session
  · 登录不再读 days(0 = 永不过期会绕过 24h 这条线);超限回 409 且**不动任何一行**
  · 老客户端兜底固定槽位 auto-unknown(随机设备号会撞满 10 台把账号锁死)
  · Schema 34 → 35

顺带:首页近期任务/往年的今天(Home::memories)、名册派生字段口径统一。

验证:设备会话探针 56/0、设备管理渲染 22/0、登录全链路 30/0、反向验证 12/0、
      e2e 1519/0、静态断言本地空跑 357/0、线上 /api/v1/{ping,devices,logout} 复核通过。
This commit is contained in:
2026-10-18 03:34:47 +08:00
parent 190dc56301
commit ebaed21920
8 changed files with 1329 additions and 140 deletions
+9
View File
@@ -109,6 +109,15 @@ final class Api
'person_id' => $personId,
'family_id' => (int)$p['family_id'],
'token_id' => (int)$hit[1],
/* ---- v57:当前这次请求是「哪台设备」发来的 ----
`devices` 接口靠它标出「这台设备」(不能误踢自己),
`/me` 靠它把"会话还剩多久"回给端上。
⚠️ 老令牌(v35 之前签的)没有 device_id,这里会是空串 ——
下游判断必须容忍空串,不能拿它当"设备不存在"。 */
'device_id' => (string)($hit['device_id'] ?? ''),
'device_name' => (string)($hit['device_name'] ?? ''),
'expires_at' => isset($hit['expires_at']) && $hit['expires_at'] !== null
? (string)$hit['expires_at'] : null,
'role' => $role,
'can_write' => $role !== 'guest',
];