From eaea693a82f45ebcff41cfd6b058721e8f990438 Mon Sep 17 00:00:00 2001 From: RRRosmontis Date: Tue, 18 Aug 2026 08:14:03 +0000 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=E3=80=8Cfull=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- full/RRPP.spec | 79 ++++++++++++++++++++++++ full/upload_to_server.py | 129 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 full/RRPP.spec create mode 100644 full/upload_to_server.py diff --git a/full/RRPP.spec b/full/RRPP.spec new file mode 100644 index 0000000..3e31804 --- /dev/null +++ b/full/RRPP.spec @@ -0,0 +1,79 @@ +# -*- mode: python ; coding: utf-8 -*- + +from PyInstaller.utils.hooks import collect_all + +# 强制收集 PyQt5 全部模块及二进制文件 +qt_datas, qt_binaries, qt_hiddenimports = collect_all('PyQt5') + +# 自定义 manifest:要求管理员 + 声明 PerMonitorV2 DPI 感知, +# 避免程序在不同缩放比例的显示器之间移动时被 Windows 位图拉伸。 +_MANIFEST = r''' + + + Royirin Rosmontis Private Proxy + + + + + + + + + + + + + + + + PerMonitorV2, PerMonitor, system + true/pm + + + + + + + + +''' + +a = Analysis( + ['proxy.py'], + pathex=['.venv\\Lib\\site-packages'], + binaries=qt_binaries, + datas=[('jp_client.conf', '.'), ('wg', 'wg'), ('ico.ico', '.')] + qt_datas, + hiddenimports=qt_hiddenimports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='Royirin Rosmontis Private Proxy', + debug=False, + bootloader_ignore_signals=False, + strip=False, + # UPX 会压缩资源段(含图标),冷缓存下 Explorer 可能提取不到图标导致任务栏空白 + upx=False, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + uac_admin=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['ico.ico'], + manifest=_MANIFEST, +) diff --git a/full/upload_to_server.py b/full/upload_to_server.py new file mode 100644 index 0000000..75a5d08 --- /dev/null +++ b/full/upload_to_server.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +""" +RRPP 发布上传脚本 + +负责把 PyInstaller 打包好的 EXE 上传到服务器,并更新 latest_version.json, +使客户端启动时能检测并下载新版本。 + +## 服务器连接信息 + + 主机: 172.207.81.145 + 端口: 22 + 用户: RRRosmontis + 密码: ftA406u@all#2309 + +服务器可能启用了键盘交互认证,sshpass 等非交互式工具会报 Permission denied, +因此使用 .venv 中的 paramiko 进行 SSH/SFTP。 + +## 完整发布流程 + +1. 修改 proxy.py 与本脚本中的 VERSION 为当前年月日时分(例如 202607022100)。 +2. 重新打包 EXE: + .venv/Scripts/python.exe -m PyInstaller RRPP.spec --noconfirm + 或双击运行 build.bat,输出统一在 dist/ 目录。 +3. 运行本脚本上传并更新服务器版本信息: + .venv/Scripts/python.exe upload_to_server.py + +脚本执行步骤: + - SSH 连接到 172.207.81.145 + - 把 dist/Royirin Rosmontis Private Proxy.exe 上传到 /tmp/proxy.exe + - 用 sudo 创建 /var/www/html/release// 并移动 EXE + - 用 sudo 写入 /var/www/html/latest_version.json(https 下载链接) + - 回显服务器上的 latest_version.json 内容用于验证 + +## 注意事项 + +- 上传前请确保 dist/ 下的 EXE 已重新打包,且版本号与 VERSION 一致。 +- 若服务器连接信息或证书有变,请修改下方的 HOST / PORT / USER / PASSWORD。 +""" +import shlex +import sys +from pathlib import Path + +import paramiko + +# ── 服务器连接与版本配置 ─────────────────────────────── +HOST = "172.207.81.145" +PORT = 22 +USER = "RRRosmontis" +PASSWORD = "ftA406u@all#2309" +VERSION = "202607192300" + +# ── 本地 / 远程路径 ──────────────────────────────────── +LOCAL_EXE = Path("dist/Royirin Rosmontis Private Proxy.exe") +REMOTE_DIR = f"/var/www/html/release/{VERSION}" +REMOTE_EXE_NAME = "Royirin Rosmontis Private Proxy.exe" +REMOTE_JSON = "/var/www/html/latest_version.json" + + +def sudo_exec(client, command, password, timeout=30): + """通过 sudo -S 执行远程命令,并从 stdin 传入密码。""" + cmd = f"sudo -S bash -c {shlex.quote(command)}" + stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout) + stdin.write(password + "\n") + stdin.flush() + stdin.channel.shutdown_write() + + exit_status = stdout.channel.recv_exit_status() + out = stdout.read().decode("utf-8", errors="replace") + err = stderr.read().decode("utf-8", errors="replace") + return exit_status, out, err + + +def main(): + """主流程:上传 EXE 并更新服务器 latest_version.json。""" + if not LOCAL_EXE.exists(): + print(f"错误:本地文件不存在: {LOCAL_EXE}", file=sys.stderr) + sys.exit(1) + + print(f"连接 {HOST}:{PORT} ...") + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect(HOST, port=PORT, username=USER, password=PASSWORD) + print("SSH 连接成功") + + try: + # 1. 上传 EXE 到 /tmp + remote_tmp = "/tmp/proxy.exe" + print(f"上传 {LOCAL_EXE} -> {remote_tmp} ...") + sftp = client.open_sftp() + sftp.put(str(LOCAL_EXE), remote_tmp) + sftp.close() + print("上传完成") + + # 2. 创建发布目录并移动文件到最终位置 + print(f"创建目录 {REMOTE_DIR} 并移动 EXE ...") + cmd = f"mkdir -p {REMOTE_DIR} && mv {remote_tmp} '{REMOTE_DIR}/{REMOTE_EXE_NAME}'" + status, out, err = sudo_exec(client, cmd, PASSWORD) + if status != 0: + print(f"移动文件失败: {err}", file=sys.stderr) + sys.exit(1) + print("文件已移动到发布目录") + + # 3. 更新 latest_version.json,客户端据此检测新版本 + json_content = ( + '{"latest_version":"' + VERSION + '",' + '"download_url":"https://tunnel.rrrosmontis.icu/release/' + + VERSION + '/Royirin%20Rosmontis%20Private%20Proxy.exe"}' + ) + print(f"更新 {REMOTE_JSON} ...") + cmd = f"echo '{json_content}' | tee {REMOTE_JSON}" + status, out, err = sudo_exec(client, cmd, PASSWORD) + if status != 0: + print(f"更新 JSON 失败: {err}", file=sys.stderr) + sys.exit(1) + print("latest_version.json 已更新") + print(f"服务器返回: {out.strip()}") + + # 4. 验证远程 JSON 内容 + print("验证远程文件...") + stdin, stdout, stderr = client.exec_command(f"cat {REMOTE_JSON}") + print(stdout.read().decode("utf-8").strip()) + + finally: + client.close() + print("连接已关闭") + + +if __name__ == "__main__": + main()