2025-08-05 17:49:23 +08:00
|
|
|
|
"""
|
|
|
|
|
|
真寻仓库管理器
|
|
|
|
|
|
负责真寻主仓库的更新、版本检查、文件处理等功能
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
import shutil
|
|
|
|
|
|
from typing import ClassVar, Literal
|
|
|
|
|
|
import zipfile
|
|
|
|
|
|
|
|
|
|
|
|
import aiofiles
|
|
|
|
|
|
|
|
|
|
|
|
from zhenxun.configs.path_config import DATA_PATH, TEMP_PATH
|
|
|
|
|
|
from zhenxun.services.log import logger
|
|
|
|
|
|
from zhenxun.utils.github_utils import GithubUtils
|
|
|
|
|
|
from zhenxun.utils.http_utils import AsyncHttpx
|
|
|
|
|
|
from zhenxun.utils.manager.virtual_env_package_manager import VirtualEnvPackageManager
|
|
|
|
|
|
from zhenxun.utils.repo_utils import AliyunRepoManager, GithubRepoManager
|
|
|
|
|
|
from zhenxun.utils.repo_utils.models import RepoUpdateResult
|
|
|
|
|
|
from zhenxun.utils.repo_utils.utils import check_git
|
|
|
|
|
|
|
|
|
|
|
|
LOG_COMMAND = "ZhenxunRepoManager"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ZhenxunUpdateException(Exception):
|
|
|
|
|
|
"""资源下载异常"""
|
|
|
|
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ZhenxunRepoConfig:
|
|
|
|
|
|
"""真寻仓库配置"""
|
|
|
|
|
|
|
|
|
|
|
|
# Zhenxun Bot 相关配置
|
|
|
|
|
|
ZHENXUN_BOT_GIT = "https://github.com/zhenxun-org/zhenxun_bot.git"
|
|
|
|
|
|
ZHENXUN_BOT_GITHUB_URL = "https://github.com/HibiKier/zhenxun_bot/tree/main"
|
|
|
|
|
|
ZHENXUN_BOT_DOWNLOAD_FILE_STRING = "zhenxun_bot.zip"
|
|
|
|
|
|
ZHENXUN_BOT_DOWNLOAD_FILE = TEMP_PATH / ZHENXUN_BOT_DOWNLOAD_FILE_STRING
|
|
|
|
|
|
ZHENXUN_BOT_UNZIP_PATH = TEMP_PATH / "zhenxun_bot"
|
|
|
|
|
|
ZHENXUN_BOT_CODE_PATH = Path() / "zhenxun"
|
|
|
|
|
|
ZHENXUN_BOT_RELEASES_API_URL = (
|
|
|
|
|
|
"https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest"
|
|
|
|
|
|
)
|
|
|
|
|
|
ZHENXUN_BOT_BACKUP_PATH = Path() / "backup"
|
|
|
|
|
|
# 需要替换的文件夹
|
|
|
|
|
|
ZHENXUN_BOT_UPDATE_FOLDERS: ClassVar[list[str]] = [
|
|
|
|
|
|
"zhenxun/builtin_plugins",
|
|
|
|
|
|
"zhenxun/services",
|
|
|
|
|
|
"zhenxun/utils",
|
|
|
|
|
|
"zhenxun/models",
|
|
|
|
|
|
"zhenxun/configs",
|
2025-08-26 16:53:14 +08:00
|
|
|
|
"zhenxun/ui",
|
2025-08-05 17:49:23 +08:00
|
|
|
|
]
|
|
|
|
|
|
ZHENXUN_BOT_VERSION_FILE_STRING = "__version__"
|
|
|
|
|
|
ZHENXUN_BOT_VERSION_FILE = Path() / ZHENXUN_BOT_VERSION_FILE_STRING
|
|
|
|
|
|
|
|
|
|
|
|
# 备份杂项
|
|
|
|
|
|
BACKUP_FILES: ClassVar[list[str]] = [
|
|
|
|
|
|
"pyproject.toml",
|
|
|
|
|
|
"poetry.lock",
|
|
|
|
|
|
"requirements.txt",
|
|
|
|
|
|
".env.dev",
|
|
|
|
|
|
".env.example",
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# WEB UI 相关配置
|
|
|
|
|
|
WEBUI_GIT = "https://github.com/HibiKier/zhenxun_bot_webui.git"
|
|
|
|
|
|
WEBUI_DIST_GITHUB_URL = "https://github.com/HibiKier/zhenxun_bot_webui/tree/dist"
|
|
|
|
|
|
WEBUI_DOWNLOAD_FILE_STRING = "webui_assets.zip"
|
|
|
|
|
|
WEBUI_DOWNLOAD_FILE = TEMP_PATH / WEBUI_DOWNLOAD_FILE_STRING
|
|
|
|
|
|
WEBUI_UNZIP_PATH = TEMP_PATH / "web_ui"
|
|
|
|
|
|
WEBUI_PATH = DATA_PATH / "web_ui" / "public"
|
|
|
|
|
|
WEBUI_BACKUP_PATH = DATA_PATH / "web_ui" / "backup_public"
|
|
|
|
|
|
|
|
|
|
|
|
# 资源管理相关配置
|
|
|
|
|
|
RESOURCE_GIT = "https://github.com/zhenxun-org/zhenxun-bot-resources.git"
|
|
|
|
|
|
RESOURCE_GITHUB_URL = (
|
|
|
|
|
|
"https://github.com/zhenxun-org/zhenxun-bot-resources/tree/main"
|
|
|
|
|
|
)
|
|
|
|
|
|
RESOURCE_ZIP_FILE_STRING = "resources.zip"
|
|
|
|
|
|
RESOURCE_ZIP_FILE = TEMP_PATH / RESOURCE_ZIP_FILE_STRING
|
|
|
|
|
|
RESOURCE_UNZIP_PATH = TEMP_PATH / "resources"
|
|
|
|
|
|
RESOURCE_PATH = Path() / "resources"
|
|
|
|
|
|
|
|
|
|
|
|
REQUIREMENTS_FILE_STRING = "requirements.txt"
|
|
|
|
|
|
REQUIREMENTS_FILE = Path() / REQUIREMENTS_FILE_STRING
|
|
|
|
|
|
|
|
|
|
|
|
PYPROJECT_FILE_STRING = "pyproject.toml"
|
|
|
|
|
|
PYPROJECT_FILE = Path() / PYPROJECT_FILE_STRING
|
|
|
|
|
|
|
|
|
|
|
|
PYPROJECT_LOCK_FILE_STRING = "poetry.lock"
|
|
|
|
|
|
PYPROJECT_LOCK_FILE = Path() / PYPROJECT_LOCK_FILE_STRING
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ZhenxunRepoManagerClass:
|
|
|
|
|
|
"""真寻仓库管理器"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self.config = ZhenxunRepoConfig()
|
|
|
|
|
|
|
|
|
|
|
|
def __clear_folder(self, folder_path: Path):
|
|
|
|
|
|
"""
|
|
|
|
|
|
清空文件夹
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
folder_path: 文件夹路径
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not folder_path.exists():
|
|
|
|
|
|
return
|
|
|
|
|
|
for filename in os.listdir(folder_path):
|
|
|
|
|
|
file_path = folder_path / filename
|
|
|
|
|
|
try:
|
|
|
|
|
|
if file_path.is_file():
|
|
|
|
|
|
os.unlink(file_path)
|
|
|
|
|
|
elif file_path.is_dir() and not filename.startswith("."):
|
|
|
|
|
|
shutil.rmtree(file_path)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.warning(f"无法删除 {file_path}", LOG_COMMAND, e=e)
|
|
|
|
|
|
|
|
|
|
|
|
def __copy_files(self, src_path: Path, dest_path: Path, incremental: bool = False):
|
|
|
|
|
|
"""
|
|
|
|
|
|
复制文件或文件夹
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
src_path: 源文件或文件夹路径
|
|
|
|
|
|
dest_path: 目标文件或文件夹路径
|
|
|
|
|
|
incremental: 是否增量复制
|
|
|
|
|
|
"""
|
|
|
|
|
|
if src_path.is_file():
|
|
|
|
|
|
shutil.copy(src_path, dest_path)
|
|
|
|
|
|
logger.debug(f"复制文件 {src_path} -> {dest_path}", LOG_COMMAND)
|
|
|
|
|
|
elif src_path.is_dir():
|
|
|
|
|
|
for filename in os.listdir(src_path):
|
|
|
|
|
|
file_path = src_path / filename
|
|
|
|
|
|
dest_file = dest_path / filename
|
|
|
|
|
|
dest_file.parent.mkdir(exist_ok=True, parents=True)
|
|
|
|
|
|
if file_path.is_file():
|
|
|
|
|
|
if dest_file.exists():
|
|
|
|
|
|
dest_file.unlink()
|
|
|
|
|
|
shutil.copy(file_path, dest_file)
|
|
|
|
|
|
logger.debug(f"复制文件 {file_path} -> {dest_file}", LOG_COMMAND)
|
|
|
|
|
|
elif file_path.is_dir():
|
|
|
|
|
|
if incremental:
|
|
|
|
|
|
self.__copy_files(file_path, dest_file, incremental=True)
|
|
|
|
|
|
else:
|
|
|
|
|
|
if dest_file.exists():
|
|
|
|
|
|
shutil.rmtree(dest_file, True)
|
|
|
|
|
|
shutil.copytree(file_path, dest_file)
|
|
|
|
|
|
logger.debug(
|
|
|
|
|
|
f"复制文件夹 {file_path} -> {dest_file}",
|
|
|
|
|
|
LOG_COMMAND,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== Zhenxun Bot 相关方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_get_version_from_repo(self) -> str:
|
|
|
|
|
|
"""从指定分支获取版本号
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
str: 版本号
|
|
|
|
|
|
"""
|
|
|
|
|
|
repo_info = GithubUtils.parse_github_url(self.config.ZHENXUN_BOT_GITHUB_URL)
|
|
|
|
|
|
version_url = await repo_info.get_raw_download_urls(
|
|
|
|
|
|
path=self.config.ZHENXUN_BOT_VERSION_FILE_STRING
|
|
|
|
|
|
)
|
|
|
|
|
|
try:
|
|
|
|
|
|
res = await AsyncHttpx.get(version_url)
|
|
|
|
|
|
if res.status_code == 200:
|
|
|
|
|
|
return res.text.strip()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取 {repo_info.branch} 分支版本失败", LOG_COMMAND, e=e)
|
|
|
|
|
|
return "未知版本"
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_write_version_file(self, version: str):
|
|
|
|
|
|
"""写入版本文件"""
|
|
|
|
|
|
async with aiofiles.open(
|
|
|
|
|
|
self.config.ZHENXUN_BOT_VERSION_FILE, "w", encoding="utf8"
|
|
|
|
|
|
) as f:
|
|
|
|
|
|
await f.write(f"__version__: {version}")
|
|
|
|
|
|
|
|
|
|
|
|
def __backup_zhenxun(self):
|
|
|
|
|
|
"""备份真寻文件"""
|
|
|
|
|
|
for filename in os.listdir(self.config.ZHENXUN_BOT_CODE_PATH):
|
|
|
|
|
|
file_path = self.config.ZHENXUN_BOT_CODE_PATH / filename
|
|
|
|
|
|
if file_path.exists():
|
|
|
|
|
|
self.__copy_files(
|
|
|
|
|
|
file_path,
|
|
|
|
|
|
self.config.ZHENXUN_BOT_BACKUP_PATH / filename,
|
|
|
|
|
|
True,
|
|
|
|
|
|
)
|
|
|
|
|
|
for filename in self.config.BACKUP_FILES:
|
|
|
|
|
|
file_path = Path() / filename
|
|
|
|
|
|
if file_path.exists():
|
|
|
|
|
|
self.__copy_files(
|
|
|
|
|
|
file_path,
|
|
|
|
|
|
self.config.ZHENXUN_BOT_BACKUP_PATH / filename,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_get_latest_releases_data(self) -> dict:
|
|
|
|
|
|
"""获取真寻releases最新版本信息
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
dict: 最新版本数据
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
res = await AsyncHttpx.get(self.config.ZHENXUN_BOT_RELEASES_API_URL)
|
|
|
|
|
|
if res.status_code == 200:
|
|
|
|
|
|
return res.json()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error("检查更新真寻获取版本失败", LOG_COMMAND, e=e)
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_download_zip(self, ver_type: Literal["main", "release"]) -> str:
|
|
|
|
|
|
"""下载真寻最新版文件
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
ver_type: 版本类型,main 为最新版,release 为最新release版
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
str: 版本号
|
|
|
|
|
|
"""
|
|
|
|
|
|
repo_info = GithubUtils.parse_github_url(self.config.ZHENXUN_BOT_GITHUB_URL)
|
|
|
|
|
|
if ver_type == "main":
|
|
|
|
|
|
download_url = await repo_info.get_archive_download_urls()
|
|
|
|
|
|
new_version = await self.zhenxun_get_version_from_repo()
|
|
|
|
|
|
else:
|
|
|
|
|
|
release_data = await self.zhenxun_get_latest_releases_data()
|
|
|
|
|
|
logger.debug(f"获取真寻RELEASES最新版本信息: {release_data}", LOG_COMMAND)
|
|
|
|
|
|
if not release_data:
|
|
|
|
|
|
raise ZhenxunUpdateException("获取真寻RELEASES最新版本失败...")
|
|
|
|
|
|
new_version = release_data.get("name", "")
|
|
|
|
|
|
download_url = await repo_info.get_release_source_download_urls_tgz(
|
|
|
|
|
|
new_version
|
|
|
|
|
|
)
|
|
|
|
|
|
if not download_url:
|
|
|
|
|
|
raise ZhenxunUpdateException("获取真寻最新版文件下载链接失败...")
|
|
|
|
|
|
if self.config.ZHENXUN_BOT_DOWNLOAD_FILE.exists():
|
|
|
|
|
|
self.config.ZHENXUN_BOT_DOWNLOAD_FILE.unlink()
|
|
|
|
|
|
if await AsyncHttpx.download_file(
|
|
|
|
|
|
download_url, self.config.ZHENXUN_BOT_DOWNLOAD_FILE, stream=True
|
|
|
|
|
|
):
|
|
|
|
|
|
logger.debug("下载真寻最新版文件完成...", LOG_COMMAND)
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise ZhenxunUpdateException("下载真寻最新版文件失败...")
|
|
|
|
|
|
return new_version
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_unzip(self):
|
|
|
|
|
|
"""解压真寻最新版文件"""
|
|
|
|
|
|
if not self.config.ZHENXUN_BOT_DOWNLOAD_FILE.exists():
|
|
|
|
|
|
raise FileNotFoundError("真寻最新版文件不存在")
|
|
|
|
|
|
if self.config.ZHENXUN_BOT_UNZIP_PATH.exists():
|
|
|
|
|
|
shutil.rmtree(self.config.ZHENXUN_BOT_UNZIP_PATH)
|
|
|
|
|
|
tf = None
|
|
|
|
|
|
try:
|
|
|
|
|
|
tf = zipfile.ZipFile(self.config.ZHENXUN_BOT_DOWNLOAD_FILE)
|
|
|
|
|
|
tf.extractall(self.config.ZHENXUN_BOT_UNZIP_PATH)
|
|
|
|
|
|
logger.debug("解压Zhenxun Bot文件压缩包完成!", LOG_COMMAND)
|
|
|
|
|
|
self.__backup_zhenxun()
|
|
|
|
|
|
for filename in self.config.BACKUP_FILES:
|
|
|
|
|
|
self.__copy_files(
|
|
|
|
|
|
self.config.ZHENXUN_BOT_UNZIP_PATH / filename,
|
|
|
|
|
|
Path() / filename,
|
|
|
|
|
|
)
|
|
|
|
|
|
logger.debug("备份真寻更新文件完成!", LOG_COMMAND)
|
|
|
|
|
|
unzip_dir = next(self.config.ZHENXUN_BOT_UNZIP_PATH.iterdir())
|
|
|
|
|
|
for folder in self.config.ZHENXUN_BOT_UPDATE_FOLDERS:
|
|
|
|
|
|
self.__copy_files(unzip_dir / folder, Path() / folder)
|
|
|
|
|
|
logger.debug("移动真寻更新文件完成!", LOG_COMMAND)
|
|
|
|
|
|
if self.config.ZHENXUN_BOT_UNZIP_PATH.exists():
|
|
|
|
|
|
shutil.rmtree(self.config.ZHENXUN_BOT_UNZIP_PATH)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error("解压真寻最新版文件失败...", LOG_COMMAND, e=e)
|
|
|
|
|
|
raise
|
|
|
|
|
|
finally:
|
|
|
|
|
|
if tf:
|
|
|
|
|
|
tf.close()
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_zip_update(self, ver_type: Literal["main", "release"]) -> str:
|
|
|
|
|
|
"""使用zip更新真寻
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
ver_type: 版本类型,main 为最新版,release 为最新release版
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
str: 版本号
|
|
|
|
|
|
"""
|
|
|
|
|
|
new_version = await self.zhenxun_download_zip(ver_type)
|
|
|
|
|
|
await self.zhenxun_unzip()
|
|
|
|
|
|
await self.zhenxun_write_version_file(new_version)
|
|
|
|
|
|
return new_version
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_git_update(
|
|
|
|
|
|
self, source: Literal["git", "ali"], branch: str = "main", force: bool = False
|
|
|
|
|
|
) -> RepoUpdateResult:
|
|
|
|
|
|
"""使用git或阿里云更新真寻
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
|
|
|
|
|
branch: 分支名称
|
|
|
|
|
|
force: 是否强制更新
|
|
|
|
|
|
"""
|
|
|
|
|
|
if source == "git":
|
|
|
|
|
|
return await GithubRepoManager.update_via_git(
|
|
|
|
|
|
self.config.ZHENXUN_BOT_GIT,
|
|
|
|
|
|
Path(),
|
|
|
|
|
|
branch=branch,
|
|
|
|
|
|
force=force,
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
return await AliyunRepoManager.update_via_git(
|
|
|
|
|
|
self.config.ZHENXUN_BOT_GIT,
|
|
|
|
|
|
Path(),
|
|
|
|
|
|
branch=branch,
|
|
|
|
|
|
force=force,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def zhenxun_update(
|
|
|
|
|
|
self,
|
|
|
|
|
|
source: Literal["git", "ali"] = "ali",
|
|
|
|
|
|
branch: str = "main",
|
|
|
|
|
|
force: bool = False,
|
|
|
|
|
|
ver_type: Literal["main", "release"] = "main",
|
|
|
|
|
|
):
|
|
|
|
|
|
"""更新真寻
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
|
|
|
|
|
branch: 分支名称
|
|
|
|
|
|
force: 是否强制更新
|
|
|
|
|
|
ver_type: 版本类型,main 为最新版,release 为最新release版
|
|
|
|
|
|
"""
|
|
|
|
|
|
if await check_git():
|
|
|
|
|
|
await self.zhenxun_git_update(source, branch, force)
|
|
|
|
|
|
logger.debug("使用git更新真寻!", LOG_COMMAND)
|
|
|
|
|
|
else:
|
|
|
|
|
|
await self.zhenxun_zip_update(ver_type)
|
|
|
|
|
|
logger.debug("使用zip更新真寻!", LOG_COMMAND)
|
|
|
|
|
|
|
|
|
|
|
|
async def install_requirements(self):
|
|
|
|
|
|
"""安装真寻依赖"""
|
|
|
|
|
|
await VirtualEnvPackageManager.install_requirement(
|
|
|
|
|
|
self.config.REQUIREMENTS_FILE
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== 资源管理相关方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
def check_resources_exists(self) -> bool:
|
|
|
|
|
|
"""检查资源文件是否存在
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
bool: 是否存在
|
|
|
|
|
|
"""
|
|
|
|
|
|
if self.config.RESOURCE_PATH.exists():
|
|
|
|
|
|
font_path = self.config.RESOURCE_PATH / "font"
|
|
|
|
|
|
if font_path.exists() and os.listdir(font_path):
|
|
|
|
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
async def resources_download_zip(self):
|
|
|
|
|
|
"""下载资源文件"""
|
|
|
|
|
|
download_url = await GithubUtils.parse_github_url(
|
|
|
|
|
|
self.config.RESOURCE_GITHUB_URL
|
|
|
|
|
|
).get_archive_download_urls()
|
|
|
|
|
|
logger.debug("开始下载resources资源包...", LOG_COMMAND)
|
|
|
|
|
|
if await AsyncHttpx.download_file(
|
|
|
|
|
|
download_url, self.config.RESOURCE_ZIP_FILE, stream=True
|
|
|
|
|
|
):
|
|
|
|
|
|
logger.debug("下载resources资源文件压缩包成功!", LOG_COMMAND)
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise ZhenxunUpdateException("下载resources资源包失败...")
|
|
|
|
|
|
|
|
|
|
|
|
async def resources_unzip(self):
|
|
|
|
|
|
"""解压资源文件"""
|
|
|
|
|
|
if not self.config.RESOURCE_ZIP_FILE.exists():
|
|
|
|
|
|
raise FileNotFoundError("资源文件压缩包不存在")
|
|
|
|
|
|
if self.config.RESOURCE_UNZIP_PATH.exists():
|
|
|
|
|
|
shutil.rmtree(self.config.RESOURCE_UNZIP_PATH)
|
|
|
|
|
|
tf = None
|
|
|
|
|
|
try:
|
|
|
|
|
|
tf = zipfile.ZipFile(self.config.RESOURCE_ZIP_FILE)
|
|
|
|
|
|
tf.extractall(self.config.RESOURCE_UNZIP_PATH)
|
|
|
|
|
|
logger.debug("解压文件压缩包完成...", LOG_COMMAND)
|
|
|
|
|
|
unzip_dir = next(self.config.RESOURCE_UNZIP_PATH.iterdir())
|
|
|
|
|
|
self.__copy_files(unzip_dir, self.config.RESOURCE_PATH, True)
|
|
|
|
|
|
logger.debug("复制资源文件完成!", LOG_COMMAND)
|
|
|
|
|
|
shutil.rmtree(self.config.RESOURCE_UNZIP_PATH, ignore_errors=True)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error("解压资源文件失败...", LOG_COMMAND, e=e)
|
|
|
|
|
|
raise
|
|
|
|
|
|
finally:
|
|
|
|
|
|
if tf:
|
|
|
|
|
|
tf.close()
|
|
|
|
|
|
|
|
|
|
|
|
async def resources_zip_update(self):
|
|
|
|
|
|
"""使用zip更新资源文件"""
|
|
|
|
|
|
await self.resources_download_zip()
|
|
|
|
|
|
await self.resources_unzip()
|
|
|
|
|
|
|
|
|
|
|
|
async def resources_git_update(
|
|
|
|
|
|
self, source: Literal["git", "ali"], branch: str = "main", force: bool = False
|
|
|
|
|
|
) -> RepoUpdateResult:
|
|
|
|
|
|
"""使用git或阿里云更新资源文件
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
|
|
|
|
|
branch: 分支名称
|
|
|
|
|
|
force: 是否强制更新
|
|
|
|
|
|
"""
|
|
|
|
|
|
if source == "git":
|
|
|
|
|
|
return await GithubRepoManager.update_via_git(
|
|
|
|
|
|
self.config.RESOURCE_GIT,
|
|
|
|
|
|
self.config.RESOURCE_PATH,
|
|
|
|
|
|
branch=branch,
|
|
|
|
|
|
force=force,
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
return await AliyunRepoManager.update_via_git(
|
|
|
|
|
|
self.config.RESOURCE_GIT,
|
|
|
|
|
|
self.config.RESOURCE_PATH,
|
|
|
|
|
|
branch=branch,
|
|
|
|
|
|
force=force,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def resources_update(
|
|
|
|
|
|
self,
|
|
|
|
|
|
source: Literal["git", "ali"] = "ali",
|
|
|
|
|
|
branch: str = "main",
|
|
|
|
|
|
force: bool = False,
|
|
|
|
|
|
):
|
|
|
|
|
|
"""更新资源文件
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
|
|
|
|
|
branch: 分支名称
|
|
|
|
|
|
force: 是否强制更新
|
|
|
|
|
|
"""
|
|
|
|
|
|
if await check_git():
|
|
|
|
|
|
await self.resources_git_update(source, branch, force)
|
|
|
|
|
|
logger.debug("使用git更新资源文件!", LOG_COMMAND)
|
|
|
|
|
|
else:
|
|
|
|
|
|
await self.resources_zip_update()
|
|
|
|
|
|
logger.debug("使用zip更新资源文件!", LOG_COMMAND)
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== Web UI 管理相关方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
def check_webui_exists(self) -> bool:
|
|
|
|
|
|
"""检查 Web UI 资源是否存在"""
|
|
|
|
|
|
return bool(
|
|
|
|
|
|
self.config.WEBUI_PATH.exists() and os.listdir(self.config.WEBUI_PATH)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def webui_download_zip(self):
|
|
|
|
|
|
"""下载 WEBUI_ASSETS 资源"""
|
|
|
|
|
|
download_url = await GithubUtils.parse_github_url(
|
|
|
|
|
|
self.config.WEBUI_DIST_GITHUB_URL
|
|
|
|
|
|
).get_archive_download_urls()
|
|
|
|
|
|
logger.info("开始下载 WEBUI_ASSETS 资源...", LOG_COMMAND)
|
|
|
|
|
|
if await AsyncHttpx.download_file(
|
|
|
|
|
|
download_url, self.config.WEBUI_DOWNLOAD_FILE, follow_redirects=True
|
|
|
|
|
|
):
|
|
|
|
|
|
logger.info("下载 WEBUI_ASSETS 成功!", LOG_COMMAND)
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise ZhenxunUpdateException("下载 WEBUI_ASSETS 失败", LOG_COMMAND)
|
|
|
|
|
|
|
|
|
|
|
|
def __backup_webui(self):
|
|
|
|
|
|
"""备份 WEBUI_ASSERT 资源"""
|
|
|
|
|
|
if self.config.WEBUI_PATH.exists():
|
|
|
|
|
|
if self.config.WEBUI_BACKUP_PATH.exists():
|
|
|
|
|
|
logger.debug(
|
|
|
|
|
|
f"删除旧的备份webui文件夹 {self.config.WEBUI_BACKUP_PATH}",
|
|
|
|
|
|
LOG_COMMAND,
|
|
|
|
|
|
)
|
|
|
|
|
|
shutil.rmtree(self.config.WEBUI_BACKUP_PATH)
|
|
|
|
|
|
shutil.copytree(self.config.WEBUI_PATH, self.config.WEBUI_BACKUP_PATH)
|
|
|
|
|
|
|
|
|
|
|
|
async def webui_unzip(self):
|
|
|
|
|
|
"""解压 WEBUI_ASSETS 资源
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
str: 更新结果
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not self.config.WEBUI_DOWNLOAD_FILE.exists():
|
|
|
|
|
|
raise FileNotFoundError("webui文件压缩包不存在")
|
|
|
|
|
|
tf = None
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.__backup_webui()
|
|
|
|
|
|
self.__clear_folder(self.config.WEBUI_PATH)
|
|
|
|
|
|
tf = zipfile.ZipFile(self.config.WEBUI_DOWNLOAD_FILE)
|
|
|
|
|
|
tf.extractall(self.config.WEBUI_UNZIP_PATH)
|
|
|
|
|
|
logger.debug("Web UI 解压文件压缩包完成...", LOG_COMMAND)
|
|
|
|
|
|
unzip_dir = next(self.config.WEBUI_UNZIP_PATH.iterdir())
|
|
|
|
|
|
self.__copy_files(unzip_dir, self.config.WEBUI_PATH)
|
|
|
|
|
|
logger.debug("Web UI 复制 WEBUI_ASSETS 成功!", LOG_COMMAND)
|
|
|
|
|
|
shutil.rmtree(self.config.WEBUI_UNZIP_PATH, ignore_errors=True)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
if self.config.WEBUI_BACKUP_PATH.exists():
|
|
|
|
|
|
self.__copy_files(self.config.WEBUI_BACKUP_PATH, self.config.WEBUI_PATH)
|
|
|
|
|
|
logger.debug("恢复备份 WEBUI_ASSETS 成功!", LOG_COMMAND)
|
|
|
|
|
|
shutil.rmtree(self.config.WEBUI_BACKUP_PATH, ignore_errors=True)
|
|
|
|
|
|
logger.error("Web UI 更新失败", LOG_COMMAND, e=e)
|
|
|
|
|
|
raise
|
|
|
|
|
|
finally:
|
|
|
|
|
|
if tf:
|
|
|
|
|
|
tf.close()
|
|
|
|
|
|
|
|
|
|
|
|
async def webui_zip_update(self):
|
|
|
|
|
|
"""使用zip更新 Web UI"""
|
|
|
|
|
|
await self.webui_download_zip()
|
|
|
|
|
|
await self.webui_unzip()
|
|
|
|
|
|
|
|
|
|
|
|
async def webui_git_update(
|
|
|
|
|
|
self, source: Literal["git", "ali"], branch: str = "dist", force: bool = False
|
|
|
|
|
|
) -> RepoUpdateResult:
|
|
|
|
|
|
"""使用git或阿里云更新 Web UI
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
|
|
|
|
|
branch: 分支名称
|
|
|
|
|
|
force: 是否强制更新
|
|
|
|
|
|
"""
|
|
|
|
|
|
if source == "git":
|
|
|
|
|
|
return await GithubRepoManager.update_via_git(
|
|
|
|
|
|
self.config.WEBUI_GIT,
|
|
|
|
|
|
self.config.WEBUI_PATH,
|
|
|
|
|
|
branch=branch,
|
|
|
|
|
|
force=force,
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
return await AliyunRepoManager.update_via_git(
|
|
|
|
|
|
self.config.WEBUI_GIT,
|
|
|
|
|
|
self.config.WEBUI_PATH,
|
|
|
|
|
|
branch=branch,
|
|
|
|
|
|
force=force,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def webui_update(
|
|
|
|
|
|
self,
|
|
|
|
|
|
source: Literal["git", "ali"] = "ali",
|
|
|
|
|
|
branch: str = "dist",
|
|
|
|
|
|
force: bool = False,
|
|
|
|
|
|
):
|
|
|
|
|
|
"""更新 Web UI
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
|
|
|
|
|
"""
|
|
|
|
|
|
if await check_git():
|
|
|
|
|
|
await self.webui_git_update(source, branch, force)
|
|
|
|
|
|
logger.debug("使用git更新Web UI!", LOG_COMMAND)
|
|
|
|
|
|
else:
|
|
|
|
|
|
await self.webui_zip_update()
|
|
|
|
|
|
logger.debug("使用zip更新Web UI!", LOG_COMMAND)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ZhenxunRepoManager = ZhenxunRepoManagerClass()
|