Files
ykxmz/_build/final_acceptance3.sh
xmz7891 7b7830d8c5 阶段3+4+5:管理页白色化 + 局域网诊断 + 全量验收
阶段3 管理页
- 清掉全部深色主题残留(地址框 #0b1220、按钮悬停 #262c36、
  删除按钮 #2d1214 等 11 处),现在 0 处深色
- 接口全部正常:health / downloads / netcheck

阶段4 局域网诊断
- 查清楚"电脑不在同一网络"是怎么回事:被控端是**对的**
  (lan_ips() 报 ['192.168.2.64', '169.254.83.107'],真实地址排第一),
  是手表当时真不在 192.168.2.x —— 可能连了 5G 频段的另一个路由器,或开着移动数据
- 所以改了手表的提示:不再只说"不在同一网络",
  而是把**双方地址都打出来**,用户一眼能看出是网段不同还是连错 WiFi
- 端到端验证局域网路径:认证 1 ms,功能列表 207 项

阶段1 结论修正(一天内推翻自己两次,如实记录)
- 第一次判断"优选 IP 无用":错。半小时内确实能避开超时 IP
- 第二次固定优选 IP:也错。那些实测 138ms 的 IP 半小时后全不通了
  —— Cloudflare anycast 可达性随运营商路由变化,固定 IP 等于钉死到会失效的地址
- 最终:不固定,靠 GeoDNS + cloudflared 重试 + ha-connections 2 冗余
- 撤销后立即变好:连接稳定 2 条、超时告警 0 次、设备侧延迟 4.40s -> 1.66s
- **真正的瓶颈**:分段实测发现本机到 Cloudflare 的**裸 RTT 就 1439 ms**,
  比经隧道的请求(1215 ms)还慢。说明瓶颈是这条网络的 4G 上行本身,
  不是设备的隧道。公网侧已无优化空间。
- 局域网 118 ms vs 公网 1215 ms,快 10 倍 —— 这才是该走的路

阶段5 验收
- 全量回归 29 项全通过
- 设备侧验收 38 项全通过
- 成品:setup exe 14.37 MB / server zip / watch apk,已同步到设备下载区
2026-10-07 00:13:10 +08:00

117 lines
5.7 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 最终验收(修正版 2)
# 上次的坑:grep -c 数的是**匹配行数**,HTML 压缩后 9 张卡在同一行 -> 返回 1。
# 要数出现次数必须 grep -o | wc -l。
set -u
BASE=http://127.0.0.1:8080
PUB=https://relay.xxmxxs.top
KEY=$(cat /root/lanremote-relay/data/admin_key.txt 2>/dev/null)
pass=0; fail=0
ok() { pass=$((pass+1)); echo " ✓ $1"; }
bad() { fail=$((fail+1)); echo " ✗ $1"; }
code() { curl -s -o /dev/null -w '%{http_code}' --max-time 25 "$@"; }
# 数**出现次数**(不是行数)
occ() { grep -o "$1" "$2" 2>/dev/null | wc -l | tr -d ' '; }
# 数**包含该串的行数**(用于"有没有出现")
has() { grep -q "$1" "$2" 2>/dev/null && echo 1 || echo 0; }
echo "════════ 一、官网 ════════"
c=$(code "$BASE/")
[ "$c" = "200" ] && ok "首页 HTTP 200" || bad "首页 HTTP $c"
curl -s --max-time 20 "$BASE/" -o /tmp/s.html
size=$(wc -c < /tmp/s.html | tr -d ' ')
[ "$size" -gt 10000 ] && ok "页面 $size 字节" || bad "页面只有 $size 字节"
check() { [ "$(has "$2" /tmp/s.html)" = "1" ] && ok "$1" || bad "$1"; }
check "吸顶导航" 'nav-in'
check "Hero" 'hero'
check "功能网格" 'id="features"'
check "三步上手" 'id="how"'
check "下载区" 'id="download"'
check "FAQ" 'id="faq"'
check "页脚" 'foot-in'
check "版本徽章" 'badge'
echo
n=$(occ 'class="feat"' /tmp/s.html); [ "$n" = "9" ] && ok "9 张功能卡" || bad "功能卡 $n 张"
n=$(occ '<details class="faq"' /tmp/s.html); [ "$n" = "6" ] && ok "6 条 FAQ" || bad "FAQ $n 条"
n=$(occ 'class="dl-card"' /tmp/s.html); [ "$n" = "3" ] && ok "3 张下载卡" || bad "下载卡 $n 张"
n=$(occ 'class="step"' /tmp/s.html); [ "$n" = "3" ] && ok "3 个步骤卡" || bad "步骤卡 $n 个"
n=$(occ '<svg' /tmp/s.html); [ "$n" -ge 11 ] && ok "图标 $n 个" || bad "图标只有 $n 个"
n=$(occ '管理入口' /tmp/s.html); [ "$n" = "0" ] && ok "没有管理入口" || bad "还有管理入口"
n=$(occ 'src="/favicon.ico" alt=""' /tmp/s.html); [ "$n" = "0" ] && ok "没有破图 logo" || bad "还有破图"
echo
echo "════════ 二、管理页 ════════"
c=$(code -H "X-Admin: $KEY" "$BASE/zhongzhuanzhan")
[ "$c" = "200" ] && ok "登录后 HTTP 200" || bad "HTTP $c"
curl -s --max-time 20 -H "X-Admin: $KEY" "$BASE/zhongzhuanzhan" -o /tmp/a.html
for pair in "下载区:dlPanel" "网络体检:网络体检" "退出登录:退出登录"; do
[ "$(has "${pair#*:}" /tmp/a.html)" = "1" ] && ok "${pair%%:*}" || bad "${pair%%:*}"
done
dark=0
for cc in 0b1220 262c36 1b222c 1c2733 2d1214 4a1a1e 484f58 0d1117; do
n=$(occ "#$cc" /tmp/a.html)
[ "$n" != "0" ] && dark=$((dark + n))
done
[ "$dark" = "0" ] && ok "无深色残留" || bad "深色残留 $dark 处"
[ "$(code "$BASE/admin")" = "302" ] && ok "/admin 跳转正常" || bad "/admin 跳转异常"
[ "$(code "$BASE/zhongzhuanzhan")" = "200" ] && ok "未登录看到登录页" || bad "未登录页异常"
echo
echo "════════ 三、下载 ════════"
for f in ykxmz-setup-1.7.1.exe ykxmz-server-1.7.1.zip ykxmz-watch-1.7.1.apk; do
c=$(code "$BASE/download/$f")
[ "$c" = "200" ] && ok "$f" || bad "$f HTTP $c"
done
[ "$(code "$BASE/v1/downloads")" = "200" ] && ok "/v1/downloads" || bad "/v1/downloads"
tc=$(code "$BASE/download/../relay.py")
{ [ "$tc" = "404" ] || [ "$tc" = "400" ]; } && ok "路径穿越被挡($tc)" || bad "路径穿越 HTTP $tc"
echo
echo "════════ 四、加速 ════════"
proto=$(grep -oE 'Initial protocol [a-z0-9]+' /root/lanremote-relay/tunnel.log 2>/dev/null | tail -1)
echo "$proto" | grep -q http2 && ok "隧道走 HTTP/2(不是 QUIC)" || bad "隧道协议: $proto"
conn=$(curl -s --max-time 5 http://127.0.0.1:20241/metrics 2>/dev/null \
| grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}' | head -1)
[ "$conn" = "2" ] && ok "隧道连接数 2" || echo " · 隧道连接数 $conn(1 也能用,2 才有冗余)"
[ "$(sysctl -n net.ipv4.tcp_mtu_probing)" = "1" ] && ok "MTU 探测开着" || bad "MTU 探测关了"
[ "$(sysctl -n net.ipv4.tcp_slow_start_after_idle)" = "0" ] && ok "空闲后不重置窗口" || bad "slow_start 没关"
[ "$(sysctl -n net.ipv4.tcp_fastopen)" = "3" ] && ok "TCP FastOpen 开着" || bad "FastOpen 没开"
[ "$(occ argotunnel /etc/hosts)" = "0" ] && ok "无效的 hosts 覆盖已清" || bad "hosts 有残留"
[ "$(sysctl -n net.core.rmem_max)" = "4194304" ] && ok "TCP 接收缓冲 4MB" || bad "接收缓冲不对"
echo
echo "════════ 五、功能 ════════"
for api in /v1/health /favicon.ico; do
c=$(code "$BASE$api")
[ "$c" = "200" ] && ok "$api" || bad "$api HTTP $c"
done
curl -s --max-time 10 "$BASE/v1/health" | grep -o '"version": *"[^"]*"' | sed 's/^/ /'
echo
echo "════════ 六、公网可用性(设备侧发夹回路 = 最坏情况)════════"
okc=0; total=0
for i in $(seq 1 15); do
r=$(curl -s -o /dev/null -w '%{http_code} %{time_total}' --max-time 25 \
-H 'User-Agent: LanRemote/1.4.0' "$PUB/" 2>/dev/null)
c=$(echo "$r" | cut -d' ' -f1)
t=$(echo "$r" | cut -d' ' -f2)
if [ "$c" = "200" ]; then
okc=$((okc+1))
total=$(echo "$total + $t" | bc)
fi
sleep 0.5
done
avg=$(echo "scale=2; $total / $okc" | bc 2>/dev/null || echo "?")
[ "$okc" -ge 14 ] && ok "可用 $okc/15" || bad "可用 $okc/15"
echo " 平均 ${avg} 秒(这是设备自己请求自己的最坏情况,"
echo " 真实用户路径从 Windows 侧实测约 0.69 秒)"
echo
echo "════════════════════════════════"
echo " 通过 $pass 项,失败 $fail 项"
[ "$fail" = "0" ] && echo " 全部通过 ✓" || echo " 有失败"