mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 13:40:45 +00:00
清理对外材料里的中转管理后台 + 把规矩变成测试
用户定的新规矩:
「你对外公开的数据里不要包含那个中转站管理的页面,你就只能提官网」
用户确认的范围:只清理「我对外的公开发言」
(发布说明 / 更新日志 / 官网 / README / 我给出的汇报),
服务端 zip 里的运维文档保留(装服务器的人需要它),
源码实现保留(路径是功能本身,藏起来没法维护)。
清掉的内容:
* CHANGELOG.md:1.8.0 条目里"管理后台重做"整段、
"管理页显示成一屏源码"、"管理页标题被切掉";
1.7.1 条目里**直接写出的路径 /zhongzhuanzhan**、
"管理入口从官网上移除";1.7.0 的"管理页加图标"
* RELEASE-1.8.0.md:第四节「中转管理后台完全重做」整节删掉,
改成「官网重新设计」
新增 _build/test_public_clean.py(15 项)并加进套件(阶段 3b7):
* CHANGELOG / README / docs 里不许出现
zhongzhuanzhan、/admin、管理后台、管理页、管理入口、admin_password
* 官网渲染出来的 HTML 逐词检查,且站内链接只允许
指向 /download/、/shot/、首页、图标、页内锚点
* 发布说明里不许出现安装教程类内容(用户要求只放更新内容)
为什么写成测试:文档没人看。这一天里我因为"以为记住了"
翻了三次车,所以把规矩变成会红的断言。
新增 docs/项目规矩.md:把用户历次明确的约束集中记下来
(发布流程、对外清洁度、版本号、产物组成、README 规则、
白色简洁设计、改完必须验、测试工具不能当干扰源),
新会话接手时先读这个。
顺带修的工程问题(今天反复踩的坑):
* 用 PowerShell 的 @"..."@ 嵌 Python 去改 .ps1 时,
PowerShell 会先把 $var 展开成空,写进去一堆反斜杠
-> verify_all.ps1 被写坏,& 语法报错
现在改 .ps1 一律用 Python 读写文件
* re.sub 的替换串会解释反斜杠转义,
`"\\test_public_clean.py"` 里的 \t 变成制表符,
路径成了 "_build<TAB>est_public_clean.py"
-> 用 lambda 替换,避开转义
验收:全量回归 34 个阶段全部通过(含新的清洁度守卫)。
This commit is contained in:
+12
-22
@@ -1,6 +1,6 @@
|
||||
## 1.8.0
|
||||
|
||||
**这一版主要是「修看不见的稳定性问题」+「把中转管理后台重做」。**
|
||||
**这一版主要是「修看不见的稳定性问题」。**
|
||||
|
||||
- 修**跨网络老断**(Error 1033 / 502 / 530 的真因):
|
||||
4G 链路会**静默断开 TCP 连接**(不发 FIN/RST,双方都以为还活着),
|
||||
@@ -21,26 +21,20 @@
|
||||
|
||||
- 修**看门狗自己制造不稳定**:它每隔 50 秒重启一次隧道,
|
||||
但隧道重建 4 条连接要 30~60 秒 —— 每次还没建齐就被下一次打断,
|
||||
永远停在"只剩 1 条"的状态。现在重启后有 150 秒宽限期
|
||||
永远停在"只剩 1 条"的状态。现在重启后有宽限期,
|
||||
并且会区分"没建好过"和"建好过又垮",后者立刻重启
|
||||
|
||||
- **中转管理后台完全重做**:原来是几百行内联代码,
|
||||
改一处样式要在一堆转义符里翻。拆成独立模块,
|
||||
界面重排成:顶栏状态 → 公网地址(最重要,给最大权重)→
|
||||
四个数字 → 设备表 → 工具卡。窄屏自动变卡片布局
|
||||
|
||||
- 修**管理页显示成一屏源码**:响应头类型写错了,
|
||||
浏览器把整页 HTML 当纯文本显示
|
||||
|
||||
- 修**管理页标题被切掉**:顶栏用了固定高度装不下内容,
|
||||
标题被顶出容器、状态徽章压在文字上
|
||||
|
||||
- 清理**僵尸设备记录**:设备列表只增不减,
|
||||
实测一天攒了 75 条无效记录,拖慢手表和网页。
|
||||
现在超过 6 小时没上报的自动清掉,占用的配对码一起释放
|
||||
- **官网重新设计**:层次更清楚,白色简洁风格
|
||||
|
||||
- **官网新增产品展示**:手表界面预览(颜色和布局与真实手表一致)、
|
||||
电脑端真实截图、两种连接方式对比图
|
||||
|
||||
- 清理**僵尸设备记录**:设备列表原来只增不减,
|
||||
实测一天攒了 75 条无效记录,拖慢手表和网页访问。
|
||||
现在超过 6 小时没上报的自动清掉,占用的配对码一起释放
|
||||
|
||||
- 修下载文件用 `HEAD` 探测时长度报 0(部分下载器会误判成空文件)
|
||||
|
||||
- 支持 `HEAD` 请求(原来一律返回 501)
|
||||
|
||||
## 1.7.1
|
||||
@@ -63,11 +57,8 @@
|
||||
空闲后不重置拥塞窗口(长轮询场景关键)
|
||||
- 手表提示改成自诊断式:连不上时直接打出双方地址,
|
||||
一眼看出是网段不同还是连错了 WiFi
|
||||
- 中转站的官网和管理后台分开:
|
||||
`/` 是官网(公开),`/zhongzhuanzhan` 是管理后台(要登录)
|
||||
- 官网重新设计:导航 / 功能 / 三步上手 / 下载 / 常见问题
|
||||
- 官网和管理页统一改成白色简洁风格
|
||||
- 管理入口从官网上移除
|
||||
- **官网重新设计**:导航 / 功能 / 三步上手 / 下载 / 常见问题
|
||||
- 官网改成白色简洁风格
|
||||
|
||||
## 1.7.0
|
||||
|
||||
@@ -78,4 +69,3 @@
|
||||
- 修配置分裂问题:管理员装的配对码,普通用户账号看不到(数据目录改成按安装位置判断)
|
||||
- 修装到 Program Files 后报「拒绝访问」:ProgramData 数据目录自动授权
|
||||
- 修安装卡死:`--code` 命令处理完不返回
|
||||
- 中转管理页加上网站图标
|
||||
|
||||
+22
-27
@@ -1,6 +1,7 @@
|
||||
# ykxmz 1.8.0 发布说明
|
||||
|
||||
**发布日期**:2026-10-07
|
||||
**官网**:https://relay.xxmxxs.top
|
||||
**产物**:
|
||||
- Windows 被控端:`ykxmz-setup-1.8.0.exe`(14.37 MB,安装包)
|
||||
- 服务端:`ykxmz-server-1.8.0.zip`(0.21 MB,随身 WiFi / Linux)
|
||||
@@ -44,37 +45,26 @@ Cloudflare 的 Error 1033 或者 502、530,过一会又自己好了。
|
||||
- 加了宽限期;又发现宽限期太长会在链路持续不好时干等
|
||||
- 最终逻辑:**建好过又断 = 真故障,立刻重启;一直没建好 = 它在努力,继续等**
|
||||
|
||||
## 四、中转管理后台完全重做
|
||||
## 四、官网重新设计
|
||||
|
||||
界面重新排成:
|
||||
导航结构更清楚,整体白色简洁风格。
|
||||
|
||||
```
|
||||
顶栏(运行状态 / 版本 / 内存)
|
||||
↓
|
||||
公网地址(最重要,给你最大的一块)
|
||||
↓
|
||||
四个数字(登记设备 / 在线 / 手表会话 / 配对码占用)
|
||||
↓
|
||||
设备表(状态圆点、等宽配对码、一键删除)
|
||||
↓
|
||||
工具卡(网络体检 + 下载区)
|
||||
```
|
||||
新增产品展示区:
|
||||
- **手表界面预览**(颜色和布局与真实手表一致)
|
||||
- **电脑端真实截图**
|
||||
- **两种连接方式对比图**(局域网直连 vs 跨网络中转)
|
||||
|
||||
窄屏下表格自动变成卡片布局,手机上也能看。
|
||||
## 五、清理僵尸设备记录
|
||||
|
||||
顺带修的:
|
||||
- 管理页有时候显示成**一屏源码**(响应头类型写错)
|
||||
- 顶栏**标题被切掉**、状态标签压在文字上
|
||||
- 下载文件用 HEAD 探测时长度报 0(下载器会误判成空文件)
|
||||
设备列表原来只增不减,实测一天攒了 **75 条**无效记录,
|
||||
拖慢手表和网页访问,配对码占用数也虚高。
|
||||
|
||||
## 五、其它
|
||||
现在超过 6 小时没上报的设备自动清掉,它占用的配对码一起释放。
|
||||
|
||||
- **清理僵尸设备记录**:设备列表原来只增不减,实测一天攒了 75 条
|
||||
无效记录,拖慢手表和网页。现在超过 6 小时没上报的自动清掉,
|
||||
它占用的配对码一起释放
|
||||
- **官网新增产品展示**:手表界面预览、电脑端真实截图、
|
||||
两种连接方式对比
|
||||
- 支持 `HEAD` 请求
|
||||
## 六、其它
|
||||
|
||||
- 修下载文件用 `HEAD` 探测时长度报 0(部分下载器会误判成空文件)
|
||||
- 支持 `HEAD` 请求(原来一律返回 501)
|
||||
|
||||
---
|
||||
|
||||
@@ -88,5 +78,10 @@ Cloudflare 的 Error 1033 或者 502、530,过一会又自己好了。
|
||||
| 公网可用率 | 26/30 = 86%(底层 4G 丢包 30%,这是链路的上限) |
|
||||
|
||||
> 说明:可用率受限于现场 4G 网络质量。换有线网络或把中转
|
||||
> 放到有公网 IP 的服务器上可以显著改善,方案见
|
||||
> `docs/免费托管方案.md`。
|
||||
> 放到有公网 IP 的服务器上可以显著改善。
|
||||
|
||||
---
|
||||
|
||||
## 下载
|
||||
|
||||
**https://relay.xxmxxs.top**
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
清理对外材料里的中转管理后台 + 把规矩变成测试
|
||||
|
||||
用户定的新规矩:
|
||||
「你对外公开的数据里不要包含那个中转站管理的页面,你就只能提官网」
|
||||
|
||||
用户确认的范围:只清理「我对外的公开发言」
|
||||
(发布说明 / 更新日志 / 官网 / README / 我给出的汇报),
|
||||
服务端 zip 里的运维文档保留(装服务器的人需要它),
|
||||
源码实现保留(路径是功能本身,藏起来没法维护)。
|
||||
|
||||
清掉的内容:
|
||||
* CHANGELOG.md:1.8.0 条目里"管理后台重做"整段、
|
||||
"管理页显示成一屏源码"、"管理页标题被切掉";
|
||||
1.7.1 条目里**直接写出的路径 /zhongzhuanzhan**、
|
||||
"管理入口从官网上移除";1.7.0 的"管理页加图标"
|
||||
* RELEASE-1.8.0.md:第四节「中转管理后台完全重做」整节删掉,
|
||||
改成「官网重新设计」
|
||||
|
||||
新增 _build/test_public_clean.py(15 项)并加进套件(阶段 3b7):
|
||||
* CHANGELOG / README / docs 里不许出现
|
||||
zhongzhuanzhan、/admin、管理后台、管理页、管理入口、admin_password
|
||||
* 官网渲染出来的 HTML 逐词检查,且站内链接只允许
|
||||
指向 /download/、/shot/、首页、图标、页内锚点
|
||||
* 发布说明里不许出现安装教程类内容(用户要求只放更新内容)
|
||||
|
||||
为什么写成测试:文档没人看。这一天里我因为"以为记住了"
|
||||
翻了三次车,所以把规矩变成会红的断言。
|
||||
|
||||
新增 docs/项目规矩.md:把用户历次明确的约束集中记下来
|
||||
(发布流程、对外清洁度、版本号、产物组成、README 规则、
|
||||
白色简洁设计、改完必须验、测试工具不能当干扰源),
|
||||
新会话接手时先读这个。
|
||||
|
||||
顺带修的工程问题(今天反复踩的坑):
|
||||
* 用 PowerShell 的 @"..."@ 嵌 Python 去改 .ps1 时,
|
||||
PowerShell 会先把 $var 展开成空,写进去一堆反斜杠
|
||||
-> verify_all.ps1 被写坏,& 语法报错
|
||||
现在改 .ps1 一律用 Python 读写文件
|
||||
* re.sub 的替换串会解释反斜杠转义,
|
||||
`"\\test_public_clean.py"` 里的 \t 变成制表符,
|
||||
路径成了 "_build<TAB>est_public_clean.py"
|
||||
-> 用 lambda 替换,避开转义
|
||||
|
||||
验收:全量回归 34 个阶段全部通过(含新的清洁度守卫)。
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''最后一次修 verify_all.ps1 的路径。
|
||||
|
||||
为什么要写第三个脚本:
|
||||
上一次修的时候,re.sub 把 `\t` 当成了制表符,
|
||||
路径 `_build\test_public_clean.py` 变成了
|
||||
`_build<TAB>est_public_clean.py` —— **连 "t" 都被吃掉了**,
|
||||
所以按 `test_public_clean` 去搜是搜不到的
|
||||
(实际内容是 `est_public_clean`)。
|
||||
|
||||
这次用宽松模式:凡是那一行里有 "public_clean" 就整体替换掉。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
VERIFY = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote"
|
||||
r"\_tests\verify_all.ps1")
|
||||
BOM = b"\xef\xbb\xbf"
|
||||
|
||||
WANT = '$stageOut = & python (Join-Path $root "_build\\test_public_clean.py") 2>&1'
|
||||
|
||||
|
||||
def main() -> int:
|
||||
text = VERIFY.read_text(encoding="utf-8-sig")
|
||||
|
||||
# 宽松匹配:整行里出现 public_clean 就算
|
||||
pattern = re.compile(r'^.*public_clean.*$', re.M)
|
||||
found = pattern.findall(text)
|
||||
print(" 现场(%d 行):" % len(found))
|
||||
for line in found:
|
||||
print(" %r" % line)
|
||||
|
||||
if WANT in text and len(found) == 1 and found[0] == WANT:
|
||||
print()
|
||||
print(" 已经是正确写法")
|
||||
return 0
|
||||
|
||||
if not found:
|
||||
print()
|
||||
print(" [失败] 整行里连 public_clean 都没有")
|
||||
# 把那一带打出来看
|
||||
lines = text.splitlines()
|
||||
for index, line in enumerate(lines, 1):
|
||||
if "3b7" in line or "对外材料" in line:
|
||||
for offset in range(-3, 5):
|
||||
pos = index - 1 + offset
|
||||
if 0 <= pos < len(lines):
|
||||
print(" %d: %r" % (pos + 1, lines[pos]))
|
||||
break
|
||||
return 1
|
||||
|
||||
# 用 lambda 替换到底,绝不碰转义
|
||||
new_text, count = pattern.subn(lambda _m: WANT, text)
|
||||
print()
|
||||
print(" 替换 %d 行" % count)
|
||||
|
||||
VERIFY.write_bytes(BOM + new_text.replace("\r\n", "\n").encode("utf-8"))
|
||||
|
||||
check = VERIFY.read_text(encoding="utf-8-sig")
|
||||
lines = [ln for ln in check.splitlines() if "public_clean" in ln]
|
||||
print(" 回读 %d 行:" % len(lines))
|
||||
ok = True
|
||||
for line in lines:
|
||||
same = line == WANT
|
||||
ok = ok and same
|
||||
print(" %s %r" % ("✓" if same else "✗", line))
|
||||
print(" BOM: %s" % VERIFY.read_bytes().startswith(BOM))
|
||||
|
||||
# 顺便确认没有再出现制表符或被吃掉的痕迹
|
||||
print()
|
||||
print(" 那一段完整内容:")
|
||||
all_lines = check.splitlines()
|
||||
for index, line in enumerate(all_lines, 1):
|
||||
if "3b7" in line:
|
||||
for offset in range(0, 7):
|
||||
if index - 1 + offset < len(all_lines):
|
||||
print(" %d: %s" % (index + offset,
|
||||
all_lines[index - 1 + offset]))
|
||||
break
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''修复 verify_all.ps1 里被转义吃掉的那两行。
|
||||
|
||||
事故:
|
||||
我用 PowerShell 的 @"..."@ 里嵌 Python 去改 verify_all.ps1,
|
||||
PowerShell 先把 $stageOut / $root 当自己的变量展开成空,
|
||||
写进去的代码就变成了 `\ = & python (Join-Path \ "...")`,
|
||||
PowerShell 直接语法报错 & is not allowed。
|
||||
|
||||
这个坑今天踩了十几次了。**改 .ps1 一律用 Python 读写文件,
|
||||
不要把代码嵌在 PowerShell 字符串里。**
|
||||
|
||||
补丁内容:把损坏的两行换成和其他阶段一致的写法。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
VERIFY = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote"
|
||||
r"\_tests\verify_all.ps1")
|
||||
BOM = b"\xef\xbb\xbf"
|
||||
|
||||
# 损坏的两行(用正则宽松匹配,因为损坏后的具体字符可能不同)
|
||||
import re # noqa: E402
|
||||
|
||||
BROKEN = re.compile(
|
||||
r"^.*= & python \(Join-Path .*test_public_clean\.py.*\) 2>&1\s*\n"
|
||||
r"\s*Show-Fails Record \"对外材料清洁度\".*$",
|
||||
re.M)
|
||||
|
||||
FIXED = (
|
||||
'$stageOut = & python (Join-Path $root '
|
||||
'"_build\\test_public_clean.py") 2>&1\n'
|
||||
' Show-Fails $stageOut\n'
|
||||
'Record "对外材料清洁度" ($LASTEXITCODE -eq 0)'
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
raw = VERIFY.read_bytes()
|
||||
had_bom = raw.startswith(BOM)
|
||||
text = raw.decode("utf-8-sig")
|
||||
|
||||
if "test_public_clean" not in text:
|
||||
print(" [失败] 找不到那个阶段")
|
||||
return 1
|
||||
|
||||
# 检查是否已经正确
|
||||
if '$stageOut = & python (Join-Path $root "_build\\test_public_clean.py") 2>&1' in text:
|
||||
print(" 已经是正确写法了")
|
||||
else:
|
||||
new_text, count = BROKEN.subn(FIXED, text)
|
||||
if count == 0:
|
||||
print(" [失败] 正则没匹配到损坏的行")
|
||||
# 打印现场
|
||||
for index, line in enumerate(text.splitlines(), 1):
|
||||
if "test_public_clean" in line or "对外材料清洁度" in line:
|
||||
print(" %d: %r" % (index, line))
|
||||
return 1
|
||||
text = new_text
|
||||
print(" 修好了 %d 处" % count)
|
||||
|
||||
VERIFY.write_bytes(BOM + text.replace("\r\n", "\n").encode("utf-8")
|
||||
if not had_bom else
|
||||
BOM + text.replace("\r\n", "\n").encode("utf-8"))
|
||||
print(" BOM: %s" % VERIFY.read_bytes().startswith(BOM))
|
||||
|
||||
# 回读确认
|
||||
print()
|
||||
print(" 修复后的那一段:")
|
||||
lines = VERIFY.read_text(encoding="utf-8-sig").splitlines()
|
||||
for index, line in enumerate(lines, 1):
|
||||
if "3b7" in line:
|
||||
for offset in range(0, 7):
|
||||
if index - 1 + offset < len(lines):
|
||||
print(" %d: %s" % (index + offset,
|
||||
lines[index - 1 + offset]))
|
||||
break
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''修掉 verify_all.ps1 里被写成制表符的路径。
|
||||
|
||||
事故链:
|
||||
1. 我先用 PowerShell 嵌 Python 去写这个文件
|
||||
-> PowerShell 把 $stageOut/$root 展开成空,写进去一堆 `\`
|
||||
2. 改用 Python 的 re.sub 修
|
||||
-> **re.sub 的替换串会解释反斜杠转义**,
|
||||
`"\\test_public_clean.py"` 里的 `\t` 被当成制表符,
|
||||
路径变成 "_build<TAB>est_public_clean.py"
|
||||
|
||||
教训:
|
||||
* 改 .ps1 一律用 Python 读写,不要嵌在 PowerShell 字符串里
|
||||
* re.sub 的替换串要么用 lambda,要么把反斜杠写满四层
|
||||
(这次用 lambda,最省心)
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
VERIFY = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote"
|
||||
r"\_tests\verify_all.ps1")
|
||||
BOM = b"\xef\xbb\xbf"
|
||||
|
||||
# 目标:这一行必须长这样
|
||||
WANT = '$stageOut = & python (Join-Path $root "_build\\test_public_clean.py") 2>&1'
|
||||
|
||||
|
||||
def main() -> int:
|
||||
text = VERIFY.read_text(encoding="utf-8-sig")
|
||||
|
||||
# 找出所有引用 test_public_clean 的行
|
||||
pattern = re.compile(r'^.*test_public_clean.*$', re.M)
|
||||
found = pattern.findall(text)
|
||||
print(" 现场(%d 行):" % len(found))
|
||||
for line in found:
|
||||
print(" %r" % line)
|
||||
|
||||
if WANT in text:
|
||||
print()
|
||||
print(" 已经是正确写法,不用改")
|
||||
return 0
|
||||
|
||||
# 用 lambda 替换,避免 re.sub 解释转义
|
||||
new_text, count = pattern.subn(lambda _m: WANT, text)
|
||||
print()
|
||||
print(" 替换 %d 行" % count)
|
||||
if count == 0:
|
||||
print(" [失败] 没找到目标行")
|
||||
return 1
|
||||
|
||||
VERIFY.write_bytes(BOM + new_text.replace("\r\n", "\n").encode("utf-8"))
|
||||
|
||||
# 回读确认(用 repr 看有没有隐藏字符)
|
||||
check = VERIFY.read_text(encoding="utf-8-sig")
|
||||
line = [ln for ln in check.splitlines() if "test_public_clean" in ln][0]
|
||||
print(" 回读: %r" % line)
|
||||
ok = line == WANT
|
||||
print(" %s" % ("✓ 完全一致" if ok else "**还不一致**"))
|
||||
if not ok:
|
||||
print(" 期望: %r" % WANT)
|
||||
print(" BOM: %s" % VERIFY.read_bytes().startswith(BOM))
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,169 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''守卫:对外公开的材料里不许出现「中转管理后台」。
|
||||
|
||||
用户定的规矩(原话):
|
||||
「你对外公开的数据里不要包含那个中转站管理的页面,你就只能提官网」
|
||||
|
||||
范围(用户已确认):
|
||||
✅ 必须干净:发布说明、CHANGELOG、官网页面、README、我对外的汇报
|
||||
❌ 不用管:服务端 zip 里的运维文档(给装机的人看)、
|
||||
源码实现(路径是功能本身)、_build/_tests 内部材料
|
||||
|
||||
为什么写成测试而不是写进文档:
|
||||
文档没人看。这一天里我因为"以为记住了"翻了三次车,
|
||||
所以把规矩变成会红的断言 —— 违反了立刻报错。
|
||||
|
||||
顺便也守住另外两条用户明确说过的:
|
||||
* 官网里不能出现管理入口
|
||||
* 发布说明只能是更新内容
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
|
||||
|
||||
# 这些词一律不许出现在对外材料里
|
||||
FORBIDDEN = [
|
||||
"zhongzhuanzhan",
|
||||
"/admin",
|
||||
"管理后台",
|
||||
"管理页",
|
||||
"管理入口",
|
||||
"管理面板",
|
||||
"admin_password",
|
||||
]
|
||||
|
||||
# 对外公开的文件(相对路径)
|
||||
PUBLIC_FILES = [
|
||||
"_build/CHANGELOG.md",
|
||||
"README.md",
|
||||
]
|
||||
|
||||
# 对外公开的目录:里面所有 .md 都要干净
|
||||
PUBLIC_DIRS = [
|
||||
"docs",
|
||||
]
|
||||
|
||||
# docs 里这些是**运维文档**,是给装服务器的人看的,允许提到管理页
|
||||
DOCS_ALLOWLIST = {
|
||||
"AFTER-INSTALL.txt",
|
||||
"BEFORE-INSTALL.txt",
|
||||
"LICENSE.txt",
|
||||
"项目规矩.md", # 这份文件本身就是在讲这条规矩
|
||||
"免费托管方案.md", # 讲托管方案,不涉及
|
||||
}
|
||||
|
||||
# 官网源码里这些词也不许有(landing.py 渲染出来的页面)
|
||||
SITE_FILES = [
|
||||
"relay/landing.py",
|
||||
"relay/showcase.py",
|
||||
]
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
|
||||
def check(name: str, ok: bool, detail: str = "") -> None:
|
||||
global passed, failed
|
||||
if ok:
|
||||
passed += 1
|
||||
print(" [PASS] " + name)
|
||||
else:
|
||||
failed += 1
|
||||
print(" [FAIL] " + name + " " + str(detail))
|
||||
|
||||
|
||||
def scan(path: Path) -> list:
|
||||
"""返回 [(行号, 关键词, 那一行)]"""
|
||||
hits = []
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8", errors="replace")
|
||||
except OSError:
|
||||
return hits
|
||||
for index, line in enumerate(text.splitlines(), 1):
|
||||
for word in FORBIDDEN:
|
||||
if word in line:
|
||||
hits.append((index, word, line.strip()[:70]))
|
||||
return hits
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(" 一、对外公开的单文件")
|
||||
for rel in PUBLIC_FILES:
|
||||
path = ROOT / rel
|
||||
if not path.is_file():
|
||||
check("%s 存在" % rel, False, "文件不存在")
|
||||
continue
|
||||
hits = scan(path)
|
||||
check("%s 干净" % rel, not hits,
|
||||
"第 %d 行含「%s」" % (hits[0][0], hits[0][1]) if hits else "")
|
||||
|
||||
print()
|
||||
print(" 二、docs 目录(运维文档除外)")
|
||||
for rel in PUBLIC_DIRS:
|
||||
directory = ROOT / rel
|
||||
if not directory.is_dir():
|
||||
continue
|
||||
for path in sorted(directory.iterdir()):
|
||||
if not path.is_file():
|
||||
continue
|
||||
if path.name in DOCS_ALLOWLIST:
|
||||
print(" [跳过] %s(运维文档,允许提及)" % path.name)
|
||||
continue
|
||||
hits = scan(path)
|
||||
check("docs/%s 干净" % path.name, not hits,
|
||||
"第 %d 行含「%s」" % (hits[0][0], hits[0][1]) if hits else "")
|
||||
|
||||
print()
|
||||
print(" 三、官网页面(渲染出来给所有人看的)")
|
||||
sys.path.insert(0, str(ROOT / "relay"))
|
||||
try:
|
||||
import landing
|
||||
html = landing.render_site([
|
||||
{"name": "ykxmz-setup-1.8.0.exe", "size": 15066550},
|
||||
{"name": "ykxmz-server-1.8.0.zip", "size": 214661},
|
||||
{"name": "ykxmz-watch-1.8.0.apk", "size": 201523},
|
||||
])
|
||||
except Exception as exc: # noqa: BLE001
|
||||
check("官网能渲染", False, exc)
|
||||
html = ""
|
||||
|
||||
if html:
|
||||
check("官网能渲染", len(html) > 5000, "%d 字符" % len(html))
|
||||
for word in FORBIDDEN:
|
||||
check("官网里没有「%s」" % word, word not in html.lower()
|
||||
if word.isascii() else word not in html)
|
||||
# 只允许链接到下载 / 截图 / 首页 / 图标 / 页内锚点。
|
||||
# 出现别的站内路径就说明可能把管理入口又加回去了。
|
||||
allowed = ("/download/", "/shot/", "/", "/favicon.ico", "#")
|
||||
hrefs = set(re.findall(r'href="([^"]+)"', html))
|
||||
bad = [h for h in hrefs
|
||||
if h.startswith("/") and not h.startswith(allowed)]
|
||||
check("官网里的站内链接只指向下载/首页/锚点", not bad, bad)
|
||||
|
||||
print()
|
||||
print(" 四、发布说明只能是「更新内容」")
|
||||
rel_notes = ROOT / "_build" / "RELEASE-1.8.0.md"
|
||||
if rel_notes.is_file():
|
||||
text = rel_notes.read_text(encoding="utf-8")
|
||||
# 不该出现安装教程这种内容(用户要求"只放更新内容")
|
||||
for bad_title in ("安装教程", "安装步骤", "怎么安装"):
|
||||
check("发布说明里没有「%s」" % bad_title, bad_title not in text)
|
||||
# 也不该提管理页
|
||||
hits = scan(rel_notes)
|
||||
check("RELEASE-1.8.0.md 干净", not hits,
|
||||
"第 %d 行含「%s」" % (hits[0][0], hits[0][1]) if hits else "")
|
||||
else:
|
||||
print(" [跳过] 没有 RELEASE-1.8.0.md")
|
||||
|
||||
print()
|
||||
print("=" * 54)
|
||||
print(" 通过 %d 项,失败 %d 项" % (passed, failed))
|
||||
return 0 if failed == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user