Skip to content

Commit d9985f9

Browse files
authored
Add plugin system and notice plugin (#503)
* Update system notice to plugin * Add plugin model alembic support * update plugin conf * add plugin route injection * update plugin route inject * fix and optimize plugin router inject
1 parent 1e0b040 commit d9985f9

30 files changed

+269
-57
lines changed

backend/alembic/env.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,20 @@
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.tools import get_plugin_models
1920

2021
# import your new model here
2122
from backend.app.admin.model import * # noqa: F401
2223
from backend.app.generator.model import * # noqa: F401
2324

25+
# import plugin model
26+
for cls in get_plugin_models():
27+
class_name = cls.__name__
28+
if class_name in globals():
29+
print(f'\nWarning: Class "{class_name}" already exists in global namespace.')
30+
else:
31+
globals()[class_name] = cls
32+
2433
if not os.path.exists(path_conf.ALEMBIC_VERSION_DIR):
2534
os.makedirs(path_conf.ALEMBIC_VERSION_DIR)
2635

backend/app/admin/api/v1/sys/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from backend.app.admin.api.v1.sys.dict_data import router as dict_data_router
1111
from backend.app.admin.api.v1.sys.dict_type import router as dict_type_router
1212
from backend.app.admin.api.v1.sys.menu import router as menu_router
13-
from backend.app.admin.api.v1.sys.notice import router as notice_router
1413
from backend.app.admin.api.v1.sys.role import router as role_router
1514
from backend.app.admin.api.v1.sys.token import router as token_router
1615
from backend.app.admin.api.v1.sys.user import router as user_router
@@ -23,9 +22,8 @@
2322
router.include_router(dept_router, prefix='/depts', tags=['系统部门'])
2423
router.include_router(dict_data_router, prefix='/dict-datas', tags=['系统字典数据'])
2524
router.include_router(dict_type_router, prefix='/dict-types', tags=['系统字典类型'])
26-
router.include_router(menu_router, prefix='/menus', tags=['系统目录'])
25+
router.include_router(menu_router, prefix='/menus', tags=['系统菜单'])
2726
router.include_router(role_router, prefix='/roles', tags=['系统角色'])
2827
router.include_router(user_router, prefix='/users', tags=['系统用户'])
2928
router.include_router(data_rule_router, prefix='/data-rules', tags=['系统数据权限规则'])
30-
router.include_router(notice_router, prefix='/notices', tags=['系统通知公告'])
3129
router.include_router(token_router, prefix='/tokens', tags=['系统令牌'])

backend/app/admin/model/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from backend.app.admin.model.dict_type import DictType
1010
from backend.app.admin.model.login_log import LoginLog
1111
from backend.app.admin.model.menu import Menu
12-
from backend.app.admin.model.notice import Notice
1312
from backend.app.admin.model.opera_log import OperaLog
1413
from backend.app.admin.model.role import Role
1514
from backend.app.admin.model.user import User

backend/app/admin/service/data_rule_service.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from backend.core.conf import settings
1414
from backend.database.db import async_db_session
1515
from backend.database.redis import redis_client
16-
from backend.utils.import_parse import dynamic_import
16+
from backend.utils.import_parse import dynamic_import_data_model
1717

1818

1919
class DataRuleService:
@@ -42,7 +42,10 @@ async def get_models() -> list[str]:
4242
async def get_columns(model: str) -> list[str]:
4343
if model not in settings.DATA_PERMISSION_MODELS:
4444
raise errors.NotFoundError(msg='数据模型不存在')
45-
model_ins = dynamic_import(settings.DATA_PERMISSION_MODELS[model])
45+
try:
46+
model_ins = dynamic_import_data_model(settings.DATA_PERMISSION_MODELS[model])
47+
except (ImportError, AttributeError):
48+
raise errors.ServerError(msg=f'数据模型 {model} 动态导入失败,请联系系统超级管理员')
4649
model_columns = [
4750
key for key in model_ins.__table__.columns.keys() if key not in settings.DATA_PERMISSION_COLUMN_EXCLUDE
4851
]

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/common/security/permission.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from backend.common.exception import errors
1010
from backend.common.exception.errors import ServerError
1111
from backend.core.conf import settings
12-
from backend.utils.import_parse import dynamic_import
12+
from backend.utils.import_parse import dynamic_import_data_model
1313

1414
if TYPE_CHECKING:
1515
from backend.app.admin.schema.data_rule import GetDataRuleDetail
@@ -60,7 +60,10 @@ def filter_data_permission(request: Request) -> ColumnElement[bool]:
6060
rule_model = rule.model
6161
if rule_model not in settings.DATA_PERMISSION_MODELS:
6262
raise errors.NotFoundError(msg='数据规则模型不存在')
63-
model_ins = dynamic_import(settings.DATA_PERMISSION_MODELS[rule_model])
63+
try:
64+
model_ins = dynamic_import_data_model(settings.DATA_PERMISSION_MODELS[rule_model])
65+
except (ImportError, AttributeError):
66+
raise errors.ServerError(msg=f'数据模型 {rule_model} 动态导入失败,请联系系统超级管理员')
6467
model_columns = [
6568
key for key in model_ins.__table__.columns.keys() if key not in settings.DATA_PERMISSION_COLUMN_EXCLUDE
6669
]

backend/core/path_conf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@
2222

2323
# jinja2 模版文件路径
2424
JINJA2_TEMPLATE_DIR = os.path.join(BasePath, 'templates')
25+
26+
# 插件目录
27+
PLUGIN_DIR = os.path.join(BasePath, 'plugin')

backend/core/registrar.py

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

13-
from backend.app.router import route
1413
from backend.common.exception.exception_handler import register_exception
1514
from backend.common.log import set_customize_logfile, setup_logging
1615
from backend.core.conf import settings
@@ -20,6 +19,7 @@
2019
from backend.middleware.jwt_auth_middleware import JwtAuthMiddleware
2120
from backend.middleware.opera_log_middleware import OperaLogMiddleware
2221
from backend.middleware.state_middleware import StateMiddleware
22+
from backend.plugin.tools import plugin_router_inject
2323
from backend.utils.demo_site import demo_site
2424
from backend.utils.health_check import ensure_unique_route_names, http_limit_callback
2525
from backend.utils.openapi import simplify_operation_ids
@@ -39,7 +39,9 @@ async def register_init(app: FastAPI):
3939
await redis_client.open()
4040
# 初始化 limiter
4141
await FastAPILimiter.init(
42-
redis=redis_client, prefix=settings.REQUEST_LIMITER_REDIS_PREFIX, http_callback=http_limit_callback
42+
redis=redis_client,
43+
prefix=settings.REQUEST_LIMITER_REDIS_PREFIX,
44+
http_callback=http_limit_callback,
4345
)
4446

4547
yield
@@ -156,7 +158,11 @@ def register_router(app: FastAPI):
156158
dependencies = [Depends(demo_site)] if settings.DEMO_MODE else None
157159

158160
# API
159-
app.include_router(route, dependencies=dependencies)
161+
plugin_router_inject()
162+
163+
from backend.app.router import router # 必须在插件路由注入后导入
164+
165+
app.include_router(router, dependencies=dependencies)
160166

161167
# Extra
162168
ensure_unique_route_names(app)

backend/main.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
from pathlib import Path
4-
5-
import uvicorn
6-
73
from backend.core.registrar import register_app
84

95
app = register_app()
10-
11-
12-
if __name__ == '__main__':
13-
# 如果你喜欢在 IDE 中进行 DEBUG,main 启动方法会很有帮助
14-
# 如果你喜欢通过 print 方式进行调试,建议使用 fastapi cli 方式启动服务
15-
try:
16-
config = uvicorn.Config(app=f'{Path(__file__).stem}:app', reload=True)
17-
server = uvicorn.Server(config)
18-
server.run()
19-
except Exception as e:
20-
raise e

backend/plugin/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-

0 commit comments

Comments
 (0)