# -*- coding: utf-8 -*- r'''重写 Halo 的 nginx 配置:**只写 80 端口那部分**。 == 上一版怎么坏的 == 我想"临时把 443 块注释掉",就用脚本给 443 那几行前面加 `#`。 但那几行里包含 **server 块的结束 `}`** —— 一注释就变成 "unexpected end of file, expecting }",配置直接不合法。 **教训**:注释掉一个配置块要**整块删掉**,不能逐行加 `#`。 逐行注释会破坏"开括号/闭括号配对",而这种错误 nginx 只会报 "文件意外结束",不会告诉你少的是哪个括号。 == 这一版怎么组织 == 443 块**根本不写** —— 等 DNS 指过来、certbot 签完证书之后, 再作为独立一步加上去。这样每一步的配置都是**自洽且能通过检查的**, 不会出现"半截配置"。 == 顺带 == ykxmz 那边原来占着 80 的 default_server,上一版已经把它让出来了 (`listen 80 default_server` -> `listen 80`)。要看它现在是否正常。 ''' 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 个人博客(**只有 80 端口**,443 等证书) # # 入口: # http://<服务器IP>/ 博客(IP 直接访问) # http://<服务器IP>/console 管理后台 # http://xxmxxs.top/ 301 到 https(证书好了之后) # # 后端:容器 halo,监听 0.0.0.0:3001,Halo 原生控制台路径是 /console # # ⚠ 443 那段**故意不在这里** —— DNS 还没指过来,签不到证书。 # 没证书的 `listen 443 ssl` 会让 nginx -t 直接失败 # ("no ssl_certificate is defined")。 # 等 DNS 生效后跑 certbot,它会自己把 443 和证书加进来。 # ====================================================================== # 80 端口默认站点:用 IP 访问时直接出博客 # ====================================================================== # # nginx 规则:Host 匹配不上任何 server_name 时,用该端口的 default_server。 # 所以这里设成 default_server -> `http://<服务器IP>/` 就是博客。 # # ⚠ 不做 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; } } # ====================================================================== # 80 端口 + 域名:跳 https,并留出 ACME 挑战的位置 # ====================================================================== 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; client_max_body_size 100m; # ⚠ 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; } } """ def main() -> int: print("=" * 62) print(" 重写 Halo 的 nginx 配置(只 80 端口)") 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''' echo "==== 1. 先把坏掉的备份都清掉,重写 ====" sudo rm -f /etc/nginx/sites-available/halo.bak.* 2>/dev/null sudo mv -f /tmp/halo-nginx.conf /etc/nginx/sites-available/halo sudo ln -sf /etc/nginx/sites-available/halo /etc/nginx/sites-enabled/halo echo " 写好了" echo echo "==== 2. ykxmz 那边现在长什么样(确认 default_server 让出来了)====" sudo grep -n "default_server" /etc/nginx/sites-available/ykxmz 2>/dev/null | sed 's/^/ /' || echo " 没有 default_server ✓" echo echo "==== 3. 检查配置 ====" 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(" ✗ 配置有错,恢复 ykxmz 的 default_server 并停用 halo 站点") run("sudo rm -f /etc/nginx/sites-enabled/halo", timeout=90, quiet=True) return 1 out = run(r''' echo "==== 4. 重载 ====" sudo systemctl reload nginx && sleep 1 echo " nginx: $(systemctl is-active nginx)" echo echo "==== 5. 复核(对用户要求逐条)====" printf " %-46s " "http://127.0.0.1/ (IP -> 博客)" curl -s -o /dev/null -w "%{http_code} %{size_download} 字节\n" --max-time 10 http://127.0.0.1/ printf " %-46s " "http://127.0.0.1/console (控制台)" curl -s -o /dev/null -w "%{http_code} %{size_download} 字节\n" --max-time 10 http://127.0.0.1/console printf " %-46s " "http://127.0.0.1:3001/ (直连 Halo)" curl -s -o /dev/null -w "%{http_code}\n" --max-time 10 http://127.0.0.1:3001/ printf " %-46s " "https://yk.xxmxxs.top/ (中转站)" curl -s -o /dev/null -w "%{http_code} %{size_download} 字节\n" --max-time 12 https://yk.xxmxxs.top/ printf " %-46s " "https://yk.xxmxxs.top/v1/health" curl -s -o /dev/null -w "%{http_code}\n" --max-time 12 https://yk.xxmxxs.top/v1/health printf " %-46s " "http://127.0.0.1:9100/ (官网直连通道)" curl -s -o /dev/null -w "%{http_code}\n" --max-time 10 http://127.0.0.1:9100/ echo echo "==== 6. 监听端口 ====" sudo ss -tlnp 2>/dev/null | grep -E ':(80|443|9100|3001) ' | awk '{print " "$4}' | sort -u ''', timeout=300, quiet=True) print(out[1] if isinstance(out, tuple) else out) return 0 if __name__ == "__main__": sys.exit(main())