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()