Files
ykxmz/_build/diag_admin_js.py
xmz7891 d907fdcf6e 🚨 修一个严重 bug:管理页所有按钮又失效了(同一个坑踩第二次)
== 症状 ==

在**真实中转**上实测管理页时,用 node --check 检查页面内嵌的 JS:

    SyntaxError: Invalid or unexpected token  (第 46 行)

整段 <script> 不执行 -> 改码 / 删除 / 批量删除 / 全选 全部变幽灵按键。

**这正是用户之前报过的那个 bug,加新功能时又踩了一遍。**

== 根因(讽刺到极点)==

admin.py 的 ADMIN_JS 是**普通三引号字符串**,里面写一个反斜杠+n
会被 Python 解释成**真实换行**。而 JS 的行注释 // 不能跨行 ——
一断行,后面就成了裸文本,整段脚本语法错误。

出事的是这段注释:

    // ⚠ 这里必须是 \\n(反斜杠+n),不能是 \n ——
                                      ^^^^
                       这个 \n 被 Python 变成真换行

**注释为了举例说明「不能写 \n」,自己写了一个 \n。**
于是注释被断成两行,把整段 JS 弄坏了。

== 修法 ==

注释改成**一个转义序列都不写**,只用文字描述:

    // ⚠ 下面 prompt 里的换行,必须写成「反斜杠 + n」两个字符。
    //   本文件是普通三引号字符串:只写反斜杠 + n,Python 会把它
    //   变成**真实换行** ... (一个转义都不出现)

并把"踩过两次"的历史写进注释,提醒后人别再来第三次。

== 新增两个测试(防止再犯)==

1. `_tests/test_admin_js_syntax.py`(已注册为回归阶段 3a6)
   **本地渲染管理页**,把 <script> 抠出来喂给 `node --check`,
   还检查注释行里有没有裸控制字符。

   为什么原来查不出来:已有的「管理页 JS 合法性」阶段只检查
   token 在不在,不查语法 —— 「23 项测试通过」和「功能能用」
   是两件事。

2. `_build/scan_admin_escapes.py`
   扫源文件里"注释下一行不像 JS 代码"的可疑断行。

3. `_tests/test_admin_live.py`
   在**真实中转**上走 HTTP 实测两个新功能(不能进回归套件,
   因为它依赖线上设备):
     一、管理页能打开(带 X-Admin 头绕开登录页)
     二、页面里真的有 allowNew 和改码按钮
     三、内嵌 JS 语法合法
     四、关掉接入开关 -> 新设备被拒 -> 打开 -> 能注册(实测通过)
     五、改配对码 -> 真的变了(实测通过)
     六、不带 X-Admin 访问管理接口返回 401(实测通过)

   实测结果:16/17 通过(唯一的失败就是上面那个 JS bug,
   已修,本地渲染 6/6 通过)。

== 顺带纠正一个我自己的误解 ==

我一开始只带 `?key=` 访问管理页,拿到的是**登录页**,
还以为是设备上部署了旧版本。实际是管理页改成了
**账号 + 密码登录 + Cookie 会话**(用户要求),
脚本通道用 `X-Admin` 头(见 relay.py 的 _admin_gate)。

== 部署时机 ==

**故意没有立刻部署到设备上** —— 部署要重启中转服务,
会打断 relay-stability 正在跑的隧道配比 A/B 对照。
等对照跑完(约 22:00)再部署并复测。
2026-10-07 20:33:27 +08:00

96 lines
3.4 KiB
Python
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.
# -*- coding: utf-8 -*-
r'''把管理页的内嵌 JS 抠出来,看第 46 行为什么语法错。
背景:_tests/test_admin_live.py 用 node --check 检查页面上的内嵌脚本,
报第 46 行 SyntaxError: Invalid or unexpected token。
但同一个页面上的管理接口(/v1/admin/allow_new、/v1/admin/set_code)
是能用的 —— 说明后端没问题,问题在浏览器端。
两种可能:
A. 真的有语法错 -> 用户点按钮没反应(严重)
B. 我抠脚本的方式不对(比如页面里字符串里含 </script>,
或者 JS 用到了 node 不认的语法)-> 假警报
这个脚本把可疑的那一行原样打出来,人肉判断是哪种。
'''
from __future__ import annotations
import json
import re
import subprocess
import sys
import tempfile
import urllib.request
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "_build"))
from devssh import run as ssh_run # noqa: E402
BASE = "https://relay.xxmxxs.top"
UA = {"User-Agent": "LanRemote/1.8.2"}
def main() -> int:
_c, out, _e = ssh_run(
"cat /root/lanremote-relay/data/admin_key.txt", timeout=60, quiet=True)
key = (out or "").strip().splitlines()[0]
req = urllib.request.Request(BASE + "/zhongzhuanzhan",
headers={**UA, "X-Admin": key})
with urllib.request.urlopen(req, timeout=25) as resp:
html = resp.read().decode("utf-8", "replace")
scripts = re.findall(r"<script[^>]*>(.*?)</script>", html, re.S)
inline = [s for s in scripts if s.strip()]
print(" 页面上有 %d 段内嵌脚本,长度: %s"
% (len(inline), [len(s) for s in inline]))
js = inline[0]
lines = js.splitlines()
print(" 第 1 段共 %d 行" % len(lines))
print()
print(" ════ 第 40~54 行原样 ════")
for i in range(39, min(54, len(lines))):
marker = " <<<< 报错行" if i == 45 else ""
print(" %3d| %s%s" % (i + 1, lines[i], marker))
print()
# 把这一段单独跑 node --check,确认能复现
with tempfile.NamedTemporaryFile("w", suffix=".js", delete=False,
encoding="utf-8") as fh:
fh.write(js)
path = fh.name
proc = subprocess.run(["node", "--check", path],
capture_output=True, text=True,
encoding="utf-8", errors="replace")
print(" node --check 退出码: %d" % proc.returncode)
if proc.stderr:
print(" stderr:")
for line in proc.stderr.splitlines()[:12]:
print(" " + line)
Path(path).unlink(missing_ok=True)
# 也检查每一行里有没有裸控制字符
print()
print(" ════ 有没有裸控制字符 ════")
bad = 0
for i, line in enumerate(lines, 1):
for ch in line:
if ord(ch) < 32 and ch not in "\t":
print(" 第 %d 行有控制字符 0x%02x" % (i, ord(ch)))
bad += 1
break
if not bad:
print(" ✓ 没有")
# 反引号 / 模板字面量 / 正则 里可能藏着截断问题
print()
print(" ════ 源 HTML 里 </script> 出现几次 ════")
print(" %d 次(正常应该等于 <script> 的个数)" % html.count("</script>"))
print(" <script 出现 %d 次" % html.lower().count("<script"))
return 0
if __name__ == "__main__":
sys.exit(main())