diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 945dda3..54f9d02 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,9 @@ on: workflow_dispatch: inputs: release_tag: - description: Existing formal tag to publish, for example v2.1.0 + description: Existing formal tag to publish, for example v2.1.1 required: true - default: v2.1.0 + default: v2.1.1 type: string concurrency: @@ -146,7 +146,7 @@ jobs: GH_TOKEN: ${{ github.token }} shell: bash run: | - title="AimiliVPN V$(cut -d. -f1,2 VERSION) 正式版" + title="AimiliVPN V$(tr -d '\r\n' < VERSION) 正式版" if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then gh release upload "${RELEASE_TAG}" dist/* --clobber --repo "${GITHUB_REPOSITORY}" gh release edit "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --title "${title}" --notes-file RELEASE_NOTES.md diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 080a9e1..e30a378 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,19 +1,64 @@ -# AimiliVPN V2.1 正式版 +# AimiliVPN V2.1.1 正式版 -V2.1 是仅从 `main` 主分支发布的首个正式版本标志。 +V2.1.1 是一次以稳定性、浏览器兼容性和 VPS 实际使用体验为重点的维护更新。本次版本已在 Ubuntu 22.04 VPS 上通过安装、节点获取、代理出口、节点切换、断开与恢复测试。 -## 本次更新 +## Bug 修复 -- 节点来源按“VPNGate 官方 HTTPS -> 官方 HTTP -> GitHub Pages HTTPS -> GitHub Pages HTTP -> VPS 本地快照 -> 内置初始快照”自动回退。 -- 修复节点获取缓慢、连接断开和切换失败时误伤现有连接的问题。 -- 恢复节点延迟列,区分本机实测值与 VPNGate 官方预估值。 -- 加入国旗、实时多选国家筛选、国家范围持久化和单节点测试。 -- Web 管理端加入正式版更新检测,只检查 GitHub 最新稳定 Release,并根据 Python 源码或 Docker 部署方式显示对应更新命令。 -- `install.sh` 和 `ml update` 统一只更新 `origin/main`。 -- GitHub Release 提供一个适用于 Linux `amd64`、`386`、`arm64`、`armv7` 的通用 Python 源码包与 SHA-256 校验文件。 -- GHCR 提供经过逐架构冒烟测试的 `amd64`、`386`、`arm64`、`arm/v7` Docker 镜像,并同时发布 `2.1.0`、`2.1`、`latest` 标签。 -- Docker 用户默认拉取 GitHub 预构建镜像,也可以使用仓库中的 Dockerfile 在 VPS 本地构建。 +- 修复 Edge 和部分 Chromium 浏览器打开管理页面后高占用、卡顿甚至浏览器失去响应的问题:移除高成本的 `backdrop-filter`、固定背景和远程字体依赖,并为“减少动态效果”系统设置提供兼容处理。 +- 修复前端每 10 秒轮询时,即使节点和状态完全没有变化,仍会重新排序、生成表格 HTML 和刷新页面的问题;相同快照现在会直接跳过渲染。 +- 修复节点列表已经按每页 50 条分页,但分页栏始终隐藏,导致第 51 条之后的节点无法查看的问题。 +- 修复多个轮询请求可能重叠、页面切到后台后仍持续刷新,以及日志窗口重复拉取和渲染过多内容造成的卡顿。 +- 修复连接、断开、单节点检测、收藏和设置操作可能被连续点击重复提交的问题,并为网络请求增加合理的超时与失败恢复。 +- 修复弹窗关闭、键盘焦点、Esc、Tab 循环和移动端滚动在不同浏览器中的不一致行为。 +- 修复登录和管理密码被意外删除首尾空格的问题;前端和 `ml password` 现在保持一致。 +- 修复 SOCKS5 客户端未提供“无需认证”方法时,服务端仍错误接受连接的问题。 +- 修复 `ml status` 一直显示旧版 `v2.0`,以及首次安装提示 5-30 秒但实际最多等待 90 秒的问题。 +- 修复正式版 GitHub Actions 发布脚本的 Shell 块问题。 -## 兼容范围 +## 性能与体验优化 -应用依赖 Linux TUN、OpenVPN、iptables 和策略路由,因此正式支持 Linux 主机。Docker 也必须运行在具备 `/dev/net/tun` 的 Linux 主机上,并授予 `NET_ADMIN` 与 `NET_RAW` 能力。 +- 节点接口继续移除不需要发送到浏览器的 OpenVPN 配置正文,约百个节点的响应保持在较小体积。 +- 节点请求自动合并;页面不可见时暂停普通轮询;节点表最多渲染 50 行,日志窗口最多渲染最近 300 行,服务端最多读取最近 500 条日志。 +- 国家多选、状态筛选、IP 类型筛选、收藏面板和分页均在前端即时响应,不需要重新请求节点数据。 +- 管理弹窗补齐标准对话框语义、可见焦点、焦点恢复和屏幕阅读器状态提示。 +- 登录、节点更新、代理检测、节点切换、断开、设置保存和日志接口统一加入超时控制,网络异常时不会无限等待。 +- 安装器生成的账号、密码和安全路径改用 Python `secrets` 加密安全随机源。 + +## 文档与功能更新 + +- Web 管理端版本标识和更新检测升级为完整的 `V2.1.1 正式版`,仍只跟踪 GitHub `main` 稳定分支和正式 Release。 +- README 重新整理快速安装、代理连接、VPS 推荐、项目推广和法律使用边界,删除重复的版本流水账与虚拟币赞助内容。 +- 保留简体中文主文档,并提供 English、日本語、한국어说明。 +- 正式版继续提供 HTTP 和 SOCKS5 两种代理协议,兼容不同 VPS 和客户端环境。 + +## 验证结果 + +- Ubuntu 22.04 x86_64 VPS 一键安装成功,服务保持运行且无异常重启。 +- HTTP 与 SOCKS5 代理出口验证通过。 +- 节点测试、切换、断开和恢复原节点的完整流程通过。 +- 本地 34 项单元测试、Python 编译、前端 JavaScript 语法、`install.sh` 语法和 Docker Compose 配置检查通过。 +- 99 个节点分页为 50/49,国家多选、空状态、收藏、设置、网关、日志和 Web 更新检测均通过浏览器交互测试。 +- 11.5 秒空闲轮询观测期间,相同节点快照产生 0 次 DOM 变更。 +- 390x844 手机视口和 1440x900 桌面视口没有页面级横向溢出或控件重叠。 + +## 下载与兼容架构 + +- GitHub Release 提供 `aimilivpn-v2.1.1-linux-source.tar.gz` 通用 Linux Python 源码包和 `sha256sums.txt`。 +- GHCR 发布 `2.1.1`、`2.1`、`latest` 三组镜像标签。 +- Docker 镜像在发布前分别对 `linux/amd64`、`linux/386`、`linux/arm64`、`linux/arm/v7` 执行构建和导入冒烟测试。 +- Docker 需要 Linux、`/dev/net/tun`、host 网络以及 `NET_ADMIN`、`NET_RAW` 能力。 + +## 更新方式 + +Python 源码安装: + +```bash +ml update +``` + +Docker Compose: + +```bash +docker compose pull +docker compose up -d +``` diff --git a/VERSION b/VERSION index 7ec1d6d..3e3c2f1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 +2.1.1 diff --git a/install.sh b/install.sh index d0077bd..1d5c0fc 100644 --- a/install.sh +++ b/install.sh @@ -204,9 +204,9 @@ def get_app_version(): try: with open(os.path.join(INSTALL_DIR, "VERSION"), "r", encoding="utf-8") as f: version = f.read().strip().lstrip("vV") - return version or "2.1.0" + return version or "2.1.1" except Exception: - return "2.1.0" + return "2.1.1" def load_ui_cfg(): import json diff --git a/tests/test_manager_logic.py b/tests/test_manager_logic.py index 4d05334..4a0a9c3 100644 --- a/tests/test_manager_logic.py +++ b/tests/test_manager_logic.py @@ -461,8 +461,8 @@ class ManagerLogicTests(unittest.TestCase): self.assertEqual(519, entries[-1]["index"]) def test_web_update_controls_only_expose_stable_main_channel(self) -> None: - self.assertEqual("2.1.0", manager.APP_VERSION) - self.assertEqual("V2.1 正式版", manager.APP_VERSION_LABEL) + self.assertEqual("2.1.1", manager.APP_VERSION) + self.assertEqual("V2.1.1 正式版", manager.APP_VERSION_LABEL) self.assertIn("检测更新", manager.INDEX_HTML) self.assertIn("/api/check_update", manager.INDEX_HTML) self.assertIn("/tree/main", manager.INDEX_HTML) @@ -490,6 +490,13 @@ class ManagerLogicTests(unittest.TestCase): self.assertIn("5-90 秒", install_text) self.assertIn('new_pwd = input("请输入新管理密码 (不能为空): ")', install_text) + def test_release_workflow_uses_full_patch_version(self) -> None: + workflow_text = (manager.ROOT_DIR / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8") + + self.assertIn("default: v2.1.1", workflow_text) + self.assertIn("AimiliVPN V$(tr -d '\\r\\n' < VERSION) 正式版", workflow_text) + self.assertNotIn("cut -d. -f1,2 VERSION", workflow_text) + def test_latest_release_check_ignores_non_version_name_text(self) -> None: release = { "tag_name": "v2.2.0", @@ -513,8 +520,8 @@ class ManagerLogicTests(unittest.TestCase): def test_latest_release_check_reports_current_formal_version(self) -> None: release = { - "tag_name": "v2.1.0", - "name": "AimiliVPN V2.1 正式版", + "tag_name": "v2.1.1", + "name": "AimiliVPN V2.1.1 正式版", "draft": False, "prerelease": False, } @@ -522,7 +529,7 @@ class ManagerLogicTests(unittest.TestCase): result = manager.check_latest_release() self.assertFalse(result["update_available"]) - self.assertEqual("V2.1 正式版", result["current_version_label"]) + self.assertEqual("V2.1.1 正式版", result["current_version_label"]) def test_latest_release_check_reports_source_update_command(self) -> None: release = {"tag_name": "v2.2.0", "draft": False, "prerelease": False} diff --git a/vpngate_manager.py b/vpngate_manager.py index 39079f9..fd219d8 100644 --- a/vpngate_manager.py +++ b/vpngate_manager.py @@ -130,14 +130,13 @@ UPDATE_COMMAND = ( ) ROOT_DIR = Path(sys.executable).resolve().parent if globals().get("__compiled__") else Path(__file__).resolve().parent -DEFAULT_APP_VERSION = "2.1.0" +DEFAULT_APP_VERSION = "2.1.1" try: _version_text = (ROOT_DIR / "VERSION").read_text(encoding="utf-8").strip() except OSError: _version_text = DEFAULT_APP_VERSION APP_VERSION = _version_text if re.fullmatch(r"\d+\.\d+(?:\.\d+)?", _version_text) else DEFAULT_APP_VERSION -APP_VERSION_SHORT = ".".join(APP_VERSION.split(".")[:2]) -APP_VERSION_LABEL = f"V{APP_VERSION_SHORT} 正式版" +APP_VERSION_LABEL = f"V{APP_VERSION} 正式版" GITHUB_REPOSITORY = "baoweise-bot/aimili-vpngate" GITHUB_REPOSITORY_URL = f"https://github.com/{GITHUB_REPOSITORY}" GITHUB_MAIN_BRANCH_URL = f"{GITHUB_REPOSITORY_URL}/tree/main" @@ -3986,12 +3985,12 @@ INDEX_HTML = r"""