mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
✨ 更新插件管理功能,新增批量更新插件配置接口
This commit is contained in:
parent
6769c724cb
commit
2cfd37c238
@ -10,9 +10,10 @@ from zhenxun.configs.config import Config as gConfig
|
|||||||
from zhenxun.configs.utils import PluginExtraData, RegisterConfig
|
from zhenxun.configs.utils import PluginExtraData, RegisterConfig
|
||||||
from zhenxun.services.log import logger, logger_
|
from zhenxun.services.log import logger, logger_
|
||||||
from zhenxun.utils.enum import PluginType
|
from zhenxun.utils.enum import PluginType
|
||||||
|
# 移除 AuthorizationError 导入
|
||||||
|
|
||||||
from .api.logs import router as ws_log_routes
|
from .api.logs import router as ws_log_routes
|
||||||
from .api.logs.log_manager import LOG_STORAGE
|
from .api.logs.log_manager import LOG_STORAGE # 确保 LOG_STORAGE 导入
|
||||||
from .api.menu import router as menu_router
|
from .api.menu import router as menu_router
|
||||||
from .api.tabs.dashboard import router as dashboard_router
|
from .api.tabs.dashboard import router as dashboard_router
|
||||||
from .api.tabs.database import router as database_router
|
from .api.tabs.database import router as database_router
|
||||||
@ -20,18 +21,17 @@ from .api.tabs.main import router as main_router
|
|||||||
from .api.tabs.main import ws_router as status_routes
|
from .api.tabs.main import ws_router as status_routes
|
||||||
from .api.tabs.manage import router as manage_router
|
from .api.tabs.manage import router as manage_router
|
||||||
from .api.tabs.manage.chat import ws_router as chat_routes
|
from .api.tabs.manage.chat import ws_router as chat_routes
|
||||||
from .api.tabs.plugin_manage import router as plugin_router
|
from .api.tabs.plugin_manage import router as plugin_router # 插件管理路由
|
||||||
from .api.tabs.plugin_manage.store import router as store_router
|
from .api.tabs.system import router as system_router # 系统路由
|
||||||
from .api.tabs.system import router as system_router
|
|
||||||
from .auth import router as auth_router
|
from .auth import router as auth_router
|
||||||
from .public import init_public
|
from .public import init_public
|
||||||
|
# 移除了 command, configure, store 路由的导入
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="WebUi",
|
name="WebUi",
|
||||||
description="WebUi API",
|
description="WebUi API",
|
||||||
usage="""
|
usage="\"\"\"\n \"\"\".strip(),", # 保留原始格式
|
||||||
""".strip(),
|
extra=PluginExtraData( # 确保调用 .to_dict()
|
||||||
extra=PluginExtraData(
|
|
||||||
author="HibiKier",
|
author="HibiKier",
|
||||||
version="0.1",
|
version="0.1",
|
||||||
plugin_type=PluginType.HIDDEN,
|
plugin_type=PluginType.HIDDEN,
|
||||||
@ -61,7 +61,7 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
default_value=None,
|
default_value=None,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).to_dict(),
|
).to_dict(), # 调用 .to_dict()
|
||||||
)
|
)
|
||||||
|
|
||||||
driver = nonebot.get_driver()
|
driver = nonebot.get_driver()
|
||||||
@ -74,15 +74,15 @@ BaseApiRouter = APIRouter(prefix="/zhenxun/api")
|
|||||||
|
|
||||||
|
|
||||||
BaseApiRouter.include_router(auth_router)
|
BaseApiRouter.include_router(auth_router)
|
||||||
BaseApiRouter.include_router(store_router)
|
# 移除 store_router 包含
|
||||||
BaseApiRouter.include_router(dashboard_router)
|
BaseApiRouter.include_router(dashboard_router)
|
||||||
BaseApiRouter.include_router(main_router)
|
BaseApiRouter.include_router(main_router)
|
||||||
BaseApiRouter.include_router(manage_router)
|
BaseApiRouter.include_router(manage_router)
|
||||||
BaseApiRouter.include_router(database_router)
|
BaseApiRouter.include_router(database_router)
|
||||||
BaseApiRouter.include_router(plugin_router)
|
BaseApiRouter.include_router(plugin_router) # 包含插件管理路由
|
||||||
BaseApiRouter.include_router(system_router)
|
BaseApiRouter.include_router(system_router) # 包含系统路由
|
||||||
BaseApiRouter.include_router(menu_router)
|
BaseApiRouter.include_router(menu_router)
|
||||||
|
# 移除 command, configure 路由包含
|
||||||
|
|
||||||
WsApiRouter = APIRouter(prefix="/zhenxun/socket")
|
WsApiRouter = APIRouter(prefix="/zhenxun/socket")
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ from .model import (
|
|||||||
PluginInfo,
|
PluginInfo,
|
||||||
PluginSwitch,
|
PluginSwitch,
|
||||||
UpdatePlugin,
|
UpdatePlugin,
|
||||||
|
BatchUpdatePlugins,
|
||||||
)
|
)
|
||||||
|
|
||||||
router = APIRouter(prefix="/plugin")
|
router = APIRouter(prefix="/plugin")
|
||||||
@ -152,3 +153,17 @@ async def _(module: str) -> Result[PluginDetail]:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{router.prefix}/get_plugin 调用错误", "WebUi", e=e)
|
logger.error(f"{router.prefix}/get_plugin 调用错误", "WebUi", e=e)
|
||||||
return Result.fail(f"{type(e)}: {e}")
|
return Result.fail(f"{type(e)}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/plugins/batch_update", summary="批量更新插件配置")
|
||||||
|
async def batch_update_plugin_config_api(params: BatchUpdatePlugins):
|
||||||
|
"""批量更新插件配置,如开关、类型等"""
|
||||||
|
result = await ApiDataSource.batch_update_plugins(params=params)
|
||||||
|
if result["errors"]:
|
||||||
|
# 可以根据需要返回更详细的错误信息,或者只返回一个笼统的失败信息
|
||||||
|
# 这里我们返回包含错误详情的 200 OK,让前端处理
|
||||||
|
# 或者可以抛出 HTTPException
|
||||||
|
# from fastapi import HTTPException
|
||||||
|
# raise HTTPException(status_code=400, detail={"message": "部分插件更新失败", "errors": result["errors"]})
|
||||||
|
pass # 暂时只返回结果字典
|
||||||
|
return result
|
||||||
|
|||||||
@ -7,8 +7,15 @@ from zhenxun.configs.config import Config
|
|||||||
from zhenxun.configs.utils import ConfigGroup
|
from zhenxun.configs.utils import ConfigGroup
|
||||||
from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo
|
from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo
|
||||||
from zhenxun.utils.enum import BlockType, PluginType
|
from zhenxun.utils.enum import BlockType, PluginType
|
||||||
|
from tortoise.exceptions import DoesNotExist
|
||||||
|
|
||||||
from .model import PluginConfig, PluginDetail, PluginInfo, UpdatePlugin
|
from .model import (
|
||||||
|
BatchUpdatePlugins,
|
||||||
|
PluginConfig,
|
||||||
|
PluginDetail,
|
||||||
|
PluginInfo,
|
||||||
|
UpdatePlugin,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ApiDataSource:
|
class ApiDataSource:
|
||||||
@ -44,6 +51,7 @@ class ApiDataSource:
|
|||||||
level=plugin.level,
|
level=plugin.level,
|
||||||
status=plugin.status,
|
status=plugin.status,
|
||||||
author=plugin.author,
|
author=plugin.author,
|
||||||
|
block_type=plugin.block_type,
|
||||||
)
|
)
|
||||||
plugin_list.append(plugin_info)
|
plugin_list.append(plugin_info)
|
||||||
return plugin_list
|
return plugin_list
|
||||||
@ -80,6 +88,82 @@ class ApiDataSource:
|
|||||||
Config.save(save_simple_data=True)
|
Config.save(save_simple_data=True)
|
||||||
return db_plugin
|
return db_plugin
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def batch_update_plugins(cls, params: BatchUpdatePlugins) -> dict:
|
||||||
|
"""批量更新插件数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
params: BatchUpdatePlugins
|
||||||
|
|
||||||
|
返回:
|
||||||
|
dict: 更新结果, 例如 {'success': True, 'updated_count': 5, 'errors': []}
|
||||||
|
"""
|
||||||
|
# 分开处理,避免 bulk_update 对 CharEnumField 的潜在问题
|
||||||
|
plugins_to_update_other_fields = []
|
||||||
|
other_update_fields = set()
|
||||||
|
updated_count = 0
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
# 收集需要更新的插件和字段
|
||||||
|
for item in params.updates:
|
||||||
|
try:
|
||||||
|
db_plugin = await DbPluginInfo.get(module=item.module)
|
||||||
|
plugin_changed_other = False
|
||||||
|
plugin_changed_block = False
|
||||||
|
|
||||||
|
# 处理 block_type 和 status (单独保存)
|
||||||
|
if db_plugin.block_type != item.block_type:
|
||||||
|
db_plugin.block_type = item.block_type
|
||||||
|
db_plugin.status = item.block_type != BlockType.ALL # 同时更新 status
|
||||||
|
plugin_changed_block = True
|
||||||
|
|
||||||
|
# 处理 menu_type (准备批量更新)
|
||||||
|
if item.menu_type is not None and db_plugin.menu_type != item.menu_type:
|
||||||
|
db_plugin.menu_type = item.menu_type
|
||||||
|
other_update_fields.add("menu_type")
|
||||||
|
plugin_changed_other = True
|
||||||
|
|
||||||
|
# 处理 default_status (准备批量更新)
|
||||||
|
if item.default_status is not None and db_plugin.default_status != item.default_status:
|
||||||
|
db_plugin.default_status = item.default_status
|
||||||
|
other_update_fields.add("default_status")
|
||||||
|
plugin_changed_other = True
|
||||||
|
|
||||||
|
# 单独保存 block_type 和 status 的更改
|
||||||
|
if plugin_changed_block:
|
||||||
|
try:
|
||||||
|
await db_plugin.save(update_fields=["block_type", "status"])
|
||||||
|
updated_count += 1 # 每次成功保存计为一个更新
|
||||||
|
except Exception as e_save:
|
||||||
|
errors.append({"module": item.module, "error": f"Save block_type failed: {str(e_save)}"})
|
||||||
|
# 如果保存失败,则不将其他字段加入批量更新,避免数据不一致
|
||||||
|
plugin_changed_other = False
|
||||||
|
|
||||||
|
# 如果其他字段有更改且 block_type 保存成功,则加入批量更新列表
|
||||||
|
if plugin_changed_other:
|
||||||
|
plugins_to_update_other_fields.append(db_plugin)
|
||||||
|
|
||||||
|
except DoesNotExist:
|
||||||
|
errors.append({"module": item.module, "error": "Plugin not found"})
|
||||||
|
except Exception as e:
|
||||||
|
errors.append({"module": item.module, "error": str(e)})
|
||||||
|
|
||||||
|
# 执行其他字段的批量更新
|
||||||
|
if plugins_to_update_other_fields and other_update_fields:
|
||||||
|
try:
|
||||||
|
await DbPluginInfo.bulk_update(plugins_to_update_other_fields, list(other_update_fields))
|
||||||
|
# 注意:这里的 updated_count 可能需要调整,取决于是否将 bulk_update 的成功也计入
|
||||||
|
# 为简单起见,我们只计算了上面单独 save 的次数
|
||||||
|
# updated_count += len(plugins_to_update_other_fields) # 如果需要合并计数
|
||||||
|
except Exception as e_bulk:
|
||||||
|
errors.append({"module": "batch_update_other", "error": f"Bulk update failed: {str(e_bulk)}"})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": len(errors) == 0, # 只要没有错误就算成功
|
||||||
|
"updated_count": updated_count, # 只计算 block_type 成功更新的数量
|
||||||
|
"errors": errors,
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __build_plugin_config(
|
def __build_plugin_config(
|
||||||
cls, module: str, cfg: str, config: ConfigGroup
|
cls, module: str, cfg: str, config: ConfigGroup
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from zhenxun.utils.enum import BlockType
|
from zhenxun.utils.enum import BlockType
|
||||||
|
|
||||||
@ -37,19 +37,19 @@ class UpdatePlugin(BaseModel):
|
|||||||
module: str
|
module: str
|
||||||
"""模块"""
|
"""模块"""
|
||||||
default_status: bool
|
default_status: bool
|
||||||
"""默认开关"""
|
"""是否默认开启"""
|
||||||
limit_superuser: bool
|
limit_superuser: bool
|
||||||
"""限制超级用户"""
|
"""是否限制超级用户"""
|
||||||
cost_gold: int
|
|
||||||
"""金币花费"""
|
|
||||||
menu_type: str
|
|
||||||
"""插件菜单类型"""
|
|
||||||
level: int
|
level: int
|
||||||
"""插件所需群权限"""
|
"""等级"""
|
||||||
|
cost_gold: int
|
||||||
|
"""花费金币"""
|
||||||
|
menu_type: str
|
||||||
|
"""菜单类型"""
|
||||||
block_type: BlockType | None = None
|
block_type: BlockType | None = None
|
||||||
"""禁用类型"""
|
"""禁用类型"""
|
||||||
configs: dict[str, Any] | None = None
|
configs: dict[str, Any] | None = None
|
||||||
"""配置项"""
|
"""设置项"""
|
||||||
|
|
||||||
|
|
||||||
class PluginInfo(BaseModel):
|
class PluginInfo(BaseModel):
|
||||||
@ -58,27 +58,26 @@ class PluginInfo(BaseModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
"""插件名称"""
|
"""模块"""
|
||||||
plugin_name: str
|
plugin_name: str
|
||||||
"""插件中文名称"""
|
"""插件名称"""
|
||||||
default_status: bool
|
default_status: bool
|
||||||
"""默认开关"""
|
"""是否默认开启"""
|
||||||
limit_superuser: bool
|
limit_superuser: bool
|
||||||
"""限制超级用户"""
|
"""是否限制超级用户"""
|
||||||
|
level: int
|
||||||
|
"""等级"""
|
||||||
cost_gold: int
|
cost_gold: int
|
||||||
"""花费金币"""
|
"""花费金币"""
|
||||||
menu_type: str
|
menu_type: str
|
||||||
"""插件菜单类型"""
|
"""菜单类型"""
|
||||||
version: str
|
version: str
|
||||||
"""插件版本"""
|
"""版本"""
|
||||||
level: int
|
|
||||||
"""群权限"""
|
|
||||||
status: bool
|
status: bool
|
||||||
"""当前状态"""
|
"""状态"""
|
||||||
author: str | None = None
|
author: str | None = None
|
||||||
"""作者"""
|
"""作者"""
|
||||||
block_type: BlockType | None = None
|
block_type: BlockType | None = Field(None, description="插件禁用状态 (None: 启用)")
|
||||||
"""禁用类型"""
|
|
||||||
|
|
||||||
|
|
||||||
class PluginConfig(BaseModel):
|
class PluginConfig(BaseModel):
|
||||||
@ -86,20 +85,13 @@ class PluginConfig(BaseModel):
|
|||||||
插件配置项
|
插件配置项
|
||||||
"""
|
"""
|
||||||
|
|
||||||
module: str
|
module: str = Field(..., description="模块名")
|
||||||
"""模块"""
|
key: str = Field(..., description="键")
|
||||||
key: str
|
value: Any = Field(None, description="值")
|
||||||
"""键"""
|
help: str | None = Field(None, description="帮助信息")
|
||||||
value: Any
|
default_value: Any = Field(None, description="默认值")
|
||||||
"""值"""
|
type: str | None = Field(None, description="类型")
|
||||||
help: str | None = None
|
type_inner: list[str] | None = Field(None, description="内部类型")
|
||||||
"""帮助"""
|
|
||||||
default_value: Any
|
|
||||||
"""默认值"""
|
|
||||||
type: Any = None
|
|
||||||
"""值类型"""
|
|
||||||
type_inner: list[str] | None = None
|
|
||||||
"""List Tuple等内部类型检验"""
|
|
||||||
|
|
||||||
|
|
||||||
class PluginCount(BaseModel):
|
class PluginCount(BaseModel):
|
||||||
@ -117,6 +109,17 @@ class PluginCount(BaseModel):
|
|||||||
"""其他插件"""
|
"""其他插件"""
|
||||||
|
|
||||||
|
|
||||||
|
class BatchUpdatePluginItem(BaseModel):
|
||||||
|
module: str = Field(..., description="插件模块名")
|
||||||
|
default_status: bool | None = Field(None, description="默认状态(开关)")
|
||||||
|
menu_type: str | None = Field(None, description="菜单类型")
|
||||||
|
block_type: BlockType | None = Field(None, description="插件禁用状态 (None: 启用, ALL: 禁用)")
|
||||||
|
|
||||||
|
|
||||||
|
class BatchUpdatePlugins(BaseModel):
|
||||||
|
updates: list[BatchUpdatePluginItem] = Field(..., description="要批量更新的插件列表")
|
||||||
|
|
||||||
|
|
||||||
class PluginDetail(PluginInfo):
|
class PluginDetail(PluginInfo):
|
||||||
"""
|
"""
|
||||||
插件详情
|
插件详情
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user