chore(todo): 更新 TODO 看板(stats-overhaul 收口期新增条目)
ci-pangolin / Lint — shellcheck (push) Successful in 10s
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 29s
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 12s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 13s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m22s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
ci-pangolin / Lint — shellcheck (pull_request) Successful in 4s
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 21s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 5s
ci-pangolin / Go — build + test (pull_request) Successful in 8s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (pull_request) Successful in 4m17s
ci-pangolin / Golden — 视觉回归 (components + auth) (pull_request) Successful in 13s

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-28 18:14:05 +08:00
parent 1a6eb7ac9a
commit 0698dc6138
2 changed files with 320 additions and 10 deletions
+210 -8
View File
@@ -252,12 +252,12 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
<header>
<div class="wrap">
<h1>feature+windows — 项目 TODO</h1>
<div class="header-meta">生成于 2026-06-22 · 真相源 todo/todo.json</div>
<div class="header-meta">生成于 2026-06-28 · 真相源 todo/todo.json</div>
<div class="stats">
<div class="stat-pill"><strong>3</strong>全部</div>
<div class="stat-pill"><strong>3</strong>待开始</div>
<div class="stat-pill"><strong>10</strong>全部</div>
<div class="stat-pill"><strong>7</strong>待开始</div>
<div class="stat-pill"><strong>0</strong>开发中</div>
<div class="stat-pill"><strong>0</strong>待验收</div>
<div class="stat-pill"><strong>3</strong>待验收</div>
<div class="stat-pill"><strong>0</strong>已验收</div>
</div>
@@ -292,7 +292,7 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
<div class="filter-sep"></div>
<div class="filter-group">
<span class="filter-label">平台 / 标签</span>
<button class="filter-chip" data-filter-tag="Android">Android</button><button class="filter-chip" data-filter-tag="Windows">Windows</button><button class="filter-chip" data-filter-tag="mac">mac</button>
<button class="filter-chip" data-filter-tag="Android">Android</button><button class="filter-chip" data-filter-tag="Windows">Windows</button><button class="filter-chip" data-filter-tag="mac">mac</button><button class="filter-chip" data-filter-tag="前端">前端</button><button class="filter-chip" data-filter-tag="后端">后端</button><button class="filter-chip" data-filter-tag="运维">运维</button>
</div>
</div>
@@ -301,7 +301,7 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
<div class="section-block" id="section-open">
<div class="section-title st-open" data-toggle="open">
📋 待开始 <span class="s-count">3</span>
📋 待开始 <span class="s-count">7</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-open">
@@ -365,6 +365,93 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
</div>
</li>
<li class="todo-card s-open"
data-id="4"
data-level="mid"
data-status="open"
data-tier="3"
data-tags="后端,运维">
<div class="card-header">
<span class="item-title">线上启用 TOTP 两步验证并验 2FA</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
<span class="tag tier-3">三级</span>
</div>
</div>
<div class="item-desc">pangolin1 的 /etc/pangolin/server.env 配 USER_TOTP_ENC_KEY(32字节或64位hex)并重启 pangolin-server,启用后验 A.1 客户端 2FA:绑定 Authenticator→重登 6 位码校验→备份码可用。当前该键未设置,/auth/login/totp 与 /me/totp/* 未挂载,2FA 无法测。属 A.1 手动验证清单第 4 项的前置。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="运维">运维</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="8"
data-level="mid"
data-status="open"
data-tier="2"
data-tags="后端">
<div class="card-header">
<span class="item-title">agent 掉线告警 + 静默吞错全量审计</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
</div>
</div>
<div class="item-desc">TODO #7 的剩余两块:① Hub 检测到 agent gRPC 流断开时触发告警(alert 包),避免再像这次停 6 天没人知道;② 全仓审计 _ = err / 忽略错误的静默吞错点(今天已遇邮件/连接500/connect-push 三处),逐一改为记日志或上抛。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="9"
data-level="mid"
data-status="open"
data-tier="2"
data-tags="前端,后端">
<div class="card-header">
<span class="item-title">打通每设备归因(设备注册 + per-device dp_uuid)</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
</div>
</div>
<div class="item-desc">现状:多设备共用账户级 dp_uuid,统计无法区分设备;usage_device_daily 永远空,统计页设备下拉只有「全部」。基础设施已具备(devices.dp_uuid / EnsureDeviceDpUUID / usage_device_daily / UserDeviceByDpUUID)但未激活——客户端没 POST /v1/me/devices 注册本机,连接时 EnsureDeviceDpUUID 查不到设备行→回退账户 uuid。需:① 客户端启动/登录时注册本机设备(稳定 uuid + name + platform);② 连接时带该 device_id;③ 或让 EnsureDeviceDpUUID 缺行时自动建设备行。打通后每设备统计 + 统计页设备下拉才有真数据。是统计重设计「设备下拉」的前置依赖。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="3"
data-level="low"
@@ -390,6 +477,35 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
<div class="item-meta">
<span class="meta-date">🕐 2026-06-22</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="10"
data-level="low"
data-status="open"
data-tier="2"
data-tags="前端">
<div class="card-header">
<span class="item-title">统计页设备下拉接真实设备数据(依赖#上条)</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-low">一般 / 优化</span>
<span class="tag tier-2">二级</span>
</div>
</div>
<div class="item-desc">统计重设计的设备下拉,先只挂「全部」上线;每设备归因打通后,把下拉填上「全部 + 近30天用过的设备名」,选设备走 /v1/usage/devices 过滤。依赖每设备归因 TODO。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
@@ -409,12 +525,98 @@ ul.todo-list { list-style: none; margin: 0; padding: 0; }
</div>
<div class="section-block" id="section-done">
<div class="section-title st-done" data-toggle="done">
🔍 待验收 <span class="s-count">0</span>
🔍 待验收 <span class="s-count">3</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-done">
<ul class="todo-list" id="list-done">
<p class="empty-tip">暂无条目</p>
<li class="todo-card s-done"
data-id="7"
data-level="high"
data-status="done"
data-tier="1"
data-tags="后端">
<div class="card-header">
<span class="item-title">节点健康绑定 agent 在线 + connect 校验凭证下发 + 掉线告警</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-1">一级</span>
<button class="reject-btn" data-id="7" data-title="节点健康绑定 agent 在线 + connect 校验凭证下发 + 掉线告警">拒绝验收</button>
</div>
</div>
<div class="item-desc">本次 google 打不开暴露的架构漏洞:① 节点 status 是静态 DB 列(种子 up),只有 SCHED 健康循环会改、而单节点没开 → Hub 已知 agent 离线却不反映到节点状态/列表,客户端永远显示健康。应把 Hub 的 agent 在线状态接进 ListNodes/节点 status。② ConnectNode 把 hub.Push 错误吞了(_ = pushErr)且不校验 agent 在线 → 凭证没下发也返回 200,客户端误以为连上。应在 agent 离线/push 失败时降级或明确报错,并把推送结果纳入响应。③ agent 掉线无告警(本次停 6 天没发现),应在 Hub 检测到 agent stream 断开时告警。④ 通病:错误被 _ = 静默吞掉(今天邮件/连接500/此处共三处),需排查同类静默吞错。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="5"
data-level="mid"
data-status="done"
data-tier="3"
data-tags="后端">
<div class="card-header">
<span class="item-title">SendCode 发信失败记日志 + 测试</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-3">三级</span>
<button class="reject-btn" data-id="5" data-title="SendCode 发信失败记日志 + 测试">拒绝验收</button>
</div>
</div>
<div class="item-desc">auth/service.go 异步发信把错误静默吞了(:157 SendCode 的 _=s.mailer.SendCode(...):133 SendAlreadyRegistered 同样),导致 SMTP 故障日志无痕、本次 587 被封难排查。改为发信失败时记日志(slog.Error,脱敏:只记 send failed:&lt;err&gt;,绝不记验证码,邮箱可打码,守 no-secret-in-logs)。并加测试:注入会失败的 mock Mailer,断言发送失败时确实记了一条错误日志(可用 LogMailer 思路/捕获 slog handler 验证)。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="6"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="前端,后端">
<div class="card-header">
<span class="item-title">节点列表:正式名(地区·国家) + 国旗图标</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<button class="reject-btn" data-id="6" data-title="节点列表:正式名(地区·国家) + 国旗图标">拒绝验收</button>
</div>
</div>
<div class="item-desc">客户端节点列表项展示改造:① 节点名改正式格式「地区 · 国家」(如 香港 · 中国),去掉测试名(当前 prod 节点 name_zh=单节点·测试);② 节点图标用国旗(按 region/国家码渲染 emoji 或 SVG 旗)。涉及:服务端 nodes 行的 name/region/国家码数据(可能需补国家码字段),客户端节点卡片渲染国旗+名格式。HK 节点国旗/国家标签需产品确认(🇭🇰香港 vs 🇨🇳中国)。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-28</span>
</div>
</div>
</li>
</ul>
</div>
</div>
+110 -2
View File
@@ -1,9 +1,9 @@
{
"meta": {
"title": "feature+windows — 项目 TODO",
"updated_at": "2026-06-22T09:32:56.170Z"
"updated_at": "2026-06-28T09:32:26.080Z"
},
"seq": 3,
"seq": 10,
"items": [
{
"id": 1,
@@ -49,6 +49,114 @@
"done": false,
"completed_at": null,
"version": null
},
{
"id": 4,
"title": "线上启用 TOTP 两步验证并验 2FA",
"desc": "pangolin1 的 /etc/pangolin/server.env 配 USER_TOTP_ENC_KEY(32字节或64位hex)并重启 pangolin-server,启用后验 A.1 客户端 2FA:绑定 Authenticator→重登 6 位码校验→备份码可用。当前该键未设置,/auth/login/totp 与 /me/totp/* 未挂载,2FA 无法测。属 A.1 手动验证清单第 4 项的前置。",
"level": "mid",
"tier": 3,
"tags": [
"后端",
"运维"
],
"status": "open",
"created_at": "2026-06-27T16:39:31.549Z",
"done": false,
"completed_at": null,
"version": null
},
{
"id": 5,
"title": "SendCode 发信失败记日志 + 测试",
"desc": "auth/service.go 异步发信把错误静默吞了(:157 SendCode 的 _=s.mailer.SendCode(...):133 SendAlreadyRegistered 同样),导致 SMTP 故障日志无痕、本次 587 被封难排查。改为发信失败时记日志(slog.Error,脱敏:只记 send failed:<err>,绝不记验证码,邮箱可打码,守 no-secret-in-logs)。并加测试:注入会失败的 mock Mailer,断言发送失败时确实记了一条错误日志(可用 LogMailer 思路/捕获 slog handler 验证)。",
"level": "mid",
"tier": 3,
"tags": [
"后端"
],
"status": "done",
"created_at": "2026-06-27T23:37:24.980Z",
"done": false,
"completed_at": null,
"version": null
},
{
"id": 6,
"title": "节点列表:正式名(地区·国家) + 国旗图标",
"desc": "客户端节点列表项展示改造:① 节点名改正式格式「地区 · 国家」(如 香港 · 中国),去掉测试名(当前 prod 节点 name_zh=单节点·测试);② 节点图标用国旗(按 region/国家码渲染 emoji 或 SVG 旗)。涉及:服务端 nodes 行的 name/region/国家码数据(可能需补国家码字段),客户端节点卡片渲染国旗+名格式。HK 节点国旗/国家标签需产品确认(🇭🇰香港 vs 🇨🇳中国)。",
"level": "mid",
"tier": 2,
"tags": [
"前端",
"后端"
],
"status": "done",
"created_at": "2026-06-28T00:21:30.990Z",
"done": false,
"completed_at": null,
"version": null
},
{
"id": 7,
"title": "节点健康绑定 agent 在线 + connect 校验凭证下发 + 掉线告警",
"desc": "本次 google 打不开暴露的架构漏洞:① 节点 status 是静态 DB 列(种子 up),只有 SCHED 健康循环会改、而单节点没开 → Hub 已知 agent 离线却不反映到节点状态/列表,客户端永远显示健康。应把 Hub 的 agent 在线状态接进 ListNodes/节点 status。② ConnectNode 把 hub.Push 错误吞了(_ = pushErr)且不校验 agent 在线 → 凭证没下发也返回 200,客户端误以为连上。应在 agent 离线/push 失败时降级或明确报错,并把推送结果纳入响应。③ agent 掉线无告警(本次停 6 天没发现),应在 Hub 检测到 agent stream 断开时告警。④ 通病:错误被 _ = 静默吞掉(今天邮件/连接500/此处共三处),需排查同类静默吞错。",
"level": "high",
"tier": 1,
"tags": [
"后端"
],
"status": "done",
"created_at": "2026-06-28T00:26:44.498Z",
"done": false,
"completed_at": null,
"version": null
},
{
"id": 8,
"title": "agent 掉线告警 + 静默吞错全量审计",
"desc": "TODO #7 的剩余两块:① Hub 检测到 agent gRPC 流断开时触发告警(alert 包),避免再像这次停 6 天没人知道;② 全仓审计 _ = err / 忽略错误的静默吞错点(今天已遇邮件/连接500/connect-push 三处),逐一改为记日志或上抛。",
"level": "mid",
"tier": 2,
"tags": [
"后端"
],
"status": "open",
"created_at": "2026-06-28T00:38:22.413Z",
"done": false,
"completed_at": null,
"version": null
},
{
"id": 9,
"title": "打通每设备归因(设备注册 + per-device dp_uuid)",
"desc": "现状:多设备共用账户级 dp_uuid,统计无法区分设备;usage_device_daily 永远空,统计页设备下拉只有「全部」。基础设施已具备(devices.dp_uuid / EnsureDeviceDpUUID / usage_device_daily / UserDeviceByDpUUID)但未激活——客户端没 POST /v1/me/devices 注册本机,连接时 EnsureDeviceDpUUID 查不到设备行→回退账户 uuid。需:① 客户端启动/登录时注册本机设备(稳定 uuid + name + platform);② 连接时带该 device_id;③ 或让 EnsureDeviceDpUUID 缺行时自动建设备行。打通后每设备统计 + 统计页设备下拉才有真数据。是统计重设计「设备下拉」的前置依赖。",
"level": "mid",
"tier": 2,
"tags": [
"前端",
"后端"
],
"status": "open",
"created_at": "2026-06-28T09:32:26.008Z",
"done": false,
"completed_at": null,
"version": null
},
{
"id": 10,
"title": "统计页设备下拉接真实设备数据(依赖#上条)",
"desc": "统计重设计的设备下拉,先只挂「全部」上线;每设备归因打通后,把下拉填上「全部 + 近30天用过的设备名」,选设备走 /v1/usage/devices 过滤。依赖每设备归因 TODO。",
"level": "low",
"tier": 2,
"tags": [
"前端"
],
"status": "open",
"created_at": "2026-06-28T09:32:26.080Z",
"done": false,
"completed_at": null,
"version": null
}
]
}