wangjia
58526b09e6
feat(client): P6 DevicesScreen 重做 + DevicePlatform 枚举
...
ci-pangolin / Lint — shellcheck (push) Successful in 7s
ci-pangolin / OpenAPI Sync Check (push) Successful in 19s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 7s
ci-pangolin / Flutter — analyze + test (push) Successful in 23s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m12s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
回应「状态码用枚举更好」:新增 DevicePlatform 枚举(线格式 fromWire 解析,
UI 只碰枚举,穷尽 switch),Device.platformKind getter;platform 图标按枚举 switch。
DevicesScreen 按 §7 视觉稿重做:行内 名称+「本机」标 / 平台·客户端版本·最后登录 /
在线绿点光晕·离线灰点 + 行尾 ⋯ 菜单(强制退出 / 清除登录信息·危险红)+ 危险二次
确认弹窗;本机行不显示 ⋯(不自我踢)。l10n 新增在线/离线/最后登录/从未登录/强制
退出/清除登录信息/取消 + 确认文案(中英);「当前设备」改「本机」。
新图标 moreVertical/trash/alertTriangle。localDeviceIdProvider 供本机判定。
测试:devices_screen widget 3 例(本机标+在线/离线+版本渲染 / ⋯菜单→强制退出→
确认→POST logout / 清除→DELETE);全量 flutter test 58 绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-29 06:10:37 +08:00
wangjia
bcc114088c
feat(devices): P3 强制退出 + 清除增强(per-device 凭证吊销)
...
ci-pangolin / Lint — shellcheck (push) Successful in 9s
ci-pangolin / OpenAPI Sync Check (push) Successful in 17s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 5s
ci-pangolin / Flutter — analyze + test (push) Successful in 26s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 12s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 15s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m4s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
后端:新端点 POST /v1/me/devices/{uuid}/logout(ForceLogout:吊销该设备会话+
丢 Redis JTI,设备留列表)。DeleteDevice 增强:先吊销会话再删设备(FK ON DELETE
CASCADE 清理会话行)+ 按 dp_uuid 吊销数据面凭证。CredentialRevoker 接口改
per-device RevokeDevice(dpUUID),由 nodes.Service 实现(查 connect_credentials
持有节点→推 CommandTypeRevoke + 删凭证行),main 注入替 NoopRevoker;devices 注入
SessionPort/JTIRevoker。修 SQLite 跨连接死锁(会话吊销移到 delete tx 之前)。
migration 000016 sessions FK 加 ON DELETE CASCADE。
客户端:account_api.forceLogout + devicesProvider.forceLogout(UI 留 P6)。
测试:ForceLogout(吊销会话+JTI+设备保留+403/404)+ DeleteDevice(级联+按 dp_uuid
吊销);NoopRevoker 改 dp_uuid;全量 server/flutter 测试绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-29 06:01:41 +08:00
wangjia
2f298f0a0a
feat(devices): P2 sessions 表 + 在线/最后登录/客户端版本
...
ci-pangolin / Lint — shellcheck (push) Successful in 8s
ci-pangolin / OpenAPI Sync Check (push) Successful in 18s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 4s
ci-pangolin / Go — build + test (push) Successful in 11s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m13s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
migration 000016(mysql+sqlite,含 down):新增 sessions 表(绑 device+refresh JTI)
+ devices 加 client_version/totp_trusted_until。devices 唯一键改 + platform CHECK
加 linux(需 SQLite 表重建)拆出后续迁移,降风险。
后端:新 internal/sessions Store(Create/Rotate/Revoke/RevokeByDevice/
LastLoginByDevice);TokenManager 外露 refresh JTI(IssueWithJTI/RefreshWithJTI/
ParseRefreshJTI);auth.Service 注入 SessionStore——登录建会话、刷新轮换、登出吊销;
DeviceRegistrar 返回 deviceID;ReportUsage 心跳 touch devices.last_seen(在线判定);
devices.ListDevices 经 LastLoginSource 注入返回 online(last_seen<3min)/client_version/
last_login;RegisterIfAbsent 存 client_version。
客户端:Device model 加 online/clientVersion/lastLogin(fromJson 自动解析)。
测试:sessions store 3 例 + ListDevices 在线/最后登录 + device model 2 例 +
migration v16;全量 go test/flutter test 绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-29 00:50:24 +08:00
wangjia
c0c4b94e29
feat(devices): P1 设备注册打通 —— 登录/注册即写 devices 表
...
ci-pangolin / Lint — shellcheck (push) Successful in 8s
ci-pangolin / OpenAPI Sync Check (push) Successful in 16s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 7s
ci-pangolin / Flutter — analyze + test (push) Successful in 26s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Failing after 10s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 15s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m10s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
后端:auth.Service 加 DeviceMeta + DeviceRegistrar 接口(consumer-side 解耦),
Login/Register 成功签发后 best-effort 注册设备(不强制设备上限,避免免费档重装
churn 锁死用户);handler 加 device 请求体;main 用 authDeviceRegistrar 适配
devices.Service 注入;normalizePlatform 加 linux。
客户端:新 device_identity.dart(SecureKV 接缝 + 稳定 UUIDv4 device_id 持久化 +
名称/平台/版本);弃用硬编码 'mac-001';auth_api login/register + connect 携带
device 元数据。加 uuid + device_info_plus 依赖。
测试:auth 设备注册(触发/best-effort/空 meta) + device_identity(生成/持久/
读失败不重生成/UUIDv4 形态);normalizePlatform linux=true。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-29 00:28:13 +08:00
wangjia
c550eea5d8
feat(client/auth): 登录/注册页 Enter 键导航与提交
...
登录:邮箱框回车 → 跳到密码框;密码框回车 → 直接登录(校验条件同主按钮)。
注册:邮箱框回车 → 发送验证码并聚焦验证码框;验证码满 6 位回车 → 进入设密码步
并聚焦密码框;密码框(≥6 位)回车 → 直接注册。
经 TextField.onSubmitted + FocusNode.requestFocus 实现;桌面端回车即触发。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 22:44:34 +08:00
wangjia
7dcbe04934
build(client/windows): 版本 bump 1.0.9 → 1.0.10(输入框聚焦橙边修复)
...
ci-pangolin / Lint — shellcheck (push) Successful in 10s
ci-pangolin / OpenAPI Sync Check (push) Successful in 19s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 7s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 6s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 17s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m14s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 22:33:52 +08:00
wangjia
25e8330e3b
fix(client): 修聚焦时输入框橙边漏出错位/被裁(登录·注册·搜索·兑换码)
...
ci-pangolin / Lint — shellcheck (push) Successful in 10s
ci-pangolin / OpenAPI Sync Check (push) Successful in 31s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 5s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 6s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 4s
ci-pangolin / Go — build + test (push) Successful in 10s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 18s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m13s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
主题 inputDecorationTheme 设了 focusedBorder: OutlineInputBorder(accent),
而登录/注册的邮箱·密码·验证码、节点页搜索框、兑换码框这几处的 TextField
只写 border: InputBorder.none、未覆盖 focusedBorder —— 聚焦瞬间主题的橙色
OutlineInputBorder 漏到内层 TextField 上,与外层自定义容器圆角错位/左右被裁。
统一加共享 kBareInput 装饰(pangolin_theme.dart)关掉所有边框状态泄漏,
四处替换。只压边框、不动 filled/fillColor —— 未聚焦态外观与改前一致,
不动 golden;聚焦指示仍由外层容器自己的 Border.all(accent) 负责。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 22:21:50 +08:00
wangjia
1adf088c68
build(client/windows): 版本 bump 1.0.8 → 1.0.9(节点页刷新)
...
ci-pangolin / OpenAPI Sync Check (push) Successful in 20s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 6s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 16s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m13s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
ci-pangolin / Lint — shellcheck (push) Failing after 11m42s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 22:00:26 +08:00
wangjia
0542ff027f
feat(client/nodes): 节点页刷新 — 下拉刷新 + 刷新按钮 + 进页刷新 + 10s 限流
...
- nodesProvider.refresh:10s 限流(下拉/按钮/进页多次触发只打一次远端);
不清空当前列表(后台拉、好了再换,失败保留旧数据),避免进页/下拉闪空
- nodes_page:initState 进页自动刷一次;RefreshIndicator 下拉刷新(移动端);
搜索框右侧 _RefreshButton 点击刷新(刷新中 spinner)
servers 页 golden 重生成(搜索行加按钮);CI 闸 golden 未动。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 22:00:26 +08:00
wangjia
51c171d815
build(client/windows): 版本 bump 1.0.7 → 1.0.8(节点 status 渲染等)
...
ci-pangolin / Lint — shellcheck (push) Successful in 7s
ci-pangolin / OpenAPI Sync Check (push) Successful in 23s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 5s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 6s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 18s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m17s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 21:47:54 +08:00
wangjia
8049659660
feat(client): 节点 status=down 在列表置灰+「不可用」+禁选( #7 客户端侧)
...
ci-pangolin / Lint — shellcheck (push) Successful in 8s
ci-pangolin / OpenAPI Sync Check (push) Successful in 24s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 18s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m18s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 13s
#7 服务端已在 agent 离线时把节点判 down,但客户端 Node 模型没读 status、
列表照常显示。补:Node.status 字段 + 解析 /v1/nodes 的 status;ServerTile/
_NodeGridTile 在 isDown 时 Opacity 置灰 + 末尾「不可用」+ onTap 禁用。全平台
共享(lib/widgets+screens)。up 节点 Opacity 1.0 无变化,golden 不动。加 down
状态 widget 测试。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 21:35:52 +08:00
wangjia
f0832ccfcb
fix(client): 修 pro 会员启动闪广告/统计闪 0 — 加载门覆盖 auth 加载窗口
...
ci-pangolin / Lint — shellcheck (push) Successful in 6s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 7s
ci-pangolin / Flutter — analyze + test (push) Successful in 23s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 18s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m52s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
之前 meLoadingProvider=isLoading&&!hasValue 漏了两段:启动时 auth 异步读 token
期间 isLoggedIn=false → me 同步返回免费默认(广告);token 到后 me 拉真实值时
hasValue 已是免费默认 → !hasValue 不成立、仍不转圈。改为:
- meLoadingProvider:auth.isLoading 时转圈;未登录不转;已登录看 me.isLoading
- 统计页 loading:auth.isLoading || usageProvider.isLoading
未登录(确定访客)/golden(settle 后)不转圈,golden 不受影响。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 21:12:24 +08:00
wangjia
3785fdf1c1
style(client/shell): 会员卡字体调小,有效期完整显示
...
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 10s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 16s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m16s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
有效期行被 chevron 挤得截断。套餐名 12→11.5、有效期 10.5→9.5、chevron 15→14、
间距 10→9,让「有效期 YYYY-MM-DD」完整显示。golden 重生成。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 21:06:06 +08:00
wangjia
e27bbbd384
build(client/windows): 版本 bump 1.0.6 → 1.0.7(我的入口纠正)
...
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 5s
ci-pangolin / Flutter — analyze + test (push) Successful in 23s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 8s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 16s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m27s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 13s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 20:33:30 +08:00
wangjia
2c5e8785c0
fix(client/shell): 「我的」入口改到底部会员卡(非顶部品牌)
...
按反馈纠正:顶部品牌区改回静态;底部 PlanBadgeCard(套餐/会员卡)做成可点 →
打开「我的」,选中态高亮 + 末尾 chevron。桌面/平板页 golden 重生成。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 20:33:30 +08:00
wangjia
09eab3b3b6
build(client/windows): installer 版本 bump 1.0.5 → 1.0.6(折线图/加载转圈/侧栏改版)
...
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 7s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 4s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 20s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m18s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 20:26:56 +08:00
wangjia
b1dd78fddd
feat(client/shell): 侧栏改版 — 我的移到顶部品牌按钮 + 联系放设置下面
...
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 20s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 23s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 6s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 17s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m16s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
- 顶部品牌区(logo+穿山甲)做成可点按钮 → 打开「我的」,选中态高亮;
从导航列表移除「我的」项(desktop+tablet)
- desktop 导航顺序:…统计 → 设置 → 联系(联系移到设置下面)
桌面/平板页 golden 重生成(CI 闸 components/auth 未动)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 20:24:03 +08:00
wangjia
ea1af967a0
fix(client): 统计页/连接页 加载时整页转圈,先拉数据再渲染(不闪默认态)
...
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 17s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 5s
ci-pangolin / Flutter — analyze + test (push) Successful in 22s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m11s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
之前 .valueOrNull ?? 默认 → 数据没到时先渲染默认态(统计 0 / 连接页免费广告),
me/usage 到了再跳真实态,闪烁体验差。改为关键 provider 首拉(loading 且无缓存)时
整页 CircularProgressIndicator:
- 统计页:gate on usageProvider(30)
- 连接页:gate on 新增 meLoadingProvider(me 是常驻 Notifier,只首启转一下)
未登录/golden 场景 me 同步返回默认=不加载,不影响 golden。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 20:17:39 +08:00
wangjia
07f09a1600
feat(client/stats): 两周流量折线图加 Y轴刻度 + X轴日期 + 14个数据点
...
ci-pangolin / Lint — shellcheck (push) Successful in 9s
ci-pangolin / OpenAPI Sync Check (push) Successful in 16s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 5s
ci-pangolin / Flutter — analyze + test (push) Successful in 22s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 8s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m18s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 13s
之前折线太简单(只一条线+末点)。增强 UsageLineChart:
- Y 轴:3 条网格 + 左侧 GB 刻度(max/mid/0)
- X 轴:隔几天标一个日期(M/D,始终含末点)
- 14 个数据点全画出(surface 光晕+accent 实心,末点加大)
传入每点日期(chartDates)。token 色经参数传入(axis=fg3/surface),零硬编码。
统计页 golden 重生成。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 20:12:30 +08:00
wangjia
bf84492eef
feat(client/native): stats-overhaul Phase1 — 四端实时统计采集端
...
各原生侧把 libbox/Clash 的实时上下行/延迟经冻结的 pangolin/vpn/stats channel
生产真实帧:
- iOS/macOS 新增 StatsClient.swift(LibboxNewCommandClient,statusInterval 1s,
uplink/downlink/总量 + urltest 延迟映射契约字段),VpnManager/VpnChannel 状态
观察驱动 start/stop,pbxproj 登记。
- Android PangolinVpnService.kt 延迟修复。
契约字段 uploadBytes/downloadBytes/uploadSpeed/downloadSpeed/urltestResults 不改。
真机验证(连接后实时跳动)为运行时步骤,见计划 Phase1 待办。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 18:13:48 +08:00
wangjia
4f9d2d2cf3
fix(client): 补提交 stats-overhaul 漏掉的 account_api/kernel_process WIP
...
同 device_usage:account_api.dart(deviceUsage/usage 方法)、kernel_process.dart
及对应测试一直未提交,本地有→analyze/test 过,但不在 git→Windows 构建报
「deviceUsage isn't defined」。补齐使提交树自洽可构建。
孤儿组件 device_stat_row/metric_card 已无人引用,不提交。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 17:53:56 +08:00
wangjia
a7c3795e5a
fix(client): 补提交漏掉的 device_usage 模型 + 测试(Windows 构建缺文件)
...
device_usage.dart 一直是 untracked(stats-overhaul WIP 漏提交),本地有所以
analyze/test 过,但不在 git → bundle → Windows 构建报「找不到 device_usage.dart /
DeviceUsage isn't a type」。account_providers/account_api/golden 测试都引用它,补提交。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 17:51:25 +08:00
wangjia
6f480acad5
build(client/windows): installer 版本 bump 1.0.4 → 1.0.5(含统计页重设计)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 17:47:46 +08:00
wangjia
01ebafc05e
feat(client/stats): 统计页重设计 — 月/周/日周期卡 + 设备下拉 + 两周折线 + 修刷新
...
布局重做(对照确认稿):
- 3 张周期卡(本月/本周/今日),每张 = ↓下行 · ↑上行 · 使用时长(PeriodCard)
- 右上设备下拉(_DeviceDropdown,先只挂「全部」;每设备归因打通后填设备名,见 TODO #9/#10)
- 柱状图 → 折线图:最近两周(14 天)按天流量(UsageLineChart,CustomPainter 零依赖)
数据源统一 + 修刷新(根治「本月流量冻住」):
- 月/周/日 + 折线全从单一源 usageProvider(30) 聚合(取最后 N 天点求和,末点即今日,
避开 UTC/本地日期坑);不再 me.weekly + usage 两条管线
- usageProvider/deviceUsageProvider 改 autoDispose(进页重取)+ 下拉刷新(invalidate)
+ 刷新连带 meProvider.refresh。后端每 60s 更新,无需更勤
UI 全走 PangolinColors/Text/Spacing token 单源、零硬编码;中英 l10n key 补齐。
widget 数值断言测试(刀1)按新结构重写并通过;统计/连接页 golden 重生成
(非 CI 闸;components/auth 基线未动)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 17:43:14 +08:00
wangjia
47147a1041
build(client/windows): installer 版本 bump 1.0.3 → 1.0.4(含节点国旗)
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 16:32:57 +08:00
wangjia
855d9e0e30
feat(client): 连接页桌面节点胶囊(_NodePill)加地域国旗
...
桌面连接页「已连接」下的节点框是 _NodePill,此前只有文字「节点名·延时」+
智能选择 zap,无国旗。在地域前加 CountryCode(size 20)国旗(无效码回退码块),
智能选择时国旗 + zap 并存。手机/平板的 _CurrentNodeCard 本已有旗。
桌面连接页非 golden 覆盖(含旋转动画),无 golden 变更。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 16:32:24 +08:00
wangjia
8eb7d04fe1
feat(client): 节点地区图标改国旗(SVG 填满裁切),未知码回退码块
...
CountryCode 由 2 字母码块改为渲染真国旗:country_flags 包按 region 码出 SVG
旗(BoxFit.cover 填满裁切),全平台含 Windows 都显示(emoji 国旗在 Windows
不渲染,故走 SVG)。FlagCode 判定无效码(AUTO/占位/非地区码)时回退原码块;
选中态由 accent 底色改为 accent 边框环。
CountryCode 共享于节点列表/连接页/统计页,故三处节点图标统一变国旗,
对应 tablet_servers/connect/stats + desktop_stats golden 已 Mac 重生成
(这些页非 CI golden 闸;CI 闸的 components/auth 基线未动)。
依赖:country_flags ^3.1.0(解析 3.3.0)。analyze 干净,非 golden 测试全过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-28 16:25:48 +08:00
wangjia
b12519be0a
fix(client/windows): runner 加 /utf-8 编译选项,根治 C4819 中文注释编译失败
...
win32_window.cpp 含中文注释(UTF-8 无 BOM),在 zh-CN 机器(代码页 936)+ 新版
MSVC 上报 C4819「字符无法用当前代码页表示」,叠加 runner 的 /WX 直接 error
C2220 编译失败。APPLY_STANDARD_SETTINGS 加 /utf-8,让 MSVC 按 UTF-8 解析
所有 runner 源码,永久根治。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-27 20:49:36 +08:00
wangjia
8d3c5da7ee
fix(client/windows): installer 覆盖修复 — 装新版前静默卸载遗留 AppId 的 1.0.0
...
1.0.0 的 AppId 是非法 GUID(...PANGOLIN0001),1.0.1 起改成合法 GUID。
AppId 不同 → Inno 视为两个独立产品 → 控制面板里 1.00/1.01 并排不覆盖。
当前 AppId 已稳定会原地升级;再在 ssInstall 前把遗留 AppId 的残留装
静默卸掉(/VERYSILENT),清掉已并存的 1.0.0 幽灵。版本 bump 1.0.2→1.0.3。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-27 20:42:54 +08:00
wangjia
60daa6b399
test(client): 刀1 — 统计页「上屏数值对不对」widget 断言(缺口①)
...
闭合最初诉求「统计准不准」的前端那一半:后端记账已 L4 e2e,前端此前只到
解析单测(解析对 ≠ 渲染对)。新测试把真实形态的后端响应(/v1/me·/v1/usage·
/v1/usage/devices)经真 ApiClient→AccountApi→provider 链(连带测 JSON 解析)
喂给 StatsPage,断言:
- 指标卡:本月流量 ΣgbTotal=3.0、本月时长 Σmin/60=2.0、未连接延迟=节点 ping 42
- 周柱:7 根来自 me.weekly_gb(取唯一值核对)
- 分设备:2 行,每行流量/时长正确
注入 MockClient 不打真网络;用 find.byWidgetPredicate 匹配 MetricCard/DeviceStatRow
公共字段,无需改生产代码。改坏期望值已验证转红(非永真)。自动并入 flutter-client job。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 22:59:05 +08:00
wangjia
eb1bd11439
fix(test): 契约测试解耦 stats-overhaul WIP(CI 抓到的真问题)
...
ci-pangolin / Lint — shellcheck (pull_request) Failing after 9s
ci-pangolin / OpenAPI Sync Check (pull_request) Successful in 18s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 6s
ci-pangolin / Flutter — analyze + test (pull_request) Successful in 39s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 4s
ci-pangolin / Go — build + test (pull_request) Successful in 2m2s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 2m14s
ci-pangolin / Golden — 视觉回归 (components + auth) (pull_request) Successful in 31s
CI 暴露:我的契约测试引用了未提交的 stats-overhaul 文件,本地能过(文件在工作区)、
CI checkout 已提交代码就崩:
- api_contract_test.dart import device_usage.dart(?? 未提交)→ flutter analyze 报
'Target of URI doesn't exist'
- usage/contract_test.go 引用 DeviceUsagePoint/deviceUsageResponse(HEAD service.go/
handler.go 均无)→ go-server 编译失败
摘掉 /v1/usage/devices(DeviceUsage)契约,保留 /v1/me + /v1/usage(已提交)。
待 stats-overhaul 合并后再补 DeviceUsage 契约。
验证:flutter analyze test/contract 'No issues';go vet usage 无错。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 18:13:27 +08:00
wangjia
945c2e2f8d
test(ci): 闸2 Dart 支柱4 lint 升 error 级(真守门)
...
analyze 闸修好后(exit 0),把支柱4 两条规则提升为 error 级:
- only_throw_errors / avoid_catching_errors → analyzer.errors: error
- 即使 CI --no-fatal-infos,这俩违规仍致命(挡 merge);当前零违规
- 将来 throw 字符串 / catch Error → CI 红(支柱4:错误是有类型的值)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 13:45:31 +08:00
wangjia
bb2688cede
test(ci): L3 golden 进 CI(components+auth 子集,Linux 权威基线)
...
L3 视觉回归真进 CI(此前卡 docker daemon 未起 + dirty 纠缠;现启 Docker 落地干净子集):
- 在 ghcr.io/cirruslabs/flutter 容器重生成 components+auth 的 14 张 Linux 权威基线
(auth_login/connect 三态/quota/smart_card;mac 渲染与 CI Linux 不一致,故钉死 Linux),
容器内 --update-goldens 后无 flag 复跑幂等通过
- ci.yml 新增 golden job(第9个):flutter test components_golden_test + auth_redesign_golden_test
- 解耦干净:这俩只 import clean widget;stats-overhaul 的 l10n 改动纯新增(现有键未动)
→ 渲染不受影响、不裹挟 dirty;tablet/desktop-stats golden 仍耦合 stats-overhaul,待合并后并入
- docs/test-architecture.html:L3 状态更新为「已进 CI(子集)」
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 13:20:55 +08:00
wangjia
76dbdcfa83
test(client): L2 点连接能连上 流程 + ConnectApi 注入(支柱1)
...
- connection_provider:加 connectApiFactoryProvider(原内部 new ConnectApi 改成
可注入工厂,支柱1 接缝即接口)——默认行为不变
- test/unit/flow_connect_test.dart:真 toggle → fetchConfig(注入 MockClient 回占位
config)→ bridge.start → statusStream 确认 → on → 再 toggle → off;
验证「非乐观翻转」红线(on 必由 bridge 确认)
- 全量 unit/widget 47 全过(重构不破坏)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 12:13:40 +08:00
wangjia
95fad37989
test(client): L2 登陆/重启续登/登出 流程(真 AuthNotifier)
...
驱动真实 AuthNotifier(非桩),仅在可注入接缝 TokenStore 处换内存实现,覆盖最初诉求:
- 登陆:saveTokens → isLoggedIn + 令牌持久化
- 重启续登:新 AuthNotifier 从同一 store 恢复登录态(无需重输密码)
- 登出:logout → 未登录 + store 清空,再重启仍未登录
- 3 用例全过
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 12:10:52 +08:00
wangjia
cb6fc25bc9
test(client): L1 数值格式化纯函数单测 + 从 widget 抽出(支柱3)
...
- lib/util/format.dart:抽出 formatSpeed/formatDuration(原私有于 connect_page
_speed / connect_button _fmt,混在 widget 里没法单测)
- connect_page/connect_button 改用公共纯函数(渲染输出不变)
- test/unit/format_test.dart:10 用例覆盖速率 B/s→GB/s 各单位边界 +
时长 HH:MM:SS 补零/小时不封顶/取模 60
- 验证:format 10 + widget 8 全过、analyze 无 error/warning
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 12:06:34 +08:00
wangjia
64f96fe018
refactor(api): /v1/me 收敛 expire_at → 只发 expires_at(无 web 用户中心)
...
之前后端同时发 expire_at + expires_at(分别给 app + web 用户中心)。现已无 web 端,
双发是纯冗余,统一只用 expires_at:
- account.go:meResponse 删 ExpireAt 字段 + GetMe handler 不再赋值
- client me.dart:fromJson 删 (expires_at ?? expire_at) 别名回退
- 两端契约快照同步:Go meResponse 冻结集去 expire_at(12→11 key)、删 Dart 别名测试
- 验证:go test httpapi 绿、flutter test contract(14)绿、analyze EXIT 0、e2e 全链路绿
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 10:08:50 +08:00
wangjia
dbd183ee00
fix(ci): 修 flutter analyze 闸长期 exit 1(形同虚设)
...
发现:CI flutter analyze 退出码 1(1204 个 info,0 error/warning),其中 1191 来自
第三方 vendored 包 lucide_icons_patched —— 这道闸一直红/无效。
- analysis_options.yaml:exclude packages/lucide_icons_patched/**(去 1191 噪音)
- ci.yml:flutter analyze --no-fatal-infos(info 是建议级不该硬挡 merge;
error/warning 仍致命)→ analyze EXIT 0、闸真正可用
- 顺手清 11 个 clean 文件 info:auth_api 删 >>>AUTHLOG 调试 print 残留(+unused sw)、
account_page/plan_card withOpacity→withValues、vpn_bridge where(is Map)→whereType、
测试文件删多余 import + final→const
- 剩 2 个 info 在 stats-overhaul dirty 文件(stats_page/device_stat_row),留其合并清
- 验证:flutter analyze --no-fatal-infos EXIT 0(1204→2)、flutter test 48 全过
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-25 07:39:14 +08:00
wangjia
149aa90cfa
test(contract): 第2刀b Dart 契约快照(stats + /v1 API 字段面冻结)
...
支柱 2(契约单源)守门 — 堵 mac-stats-0 一类跨进程契约悄悄分叉:
- test/contract/stats_contract_test.dart:冻结 pangolin/vpn/stats payload
字段面 + status 四值,改契约即测试红、逼同步原生产出端 + 文档
- test/contract/api_contract_test.dart:冻结 /v1/me、/v1/usage、
/v1/usage/devices 的 snake_case 字段面
- ci.yml:flutter job 测试纳入 test/contract;本地 15 用例全过
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-24 23:32:00 +08:00
wangjia
62925a6886
Merge worktree-windows-icon-tray-fix: Windows 桌面/托盘/任务栏图标 + 关窗不断连接
...
- 关窗(X/Alt+F4)隐藏到托盘并保持隧道,真退出只走托盘菜单(Platform.isWindows 隔离)
- 窗口标题设「穿山甲」(无 VPN 字眼)
- Windows 托盘改用 .ico(Win32 只认 ico,PNG 空白),mac/Linux 仍 PNG
- 更新 app_icon.ico / tray_icon.ico / Runner.rc / installer
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 10:54:23 +08:00
wangjia
e4db3beba6
Merge branch 'main' into feature/pangolin-ios
...
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
# Conflicts:
# client/lib/bridge/vpn_bridge_provider.dart
# docs/index.html
2026-06-23 10:05:20 +08:00
wangjia
e25649cd27
feat(client/ios): iPad tablet 正式适配——断点≥900 对齐设计源 + tablet golden 回归闸
...
- form_factor 触屏 tablet 阈值 600→900,对齐 design/ui_kits/tablet(1180×820 横屏
侧栏):iPad 横屏/12.9″竖屏走侧栏,标准/11″竖屏回落 mobile 底 Tab,避免双栏挤压
- form_factor_test 补全断点边界(900/899)与各 iPad 尺寸用例
- 新增 tablet_pages_golden_test:四视图×明暗×中英 12 张 golden,注入演示数据
(6 节点/周流量/30 天用量)让 2 列网格与周柱满渲染,作回归闸
- design/CONTRACT.md:tablet 像素契约(取自 tabapp.jsx)+ 逐屏验收清单
- 真机 iPad mini 6 视觉签核通过;141 测试全绿
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 10:00:14 +08:00
wangjia
11ee3342bf
feat(client/ios): 补齐 App 图标(iPhone+iPad 全尺寸,去 alpha)
...
iOS AppIcon.appiconset 只有 Contents.json、无任何 PNG → 桌面图标空白。
以 design 单源 assets/app-icon.svg(去圆角 rx=0 全幅方形)经 qlmanage 渲染 1024,
flutter_launcher_icons 铺开 21 张全尺寸(含 iPad 76/83.5)并去 alpha(App Store 合规)。
新增 dev_dep flutter_launcher_icons + 配置块,改图标重渲源图后 dart run 即可。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 00:39:37 +08:00
wangjia
bd984feff9
fix(client/ios): iOS 接通原生隧道——切真桥 + save-before-start + DNS matchDomains
...
根因:iOS 端 vpn_bridge_provider 把 iOS 归入'其他平台 → VpnBridgeMock',
整个 App 用假桥,伪造已连接状态与写死的 URLTest 延迟(18/32/54ms),从不调用
B1/C1 打通的原生 NEPacketTunnelProvider。表现为 UI 显示已连接但流量全直连
(apple 通/google 被墙、RTT 20ms),设备日志无任何隧道/sing-box/节点痕迹。
三处修复:
1. vpn_bridge_provider.dart: iOS → VpnNativeBridge(真原生 MethodChannel)。
2. VpnManager.start(): 先 saveToPreferences+重载(首启触发系统授权、持久化配置)
再 startVPNTunnel,否则对未持久化 manager 调用抛 NEVPNError。对齐 macOS。
3. PacketTunnelProvider.openTun: DNS 补 matchDomains=[""],强制所有 DNS 走隧道,
iOS 不设会把隧道 DNS 当补充解析器、查询仍走系统直连 DNS 致污染打不开站点。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 00:28:53 +08:00
wangjia
9b365c7e1c
fix(client/ios): PacketTunnel 引入 Generated.xcconfig 注入版本号
...
扩展 Info.plist 的 CFBundleVersion=$(FLUTTER_BUILD_NUMBER) 因 target 未引
Flutter 的 Generated.xcconfig 而展开为空串,iOS 装机校验 MissingBundleVersion
(code 33) 拒绝安装。给 PacketTunnel 三个 build config 加 baseConfiguration
Reference 指向 Generated.xcconfig(纯 Flutter 变量,无 Pods,不破坏 OTHER_LDFLAGS=""),
使扩展版本号 = 主 App = 1.0.0(1),满足 App Extension 版本须与 host app 一致的上架要求。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-22 23:59:05 +08:00
wangjia
f3f91cc0ff
feat(android): 启动图标改用官方穿山甲(自适应图标)
...
前景 path 取自官方 client/assets/logo-mark-white.svg(原样,非重绘),仅加 Android 安全区居中;
背景为 app-icon.svg 的橙色渐变(#C8794A→#9E5630);删除占位三角 drawable/ic_launcher.xml。
Vivo X200/华为 DCO-AL00 真机验证图标显示正常。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-22 23:42:18 +08:00
wangjia
76d8e57a44
build(client/ios): B1 完成——PacketTunnel 链接 libbox+系统框架,iOS 构建打通
...
- PacketTunnel 链 Libbox.xcframework(静态,只Link不Embed)+ NetworkExtension
+ SystemConfiguration + libresolv.tbd + UIKit(libbox iOS 引用 UIApplication);
OTHER_LDFLAGS="" 切 CocoaPods 继承,FRAMEWORK_SEARCH_PATHS 指 Frameworks/
- 桥接头手动声明 sockaddr_ctl/ctl_info(iOS SDK 无 kern_control.h,稳定内核 ABI)
- 部署目标 iOS 12→14(os.Logger 需 14;低版老机不再兼容,用户拍板)
- 修预存脚手架坑: Podfile 删悬空 RunnerTests; Info.plist 移出 Resources(双产出);
Runner 构建阶段重排(Embed App Extensions 前置, Thin Binary 殿后) 解依赖环
- 验证: flutter build ios 通过, C1 Provider 编译通过, otool -L 扩展零外部 @rpath
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-22 22:20:49 +08:00
wangjia
381a621d77
feat(client/ios): Provider 对齐 macOS libbox 集成(CommandServer+utun fd)+ 内存监控
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-22 20:58:03 +08:00
wangjia
33a3508489
build(client/ios): 引入 iOS libbox 框架 + gitignore 排除产物
2026-06-22 20:52:28 +08:00
wangjia
99695e8861
test(client): 锁定 form_factor 断点(iPad→tablet, iPhone→mobile)
2026-06-22 20:51:34 +08:00