2.3.0:手表界面精简 + 播放器 + 鼠标重做

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

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

中转站
- known_devices 从 ID 集合改成「设备ID -> 配对码」映射
- 修复:管理员改码后关机超过 6 小时,改码会被超时清理冲掉
- 老格式 state 文件平滑升级
This commit is contained in:
xmz7891
2026-10-18 11:39:14 +08:00
parent 1d368979aa
commit 2061f9525f
50 changed files with 13812 additions and 584 deletions
+172
View File
@@ -0,0 +1,172 @@
# -*- 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())