mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-08 00:46:57 +08:00
feat(plugin): 聚合插件侧栏导航
- PluginManager.get_plugin_sidebar_nav:已启用 Vue 插件且实现 get_sidebar_nav - schemas.PluginSidebarNavItem 与 verify_token 鉴权接口
This commit is contained in:
@@ -260,6 +260,14 @@ async def remotes(token: str) -> Any:
|
|||||||
return PluginManager().get_plugin_remotes()
|
return PluginManager().get_plugin_remotes()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/sidebar_nav", summary="获取插件侧栏导航项", response_model=List[schemas.PluginSidebarNavItem])
|
||||||
|
def plugin_sidebar_nav(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
聚合已启用 Vue 插件声明的侧栏入口(get_sidebar_nav),供前端主界面侧栏展示。
|
||||||
|
"""
|
||||||
|
return PluginManager().get_plugin_sidebar_nav()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/form/{plugin_id}", summary="获取插件表单页面")
|
@router.get("/form/{plugin_id}", summary="获取插件表单页面")
|
||||||
def plugin_form(plugin_id: str,
|
def plugin_form(plugin_id: str,
|
||||||
_: User = Depends(get_current_active_superuser)) -> dict:
|
_: User = Depends(get_current_active_superuser)) -> dict:
|
||||||
|
|||||||
@@ -809,6 +809,64 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
})
|
})
|
||||||
return remotes
|
return remotes
|
||||||
|
|
||||||
|
def get_plugin_sidebar_nav(self) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
聚合所有已启用 Vue 插件的侧栏导航项(get_sidebar_nav)。
|
||||||
|
"""
|
||||||
|
valid_sections = {"start", "discovery", "subscribe", "organize", "system"}
|
||||||
|
valid_permissions = {"subscribe", "discovery", "search", "manage", "admin"}
|
||||||
|
items: List[Dict[str, Any]] = []
|
||||||
|
running_plugins_snapshot = dict(self._running_plugins)
|
||||||
|
for plugin_id, plugin in running_plugins_snapshot.items():
|
||||||
|
if not plugin.get_state():
|
||||||
|
continue
|
||||||
|
if not hasattr(plugin, "get_sidebar_nav") or not ObjectUtils.check_method(plugin.get_sidebar_nav):
|
||||||
|
continue
|
||||||
|
if not hasattr(plugin, "get_render_mode"):
|
||||||
|
continue
|
||||||
|
render_mode, _ = plugin.get_render_mode()
|
||||||
|
if render_mode != "vue":
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
nav_list = plugin.get_sidebar_nav()
|
||||||
|
if not nav_list:
|
||||||
|
continue
|
||||||
|
for raw in nav_list:
|
||||||
|
if not raw or not isinstance(raw, dict):
|
||||||
|
continue
|
||||||
|
nav_key = str(raw.get("nav_key") or raw.get("key") or "main").strip()
|
||||||
|
if not nav_key or any(c in nav_key for c in ["/", "?", "#", " "]):
|
||||||
|
logger.warning(f"插件[{plugin_id}]侧栏项 nav_key 无效,已跳过: {nav_key!r}")
|
||||||
|
continue
|
||||||
|
title = raw.get("title") or plugin.plugin_name
|
||||||
|
icon = raw.get("icon") or "mdi-puzzle"
|
||||||
|
section = str(raw.get("section") or "system").lower()
|
||||||
|
if section not in valid_sections:
|
||||||
|
section = "system"
|
||||||
|
perm = raw.get("permission")
|
||||||
|
if perm is not None and str(perm) not in valid_permissions:
|
||||||
|
perm = None
|
||||||
|
else:
|
||||||
|
perm = str(perm) if perm is not None else None
|
||||||
|
order = raw.get("order", 0)
|
||||||
|
try:
|
||||||
|
order = int(order)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
order = 0
|
||||||
|
items.append({
|
||||||
|
"plugin_id": plugin_id,
|
||||||
|
"nav_key": nav_key,
|
||||||
|
"title": title,
|
||||||
|
"icon": icon,
|
||||||
|
"section": section,
|
||||||
|
"permission": perm,
|
||||||
|
"order": order,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取插件[{plugin_id}]侧栏导航出错:{str(e)}")
|
||||||
|
items.sort(key=lambda x: (x["section"], x["order"], x["plugin_id"], x["nav_key"]))
|
||||||
|
return items
|
||||||
|
|
||||||
def get_plugin_dashboard_meta(self) -> List[Dict[str, str]]:
|
def get_plugin_dashboard_meta(self) -> List[Dict[str, str]]:
|
||||||
"""
|
"""
|
||||||
获取所有插件仪表盘元信息
|
获取所有插件仪表盘元信息
|
||||||
|
|||||||
@@ -69,6 +69,24 @@ class PluginDashboard(Plugin):
|
|||||||
elements: Optional[List[dict]] = Field(default_factory=list)
|
elements: Optional[List[dict]] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
class PluginSidebarNavItem(BaseModel):
|
||||||
|
"""
|
||||||
|
插件侧栏导航项(前端全页路由)
|
||||||
|
"""
|
||||||
|
plugin_id: str = Field(description="插件 ID")
|
||||||
|
nav_key: str = Field(description="导航键,对应 URL 段")
|
||||||
|
title: str = Field(description="侧栏标题")
|
||||||
|
icon: str = Field(default="mdi-puzzle", description="MDI 图标名")
|
||||||
|
section: str = Field(
|
||||||
|
description="分组:start / discovery / subscribe / organize / system",
|
||||||
|
)
|
||||||
|
permission: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="权限:subscribe / discovery / search / manage / admin",
|
||||||
|
)
|
||||||
|
order: int = Field(default=0, description="同组内排序,越小越靠前")
|
||||||
|
|
||||||
|
|
||||||
class PluginMemoryInfo(BaseModel):
|
class PluginMemoryInfo(BaseModel):
|
||||||
"""插件内存信息"""
|
"""插件内存信息"""
|
||||||
plugin_id: str = Field(description="插件ID")
|
plugin_id: str = Field(description="插件ID")
|
||||||
|
|||||||
Reference in New Issue
Block a user