mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 00:16:57 +08:00
feat:内建重启前主动备份插件
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
|
|
||||||
@@ -42,11 +43,117 @@ class SystemChain(ChainBase):
|
|||||||
"channel": channel.value,
|
"channel": channel.value,
|
||||||
"userid": userid
|
"userid": userid
|
||||||
}, self._restart_file)
|
}, self._restart_file)
|
||||||
|
# 主动备份一次插件
|
||||||
|
self.backup_plugins()
|
||||||
# 设置停止标志,通知所有模块准备停止
|
# 设置停止标志,通知所有模块准备停止
|
||||||
global_vars.stop_system()
|
global_vars.stop_system()
|
||||||
# 重启
|
# 重启
|
||||||
SystemHelper.restart()
|
SystemHelper.restart()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def backup_plugins():
|
||||||
|
"""
|
||||||
|
备份插件到用户配置目录(仅docker环境)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 非docker环境不处理
|
||||||
|
if not SystemUtils.is_docker():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 使用绝对路径确保准确性
|
||||||
|
plugins_dir = settings.ROOT_PATH / "app" / "plugins"
|
||||||
|
backup_dir = settings.CONFIG_PATH / "plugins_backup"
|
||||||
|
|
||||||
|
if not plugins_dir.exists():
|
||||||
|
logger.info("插件目录不存在,跳过备份")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 确保备份目录存在
|
||||||
|
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 需要排除的文件和目录
|
||||||
|
exclude_items = {"__init__.py", "__pycache__", ".DS_Store"}
|
||||||
|
|
||||||
|
# 遍历插件目录,备份除排除项外的所有内容
|
||||||
|
for item in plugins_dir.iterdir():
|
||||||
|
if item.name in exclude_items:
|
||||||
|
continue
|
||||||
|
|
||||||
|
target_path = backup_dir / item.name
|
||||||
|
|
||||||
|
# 如果是目录
|
||||||
|
if item.is_dir():
|
||||||
|
if target_path.exists():
|
||||||
|
continue
|
||||||
|
shutil.copytree(item, target_path)
|
||||||
|
logger.info(f"已备份插件目录: {item.name}")
|
||||||
|
# 如果是文件
|
||||||
|
elif item.is_file():
|
||||||
|
if target_path.exists():
|
||||||
|
continue
|
||||||
|
shutil.copy2(item, target_path)
|
||||||
|
logger.info(f"已备份插件文件: {item.name}")
|
||||||
|
|
||||||
|
logger.info(f"插件备份完成,备份位置: {backup_dir}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"插件备份失败: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def restore_plugins():
|
||||||
|
"""
|
||||||
|
从备份恢复插件到app/plugins目录,恢复完成后删除备份(仅docker环境)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 非docker环境不处理
|
||||||
|
if not SystemUtils.is_docker():
|
||||||
|
return
|
||||||
|
|
||||||
|
# 使用绝对路径确保准确性
|
||||||
|
plugins_dir = settings.ROOT_PATH / "app" / "plugins"
|
||||||
|
backup_dir = settings.CONFIG_PATH / "plugins_backup"
|
||||||
|
|
||||||
|
if not backup_dir.exists():
|
||||||
|
logger.info("插件备份目录不存在,跳过恢复")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 系统被重置才恢复插件
|
||||||
|
if SystemHelper().is_system_reset():
|
||||||
|
|
||||||
|
# 确保插件目录存在
|
||||||
|
plugins_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 遍历备份目录,恢复所有内容
|
||||||
|
restored_count = 0
|
||||||
|
for item in backup_dir.iterdir():
|
||||||
|
target_path = plugins_dir / item.name
|
||||||
|
try:
|
||||||
|
# 如果是目录,且目录内有内容
|
||||||
|
if item.is_dir() and any(item.iterdir()):
|
||||||
|
if target_path.exists():
|
||||||
|
shutil.rmtree(target_path)
|
||||||
|
shutil.copytree(item, target_path)
|
||||||
|
logger.info(f"已恢复插件目录: {item.name}")
|
||||||
|
restored_count += 1
|
||||||
|
# 如果是文件
|
||||||
|
elif item.is_file():
|
||||||
|
shutil.copy2(item, target_path)
|
||||||
|
logger.info(f"已恢复插件文件: {item.name}")
|
||||||
|
restored_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"恢复插件 {item.name} 时发生错误: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(f"插件恢复完成,共恢复 {restored_count} 个项目")
|
||||||
|
|
||||||
|
# 删除备份目录
|
||||||
|
try:
|
||||||
|
shutil.rmtree(backup_dir)
|
||||||
|
logger.info(f"已删除插件备份目录: {backup_dir}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"删除备份目录失败: {str(e)}")
|
||||||
|
|
||||||
def __get_version_message(self) -> str:
|
def __get_version_message(self) -> str:
|
||||||
"""
|
"""
|
||||||
获取版本信息文本
|
获取版本信息文本
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ class SystemHelper:
|
|||||||
try:
|
try:
|
||||||
# 检查容器是否配置了自动重启策略
|
# 检查容器是否配置了自动重启策略
|
||||||
has_restart_policy = SystemHelper._check_restart_policy()
|
has_restart_policy = SystemHelper._check_restart_policy()
|
||||||
|
|
||||||
if has_restart_policy:
|
if has_restart_policy:
|
||||||
# 有重启策略,使用优雅退出方式
|
# 有重启策略,使用优雅退出方式
|
||||||
logger.info("检测到容器配置了自动重启策略,使用优雅重启方式...")
|
logger.info("检测到容器配置了自动重启策略,使用优雅重启方式...")
|
||||||
@@ -120,7 +119,6 @@ class SystemHelper:
|
|||||||
# 没有重启策略,使用Docker API强制重启
|
# 没有重启策略,使用Docker API强制重启
|
||||||
logger.info("容器未配置自动重启策略,使用Docker API重启...")
|
logger.info("容器未配置自动重启策略,使用Docker API重启...")
|
||||||
return SystemHelper._docker_api_restart()
|
return SystemHelper._docker_api_restart()
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error(f"重启失败: {str(err)}")
|
logger.error(f"重启失败: {str(err)}")
|
||||||
# 降级为Docker API重启
|
# 降级为Docker API重启
|
||||||
@@ -141,7 +139,6 @@ class SystemHelper:
|
|||||||
# 重启容器
|
# 重启容器
|
||||||
client.containers.get(container_id).restart()
|
client.containers.get(container_id).restart()
|
||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
except Exception as docker_err:
|
except Exception as docker_err:
|
||||||
return False, f"重启时发生错误:{str(docker_err)}"
|
return False, f"重启时发生错误:{str(docker_err)}"
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from app.startup.command_initializer import init_command, stop_command, restart_
|
|||||||
from app.startup.memory_initializer import init_memory_manager, stop_memory_manager
|
from app.startup.memory_initializer import init_memory_manager, stop_memory_manager
|
||||||
from app.startup.modules_initializer import init_modules, stop_modules
|
from app.startup.modules_initializer import init_modules, stop_modules
|
||||||
from app.startup.monitor_initializer import stop_monitor, init_monitor
|
from app.startup.monitor_initializer import stop_monitor, init_monitor
|
||||||
from app.startup.plugins_initializer import init_plugins, stop_plugins, sync_plugins, backup_plugins, restore_plugins
|
from app.startup.plugins_initializer import init_plugins, stop_plugins, sync_plugins
|
||||||
from app.startup.routers_initializer import init_routers
|
from app.startup.routers_initializer import init_routers
|
||||||
from app.startup.scheduler_initializer import stop_scheduler, init_scheduler, init_plugin_scheduler
|
from app.startup.scheduler_initializer import stop_scheduler, init_scheduler, init_plugin_scheduler
|
||||||
from app.startup.workflow_initializer import init_workflow, stop_workflow
|
from app.startup.workflow_initializer import init_workflow, stop_workflow
|
||||||
@@ -41,7 +41,7 @@ async def lifespan(app: FastAPI):
|
|||||||
# 初始化路由
|
# 初始化路由
|
||||||
init_routers(app)
|
init_routers(app)
|
||||||
# 恢复插件备份
|
# 恢复插件备份
|
||||||
restore_plugins()
|
SystemChain().restore_plugins()
|
||||||
# 初始化插件
|
# 初始化插件
|
||||||
init_plugins()
|
init_plugins()
|
||||||
# 初始化定时器
|
# 初始化定时器
|
||||||
@@ -70,7 +70,7 @@ async def lifespan(app: FastAPI):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
# 备份插件
|
# 备份插件
|
||||||
backup_plugins()
|
SystemChain().backup_plugins()
|
||||||
# 停止内存管理器
|
# 停止内存管理器
|
||||||
stop_memory_manager()
|
stop_memory_manager()
|
||||||
# 停止工作流
|
# 停止工作流
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import shutil
|
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
from app.core.plugin import PluginManager
|
from app.core.plugin import PluginManager
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.system import SystemUtils
|
|
||||||
from app.helper.system import SystemHelper
|
|
||||||
|
|
||||||
|
|
||||||
async def sync_plugins() -> bool:
|
async def sync_plugins() -> bool:
|
||||||
@@ -79,105 +75,3 @@ def stop_plugins():
|
|||||||
plugin_manager.stop_monitor()
|
plugin_manager.stop_monitor()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"停止插件时发生错误:{e}", exc_info=True)
|
logger.error(f"停止插件时发生错误:{e}", exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
def backup_plugins():
|
|
||||||
"""
|
|
||||||
备份插件到用户配置目录(仅docker环境)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 非docker环境不处理
|
|
||||||
if not SystemUtils.is_docker():
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 使用绝对路径确保准确性
|
|
||||||
plugins_dir = settings.ROOT_PATH / "app" / "plugins"
|
|
||||||
backup_dir = settings.CONFIG_PATH / "plugins_backup"
|
|
||||||
|
|
||||||
if not plugins_dir.exists():
|
|
||||||
logger.info("插件目录不存在,跳过备份")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 确保备份目录存在
|
|
||||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# 需要排除的文件和目录
|
|
||||||
exclude_items = {"__init__.py", "__pycache__", ".DS_Store"}
|
|
||||||
|
|
||||||
# 遍历插件目录,备份除排除项外的所有内容
|
|
||||||
for item in plugins_dir.iterdir():
|
|
||||||
if item.name in exclude_items:
|
|
||||||
continue
|
|
||||||
|
|
||||||
target_path = backup_dir / item.name
|
|
||||||
|
|
||||||
# 如果是目录
|
|
||||||
if item.is_dir():
|
|
||||||
if target_path.exists():
|
|
||||||
shutil.rmtree(target_path)
|
|
||||||
shutil.copytree(item, target_path)
|
|
||||||
logger.info(f"已备份插件目录: {item.name}")
|
|
||||||
# 如果是文件
|
|
||||||
elif item.is_file():
|
|
||||||
shutil.copy2(item, target_path)
|
|
||||||
logger.info(f"已备份插件文件: {item.name}")
|
|
||||||
|
|
||||||
logger.info(f"插件备份完成,备份位置: {backup_dir}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"插件备份失败: {str(e)}")
|
|
||||||
|
|
||||||
|
|
||||||
def restore_plugins():
|
|
||||||
"""
|
|
||||||
从备份恢复插件到app/plugins目录,恢复完成后删除备份(仅docker环境)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 非docker环境不处理
|
|
||||||
if not SystemUtils.is_docker():
|
|
||||||
return
|
|
||||||
|
|
||||||
# 使用绝对路径确保准确性
|
|
||||||
plugins_dir = settings.ROOT_PATH / "app" / "plugins"
|
|
||||||
backup_dir = settings.CONFIG_PATH / "plugins_backup"
|
|
||||||
|
|
||||||
if not backup_dir.exists():
|
|
||||||
logger.info("插件备份目录不存在,跳过恢复")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 系统被重置才恢复插件
|
|
||||||
if SystemHelper().is_system_reset():
|
|
||||||
|
|
||||||
# 确保插件目录存在
|
|
||||||
plugins_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# 遍历备份目录,恢复所有内容
|
|
||||||
restored_count = 0
|
|
||||||
for item in backup_dir.iterdir():
|
|
||||||
target_path = plugins_dir / item.name
|
|
||||||
try:
|
|
||||||
# 如果是目录,且目录内有内容
|
|
||||||
if item.is_dir() and any(item.iterdir()):
|
|
||||||
if target_path.exists():
|
|
||||||
shutil.rmtree(target_path)
|
|
||||||
shutil.copytree(item, target_path)
|
|
||||||
logger.info(f"已恢复插件目录: {item.name}")
|
|
||||||
restored_count += 1
|
|
||||||
# 如果是文件
|
|
||||||
elif item.is_file():
|
|
||||||
shutil.copy2(item, target_path)
|
|
||||||
logger.info(f"已恢复插件文件: {item.name}")
|
|
||||||
restored_count += 1
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"恢复插件 {item.name} 时发生错误: {str(e)}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.info(f"插件恢复完成,共恢复 {restored_count} 个项目")
|
|
||||||
|
|
||||||
# 删除备份目录
|
|
||||||
try:
|
|
||||||
shutil.rmtree(backup_dir)
|
|
||||||
logger.info(f"已删除插件备份目录: {backup_dir}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"删除备份目录失败: {str(e)}")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user