mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 10:10:43 +00:00
手表端 - 功能页从 10 个杂项精简到 7 个(屏幕/鼠标/媒体/文件/电脑/语音/电源) - 删掉:游戏封锁、待办、掷骰子、抽奖、壁纸、显示器开关、定时关机、 剪贴板历史、性能/磁盘/电量、文件夹浏览、教室限制等 - 改成显式列按钮,不再把电脑端所有功能组无脑铺到表盘 - 远程鼠标控制重做:滑动区 + 左键/滚轮/右键三按钮 滚轮点按=中键、上下滑=滚动 - 新增视频/音乐播放器(手表本地播放):进度拖动、左右滑快进退10秒、 点画面播放暂停、上一/下一、播完自动下一首 电脑端 - 新增 kill_all(结束所有进程),白名单保护系统关键进程和自身 - 新增 media_list / media_pull(播放器数据源) 安全边界:只允许媒体目录,用 relative_to 判断,防同前缀兄弟目录绕过 中转站 - known_devices 从 ID 集合改成「设备ID -> 配对码」映射 - 修复:管理员改码后关机超过 6 小时,改码会被超时清理冲掉 - 老格式 state 文件平滑升级
236 lines
8.8 KiB
Python
236 lines
8.8 KiB
Python
# -*- 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())
|