docs: 全栈设计审查 2026-07(前端/后端/数据库,13 项发现分级)
P0:控制面明文 HTTP、SQLite 零备份。P1:同机换账号设备注册 403 死结(migration16 注释里推迟的 UNIQUE(user_id,uuid) 未落地)、disconnect 撤账户级凭证而 connect 发 每设备凭证、agent 用量 Reset 后上报失败即丢(at-most-once)、Redis 白名单单点全员 掉线、argon2id 64MiB 并发 OOM。P2:sessions/audit 无留存、免费额度 UTC 日界、 三时钟口径、hub 内存态、单实例端口无握手、ReportUsage 无事务。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>全栈设计审查 2026-07(前端 / 后端 / 数据库)</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
|
||||
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
|
||||
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
|
||||
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
|
||||
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 96px}
|
||||
h1{font-size:30px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
|
||||
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
|
||||
h2{font-size:21px;margin:44px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
||||
h3{font-size:16px;margin:26px 0 8px;color:var(--accent2)}
|
||||
p{margin:10px 0}
|
||||
code{font-family:var(--mono);font-size:.88em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
|
||||
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:13px;line-height:1.55;color:#cdd3df}
|
||||
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px;vertical-align:middle}
|
||||
.tag.ok{background:rgba(94,194,122,.16);color:var(--ok)}
|
||||
.tag.warn{background:rgba(224,184,79,.16);color:var(--warn)}
|
||||
.tag.bad{background:rgba(224,106,106,.16);color:var(--bad)}
|
||||
.tag.info{background:rgba(95,176,201,.16);color:var(--accent2)}
|
||||
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:16px 0}
|
||||
.card.p0{border-left:3px solid var(--bad)}
|
||||
.card.p1{border-left:3px solid var(--warn)}
|
||||
.card.p2{border-left:3px solid var(--accent2)}
|
||||
.card h3{margin-top:0;color:var(--fg)}
|
||||
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px}
|
||||
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
|
||||
th{color:var(--fg2);font-weight:600;font-size:13px}
|
||||
td code{font-size:.85em}
|
||||
ul,ol{padding-left:22px;margin:10px 0}
|
||||
li{margin:5px 0}
|
||||
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
|
||||
.small{color:var(--fg2);font-size:13px}
|
||||
a{color:var(--accent2)}
|
||||
.back{display:inline-block;margin-bottom:24px;font-size:13px}
|
||||
b{color:#fff}
|
||||
.loc{font-family:var(--mono);font-size:12px;color:var(--fg2);margin-top:8px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<a class="back" href="index.html">← 返回文档索引</a>
|
||||
|
||||
<h1>全栈设计审查 · 2026-07</h1>
|
||||
<p class="sub">范围:<code>server/</code>(Go 控制面 + agent)· <code>client/</code>(Flutter)· 数据库 schema(migrations 1–20)· 部署脚本。方法:核心链路逐文件精读(认证 / 会话 / 连接下发 / 用量记账 / 配额卡控 / 设备管理),非全量逐行。<span class="tag info">13 项发现</span> <span class="tag bad">P0 ×2</span> <span class="tag warn">P1 ×5</span></p>
|
||||
|
||||
<div class="lead">
|
||||
总体评价:<b>架构底子是好的</b>——方言层数据库解耦、argon2id 密码、refresh 单次轮换 + Redis 白名单、gRPC mTLS、
|
||||
per-device dp_uuid 归因、Lua 滑窗限流、节点三态判活(DB×agent在线×数据面健康),都属同规模项目少见的干净设计。
|
||||
问题集中在<b>两条主线</b>:① 传输与数据安全的「最后一公里」没封口(明文 API、零备份);
|
||||
② 多处「一次写对、后续演进没跟上」的<b>接缝漂移</b>(connect 发每设备凭证 / disconnect 撤账户凭证;
|
||||
设备唯一键改造写进注释却没落地;agent 用量取走即焚)。
|
||||
</div>
|
||||
|
||||
<h2>发现汇总</h2>
|
||||
<table>
|
||||
<tr><th>#</th><th>严重度</th><th>域</th><th>一句话</th></tr>
|
||||
<tr><td>F1</td><td><span class="tag bad">P0 安全</span></td><td>全栈</td><td>控制面全程明文 HTTP:密码 / JWT / 会话轮询裸奔公网</td></tr>
|
||||
<tr><td>F2</td><td><span class="tag bad">P0 运维</span></td><td>数据库</td><td>SQLite 生产库零备份——单盘单机,丢了就是全部</td></tr>
|
||||
<tr><td>F3</td><td><span class="tag warn">P1 正确性</span></td><td>后端+DB</td><td>同一台机器换账号登录 → 设备注册永远 403 → 无法连接,且提示的自救方法无效</td></tr>
|
||||
<tr><td>F4</td><td><span class="tag warn">P1 正确性</span></td><td>后端</td><td>connect 下发每设备凭证,disconnect 却撤账户级凭证——断开从未真正吊销</td></tr>
|
||||
<tr><td>F5</td><td><span class="tag warn">P1 可靠性</span></td><td>agent</td><td>用量「取走即焚」:计数器已清零,上报失败数据永久丢(注释写 at-least-once,实为 at-most-once)</td></tr>
|
||||
<tr><td>F6</td><td><span class="tag warn">P1 可靠性</span></td><td>后端</td><td>Redis 重启 ≈ 全员被登出;sessions 表自称权威却不被 refresh 路径参考</td></tr>
|
||||
<tr><td>F7</td><td><span class="tag warn">P1 容量</span></td><td>后端</td><td>argon2id 64 MiB/次登录,1 GB 小机上一波并发登录即可 OOM</td></tr>
|
||||
<tr><td>F8</td><td><span class="tag info">P2 数据库</span></td><td>数据库</td><td>sessions / audit_log 无限增长,无留存策略;按登录史全量扫描</td></tr>
|
||||
<tr><td>F9</td><td><span class="tag info">P2 产品</span></td><td>后端</td><td>免费额度按 UTC 日重置 = 北京时间早上 8 点,「今日」口径与用户认知不符</td></tr>
|
||||
<tr><td>F10</td><td><span class="tag info">P2 一致性</span></td><td>全栈</td><td>免费时长三个时钟各说各话:服务端分钟(有流量才计)、凭证 TTL、客户端倒计时</td></tr>
|
||||
<tr><td>F11</td><td><span class="tag info">P2 可靠性</span></td><td>后端</td><td>agent 在线状态纯内存:server 重启后短窗内全体拒连</td></tr>
|
||||
<tr><td>F12</td><td><span class="tag info">P2 客户端</span></td><td>客户端</td><td>单实例探测固定端口 47654 无握手:被占则 App 无法启动,任意本地进程可唤窗</td></tr>
|
||||
<tr><td>F13</td><td><span class="tag info">P2 后端</span></td><td>后端</td><td>ReportUsage 多条 SQL 无事务,崩溃可留部分记账</td></tr>
|
||||
</table>
|
||||
|
||||
<h2>P0 — 必须尽快处理</h2>
|
||||
|
||||
<div class="card p0">
|
||||
<h3>F1 · 控制面全程明文 HTTP <span class="tag bad">安全</span></h3>
|
||||
<p><b>现象</b>:客户端默认 API 基址是 <code>http://103.119.13.48:8080</code>(裸 IP + 明文)。登录密码、
|
||||
access/refresh token、会话轮询、设备列表——所有控制面流量在公网明文传输。服务端 argon2id 只保护「存储」,保护不了「传输」。</p>
|
||||
<p><b>影响</b>:任何链路中间者(ISP、Wi-Fi、GFW 探测设备)可截获密码与 token 直接接管账户。对一个「主打隐私」的产品,这是与定位直接矛盾的短板;且中国链路上明文 HTTP + 可疑 payload 更易被主动探测/干扰。</p>
|
||||
<p><b>修法</b>:域名 + 反代 TLS(Caddy 一行配置自动 Let's Encrypt,或 nginx+certbot),客户端默认改
|
||||
<code>https://api.<domain></code>;Android 移除 <code>usesCleartextTraffic</code>;服务端 8080 收回 loopback。
|
||||
无域名过渡期可先自签 + 客户端证书 pinning(次优)。</p>
|
||||
<div class="loc">client/lib/services/api_config.dart:8 · scripts/local_test.sh:20(API_URL)· server :8080 直挂公网</div>
|
||||
</div>
|
||||
|
||||
<div class="card p0">
|
||||
<h3>F2 · SQLite 生产库零备份 <span class="tag bad">运维</span></h3>
|
||||
<p><b>现象</b>:<code>deploy/</code> 全目录无任何 backup / dump / litestream 痕迹。用户、订阅、激活码、用量全部在
|
||||
pangolin1 单机单盘的一个 SQLite 文件里。</p>
|
||||
<p><b>影响</b>:磁盘损坏 / 误操作 / VPS 商跑路 = 用户资产全灭,无法恢复付费用户订阅关系(直接经济损失 + 信誉损失)。这是当前全项目期望损失最大的单点。</p>
|
||||
<p><b>修法</b>(一晚可落地):① 最简:cron 每日 <code>sqlite3 .backup</code> + <code>rclone</code> 推 Cloudflare R2/S3 异地,保留 30 天;
|
||||
② 更优:Litestream 持续复制到对象存储(秒级 RPO,内存开销可忽略,适合 1GB 小机)。恢复流程写进 runbook 并演练一次。</p>
|
||||
<div class="loc">deploy/bootstrap/ · deploy/single-node/deploy.sh(均无备份任务)</div>
|
||||
</div>
|
||||
|
||||
<h2>P1 — 设计缺陷,建议排期修</h2>
|
||||
|
||||
<div class="card p1">
|
||||
<h3>F3 · 同机换账号 → 设备注册永远 403,连接被卡死 <span class="tag warn">正确性</span></h3>
|
||||
<p><b>链路</b>:客户端 <code>device_id</code> 一次生成、安全存储持久、<b>跨账号复用</b>(登出不清)。
|
||||
<code>devices.uuid</code> 是<b>全局 UNIQUE</b>(migration 000001),<code>RegisterIfAbsent</code> 遇到「uuid 已属他人」直接
|
||||
<code>ErrForbidden</code>;登录侧注册是 best-effort → <b>登录成功但设备永远注册不上</b>;随后
|
||||
<code>ConnectNode</code> 因设备未注册拒发凭证,提示「请退出后重新登录以重新注册设备」——<b>而重新登录永远解不了这个死结</b>。</p>
|
||||
<p><b>影响</b>:一台机器先后登两个账号(家人共用电脑、用户换号、测试机)→ 第二个账号完全无法连接,且用户按提示操作也无效。migration 16 头注释已写明「UNIQUE(uuid)→UNIQUE(user_id,uuid) 需表重建,风险隔离到单独迁移」——<b>该迁移至今未落地</b>,是典型的「注释里的 TODO 变成生产 bug」。</p>
|
||||
<p><b>修法</b>:① 落地推迟的迁移:<code>UNIQUE(user_id, uuid)</code>(设备身份按用户隔离,语义即「此用户的此设备」);
|
||||
dp_uuid 归因按 (user,device) 查本就成立;② 或语义改「重绑」:新登录抢走设备行(转移 owner 并吊销旧主会话)——更贴近「一台设备此刻只属一个账号」的现实;③ 客户端兜底:登出时按账号命名空间存 device_id。推荐 ①+③。</p>
|
||||
<div class="loc">server/internal/devices/service.go:209(ErrForbidden)· server/migrations/sqlite/000016_*.up.sql 头注释 · server/internal/httpapi/nodes.go:245(DEVICE_NOT_REGISTERED)· client/lib/services/device_identity.dart:66</div>
|
||||
</div>
|
||||
|
||||
<div class="card p1">
|
||||
<h3>F4 · disconnect 撤销的不是 connect 发出的凭证 <span class="tag warn">正确性</span></h3>
|
||||
<p><b>现象</b>:<code>ConnectNode</code> 走每设备凭证 <code>EnsureDeviceDpUUID → devDp</code>(nodes.go:244);
|
||||
<code>DisconnectNode</code> 却吊销<b>账户级</b> <code>ent.DpUUID</code> 并删账户凭证行(nodes.go:371-377),且接口没有
|
||||
<code>device_id</code> 入参。<b>用户主动断开从未真正吊销数据面凭证</b>——每设备凭证在节点上一直活到 TTL(付费 24h)。</p>
|
||||
<p><b>影响</b>:「断开」的服务端语义失效;被移除/被强退的设备若本地还留着 sing-box 配置,断开后的
|
||||
TTL 窗口内仍可直连数据面(绕过控制面判定)。DeleteDevice 路径有自己的 revoker 是对的,但普通 disconnect 是空转。</p>
|
||||
<p><b>修法</b>:disconnect 请求体加 <code>device_id</code>,查 <code>devDp</code> 后吊销之;账户级 dp_uuid 作为遗留兜底再撤一次亦可。顺手给 revoke 失败加告警(现在 <code>_ =</code> 吞掉)。</p>
|
||||
<div class="loc">server/internal/httpapi/nodes.go:244 vs 336-380</div>
|
||||
</div>
|
||||
|
||||
<div class="card p1">
|
||||
<h3>F5 · 用量「取走即焚」:上报失败 = 数据永久丢 <span class="tag warn">可靠性</span></h3>
|
||||
<p><b>现象</b>:v2ray 用量源 <code>QueryStats(Reset_: true)</code> <b>先清零内核计数器</b>拿到 delta;
|
||||
<code>runUsage</code> 里 <code>ReportUsage</code> 一旦失败直接 <code>return err</code> 拆会话重连——<b>刚取走的这窗口数据没有任何缓冲,永久丢失</b>。注释声称 at-least-once,实际是 at-most-once。</p>
|
||||
<p><b>影响</b>:控制面-agent 之间任何 gRPC 抖动(server 重启、网络闪断——每分钟一窗,天天发生)都在漏记:
|
||||
免费用户少计分钟 = 变相多送时长;统计页字节数偏低。计费相关数据不该按「尽力而为」设计。</p>
|
||||
<p><b>修法</b>:Collect 后先并入内存 pending 缓冲,ReportUsage 成功才清;失败保留、下窗口合并重发(按 dp_uuid 累加,幂等安全);再给报文加 <code>window_id</code>,控制面按 (node,window_id) 去重防重发双计。缓冲上限封顶(如 1h)防内存膨胀。</p>
|
||||
<div class="loc">server/internal/agentd/usage_v2ray.go:66(Reset_)· server/internal/agentd/usage.go:42-51</div>
|
||||
</div>
|
||||
|
||||
<div class="card p1">
|
||||
<h3>F6 · Redis 重启 ≈ 全员被登出;「权威」sessions 表不参与 refresh 判定 <span class="tag warn">可靠性</span></h3>
|
||||
<p><b>现象</b>:refresh token 白名单只活在 Redis(<code>jwt:refresh:*</code>)。single-node 部署用发行版默认 Redis(RDB 快照,非 AOF)——crash/重启丢最近几分钟到全部白名单 → 存量 refresh 全被拒 → <b>全体用户被迫重新登录</b>。而 sessions 表注释自称「可查询的权威记录」,refresh 路径却从不回查它——两边脑裂:DB 说会话有效,Redis 说无效,以 Redis 为准。</p>
|
||||
<p><b>影响</b>:1GB 小机上 Redis 恰是 OOM-killer 高危对象;一次意外重启= 一次全量掉线事故 + 客服风暴。</p>
|
||||
<p><b>修法</b>:refresh 白名单 miss 时<b>回查 sessions 表</b>(jti 存在且未 revoke → 放行并回填 Redis),Redis 降级为缓存而非唯一真相;同时 single-node 部署给 Redis 开 AOF (<code>appendonly yes</code>) + <code>maxmemory</code> 上限。这也顺手消除了「强退后 Redis 删失败仍可刷新」的反向缝隙。</p>
|
||||
<div class="loc">server/internal/auth/token.go:228-234 · server/internal/sessions/store.go:1-5(“authoritative”)· deploy/single-node/deploy.sh:104</div>
|
||||
</div>
|
||||
|
||||
<div class="card p1">
|
||||
<h3>F7 · argon2id 64 MiB/次登录,1 GB 机可被打 OOM <span class="tag warn">容量/安全</span></h3>
|
||||
<p><b>现象</b>:argon2id 参数 64 MiB × 4 线程。登录是公开端点:~10 个并发登录请求 ≈ 640 MB 瞬时内存——机器总共 1 GB,还要跑 sing-box + agent + Redis。滑窗限流按 scope(邮箱/IP) 计,攻击者换 IP/邮箱可绕。</p>
|
||||
<p><b>修法</b>:给密码哈希加<b>全局并发闸</b>(semaphore 1–2 个并发,其余排队),几行代码把内存上限钉死在 128 MiB;
|
||||
或按 OWASP 备选参数降到 19 MiB×2。限流再加全局维度(每秒总登录数)兜底。</p>
|
||||
<div class="loc">server/internal/auth/password.go:19-21 · server/internal/auth/ratelimit.go</div>
|
||||
</div>
|
||||
|
||||
<h2>P2 — 结构性小患 / 口径问题</h2>
|
||||
|
||||
<div class="card p2">
|
||||
<h3>F8 · sessions / audit_log 无限增长,无留存策略 <span class="tag info">数据库</span></h3>
|
||||
<p>每次登录一行 sessions、永不清理;audit_log 纯追加。<code>LastLoginByDevice</code> 按用户<b>全史扫描</b>(ORDER BY created_at ASC 无 LIMIT),<code>HasActiveSession</code>(15s 轮询热路径)只有 user_id 单列索引可用。年级尺度上小机的磁盘与查询都会被拖住。<b>修</b>:留存任务(revoked 会话 >90 天、audit >180 天定期删)+ 复合索引 <code>(user_id, device_id, revoked_at)</code>;LastLogin 改每设备 MAX 子查询或维护 devices.last_login 列。</p>
|
||||
<div class="loc">server/internal/sessions/store.go:54-62,134-152 · migrations 000016(仅两个单列索引)</div>
|
||||
</div>
|
||||
|
||||
<div class="card p2">
|
||||
<h3>F9 · 免费额度按 UTC 日重置(北京时间 08:00)<span class="tag info">产品</span></h3>
|
||||
<p><code>utcToday()</code> / <code>windowEnd.UTC().Truncate(24h)</code>:主力用户在国内,「今日剩余」却在早上 8 点跳变,倒计时/额度体验诡异且难解释。<b>修</b>:额度日界定死 <code>Asia/Shanghai</code>(产品定位明确,不必 per-user 时区),服务端集中改 <code>utcToday</code> 与记账日期两处即可,客户端展示自动跟随 /me。</p>
|
||||
<div class="loc">server/internal/usage/quota.go:40,55 · server/internal/nodes/handler_grpc.go:291</div>
|
||||
</div>
|
||||
|
||||
<div class="card p2">
|
||||
<h3>F10 · 免费时长三个时钟不一致 <span class="tag info">一致性</span></h3>
|
||||
<p>同一「10 分钟」有三种度量:① 服务端 minutes_used——<b>有流量的窗口才 +1</b>(挂着不动不扣);② 凭证 TTL——发放时定死墙钟;③ 客户端倒计时——连接起墙钟递减。后果:闲置用户被客户端切断但服务端几乎没扣分 → 重连又是满额倒计时(免费时长实际无上限,只要愿意重连);反之轻流量用户每窗口整分扣。<b>修</b>:先定口径——推荐「连接在线即计时」(agent 按凭证存活窗口计 1 分钟,不看流量),三个时钟自然对齐;或接受现状但把客户端倒计时以 /me 剩余为准动态校正(已部分做)。</p>
|
||||
<div class="loc">server/internal/agentd/usage_v2ray.go:96-101(有流量才计)· httpapi/nodes.go:235(TTL)· client connection_provider 倒计时</div>
|
||||
</div>
|
||||
|
||||
<div class="card p2">
|
||||
<h3>F11 · agent 在线状态纯内存,server 重启短窗全体拒连 <span class="tag info">可靠性</span></h3>
|
||||
<p><code>hub.IsOnline</code> 是进程内 map;server 重启后到 agent 重连前,ListNodes 全灰、ConnectNode 全拒(503)。当前单节点影响秒级,可接受;但多节点后放大。<b>修</b>:启动后给一个宽限窗(如 60s 内 unknown 视为 up),或 agent 心跳落 Redis 带 TTL。与 todo #8(掉线告警)同一片改。</p>
|
||||
</div>
|
||||
|
||||
<div class="card p2">
|
||||
<h3>F12 · 单实例探测:固定端口 47654、无握手 <span class="tag info">客户端</span></h3>
|
||||
<p>任何本地进程先占住该端口 → 真 App 启动时 bind 失败误判「已有实例」直接退出(<b>App 无法启动且无提示</b>);反之任意本地进程连一下就能唤起主窗(无害但脏)。<b>修</b>:连接后交换 magic 字节验明正身,验不过改用文件锁兜底再启动;唤窗同样验 magic。</p>
|
||||
<div class="loc">client/lib/system_tray.dart:15-38</div>
|
||||
</div>
|
||||
|
||||
<div class="card p2">
|
||||
<h3>F13 · ReportUsage 多条 SQL 无事务 <span class="tag info">后端</span></h3>
|
||||
<p>每设备 Accumulate + 每用户 Accumulate 是多条独立语句,中途崩溃留部分记账(设备有、账户无)。量级小、图表级偏差,配合 F5 的 window_id 幂等一起收进单事务即可。</p>
|
||||
<div class="loc">server/internal/nodes/handler_grpc.go:300-359</div>
|
||||
</div>
|
||||
|
||||
<h2>做得好的(保持)</h2>
|
||||
<ul>
|
||||
<li><b>方言层</b>(<code>internal/db/dialect.go</code>):裸 SQL + 中性 Upsert/锁语义,时间 Go 端算——MySQL/SQLite 真正可切换,测试免 docker。</li>
|
||||
<li><b>认证栈</b>:argon2id + RS256 双 kid 轮换 + refresh 单次使用轮换 + typ 声明防混用,教科书级。</li>
|
||||
<li><b>节点判活</b>:DB 状态 × agent gRPC 在线 × 数据面健康三合一(<code>effectiveNodeStatus</code>),并拒绝向离线 agent「假装下发成功」——正是修过 6 天静默事故后的正确形态。</li>
|
||||
<li><b>per-device dp_uuid + v2ray per-user 计数</b>:归因链路是准的(老 clash 均摊源已弃用、仅遗留代码)。</li>
|
||||
<li><b>免费额度账户级共享 + 凭证 TTL 硬切断</b>:卡控在服务端成立,客户端绕过也兜得住。</li>
|
||||
<li><b>迁移成对成套</b>(mysql/sqlite 各一份 up/down),审查期未见漂移。</li>
|
||||
</ul>
|
||||
|
||||
<h2>建议处理顺序</h2>
|
||||
<table>
|
||||
<tr><th>批次</th><th>项</th><th>理由</th></tr>
|
||||
<tr><td><b>立刻</b></td><td>F2(备份)→ F1(TLS)</td><td>F2 一晚落地、消掉最大期望损失;F1 需要域名决策,动客户端默认值要随发版</td></tr>
|
||||
<tr><td><b>下一迭代</b></td><td>F3 + F4(一起动 devices/凭证接缝);F5 + F13(一起动用量链路);F6 + F7(一起动 auth 可靠性)</td><td>三组各自同一片代码,一组一 PR</td></tr>
|
||||
<tr><td><b>排队</b></td><td>F8–F12</td><td>口径决策(F9/F10)先拍板再动手;F11 并入 todo #8</td></tr>
|
||||
</table>
|
||||
|
||||
<p class="small">备注:web/(usercenter/website)本轮未深审(改动频率与暴露面低于 server/client 核心链路);
|
||||
近期已修复且验证过的不再列出:用量多设备超计(#22)、被移除设备判活(dev==nil)、弱网看门狗误伤(#18)、统计流广播订阅。
|
||||
本报告基于 worktree-macos-killswitch @ 2026-07-02。</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -108,6 +108,11 @@
|
||||
</a>
|
||||
|
||||
<h2>知识库 / 调研</h2>
|
||||
<a class="doc" href="code-review-2026-07.html">
|
||||
<div class="t">全栈设计审查 2026-07(前端/后端/数据库)<span class="tag html">HTML</span></div>
|
||||
<div class="d">核心链路精读式审查,13 项发现分 P0/P1/P2:明文 HTTP、SQLite 零备份(P0);同机换账号 403 死结、disconnect 撤错凭证、用量取走即焚、Redis 重启全员掉线、argon2id OOM(P1);留存/UTC 日界/三时钟口径等(P2)。附「做得好的」与处理顺序建议。</div>
|
||||
<div class="path">docs/code-review-2026-07.html</div>
|
||||
</a>
|
||||
<a class="doc" href="dev-conventions.html">
|
||||
<div class="t">开发规范 · 可测试性五支柱 <span class="tag html">HTML</span></div>
|
||||
<div class="d">「怎么写才好测」——开发规范作为可测试性前置条件。五支柱(接缝即接口/契约单源/纯逻辑分离/错误是值/可观测)+ 支柱↔测试层咬合矩阵图 + 反例→真实bug→对应支柱对照表。与测试框架文档咬合。</div>
|
||||
|
||||
Reference in New Issue
Block a user