Skip to content

Commit cd21726

Browse files
committed
fix: guard desktop-managed core restart
1 parent dceacd5 commit cd21726

5 files changed

Lines changed: 92 additions & 0 deletions

File tree

astrbot/core/desktop_runtime.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import os
2+
3+
DESKTOP_MANAGED_RESTART_MESSAGE = (
4+
"AstrBot Desktop manages this backend process. Please restart or update from "
5+
"the desktop app instead of the core WebUI."
6+
)
7+
8+
9+
def is_desktop_managed_backend() -> bool:
10+
return os.environ.get("ASTRBOT_DESKTOP_MANAGED") == "1"

astrbot/core/updator.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
from astrbot.core import logger
88
from astrbot.core.config.default import VERSION
9+
from astrbot.core.desktop_runtime import (
10+
DESKTOP_MANAGED_RESTART_MESSAGE,
11+
is_desktop_managed_backend,
12+
)
913
from astrbot.core.utils.astrbot_path import get_astrbot_path
1014

1115
from .zip_updator import ReleaseInfo, RepoZipUpdator
@@ -115,6 +119,9 @@ def _reboot(self, delay: int = 3) -> None:
115119
在指定的延迟后,终止所有子进程并重新启动程序
116120
这里只能使用 os.exec* 来重启程序
117121
"""
122+
if is_desktop_managed_backend():
123+
raise RuntimeError(DESKTOP_MANAGED_RESTART_MESSAGE)
124+
118125
time.sleep(delay)
119126
self.terminate_child_processes()
120127
executable = sys.executable

astrbot/dashboard/routes/stat.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
from astrbot.core.db import BaseDatabase
2121
from astrbot.core.db.migration.helper import check_migration_needed_v4
2222
from astrbot.core.db.po import ProviderStat
23+
from astrbot.core.desktop_runtime import (
24+
DESKTOP_MANAGED_RESTART_MESSAGE,
25+
is_desktop_managed_backend,
26+
)
2327
from astrbot.core.utils.astrbot_path import get_astrbot_path
2428
from astrbot.core.utils.auth_password import (
2529
is_default_dashboard_password,
@@ -70,6 +74,8 @@ async def restart_core(self):
7074
.error("You are not permitted to do this operation in demo mode")
7175
.__dict__
7276
)
77+
if is_desktop_managed_backend():
78+
return Response().error(DESKTOP_MANAGED_RESTART_MESSAGE).__dict__
7379

7480
await self.core_lifecycle.restart()
7581
return Response().ok().__dict__

astrbot/dashboard/routes/update.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
from astrbot.core.config.default import VERSION
88
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
99
from astrbot.core.db.migration.helper import check_migration_needed_v4, do_migration_v4
10+
from astrbot.core.desktop_runtime import (
11+
DESKTOP_MANAGED_RESTART_MESSAGE,
12+
is_desktop_managed_backend,
13+
)
1014
from astrbot.core.updator import AstrBotUpdator
1115
from astrbot.core.utils.io import download_dashboard, get_dashboard_version
1216

@@ -184,6 +188,9 @@ async def get_releases(self):
184188
return Response().error(e.__str__()).__dict__
185189

186190
async def update_project(self):
191+
if is_desktop_managed_backend():
192+
return Response().error(DESKTOP_MANAGED_RESTART_MESSAGE).__dict__
193+
187194
data = await request.json
188195
version = data.get("version", "")
189196
reboot = data.get("reboot", True)

tests/test_dashboard.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,35 @@ async def mock_get_dashboard_version(*args, **kwargs):
17971797
assert data["data"]["has_new_version"] is False
17981798

17991799

1800+
@pytest.mark.asyncio
1801+
async def test_restart_core_rejects_desktop_managed_backend(
1802+
app: Quart,
1803+
authenticated_header: dict,
1804+
core_lifecycle_td: AstrBotCoreLifecycle,
1805+
monkeypatch,
1806+
):
1807+
test_client = app.test_client()
1808+
restart_called = False
1809+
1810+
async def mock_restart():
1811+
nonlocal restart_called
1812+
restart_called = True
1813+
1814+
monkeypatch.setenv("ASTRBOT_DESKTOP_MANAGED", "1")
1815+
monkeypatch.setattr(core_lifecycle_td, "restart", mock_restart)
1816+
1817+
response = await test_client.post(
1818+
"/api/stat/restart-core",
1819+
headers=authenticated_header,
1820+
)
1821+
1822+
assert response.status_code == 200
1823+
data = await response.get_json()
1824+
assert data["status"] == "error"
1825+
assert "desktop" in data["message"].lower()
1826+
assert restart_called is False
1827+
1828+
18001829
@pytest.mark.asyncio
18011830
async def test_do_update(
18021831
app: Quart,
@@ -1863,6 +1892,39 @@ async def mock_pip_install(*args, **kwargs):
18631892
assert progress_data["data"]["overall_percent"] == 100
18641893

18651894

1895+
@pytest.mark.asyncio
1896+
async def test_do_update_rejects_desktop_managed_backend(
1897+
app: Quart,
1898+
authenticated_header: dict,
1899+
core_lifecycle_td: AstrBotCoreLifecycle,
1900+
monkeypatch,
1901+
):
1902+
test_client = app.test_client()
1903+
calls = []
1904+
1905+
async def mock_update(*args, **kwargs):
1906+
calls.append("core")
1907+
1908+
async def mock_restart():
1909+
calls.append("restart")
1910+
1911+
monkeypatch.setenv("ASTRBOT_DESKTOP_MANAGED", "1")
1912+
monkeypatch.setattr(core_lifecycle_td.astrbot_updator, "update", mock_update)
1913+
monkeypatch.setattr(core_lifecycle_td, "restart", mock_restart)
1914+
1915+
response = await test_client.post(
1916+
"/api/update/do",
1917+
headers=authenticated_header,
1918+
json={"version": "v3.4.0", "progress_id": "desktop-progress"},
1919+
)
1920+
1921+
assert response.status_code == 200
1922+
data = await response.get_json()
1923+
assert data["status"] == "error"
1924+
assert "desktop" in data["message"].lower()
1925+
assert calls == []
1926+
1927+
18661928
@pytest.mark.asyncio
18671929
async def test_install_pip_package_returns_pip_install_error_message(
18681930
app: Quart,

0 commit comments

Comments
 (0)