mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 13:12:24 +00:00
121 lines
4.7 KiB
Python
121 lines
4.7 KiB
Python
import os
|
|
import warnings
|
|
|
|
from fastapi import APIRouter, Depends
|
|
|
|
from backend.common.dataclasses import PluginEntry
|
|
from backend.core.path_conf import PLUGIN_DIR
|
|
from backend.plugin.errors import PluginConfigError, PluginInjectError
|
|
from backend.plugin.status import PluginStatusChecker
|
|
from backend.utils.dynamic_import import import_module_cached
|
|
|
|
|
|
def _invalid_router_msg(plugin: str, module_path: str, level: str) -> str:
|
|
return f'{level}插件 {plugin} 模块 {module_path} 中没有有效的 router,请检查插件文件是否完整'
|
|
|
|
|
|
def inject_extend_router(plugin: PluginEntry) -> None:
|
|
"""
|
|
扩展级插件路由注入
|
|
|
|
:param plugin: 插件名称
|
|
:return:
|
|
"""
|
|
plugin_api_path = PLUGIN_DIR / plugin.name / 'api'
|
|
if not os.path.exists(plugin_api_path):
|
|
raise PluginConfigError(f'插件 {plugin.name} 缺少 api 目录,请检查插件文件是否完整')
|
|
|
|
for root, _, api_files in os.walk(plugin_api_path):
|
|
for file in api_files:
|
|
if not (file.endswith('.py') and file != '__init__.py'):
|
|
continue
|
|
|
|
# 解析插件路由配置
|
|
file_config = plugin.api[file[:-3]]
|
|
prefix = file_config['prefix']
|
|
tags = file_config['tags']
|
|
|
|
# 获取插件路由模块
|
|
file_path = os.path.join(root, file)
|
|
path_to_module_str = os.path.relpath(file_path, PLUGIN_DIR).replace(os.sep, '.')[:-3]
|
|
module_path = f'backend.plugin.{path_to_module_str}'
|
|
|
|
try:
|
|
module = import_module_cached(module_path)
|
|
plugin_router = getattr(module, 'router', None)
|
|
if not plugin_router:
|
|
msg = _invalid_router_msg(plugin.name, module_path, '扩展级')
|
|
warnings.warn(
|
|
msg,
|
|
FutureWarning,
|
|
)
|
|
continue
|
|
|
|
# 获取目标 app 路由
|
|
relative_path = os.path.relpath(root, plugin_api_path)
|
|
app_name = plugin.extend
|
|
target_module_path = f'backend.app.{app_name}.api.{relative_path.replace(os.sep, ".")}'
|
|
target_module = import_module_cached(target_module_path)
|
|
target_router = getattr(target_module, 'router', None)
|
|
|
|
if not target_router or not isinstance(target_router, APIRouter):
|
|
raise PluginInjectError(_invalid_router_msg(plugin.name, module_path, '扩展级'))
|
|
|
|
# 将插件路由注入到目标路由中
|
|
target_router.include_router(
|
|
router=plugin_router,
|
|
prefix=prefix,
|
|
tags=[tags] if tags else [],
|
|
dependencies=[Depends(PluginStatusChecker(plugin.name))],
|
|
)
|
|
except Exception as e:
|
|
raise PluginInjectError(f'扩展级插件 {plugin.name} 路由注入失败:{e!s}') from e
|
|
|
|
|
|
def inject_app_router(plugin: PluginEntry, target_router: APIRouter) -> None:
|
|
"""
|
|
应用级插件路由注入
|
|
|
|
:param plugin: 插件名称
|
|
:param target_router: FastAPI 路由器
|
|
:return:
|
|
"""
|
|
module_path = f'backend.plugin.{plugin.name}.api.router'
|
|
try:
|
|
module = import_module_cached(module_path)
|
|
routers = plugin.routers
|
|
if not routers or not isinstance(routers, list):
|
|
raise PluginConfigError(f'应用级插件 {plugin.name} 配置文件存在错误,请检查')
|
|
|
|
for router in routers:
|
|
plugin_router = getattr(module, router, None)
|
|
if not plugin_router or not isinstance(plugin_router, APIRouter):
|
|
raise PluginInjectError(_invalid_router_msg(plugin.name, module_path, '应用级'))
|
|
|
|
# 将插件路由注入到目标路由中
|
|
target_router.include_router(plugin_router, dependencies=[Depends(PluginStatusChecker(plugin.name))])
|
|
except Exception as e:
|
|
raise PluginInjectError(f'应用级插件 {plugin.name} 路由注入失败:{e!s}') from e
|
|
|
|
|
|
def build_final_router() -> APIRouter:
|
|
"""构建最终路由"""
|
|
from backend.plugin.core import parse_plugin_config, resolve_plugin_order
|
|
|
|
extend_plugins, app_plugins = parse_plugin_config()
|
|
plugins = extend_plugins + app_plugins
|
|
ordered_plugins = resolve_plugin_order(plugins)
|
|
|
|
for plugin in ordered_plugins:
|
|
if plugin.api is not None:
|
|
inject_extend_router(plugin)
|
|
|
|
# 主路由,必须在扩展级插件路由注入后,应用级插件路由注入前导入
|
|
from backend.app.router import router as main_router
|
|
|
|
for plugin in ordered_plugins:
|
|
if plugin.routers is not None:
|
|
inject_app_router(plugin, main_router)
|
|
|
|
return main_router
|