From f076799b81d227ac2e222d7e1dbd91a31a527254 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Fri, 20 Mar 2026 16:33:51 +0800 Subject: [PATCH] fix: async dashboard version read via anyio async context manager --- astrbot/core/utils/astrbot_path.py | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/astrbot/core/utils/astrbot_path.py b/astrbot/core/utils/astrbot_path.py index db9e40d1c..b4dadee96 100644 --- a/astrbot/core/utils/astrbot_path.py +++ b/astrbot/core/utils/astrbot_path.py @@ -17,8 +17,12 @@ import os from importlib import resources from pathlib import Path +import anyio + from astrbot.core.utils.runtime_env import is_packaged_desktop_runtime +_BUNDLED_DIST = resources.files("astrbot") / "dashboard" / "dist" + class AstrbotPaths: """Astrbot 项目路径管理类""" @@ -69,6 +73,66 @@ class AstrbotPaths: def root(self, value: Path) -> None: self._root_override = value + @property + def is_root(self) -> bool: + """Check if the path is an AstrBot root directory""" + if not isinstance(self.root, Path): + path = Path(self.root) + if not path.exists() or not path.is_dir(): + return False + if not (self.root / ".astrbot").exists(): + return False + return True + + @property + def has_dashboard(self) -> bool: + """Check if the dashboard is installed""" + if _BUNDLED_DIST.is_dir(): + return True + dashboard_version = self.dashboard_version + match dashboard_version: + case None: + return False + case str(): + return True + case _: + return False + + async def async_has_dashboard(self) -> bool: + """Check if the dashboard is installed (async)""" + if _BUNDLED_DIST.is_dir(): + return True + dashboard_version = await self.async_dashboard_version() + match dashboard_version: + case None: + return False + case str(): + return True + case _: + return False + + @property + def dashboard_version(self) -> str | None: + try: + with open(self.dist / "assets" / "version") as f: + return f.read().strip() + except FileNotFoundError: + return None + + async def async_dashboard_version(self) -> str | None: + try: + # Use async context manager to open and read the file asynchronously. + async with anyio.open_file(self.dist / "assets" / "version", mode="r") as f: + data = await f.read() + if data is None: + return None + return data.strip() + except (FileNotFoundError, OSError): + return None + except Exception: + # Be defensive: any unexpected error should not raise during path utils + return None + @property def project_root(self) -> Path: """获取项目根目录路径 (package root)""" @@ -79,6 +143,10 @@ class AstrbotPaths: def data(self) -> Path: return self.root / "data" + @property + def dist(self) -> Path: + return self.data / "dist" + @property def config(self) -> Path: return self.data / "config"