mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-09-21 14:35:29 +08:00
fix: resolve path conflicts and improve self-healing during backup restore and plugin installation (#7737)
* fix(数据备份与恢复): 解决备份恢复和插件安装过程中的路径冲突及自愈问题 1. 修复备份导入时目录条目被误识别为 0 字节文件的问题。 2. 增加插件加载和数据目录创建时的路径冲突自动清理逻辑。 3. 增强插件解压安装过程对现有冲突文件的兼容性。 4. 优化 remove_dir 工具类使其支持同时处理文件和目录的删除。 * fix(core): 根据 CR 建议实现通用的路径冲突自愈机制并增强损坏符号链接的处理能力
This commit is contained in:
@@ -25,6 +25,7 @@ from astrbot.core.utils.astrbot_path import (
|
||||
get_astrbot_data_path,
|
||||
get_astrbot_knowledge_base_path,
|
||||
)
|
||||
from astrbot.core.utils.io import ensure_dir
|
||||
from astrbot.core.utils.version_comparator import VersionComparator
|
||||
|
||||
# 从共享常量模块导入
|
||||
@@ -931,6 +932,11 @@ class AstrBotImporter:
|
||||
if not _validate_path_within(target_path, target_dir):
|
||||
result.add_warning(f"文件路径越界,已跳过: {name}")
|
||||
continue
|
||||
|
||||
if zf.getinfo(name).is_dir():
|
||||
ensure_dir(target_path)
|
||||
continue
|
||||
|
||||
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with zf.open(name) as src, open(target_path, "wb") as dst:
|
||||
|
||||
@@ -37,6 +37,7 @@ from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_platform_adapter import (
|
||||
from astrbot.core.star.context import Context
|
||||
from astrbot.core.star.star import star_map
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
from astrbot.core.utils.io import ensure_dir
|
||||
|
||||
|
||||
class StarTools:
|
||||
@@ -305,7 +306,7 @@ class StarTools:
|
||||
)
|
||||
|
||||
try:
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
ensure_dir(data_dir)
|
||||
except OSError as e:
|
||||
if isinstance(e, PermissionError):
|
||||
raise RuntimeError(f"无法创建目录 {data_dir}:权限不足") from e
|
||||
|
||||
@@ -4,7 +4,7 @@ import zipfile
|
||||
|
||||
from astrbot.core import logger
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_plugin_path
|
||||
from astrbot.core.utils.io import on_error, remove_dir
|
||||
from astrbot.core.utils.io import ensure_dir, on_error, remove_dir
|
||||
|
||||
from ..star.star import StarMetadata
|
||||
from ..updator import RepoZipUpdator
|
||||
@@ -71,7 +71,7 @@ class PluginUpdator(RepoZipUpdator):
|
||||
return plugin_path
|
||||
|
||||
def unzip_file(self, zip_path: str, target_dir: str) -> None:
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
ensure_dir(target_dir)
|
||||
update_dir = ""
|
||||
logger.info(f"Extracting archive: {zip_path}")
|
||||
with zipfile.ZipFile(zip_path, "r") as z:
|
||||
|
||||
@@ -31,12 +31,36 @@ def on_error(func, path, exc_info) -> None:
|
||||
|
||||
|
||||
def remove_dir(file_path: str) -> bool:
|
||||
if not os.path.exists(file_path):
|
||||
if not os.path.lexists(file_path):
|
||||
return True
|
||||
shutil.rmtree(file_path, onerror=on_error)
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.remove(file_path)
|
||||
else:
|
||||
shutil.rmtree(file_path, onerror=on_error)
|
||||
return True
|
||||
|
||||
|
||||
def ensure_dir(dir_path: str | Path) -> None:
|
||||
"""确保目录存在。如果路径处存在非目录的文件或损坏的符号链接,则先将其删除。"""
|
||||
p = Path(dir_path)
|
||||
if (p.exists() or p.is_symlink()) and not p.is_dir():
|
||||
logger.warning(f"路径 {p} 已存在但不是目录,正在清理以创建目录。")
|
||||
try:
|
||||
if p.is_dir():
|
||||
shutil.rmtree(p, onerror=on_error)
|
||||
else:
|
||||
p.unlink()
|
||||
except Exception as e:
|
||||
logger.error(f"清理冲突路径 {p} 失败: {e!s}")
|
||||
raise RuntimeError(f"无法清理冲突路径 {p}:{e!s}") from e
|
||||
|
||||
try:
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
logger.error(f"创建目录 {p} 失败: {e!s}")
|
||||
raise RuntimeError(f"无法创建目录 {p}:{e!s}") from e
|
||||
|
||||
|
||||
def port_checker(port: int, host: str = "localhost") -> bool:
|
||||
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sk.settimeout(1)
|
||||
|
||||
@@ -9,7 +9,7 @@ import certifi
|
||||
import httpx
|
||||
|
||||
from astrbot.core import logger
|
||||
from astrbot.core.utils.io import on_error
|
||||
from astrbot.core.utils.io import ensure_dir, on_error
|
||||
from astrbot.core.utils.version_comparator import VersionComparator
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class RepoZipUpdator:
|
||||
self, url: str, path: str, timeout: float = 1800.0
|
||||
) -> None:
|
||||
target_path = Path(path)
|
||||
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
ensure_dir(target_path.parent)
|
||||
|
||||
try:
|
||||
async with self._create_httpx_client(timeout=timeout) as client:
|
||||
@@ -233,7 +233,7 @@ class RepoZipUpdator:
|
||||
|
||||
def unzip_file(self, zip_path: str, target_dir: str) -> None:
|
||||
"""解压缩文件, 并将压缩包内**第一个**文件夹内的文件移动到 target_dir"""
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
ensure_dir(target_dir)
|
||||
update_dir = ""
|
||||
with zipfile.ZipFile(zip_path, "r") as z:
|
||||
update_dir = z.namelist()[0]
|
||||
|
||||
Reference in New Issue
Block a user