fix:优化插件分身性能

feat:分身插件删除时清理文件
This commit is contained in:
jxxghp
2025-05-26 13:21:47 +08:00
parent e8ab20acf2
commit a6d1bd12a2
3 changed files with 129 additions and 85 deletions
+25 -8
View File
@@ -1,4 +1,5 @@
import mimetypes import mimetypes
import shutil
from typing import Annotated, Any, List, Optional from typing import Annotated, Any, List, Optional
from fastapi import APIRouter, Depends, Header, HTTPException from fastapi import APIRouter, Depends, Header, HTTPException
@@ -331,10 +332,11 @@ def reset_plugin(plugin_id: str,
""" """
根据插件ID重置插件配置及数据 根据插件ID重置插件配置及数据
""" """
plugin_manager = PluginManager()
# 删除配置 # 删除配置
PluginManager().delete_plugin_config(plugin_id) plugin_manager.delete_plugin_config(plugin_id)
# 删除插件所有数据 # 删除插件所有数据
PluginManager().delete_plugin_data(plugin_id) plugin_manager.delete_plugin_data(plugin_id)
# 重新加载插件 # 重新加载插件
reload_plugin(plugin_id) reload_plugin(plugin_id)
return schemas.Response(success=True) return schemas.Response(success=True)
@@ -455,10 +457,11 @@ def set_plugin_config(plugin_id: str, conf: dict,
""" """
更新插件配置 更新插件配置
""" """
plugin_manager = PluginManager()
# 保存配置 # 保存配置
PluginManager().save_plugin_config(plugin_id, conf) plugin_manager.save_plugin_config(plugin_id, conf)
# 重新生效插件 # 重新生效插件
PluginManager().init_plugin(plugin_id, conf) plugin_manager.init_plugin(plugin_id, conf)
# 注册插件服务 # 注册插件服务
register_plugin(plugin_id) register_plugin(plugin_id)
return schemas.Response(success=True) return schemas.Response(success=True)
@@ -470,20 +473,34 @@ def uninstall_plugin(plugin_id: str,
""" """
卸载插件 卸载插件
""" """
config_oper = SystemConfigOper()
# 删除已安装信息 # 删除已安装信息
install_plugins = SystemConfigOper().get(SystemConfigKey.UserInstalledPlugins) or [] install_plugins = config_oper.get(SystemConfigKey.UserInstalledPlugins) or []
for plugin in install_plugins: for plugin in install_plugins:
if plugin == plugin_id: if plugin == plugin_id:
install_plugins.remove(plugin) install_plugins.remove(plugin)
break break
# 保存 config_oper.set(SystemConfigKey.UserInstalledPlugins, install_plugins)
SystemConfigOper().set(SystemConfigKey.UserInstalledPlugins, install_plugins)
# 移除插件API # 移除插件API
remove_plugin_api(plugin_id) remove_plugin_api(plugin_id)
# 移除插件服务 # 移除插件服务
Scheduler().remove_plugin_job(plugin_id) Scheduler().remove_plugin_job(plugin_id)
# 判断是否为分身
plugin_manager = PluginManager()
plugin_class = plugin_manager.plugins.get(plugin_id)
if getattr(plugin_class, "is_clone", False):
# 如果是分身插件,则删除分身数据和配置
plugin_manager.delete_plugin_config(plugin_id)
plugin_manager.delete_plugin_data(plugin_id)
# 删除分身文件
plugin_base_dir = settings.ROOT_PATH / "app" / "plugins" / plugin_id.lower()
if plugin_base_dir.exists():
try:
shutil.rmtree(plugin_base_dir)
except Exception as e:
logger.error(f"删除插件分身目录 {plugin_base_dir} 失败: {str(e)}")
# 移除插件 # 移除插件
PluginManager().remove_plugin(plugin_id) plugin_manager.remove_plugin(plugin_id)
return schemas.Response(success=True) return schemas.Response(success=True)
+50 -25
View File
@@ -226,13 +226,21 @@ class PluginManager(metaclass=Singleton):
logger.info("插件停止完成") logger.info("插件停止完成")
@property @property
def running_plugins(self): def running_plugins(self) -> Dict[str, Any]:
""" """
获取运行态插件列表 获取运行态插件列表
:return: 运行态插件列表 :return: 运行态插件列表
""" """
return self._running_plugins return self._running_plugins
@property
def plugins(self) -> Dict[str, Any]:
"""
获取插件列表
:return: 插件列表
"""
return self._plugins
def reload_monitor(self): def reload_monitor(self):
""" """
重新加载插件文件修改监测 重新加载插件文件修改监测
@@ -1019,7 +1027,7 @@ class PluginManager(metaclass=Singleton):
return None return None
def clone_plugin(self, plugin_id: str, suffix: str, name: str, description: str, def clone_plugin(self, plugin_id: str, suffix: str, name: str, description: str,
version: str = None, icon: str = None) -> Tuple[bool, str]: version: str = None, icon: str = None) -> Tuple[bool, str]:
""" """
创建插件分身 创建插件分身
:param plugin_id: 原插件ID :param plugin_id: 原插件ID
@@ -1061,7 +1069,13 @@ class PluginManager(metaclass=Singleton):
# 修改插件文件内容 # 修改插件文件内容
success, msg = self._modify_plugin_files( success, msg = self._modify_plugin_files(
clone_plugin_dir, plugin_id, clone_id, suffix, name, description, version, icon plugin_dir=clone_plugin_dir,
original_id=plugin_id,
suffix=suffix,
name=name,
description=description,
version=version,
icon=icon
) )
if not success: if not success:
@@ -1083,16 +1097,17 @@ class PluginManager(metaclass=Singleton):
# 复制原插件配置作为分身插件的初始配置 # 复制原插件配置作为分身插件的初始配置
clone_config = original_config.copy() clone_config = original_config.copy()
# 可以在这里修改一些默认值,比如禁用分身插件 # 可以在这里修改一些默认值,比如禁用分身插件
clone_config['enable'] = False # 默认禁用分身插件,让用户手动配置 # 默认禁用分身插件,让用户手动配置
clone_config['enable'] = False
clone_config['enabled'] = False
self.save_plugin_config(clone_id, clone_config) self.save_plugin_config(clone_id, clone_config)
logger.info(f"已为分身插件 {clone_id} 设置初始配置") logger.info(f"已为分身插件 {clone_id} 设置初始配置")
else: else:
logger.info(f"原插件 {plugin_id} 没有配置,分身插件 {clone_id} 将使用默认配置") logger.info(f"原插件 {plugin_id} 没有配置,分身插件 {clone_id} 将使用默认配置")
# 重新初始化插件系统以完全注册新插件 # 注册分身插件的API和服务
logger.info(f"正在重新注册插件系统以识别分身插件 {clone_id}...") logger.info(f"正在注册分身插件 {clone_id} ...")
self.init_config() PluginManager().reload_plugin(plugin_id)
# 确保分身插件正确初始化配置 # 确保分身插件正确初始化配置
if clone_id in self._running_plugins: if clone_id in self._running_plugins:
clone_instance = self._running_plugins[clone_id] clone_instance = self._running_plugins[clone_id]
@@ -1102,11 +1117,6 @@ class PluginManager(metaclass=Singleton):
clone_instance.init_plugin(clone_config) clone_instance.init_plugin(clone_config)
logger.info(f"分身插件 {clone_id} 配置重新初始化完成") logger.info(f"分身插件 {clone_id} 配置重新初始化完成")
# 注册分身插件的API和服务
logger.info(f"正在注册分身插件 {clone_id} 的API和服务...")
from app.api.endpoints.plugin import register_plugin
register_plugin(clone_id)
logger.info(f"插件分身 {clone_id} 创建成功") logger.info(f"插件分身 {clone_id} 创建成功")
return True, "插件分身创建成功" return True, "插件分身创建成功"
@@ -1114,14 +1124,13 @@ class PluginManager(metaclass=Singleton):
logger.error(f"创建插件分身失败:{str(e)}") logger.error(f"创建插件分身失败:{str(e)}")
return False, f"创建插件分身失败:{str(e)}" return False, f"创建插件分身失败:{str(e)}"
def _modify_plugin_files(self, plugin_dir: Path, original_id: str, clone_id: str, def _modify_plugin_files(self, plugin_dir: Path, original_id: str, suffix: str,
suffix: str, name: str, description: str, version: str = None, name: str, description: str, version: str = None,
icon: str = None) -> Tuple[bool, str]: icon: str = None) -> Tuple[bool, str]:
""" """
修改插件文件中的类名和相关信息 修改插件文件中的类名和相关信息
:param plugin_dir: 插件目录 :param plugin_dir: 插件目录
:param original_id: 原插件ID :param original_id: 原插件ID
:param clone_id: 分身插件ID
:param suffix: 分身后缀 :param suffix: 分身后缀
:param name: 分身名称 :param name: 分身名称
:param description: 分身描述 :param description: 分身描述
@@ -1143,7 +1152,13 @@ class PluginManager(metaclass=Singleton):
init_file = plugin_dir / "__init__.py" init_file = plugin_dir / "__init__.py"
if init_file.exists(): if init_file.exists():
success, msg = self._modify_python_file( success, msg = self._modify_python_file(
init_file, original_class_name, clone_class_name, name, description, version, icon file_path=init_file,
original_class_name=original_class_name,
clone_class_name=clone_class_name,
name=name,
description=description,
version=version,
icon=icon
) )
if not success: if not success:
return False, msg return False, msg
@@ -1152,7 +1167,9 @@ class PluginManager(metaclass=Singleton):
dist_dir = plugin_dir / "dist" dist_dir = plugin_dir / "dist"
if dist_dir.exists(): if dist_dir.exists():
success, msg = self._modify_federation_files( success, msg = self._modify_federation_files(
dist_dir, original_class_name, clone_class_name dist_dir=dist_dir,
original_class_name=original_class_name,
clone_class_name=clone_class_name
) )
if not success: if not success:
return False, msg return False, msg
@@ -1163,9 +1180,10 @@ class PluginManager(metaclass=Singleton):
logger.error(f"修改插件文件失败:{str(e)}") logger.error(f"修改插件文件失败:{str(e)}")
return False, f"修改插件文件失败:{str(e)}" return False, f"修改插件文件失败:{str(e)}"
def _modify_python_file(self, file_path: Path, original_class_name: str, @staticmethod
clone_class_name: str, name: str, description: str, def _modify_python_file(file_path: Path, original_class_name: str,
version: str = None, icon: str = None) -> Tuple[bool, str]: clone_class_name: str, name: str, description: str,
version: str = None, icon: str = None) -> Tuple[bool, str]:
""" """
修改Python文件中的类名和插件信息 修改Python文件中的类名和插件信息
""" """
@@ -1225,6 +1243,12 @@ class PluginManager(metaclass=Singleton):
else: else:
logger.info("未提供自定义图标,保持原插件图标") logger.info("未提供自定义图标,保持原插件图标")
# 添加分身标志
if "def init_plugin(self" in content:
init_index = content.index("def init_plugin(self")
# 在 def init_plugin(self 前添加 is_clone = True
content = content[:init_index] + "is_clone = True\n\n " + content[init_index:]
with open(file_path, 'w', encoding='utf-8') as f: with open(file_path, 'w', encoding='utf-8') as f:
f.write(content) f.write(content)
@@ -1236,7 +1260,7 @@ class PluginManager(metaclass=Singleton):
return False, f"修改Python文件失败:{str(e)}" return False, f"修改Python文件失败:{str(e)}"
def _modify_federation_files(self, dist_dir: Path, original_class_name: str, def _modify_federation_files(self, dist_dir: Path, original_class_name: str,
clone_class_name: str) -> Tuple[bool, str]: clone_class_name: str) -> Tuple[bool, str]:
""" """
修改联邦插件的前端文件 修改联邦插件的前端文件
""" """
@@ -1303,7 +1327,8 @@ class PluginManager(metaclass=Singleton):
logger.error(f"修改联邦插件文件失败:{str(e)}") logger.error(f"修改联邦插件文件失败:{str(e)}")
return False, f"修改联邦插件文件失败:{str(e)}" return False, f"修改联邦插件文件失败:{str(e)}"
def _rename_federation_assets(self, dist_dir: Path, original_class_name: str, clone_class_name: str): @staticmethod
def _rename_federation_assets(dist_dir: Path, original_class_name: str, clone_class_name: str):
""" """
重命名联邦插件的资源文件,避免文件名冲突 重命名联邦插件的资源文件,避免文件名冲突
""" """
@@ -1328,5 +1353,5 @@ class PluginManager(metaclass=Singleton):
logger.debug(f"重命名联邦插件文件:{file_name} -> {new_name}") logger.debug(f"重命名联邦插件文件:{file_name} -> {new_name}")
except Exception as e: except Exception as e:
logger.warning(f"重命名联邦插件资源文件失败:{str(e)}")
# 重命名失败不影响整体流程 # 重命名失败不影响整体流程
logger.warning(f"重命名联邦插件资源文件失败:{str(e)}")
+2
View File
@@ -34,6 +34,8 @@ class _PluginBase(metaclass=ABCMeta):
plugin_desc: Optional[str] = "" plugin_desc: Optional[str] = ""
# 插件顺序 # 插件顺序
plugin_order: Optional[int] = 9999 plugin_order: Optional[int] = 9999
# 是否为插件分身
is_clone: bool = False
def __init__(self): def __init__(self):
# 插件数据 # 插件数据