From a7533aacda13385636e010003e11177062bc3743 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Fri, 19 Jun 2026 23:42:33 +0800 Subject: [PATCH] fix: fall back to stale WebUI when repair fails --- astrbot/dashboard/server.py | 13 +++++++++++-- main.py | 8 ++++++++ tests/test_dashboard.py | 29 +++++++++++++++++++++++++++- tests/test_main.py | 38 +++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 41c1693d9..cece72710 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -203,10 +203,19 @@ class AstrBotDashboard: ) or is_dashboard_dist_compatible(bundled_dist, VERSION): self.data_path = str(bundled_dist) logger.info("Using bundled dashboard dist: %s", self.data_path) + elif ( + os.path.exists(user_dist) and (Path(user_dist) / "index.html").is_file() + ): + logger.warning( + "Using existing data/dist as a fallback even though WebUI version mismatches core: %s, expected v%s. " + "Some dashboard features may not work until the matching WebUI is available.", + user_version, + VERSION, + ) + self.data_path = os.path.abspath(user_dist) elif os.path.exists(user_dist): logger.warning( - "Ignoring data/dist because WebUI version mismatches core: %s, expected v%s.", - user_version, + "Ignoring data/dist because WebUI files are incomplete for core v%s.", VERSION, ) self.data_path = None diff --git a/main.py b/main.py index e38af1a35..01d3167f5 100644 --- a/main.py +++ b/main.py @@ -160,6 +160,14 @@ async def check_dashboard_files(webui_dir: str | None = None): ) except Exception as e: logger.critical(f"下载管理面板文件失败: {e}。") + if (data_dist_path / "index.html").is_file(): + logger.warning( + "Falling back to existing data/dist WebUI %s even though core expects v%s. " + "Some dashboard features may not work until the matching WebUI is available.", + v or "unknown", + VERSION, + ) + return str(data_dist_path) return None logger.info("管理面板下载完成。") return str(data_dist_path) diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index e30252f60..cd3a9b78c 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -294,7 +294,34 @@ def test_dashboard_uses_bundled_dist_when_data_dist_is_stale( assert server.data_path == str(bundled_dist) -def test_dashboard_ignores_mismatched_data_dist_without_bundled( +def test_dashboard_falls_back_to_mismatched_data_dist_without_bundled( + core_lifecycle_td: AstrBotCoreLifecycle, + monkeypatch, + tmp_path, +): + data_dir = tmp_path / "data" + user_dist = data_dir / "dist" + bundled_dist = tmp_path / "bundled-dist" + (user_dist / "assets").mkdir(parents=True) + (user_dist / "assets" / "version").write_text("v0.0.1", encoding="utf-8") + (user_dist / "index.html").write_text("stale", encoding="utf-8") + + monkeypatch.setattr( + "astrbot.dashboard.server.get_astrbot_data_path", + lambda: str(data_dir), + ) + monkeypatch.setattr( + "astrbot.dashboard.server.get_bundled_dashboard_dist_path", + lambda: bundled_dist, + ) + + shutdown_event = asyncio.Event() + server = AstrBotDashboard(core_lifecycle_td, core_lifecycle_td.db, shutdown_event) + + assert server.data_path == str(user_dist) + + +def test_dashboard_ignores_incomplete_mismatched_data_dist_without_bundled( core_lifecycle_td: AstrBotCoreLifecycle, monkeypatch, tmp_path, diff --git a/tests/test_main.py b/tests/test_main.py index 2132bd480..808c03a35 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -246,6 +246,44 @@ async def test_check_dashboard_files_exists_but_version_mismatch_downloads(tmp_p assert "WebUI version mismatch" in call_args[0] +@pytest.mark.asyncio +async def test_check_dashboard_files_falls_back_to_stale_dist_when_download_fails( + tmp_path, +): + """Tests stale dashboard fallback when the matching WebUI cannot be downloaded.""" + from main import VERSION + + data_dir = tmp_path / "data" + data_dist = data_dir / "dist" + bundled_dist = tmp_path / "bundled-dist" + (data_dist / "assets").mkdir(parents=True) + (data_dist / "assets" / "version").write_text("v0.0.1", encoding="utf-8") + (data_dist / "index.html").write_text("stale", encoding="utf-8") + + with mock.patch("main.get_astrbot_data_path", return_value=str(data_dir)): + with mock.patch( + "main.get_bundled_dashboard_dist_path", + return_value=bundled_dist, + ): + with mock.patch( + "main.download_dashboard", + side_effect=RuntimeError("missing dashboard asset"), + ) as mock_download: + with mock.patch("main.logger.warning") as mock_logger_warning: + result = await check_dashboard_files() + + assert result == str(data_dist) + mock_download.assert_called_once_with( + version=f"v{VERSION}", + latest=False, + allow_insecure_ssl_fallback=False, + ) + assert any( + "Falling back to existing data/dist WebUI" in call.args[0] + for call in mock_logger_warning.call_args_list + ) + + @pytest.mark.asyncio async def test_check_dashboard_files_downloads_when_matching_dist_is_incomplete( tmp_path,