Files
ykxmz/_build/halo_nginx_fix.py
xmz7891 2061f9525f 2.3.0:手表界面精简 + 播放器 + 鼠标重做
手表端
- 功能页从 10 个杂项精简到 7 个(屏幕/鼠标/媒体/文件/电脑/语音/电源)
- 删掉:游戏封锁、待办、掷骰子、抽奖、壁纸、显示器开关、定时关机、
  剪贴板历史、性能/磁盘/电量、文件夹浏览、教室限制等
- 改成显式列按钮,不再把电脑端所有功能组无脑铺到表盘
- 远程鼠标控制重做:滑动区 + 左键/滚轮/右键三按钮
  滚轮点按=中键、上下滑=滚动
- 新增视频/音乐播放器(手表本地播放):进度拖动、左右滑快进退10秒、
  点画面播放暂停、上一/下一、播完自动下一首

电脑端
- 新增 kill_all(结束所有进程),白名单保护系统关键进程和自身
- 新增 media_list / media_pull(播放器数据源)
  安全边界:只允许媒体目录,用 relative_to 判断,防同前缀兄弟目录绕过

中转站
- known_devices 从 ID 集合改成「设备ID -> 配对码」映射
- 修复:管理员改码后关机超过 6 小时,改码会被超时清理冲掉
- 老格式 state 文件平滑升级
2026-10-18 11:39:14 +08:00

236 lines
8.8 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'''重写 halo 的 nginx 配置:修掉 301 循环。
== 怎么坏的 ==
我上一版**故意没写 443 块**(没证书时 `nginx -t` 会失败),
打算"等证书好了让 certbot 自己加"。
结果 `certbot --nginx` 找不到一个"纯粹的 443 块",
就把 `listen 443 ssl` 和证书那几行**塞进了那个 80 端口的块里**。
而那个块的 `location /` 内容是:
return 301 https://$host$request_uri;
于是 https 的请求进来也走这条 —— **301 到自己,无限循环**。
浏览器会报 "重定向次数过多"。
**certbot 还另外新建了一个纯 80 的块**(带两条 `if ($host = ...) return 301`),
跟原来的块功能重复。
== 教训 ==
**不要让 certbot 去改一个"结构不完整"的配置。**
`--nginx` 插件是**按 server 块猜**的:它找 `server_name` 匹配的块,
把 ssl 指令加进去。如果那个块本来是 80 的、还带 301,
它就会造出一个"443 + 301"的自环。
正确做法:**自己把 80 / 443 两块都写清楚**(443 先注释掉但不留半截),
证书有了之后**只把证书路径填进去**,而不是让 certbot 重排结构。
== 这一版的结构 ==
server A: 80 default_server -> proxy 到 Halo(IP 访问用)
server B: 80 + 域名 -> ACME 例外 + 301 到 https
server C: 443 + 域名 -> proxy 到 Halo
三个块各司其职,没有重叠,也没有"同一块里既有 301 又有 ssl"。
'''
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
CONF = """# xxmxxs.top —— Halo 个人博客
#
# 入口:
# http://<服务器IP>/ 博客(IP,HTTP)
# http://<服务器IP>/console 管理后台(IP,HTTP)
# https://xxmxxs.top/ 博客(HTTPS)
# https://xxmxxs.top/console 管理后台(HTTPS)
# https://www.xxmxxs.top/ 同上
# http://<服务器IP>:3001/ 直连 Halo(用户要求)
#
# 后端:容器 halo,监听 0.0.0.0:3001
# 控制台:Halo 原生 /console(2.26.1 没有可配置的路径前缀)
# ======================================================================
# A. 80 端口默认站点 —— 用 IP 访问时直接出博客
# ======================================================================
#
# nginx 规则:Host 匹配不上任何 server_name 时用该端口的 default_server。
#
# ⚠ 这里**不做 https 跳转**:IP 没证书,跳过去浏览器报警告,
# 用户会以为网站坏了。
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
access_log /var/log/nginx/halo-access.log;
error_log /var/log/nginx/halo-error.log;
server_tokens off;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 300s;
}
}
# ======================================================================
# B. 80 端口 + 域名 —— ACME 挑战 + 跳 https
# ======================================================================
#
# ⚠ 这个块**只监听 80**,不含任何 ssl 指令。
# 上一版 certbot 把 443 塞进来,造出了"443 上也 301"的自环。
server {
listen 80;
listen [::]:80;
server_name xxmxxs.top www.xxmxxs.top;
access_log /var/log/nginx/halo-access.log;
error_log /var/log/nginx/halo-error.log;
server_tokens off;
# ACME 挑战必须在 301 之前 ——
# certbot 的 HTTP-01 要在 80 端口直接拿到文件。
# 被 301 跳走就续期失败,而**最多 90 天后才暴露**(证书到期)。
location /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
# ======================================================================
# C. 443 + 域名 —— 博客(HTTPS)
# ======================================================================
#
# ⚠ `listen ... ssl` 和证书那几行是 certbot 需要的形式,
# 续期时它会检查。**别删**,也别把 80 的 301 混进来。
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name xxmxxs.top www.xxmxxs.top;
access_log /var/log/nginx/halo-access.log;
error_log /var/log/nginx/halo-error.log;
server_tokens off;
client_max_body_size 100m;
ssl_certificate /etc/letsencrypt/live/xxmxxs.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xxmxxs.top/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 事件总线(SSE)要用,而且不能缓冲
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_connect_timeout 30s;
}
}
"""
def main() -> int:
print("=" * 62)
print(" 重写配置,修掉 301 循环")
print("=" * 62)
print()
tmp = ROOT / "_build" / "_halo_nginx.conf"
tmp.write_text(CONF, encoding="utf-8", newline="\n")
upload(str(tmp), "/tmp/halo-nginx.conf")
tmp.unlink(missing_ok=True)
out = run(r'''
sudo cp /etc/nginx/sites-available/halo /etc/nginx/sites-available/halo.bak.$(date +%s)
sudo mv /tmp/halo-nginx.conf /etc/nginx/sites-available/halo
echo " 写好了"
echo
echo " ---- 检查 ----"
sudo nginx -t 2>&1 | tail -3 | sed 's/^/ /'
''', timeout=240, quiet=True)
o = out[1] if isinstance(out, tuple) else str(out)
print(o)
if "successful" not in o:
print(" ✗ 配置有错,撤回")
run("sudo cp $(sudo ls -t /etc/nginx/sites-available/halo.bak.* | head -1) "
"/etc/nginx/sites-available/halo && sudo nginx -t", timeout=120,
quiet=True)
return 1
out = run(r'''
sudo systemctl reload nginx && sleep 1
echo " nginx: $(systemctl is-active nginx)"
echo
echo "════════ 逐条复核 ════════"
printf " %-44s " "http://<服务器IP>/ (博客)"
curl -s -o /dev/null -w "HTTP %{http_code} %{size_download} 字节\n" --max-time 10 http://127.0.0.1/
printf " %-44s " "http://<服务器IP>/console (控制台)"
curl -s -o /dev/null -w "HTTP %{http_code}\n" --max-time 10 http://127.0.0.1/console
echo
printf " %-44s " "https://xxmxxs.top/ (博客)"
curl -sk -o /dev/null -w "HTTP %{http_code} %{size_download} 字节\n" --max-time 15 --resolve xxmxxs.top:443:127.0.0.1 https://xxmxxs.top/
printf " %-44s " "https://xxmxxs.top/console (控制台)"
curl -sk -o /dev/null -w "HTTP %{http_code}\n" --max-time 15 --resolve xxmxxs.top:443:127.0.0.1 https://xxmxxs.top/console
printf " %-44s " "https://www.xxmxxs.top/ (博客)"
curl -sk -o /dev/null -w "HTTP %{http_code}\n" --max-time 15 --resolve www.xxmxxs.top:443:127.0.0.1 https://www.xxmxxs.top/
printf " %-44s " "http://xxmxxs.top/ (应 301 到 https)"
curl -s -o /dev/null -w "HTTP %{http_code} -> %{redirect_url}\n" --max-time 15 -H "Host: xxmxxs.top" http://127.0.0.1/
echo
echo " 不能弄坏的:"
printf " %-44s " "https://yk.xxmxxs.top/ (中转站)"
curl -s -o /dev/null -w "HTTP %{http_code} %{size_download} 字节\n" --max-time 15 https://yk.xxmxxs.top/
printf " %-44s " "http://<服务器IP>:9100/ (官网)"
curl -s -o /dev/null -w "HTTP %{http_code}\n" --max-time 10 http://127.0.0.1:9100/
printf " %-44s " "http://<服务器IP>:3001/ (直连 Halo)"
curl -s -o /dev/null -w "HTTP %{http_code}\n" --max-time 10 http://127.0.0.1:3001/
echo
echo "════════ ACME 挑战路径(续期的命根子)════════"
sudo sh -c 'echo probe > /var/www/html/.well-known/acme-challenge/probe'
printf " %-44s " "http://xxmxxs.top/.well-known/... (应 200)"
curl -s -o /dev/null -w "HTTP %{http_code}\n" --max-time 8 -H "Host: xxmxxs.top" http://127.0.0.1/.well-known/acme-challenge/probe
sudo rm -f /var/www/html/.well-known/acme-challenge/probe
''', timeout=400, quiet=True)
print(out[1] if isinstance(out, tuple) else out)
return 0
if __name__ == "__main__":
sys.exit(main())