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
+122
View File
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
r'''装 Halo 之前先把前提条件查清楚。
== 用户要求 ==
* Halo 个人博客,跑在 `<服务器IP>:3001`
* 加 **1G swap**(机器只有 1936MB 内存,本来没 swap)
* `xxmxxs.top` 和 `www.xxmxxs.top` 都能访问
* `xxmxxs.top/houtai` 是管理后台
* 其他博客配置用户自己弄
== 装之前必须确认的 ==
1. **`xxmxxs.top` 的 DNS 指到服务器了吗?**
之前发现 `xxmxxs.top` 上绑着一个 **Cloudflare Worker**
(用户说要自己去掉)。如果 Worker 还在,或者 A 记录没指过来,
nginx 配得再好也没用 —— 外面根本到不了服务器。
2. **内存和 swap 现状**
1936MB 总内存,现在用了 ~584MB。Halo 是 **Java 应用**,
默认堆可能吃 512MB+。**没有 swap 的话很容易被 OOM 杀掉**,
表现是"博客隔一阵就自己挂了"。
所以用户要的 1G swap 是**必须的**,不是可选项。
3. **Docker 能用吗 / 1Panel 的 MySQL**
机器上有 1Panel + Docker,还有个 `mysql:8.4.11` 容器在跑。
Halo 可以用它,也可以用**内置 H2 数据库**(更省内存、更简单)。
个人博客 H2 完全够用 —— 少一个组件少一处故障点。
4. **Java 有没有**
Halo 2.x 要 **Java 17+**。
'''
from __future__ import annotations
import socket
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "_build"))
from lssh import run # noqa: E402
HOSTFILE = ROOT / "_build" / ".lightsail_host"
IP = HOSTFILE.read_text(encoding="utf-8").strip()
def main() -> int:
print("=" * 62)
print(" 装 Halo 前的前提检查")
print("=" * 62)
print()
# ---------------- DNS ----------------
print("── 1. 域名现在解析到哪 ──")
for name in ("xxmxxs.top", "www.xxmxxs.top"):
try:
ips = sorted({sa[4][0] for sa in
socket.getaddrinfo(name, 443, proto=socket.IPPROTO_TCP)})
mark = ""
if IP in ips:
mark = " ← 就是你的服务器 ✓"
else:
mark = " ← ⚠ 不是你的服务器(要改 DNS)"
print(" %-20s %s%s" % (name, ", ".join(ips) or "(无解析)", mark))
except Exception as exc: # noqa: BLE001
print(" %-20s 解析失败: %s" % (name, type(exc).__name__))
print()
print(" ── 直接连服务器 + 带 Host 头,看现在返回什么 ──")
import ssl
import urllib.request
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
for host in ("xxmxxs.top", "www.xxmxxs.top"):
try:
req = urllib.request.Request("https://" + IP + "/",
headers={"Host": host,
"User-Agent": "probe"})
with urllib.request.urlopen(req, timeout=12, context=ctx) as r:
body = r.read(400)
print(" Host:%-20s -> HTTP %s %d 字节"
% (host, r.status, len(body)))
except Exception as exc: # noqa: BLE001
print(" Host:%-20s -> %s" % (host, type(exc).__name__))
# ---------------- 服务器现状 ----------------
print()
print("── 2. 服务器现状 ──")
out = run(r'''
echo " --- 内存 / swap ---"
free -m | sed 's/^/ /'
echo " --- 磁盘 ---"
df -h / | tail -1 | sed 's/^/ /'
echo " --- Java ---"
if command -v java >/dev/null 2>&1; then java -version 2>&1 | head -2 | sed 's/^/ /'; else echo " 没装 Java"; fi
echo " --- Docker ---"
sudo docker --version 2>/dev/null | sed 's/^/ /' || echo " docker 不可用"
echo " --- 3001 端口占了吗 ---"
sudo ss -tlnp 2>/dev/null | grep 3001 | sed 's/^/ /' || echo " 3001 空着 ✓"
echo " --- 现有 systemd 服务 ---"
systemctl list-units --type=service --state=running --no-pager --plain 2>/dev/null \
| awk '{print " "$1}' | head -14
echo " --- 1Panel 的 mysql 容器 ---"
sudo docker ps --format ' {{.Names}} {{.Image}} {{.Ports}}' 2>/dev/null
''', timeout=240, quiet=True)
print(out[1] if isinstance(out, tuple) else out)
print()
print("── 3. 结论 / 下一步 ──")
print(" * swap 现在就加(1G)—— 不加 Halo 容易被 OOM 杀掉")
print(" * Halo 用 Docker 装(机器上已经有 Docker),数据库用内置 H2")
print(" —— 个人博客够用,而且比再挂一个 MySQL 省内存")
print(" * nginx 加 xxmxxs.top + www 两个 server_name,反代到 127.0.0.1:3001")
print(" * 控制台路径设成 /houtai(Halo 2.x 支持 HALO_CONSOLE_PATH)")
print(" * ⚠ 域名 DNS 要指过来(还有那个 Cloudflare Worker 要删)")
print(" * ⚠ 3001 要在 AWS Lightsail 防火墙开")
return 0
if __name__ == "__main__":
sys.exit(main())