Skip to content

Commit e9d3623

Browse files
committed
add plugin route injection
1 parent e59671d commit e9d3623

File tree

9 files changed

+159
-50
lines changed

9 files changed

+159
-50
lines changed

backend/alembic/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from backend.common.model import MappedBase
1717
from backend.core import path_conf
1818
from backend.database.db import SQLALCHEMY_DATABASE_URL
19-
from backend.plugin.plugin import get_plugin_models
19+
from backend.plugin.tools import get_plugin_models
2020

2121
# import your new model here
2222
from backend.app.admin.model import * # noqa: F401

backend/app/router.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from backend.app.generator.api.router import v1 as generator_v1
77
from backend.app.task.api.router import v1 as task_v1
88

9-
route = APIRouter()
9+
router = APIRouter()
1010

11-
route.include_router(admin_v1)
12-
route.include_router(generator_v1)
13-
route.include_router(task_v1)
11+
router.include_router(admin_v1)
12+
router.include_router(generator_v1)
13+
router.include_router(task_v1)

backend/core/registrar.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from fastapi_pagination import add_pagination
1111
from starlette.middleware.authentication import AuthenticationMiddleware
1212

13-
from backend.app.router import route
13+
from backend.app.router import router
1414
from backend.common.exception.exception_handler import register_exception
1515
from backend.common.log import set_customize_logfile, setup_logging
1616
from backend.core.conf import settings
@@ -20,6 +20,7 @@
2020
from backend.middleware.jwt_auth_middleware import JwtAuthMiddleware
2121
from backend.middleware.opera_log_middleware import OperaLogMiddleware
2222
from backend.middleware.state_middleware import StateMiddleware
23+
from backend.plugin.tools import register_plugin_router
2324
from backend.utils.demo_site import demo_site
2425
from backend.utils.health_check import ensure_unique_route_names, http_limit_callback
2526
from backend.utils.openapi import simplify_operation_ids
@@ -39,7 +40,9 @@ async def register_init(app: FastAPI):
3940
await redis_client.open()
4041
# 初始化 limiter
4142
await FastAPILimiter.init(
42-
redis=redis_client, prefix=settings.REQUEST_LIMITER_REDIS_PREFIX, http_callback=http_limit_callback
43+
redis=redis_client,
44+
prefix=settings.REQUEST_LIMITER_REDIS_PREFIX,
45+
http_callback=http_limit_callback,
4346
)
4447

4548
yield
@@ -77,6 +80,7 @@ def register_app():
7780

7881
# 路由
7982
register_router(app)
83+
register_plugin_router(app)
8084

8185
# 分页
8286
register_page(app)
@@ -156,7 +160,7 @@ def register_router(app: FastAPI):
156160
dependencies = [Depends(demo_site)] if settings.DEMO_MODE else None
157161

158162
# API
159-
app.include_router(route, dependencies=dependencies)
163+
app.include_router(router, dependencies=dependencies)
160164

161165
# Extra
162166
ensure_unique_route_names(app)

backend/plugin/notice/plugin.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
# 属于哪个 app,如果为独立 app,则应设置为 'original'
1+
# 属于哪个 app,如果为独立 app,应设置为 ''
22
app = 'admin'
3-
# api 版本,将生成对应版本路由
4-
api_version = 'v1'
3+
4+
# api 配置,仅针对于非独立 app
5+
[api]
6+
# api 注入模块,将生成对应路由
7+
module = 'v1.sys'
8+
# 路由 prefix 配置
9+
prefix = 'notices'

backend/plugin/plugin.py

Lines changed: 0 additions & 39 deletions
This file was deleted.

backend/plugin/tools.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
import inspect
4+
import os
5+
6+
import rtoml
7+
8+
from fastapi import FastAPI
9+
10+
from backend.core.path_conf import PLUGIN_DIR
11+
from backend.utils.import_parse import import_module_cached
12+
13+
14+
def get_plugins() -> list[str]:
15+
"""获取插件"""
16+
plugin_packages = []
17+
18+
for item in os.listdir(PLUGIN_DIR):
19+
item_path = os.path.join(PLUGIN_DIR, item)
20+
21+
if os.path.isdir(item_path):
22+
if '__init__.py' in os.listdir(item_path):
23+
plugin_packages.append(item)
24+
25+
return plugin_packages
26+
27+
28+
def get_plugin_models() -> list:
29+
"""获取插件所有模型类"""
30+
classes = []
31+
plugins = get_plugins()
32+
for plugin in plugins:
33+
module_path = f'backend.plugin.{plugin}.model'
34+
module = import_module_cached(module_path)
35+
for name, obj in inspect.getmembers(module):
36+
if inspect.isclass(obj):
37+
classes.append(obj)
38+
return classes
39+
40+
41+
def register_plugin_router(app: FastAPI):
42+
"""
43+
插件路由注入
44+
45+
:param app:
46+
:return:
47+
"""
48+
plugins = get_plugins()
49+
for plugin in plugins:
50+
toml_path = os.path.join(PLUGIN_DIR, plugin, 'plugin.toml')
51+
if not os.path.exists(toml_path):
52+
raise FileNotFoundError('插件缺少 plugin.toml 配置文件')
53+
54+
toml_data = rtoml.load(toml_path)
55+
app_name = toml_data.get('app', '')
56+
api_module = toml_data.get('api', {}).get('module', '')
57+
prefix = toml_data.get('api', {}).get('prefix', '')
58+
59+
if app_name:
60+
if '.' in api_module:
61+
module_path = f'backend.app.{app_name}.api.{api_module}'
62+
else:
63+
module_path = f'backend.app.{app_name}.api'
64+
else:
65+
module_path = 'backend.app'
66+
67+
try:
68+
module = import_module_cached(module_path)
69+
except ImportError as e:
70+
raise ImportError(f'导入模块 {module_path} 失败:{e}') from e
71+
else:
72+
if app_name and '.' in api_module:
73+
# 从 backend.app.xxx.api.vx.xxx.__init__.py 文件中获取路由
74+
router = getattr(module, 'router', None)
75+
else:
76+
if 'api' in module_path:
77+
# 从 backend.app.xxx.api 下的 router.py 文件中获取路由
78+
router = getattr(module, api_module.split('.')[0], None)
79+
else:
80+
# 从 backend.app 下的 router.py 文件中获取路由
81+
router = getattr(module, 'router', None)
82+
83+
if not router:
84+
raise ImportError(f'模块 {module_path} 中不存在路由')
85+
86+
if app_name:
87+
app.include_router(router, prefix=f'/{prefix}')
88+
else:
89+
app.include_router(router)

backend/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ dependencies = [
4545
"python-jose>=3.3.0",
4646
"python-socketio>=5.12.0",
4747
"redis[hiredis]>=5.2.0",
48+
"rtoml>=0.12.0",
4849
"sqlalchemy-crud-plus==1.6.0",
4950
"sqlalchemy[asyncio]>=2.0.30",
5051
"user-agents==2.2.0",

backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pyyaml==6.0.2
9191
redis==5.2.1
9292
rich==13.9.4
9393
rsa==4.9
94+
rtoml==0.12.0
9495
ruff==0.9.5
9596
shellingham==1.5.4
9697
simple-websocket==1.1.0

backend/uv.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)