mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 11:20:43 +00:00
== 用户要求 ==
"安卓那个逻辑也要改一下,就是你输入配对码后,进入一个页面,
页面有两个按钮,第一个按钮是远控,第二个按钮是静默更新被控端"
== 做成什么样 ==
新增 choicePanel,连接成功后先进这一页:
已连接「电脑名」
电脑端 2.1.5 → 可更新到 2.2.0
[ 远控 ] <- 第一个按钮
[ 静默更新被控端 ] <- 第二个按钮
按钮顺序按用户说的来(远控在前)。
页面顶上显示电脑名和**电脑端当前版本** —— 用户之前要求
"手表端连接那个电脑端也可以查看电脑端目前所处的版本",
这里是最自然的展示位置(不弹框,只显示一行)。
为什么不让它直接进控制页:老版本的被控端可能缺功能或有 bug,
"先更新再远控"是更合理的顺序 —— 但不强制,给用户自己选。
== 一个刻意不改的地方 ==
自动切到中转服务器**恢复**时的 `showControlPanel()` **没动** ——
用户本来就在远控页,中途弹一层选择页会打断他。
测试里专门断言了这一点,免得以后有人"顺手统一一下"改坏。
== 顺手修的几个问题 ==
1. **忘了定义 lastDeviceName** —— 写选择页时凭直觉用了这个变量,
编译报"找不到符号"。补上字段并在连接回调里赋值。
(教训:写代码时要用的变量,先确认它存在。)
2. **泄漏闸门拦下我自己** —— 这两天写的一批服务器诊断/配置脚本里
硬编码了服务器 IP(26 处,5 个文件)。规则是 IP 不能进任何
被跟踪的文件。已全部改成占位符,真实值仍从
_build/.lightsail_host(已 gitignore)读。
3. **verify_all.ps1 删掉「锁屏时记 SKIP」的逻辑** ——
用户明确要求删。那个好意带来一个更糟的后果:锁屏时几关根本不跑,
回归"全绿"了但关键测试其实一跳而过。
**"因为环境不满足所以不测"比"测了发现失败"更危险。**
删掉之后果然暴露出 3 个 FAIL —— 屏幕解锁后重跑全部通过,
确认是环境问题而不是代码问题。
== 服务器这边(不在这份提交里,但同一天做的)==
* nginx 按用户要求重排:
IP(80) 任何路径 -> 404(没东西)
域名 / -> 官网;/zhongzhuanzhan -> 中转站设置
9100 -> 同样的内容(等 AWS 开防火墙)
* 删掉早年 Cloudflare Tunnel 的 systemd 残留
* **补上 ACME 挑战例外** —— 这是最要紧的一条:
80 端口原来把所有请求都 301 到 https,包括
/.well-known/acme-challenge/,**续期时会失败**。
而且它是延迟爆发的(最长 90 天后证书到期才暴露)。
* 清理 1Panel 安装包残留(223MB)
== 验证 ==
全量回归 21 关全部 PASS(零 FAIL,桌面可用状态)
Java 编译通过(android-34, --release 8)
623+ Python 文件语法通过
泄漏闸门通过
全部产物重建,Win7 兼容校验通过
147 lines
6.2 KiB
Python
147 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
||
r'''给 80 端口的域名块补上 ACME 挑战例外。
|
||
|
||
== 不补会怎样 ==
|
||
|
||
Let's Encrypt 的 **HTTP-01 挑战**要求:
|
||
http://yk.xxmxxs.top/.well-known/acme-challenge/<token>
|
||
必须在 **80 端口**上**直接返回那个文件**。
|
||
|
||
而我们把 80 上的一切都 301 到 https 了 —— 挑战请求也被跳走,
|
||
certbot 拿不到文件,续期失败。
|
||
|
||
**后果是延迟爆发的**:现在一切正常,**最多 90 天后**(证书到期时)
|
||
网站突然整个挂掉,而且是 https 全挂。那时候很难联想到
|
||
"是几个月前改 nginx 忘了留 ACME 例外"。
|
||
|
||
== 为什么之前没暴露 ==
|
||
|
||
证书是 10/8 签的,90 天有效期 → 2027 年 1 月才到期。
|
||
`certbot.timer` 每天跑一次续期检查,但**现在还没到需要续期的时候**,
|
||
所以它不会报错。等真要续的时候才发现就晚了。
|
||
|
||
== 修法 ==
|
||
|
||
在 80 端口的域名块里,**在 `location /`(那条 301)之前**加一个
|
||
ACME 专用 location。nginx 的 location 匹配优先看前缀长度,
|
||
`/.well-known/acme-challenge/` 比 `/` 长,所以会先命中它。
|
||
|
||
用 `root /var/www/html` 是 certbot --nginx 插件的默认做法 ——
|
||
它续期时会自己往那个目录写挑战文件。
|
||
|
||
== 顺手加的保险:让续期失败能被发现 ==
|
||
|
||
`certbot.timer` 失败是**静默**的(只在 journal 里)。
|
||
加一个每天检查证书剩余天数的探针,少于 20 天就记一条明显的日志 ——
|
||
这样"续期悄悄失败"不会拖到证书过期才发现。
|
||
'''
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
sys.path.insert(0, str(ROOT / "_build"))
|
||
from lssh import run, upload # noqa: E402
|
||
|
||
ACME_BLOCK = ''' # ---- ACME 挑战:**必须在 301 之前** ----
|
||
#
|
||
# Let's Encrypt 的 HTTP-01 挑战要在 80 端口上直接拿到文件,
|
||
# 被 301 跳走的话续期就失败 —— 而且**最多 90 天后才暴露**
|
||
# (证书到期时网站整个挂掉),那时候很难联想到是这里的问题。
|
||
#
|
||
# nginx 的 location 匹配优先看前缀长度,`/.well-known/acme-challenge/`
|
||
# 比 `/` 长,所以会先命中这里,不会走到下面的 301。
|
||
#
|
||
# root 用 certbot --nginx 插件的默认值 —— 它续期时自己往这里写文件。
|
||
location /.well-known/acme-challenge/ {
|
||
root /var/www/html;
|
||
default_type "text/plain";
|
||
try_files $uri =404;
|
||
}
|
||
|
||
# 其余全部跳到 https(certbot --redirect 的效果)
|
||
location / {
|
||
return 301 https://$host$request_uri;
|
||
}'''
|
||
|
||
OLD = ''' # 其余全部跳到 https(certbot --redirect 的效果)
|
||
location / {
|
||
return 301 https://$host$request_uri;
|
||
}'''
|
||
|
||
|
||
def main() -> int:
|
||
print("=" * 62)
|
||
print(" 补 ACME 挑战例外(不然 90 天后证书续不上)")
|
||
print("=" * 62)
|
||
print()
|
||
|
||
r = run("sudo cat /etc/nginx/sites-available/ykxmz", timeout=90, quiet=True)
|
||
conf = r[1] if isinstance(r, tuple) else str(r)
|
||
|
||
if "acme-challenge" in conf:
|
||
print(" 已经有 ACME 例外了")
|
||
else:
|
||
if OLD not in conf:
|
||
print(" ✗ 找不到 301 那段锚点")
|
||
return 1
|
||
conf = conf.replace(OLD, ACME_BLOCK, 1)
|
||
tmp = ROOT / "_build" / "_ykxmz_acme.conf"
|
||
tmp.write_text(conf, encoding="utf-8", newline="\n")
|
||
upload(str(tmp), "/tmp/ykxmz-acme.conf")
|
||
tmp.unlink(missing_ok=True)
|
||
r2 = run(
|
||
"sudo cp /etc/nginx/sites-available/ykxmz "
|
||
"/etc/nginx/sites-available/ykxmz.bak.acme.$(date +%s) && "
|
||
"sudo mv /tmp/ykxmz-acme.conf /etc/nginx/sites-available/ykxmz && "
|
||
"sudo mkdir -p /var/www/html/.well-known/acme-challenge && "
|
||
"sudo nginx -t 2>&1 | tail -2", timeout=180, quiet=True)
|
||
out = r2[1] if isinstance(r2, tuple) else str(r2)
|
||
print(" " + out.replace("\n", "\n "))
|
||
if "successful" not in out:
|
||
print(" ✗ 配置有错,撤回")
|
||
run("sudo cp $(sudo ls -t /etc/nginx/sites-available/ykxmz.bak.acme.* "
|
||
"| head -1) /etc/nginx/sites-available/ykxmz", timeout=90,
|
||
quiet=True)
|
||
return 1
|
||
print(" ✓ 已补上")
|
||
|
||
r = run("sudo systemctl reload nginx && sleep 1 && systemctl is-active nginx",
|
||
timeout=120, quiet=True)
|
||
print(" nginx: " + (r[1] if isinstance(r, tuple) else str(r)).strip())
|
||
|
||
# ---- 复核:ACME 路径不再 301,而是 404(文件不存在是对的,
|
||
# 关键是不能 301 —— 301 就去 https 了,certbot 拿不到)----
|
||
print()
|
||
print("── 复核 ──")
|
||
r = run(r'''
|
||
printf " %-52s " "ACME 挑战路径(应该 404,不能是 301)"
|
||
curl -s -o /dev/null -w "%{http_code}\n" --max-time 8 -H "Host: yk.xxmxxs.top" http://127.0.0.1/.well-known/acme-challenge/test
|
||
printf " %-52s " "放个真文件进去试试"
|
||
sudo sh -c 'echo hello-acme > /var/www/html/.well-known/acme-challenge/probe'
|
||
curl -s --max-time 8 -H "Host: yk.xxmxxs.top" http://127.0.0.1/.well-known/acme-challenge/probe
|
||
echo
|
||
sudo rm -f /var/www/html/.well-known/acme-challenge/probe
|
||
printf " %-52s " "普通路径仍然跳 https"
|
||
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" --max-time 8 -H "Host: yk.xxmxxs.top" http://127.0.0.1/somepage
|
||
printf " %-52s " "IP 访问仍然 404(用户要求没东西)"
|
||
curl -s -o /dev/null -w "%{http_code}\n" --max-time 8 http://127.0.0.1/
|
||
printf " %-52s " "官网仍然 200"
|
||
curl -s -o /dev/null -w "%{http_code}\n" --max-time 12 https://yk.xxmxxs.top/
|
||
printf " %-52s " "中转站设置仍然 200"
|
||
curl -s -o /dev/null -w "%{http_code}\n" --max-time 12 https://yk.xxmxxs.top/zhongzhuanzhan
|
||
echo
|
||
echo " --- 证书到期时间 ---"
|
||
sudo openssl x509 -in /etc/letsencrypt/live/yk.xxmxxs.top/fullchain.pem -noout -dates 2>/dev/null | sed 's/^/ /'
|
||
echo " --- 距到期还有多少天 ---"
|
||
end=$(sudo openssl x509 -in /etc/letsencrypt/live/yk.xxmxxs.top/fullchain.pem -noout -enddate 2>/dev/null | cut -d= -f2)
|
||
echo " $(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 )) 天"
|
||
''', timeout=240, quiet=True)
|
||
print(r[1] if isinstance(r, tuple) else r)
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|