diff --git a/docs/device-session-management-design.html b/docs/device-session-management-design.html index 15ff059..53a3a62 100644 --- a/docs/device-session-management-design.html +++ b/docs/device-session-management-design.html @@ -90,7 +90,7 @@ ← 文档索引

设备 & 会话管理 + 每设备流量归因(设计方案)

-

Pangolin · 「我的设备」管理 + 控制面会话 + 数据面按设备记账 · 2026-06-28

+

Pangolin · 「我的设备」管理 + 控制面会话 + 数据面按设备记账 · 2026-06-28 · → 实现计划 P1–P6

一句话:后端绝大部分已经建好,只是「没接线」devices 表、每设备数据面凭证 dp_uuid、 diff --git a/docs/device-session-management-plan.html b/docs/device-session-management-plan.html new file mode 100644 index 0000000..6077fb5 --- /dev/null +++ b/docs/device-session-management-plan.html @@ -0,0 +1,134 @@ + + + + + +设备 & 会话管理 + 每设备流量归因(实现计划 P1–P6) + + + +
+ +← 文档索引 + +

设备 & 会话管理 + 每设备流量归因 · 实现计划 P1–P6

+

阅读版 · 执行真相源(含 checkbox)为 docs/superpowers/plans/2026-06-29-device-session-management.md · 2026-06-29

+ +
+设计见 设备 & 会话管理 总方案。核心仍是「打通已建未接线的链路 + 新增 sessions 表 + UI 重做」,拆成 6 期、每期一刀可独立验收。 +约束:SQL 中性方言(dialect.Upsert、无 NOW());UI 走 token 单源;不碰 CI-gated golden 的 Linux 基线;每期 build+test 过即 commit。 +
+ +

已核的代码事实(计划依据)

+ + +

分期

+ +
+

P1 · 设备注册打通(登录即注册设备)

+

目标:登录/注册成功即写 devices 表 → 「我的设备」显示真实设备。

+
    +
  • 客户端加依赖 uuid + device_info_plus;新 device_identity.dart(生成+持久 device_id、设备名/平台、客户端版本);弃用硬编码 'mac-001'
  • +
  • login/register 请求体加 device:{id,name,platform,client_version}
  • +
  • 后端 auth/handler.go 加 device 字段;auth/service.go 成功签发后调 RegisterIfAbsent(注入 DeviceRegistrar 接口,返回 deviceID 供 P2);normalizePlatform 加 linux;main.go 注入。
  • +
  • ✓ 验收:登录后 GET /v1/me/devices 非空、含真实设备名/平台
  • +
+
+ +
+

P2 · sessions 表 + 在线/最后登录

+

目标:会话落库绑设备;列表显示在线/客户端版本/最后登录。

+
    +
  • migration 000016:建 sessions 表;devices 加 client_version+totp_trusted_until;唯一键 UNIQUE(uuid)UNIQUE(user_id,uuid);platform CHECK 加 linux。
  • +
  • server/internal/sessions/ Store(Create/RotateJTI/RevokeByJTI/RevokeByDevice/…)。
  • +
  • TokenManager 外露 JTI;auth.Service 注入 sessions:登录 Create、刷新 RotateJTI+last_active、登出 RevokeByJTI。
  • +
  • ReportUsage touch devices.last_seen(在线判定);ListDevices 返回 online/last_login/client_version;客户端 Device model 加字段。
  • +
  • ✓ 验收:列表显示在线/版本/最后登录;停 agent ~3min 转离线
  • +
+
+ +
+

P3 · 两个操作(强制退出 + 清除增强)

+

目标:强制退出(吊销会话留设备)/ 清除(移除+吊销会话+吊销 dp_uuid)。

+
    +
  • 新端点 POST /v1/me/devices/{uuid}/logout(ForceLogout:RevokeByDevice + 逐 jti Revoke)。
  • +
  • DeleteDevice 增强:吊销 session;CredentialRevokerper-deviceRevokeDevice(dpUUID)nodes.Hub 实现,main.go 替 NoopRevoker)。
  • +
  • 客户端 account_api.forceLogout + provider。
  • +
  • ✓ 验收:强制退出→refresh 失败需重登;清除→设备消失 + 数据面凭证作废
  • +
+
+ +
+

P4 · 每设备流量验证(打通 usage_device_daily)

+

目标:每设备流量真正写库 + 统计页设备下拉真数据。

+
    +
  • ConnectNode 兜底 RegisterIfAbsent;端到端确认 EnsureDeviceDpUUID→per-device dp_uuid→ReportUsageAccumulateDeviceUsage 填表。
  • +
  • 统计页设备下拉接真实设备 + deviceUsageProvider(呼应统计整改 #10)。
  • +
  • ✓ 验收:统计页按设备能看到分流量
  • +
+
+ +
+

P5 · 2FA 信任设备 FUTURE · 本轮不编码

+

占位:清除登录信息联动 totp_trusted_until(列已在 P2 预留),重登需重做 2FA。待 2FA 主流程就绪后单独排。

+
+ +
+

P6 · UI 重做(DevicesScreen,五端)

+

目标:按总方案 §7 视觉稿落地「我的设备」交互。

+
    +
  • DevicesScreen 重做:名称/平台·版本/在线绿点/最后登录 + 「本机」标 + 行尾 ⋯ 菜单(强制退出/清除·危险红)+ 危险二次确认弹窗;颜色/字号走 token。
  • +
  • 宽/窄屏适配;空/加载态;l10n(zh+en)新增文案;DevicesScreen 新 golden(Mac 生成,非 CI-gated)。
  • +
  • ✓ 验收:截图与 §7 视觉稿一致,五端生效(Flutter 单码)
  • +
+
+ +
+

验收(整体)

+ +

关联:设备 & 会话管理 总方案 · 统计体系整改(#5)(P4 设备下拉)。

+ +
+ + diff --git a/docs/index.html b/docs/index.html index bf0ea45..55a4d59 100644 --- a/docs/index.html +++ b/docs/index.html @@ -66,6 +66,11 @@

实现计划 / Plans

+ +
设备 & 会话管理 + 每设备流量归因 实现计划(P1–P6)HTML
+
阅读版;执行真相源为 docs/superpowers/plans/2026-06-29-device-session-management.md(含 checkbox)。P1 设备注册打通 → P2 sessions表+在线/最后登录 → P3 强制退出/清除 → P4 每设备流量 → P5 2FA信任(future) → P6 UI重做。
+
docs/device-session-management-plan.html
+
Android 客户端实现计划 MD
11 个任务:Go 1.24.3 工具链 → 构建 libbox.aar → 解析真实 API → Dart 接线 → 修原生编译 → 模拟器端到端 → 统计 → 切节点 → KillSwitch → 真机验证。
diff --git a/docs/superpowers/plans/2026-06-29-device-session-management.md b/docs/superpowers/plans/2026-06-29-device-session-management.md new file mode 100644 index 0000000..387fcc5 --- /dev/null +++ b/docs/superpowers/plans/2026-06-29-device-session-management.md @@ -0,0 +1,68 @@ +# 设备 & 会话管理 + 每设备流量归因 —— 实现计划(P1–P6) + +> 执行真相源(checkbox)。设计见 `docs/device-session-management-design.html`;阅读版 `docs/device-session-management-plan.html`。 +> SQL 走中性方言(`dialect.Upsert`、无 `NOW()`/`UTC_TIMESTAMP()`);UI 走 token 单源;不碰 CI-gated golden 的 Linux 基线。每期 build+test 过即 commit。 + +## 计划文档(先产出) +- [x] `docs/superpowers/plans/2026-06-29-device-session-management.md`(本文件) +- [ ] `docs/device-session-management-plan.html`(阅读版) +- [ ] 登记 `docs/index.html`「实现计划」 +- [ ] 总方案 `device-session-management-design.html` 加「→ 实现计划」链接 + +## P1 · 设备注册打通(登录即注册设备) +- [ ] 客户端加依赖 `uuid` + `device_info_plus`(`client/pubspec.yaml`) +- [ ] 新 `client/lib/services/device_identity.dart`:`deviceId()`(secure storage 读;无则 `Uuid().v4()` 写;读失败 ≠ 没有,不重生成)、`deviceName()`/`platform()`、`clientVersion()` +- [ ] 弃用 `connection_provider.dart` 的 `_kDeviceId='mac-001'` → 用 `device_identity` +- [ ] `auth_api.dart` login/register 请求体加 `device:{id,name,platform,client_version}` +- [ ] `auth/handler.go`:`loginRequest`/`registerRequest` 加 `Device deviceMeta` +- [ ] `auth/service.go` Login/Register:成功签发后调 `DeviceRegistrar.RegisterIfAbsent`(consumer-side 接口;`devices.Service` 实现;`RegisterIfAbsent` 增返回 `deviceID`);`MaxDevices` 由 plan 解析 +- [ ] `normalizePlatform` 加 `linux` +- [ ] `main.go` 构造 `devices.Service` 注入 `authSvc` +- [ ] 测试:`device_identity` 单测;auth 集成(登录后 devices 落行) +- [ ] 验收:登录后 `GET /v1/me/devices` 非空 + +## P2 · sessions 表 + 在线/最后登录 +- [ ] migration `000016_sessions_and_device_meta`(mysql+sqlite,含 down):建 sessions 表;devices 加 `client_version`+`totp_trusted_until`;唯一键 `UNIQUE(uuid)`→`UNIQUE(user_id,uuid)`;platform CHECK 加 `linux` +- [ ] 新 `server/internal/sessions/`:`Store`(Create/RotateJTI/RevokeByJTI/RevokeByDevice/ActiveByUserWithDevice/LastLoginByDevice) +- [ ] `TokenManager.Issue`/`Refresh` 外露 refresh JTI +- [ ] `auth.Service` 注入 `sessions.Store`:Login/Register `Create`;Refresh `RotateJTI`+last_active;Logout `RevokeByJTI` +- [ ] `nodes/handler_grpc.go: ReportUsage` deviceID>0 时 touch `devices.last_seen` +- [ ] `devices.Service.ListDevices`+API struct+store join:增 `client_version`/`online`/`last_login` +- [ ] 客户端 `Device` model 加 `clientVersion`/`online`/`lastLogin`;`account_api` 解析 +- [ ] 测试:sessions store;JTI surface;ReportUsage touch;ListDevices 计算;migration sqlite 实库 +- [ ] 验收:列表显示在线/版本/最后登录;停 agent ~3min 转离线 + +## P3 · 两个操作(强制退出 + 清除增强) +- [ ] 新端点 `POST /v1/me/devices/{uuid}/logout`(handler + `Service.ForceLogout`):`sessions.RevokeByDevice` + 逐 jti `TokenManager.Revoke` +- [ ] `DeleteDevice` 增强:加 `sessions.RevokeByDevice`;`CredentialRevoker` 改 per-device +- [ ] `CredentialRevoker` 接口改 `RevokeDevice(ctx, dpUUID)`;`nodes.Hub` 实现;`main.go` 注入 hub 替 NoopRevoker +- [ ] 客户端 `account_api.forceLogout` + `devicesProvider.forceLogout` +- [ ] 测试:ForceLogout 删 jti+标 revoked_at;DeleteDevice per-device revoke +- [ ] 验收:强制退出→refresh 失败;清除→设备消失+凭证作废 + +## P4 · 每设备流量验证 +- [ ] `ConnectNode` 兜底 `RegisterIfAbsent`(device 缺失时) +- [ ] 端到端确认 `usage_device_daily` 填充 +- [ ] 统计页设备下拉接真实设备 + `deviceUsageProvider`(#10) +- [ ] 测试:集成(注册→连接→上报→有行);统计页设备下拉 widget +- [ ] 验收:统计页按设备看分流量 + +## P5 · (未来)2FA 信任设备 —— 本轮不编码 +- 占位:清除联动 `totp_trusted_until`(P2 已预留列)。 + +## P6 · UI 重做(DevicesScreen,五端) +- [ ] `DevicesScreen` 重做:名称/平台·版本/在线绿点/最后登录 + 本机标 + 行尾 ⋯ 菜单 + 危险确认弹窗;走 token +- [ ] 宽/窄屏适配;空/加载态 +- [ ] l10n:`app_text.dart`+`strings_{zh,en}.dart` 新增文案 +- [ ] golden:DevicesScreen 新 golden(Mac 生成,非 CI-gated) +- [ ] 测试:widget(在线/离线/菜单/确认/调用) +- [ ] 验收:与 §7 视觉稿一致,五端生效 + +## Verification +- 后端 `go build ./... && go test ./...`(含 sqlite 实库)+ `./server/run_sqlite_test.sh` +- 客户端 `flutter analyze` + `flutter test` +- 端到端:登录→设备列表;停 agent→离线;强制退出→refresh 失败;清除→消失+凭证作废;走流量→`usage_device_daily` 有行 +- **线上 000016 迁移在 pangolin1 跑 `pangolin-migrate up` —— 生产写,执行前单独确认** + +## 不在本轮 +- P5 只占位;存量空 devices 无需回填(新登录自然铺开)