diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py index c9c8a8ca..4e03feda 100644 --- a/plugins/web_ui/__init__.py +++ b/plugins/web_ui/__init__.py @@ -16,9 +16,7 @@ from utils.manager import plugins2settings_manager # from .api.group import router as group_routes from .api.logs import router as ws_log_routes from .api.logs.log_manager import LOG_STORAGE - -# from .api.plugins import router as plugin_routes -from .api.request import router as request_routes +from .api.tabs.database import router as database_router # from .api.system import router as system_routes from .api.tabs.main import router as main_router @@ -28,6 +26,10 @@ from .api.tabs.manage import router as manage_router # from .api.g import * from .auth import router as auth_router +# from .api.plugins import router as plugin_routes +# from .api.request import router as request_routes + + driver = nonebot.get_driver() gConfig.add_plugin_config("web-ui", "username", "admin", name="web-ui", help_="前端管理用户名") @@ -40,10 +42,11 @@ BaseApiRouter = APIRouter(prefix="/zhenxun/api") BaseApiRouter.include_router(auth_router) # BaseApiRouter.include_router(plugin_routes) # BaseApiRouter.include_router(group_routes) -BaseApiRouter.include_router(request_routes) +# BaseApiRouter.include_router(request_routes) # BaseApiRouter.include_router(system_routes) BaseApiRouter.include_router(main_router) BaseApiRouter.include_router(manage_router) +BaseApiRouter.include_router(database_router) @driver.on_startup diff --git a/plugins/web_ui/api/tabs/__init__.py b/plugins/web_ui/api/tabs/__init__.py index f750065f..97abef9c 100644 --- a/plugins/web_ui/api/tabs/__init__.py +++ b/plugins/web_ui/api/tabs/__init__.py @@ -1,2 +1,3 @@ +from .database import * from .main import * from .manage import * diff --git a/plugins/web_ui/api/tabs/database/__init__.py b/plugins/web_ui/api/tabs/database/__init__.py new file mode 100644 index 00000000..3df9d212 --- /dev/null +++ b/plugins/web_ui/api/tabs/database/__init__.py @@ -0,0 +1,76 @@ +from os import name +from typing import Optional + +import nonebot +from fastapi import APIRouter, Request +from nonebot.drivers import Driver +from tortoise import Tortoise +from tortoise.exceptions import OperationalError + +from configs.config import NICKNAME +from services.db_context import TestSQL +from utils.utils import get_matchers + +from ....base_model import QueryModel, Result +from ....config import QueryDateType +from ....utils import authentication +from .models.model import SqlModel, SqlText +from .models.sql_log import SqlLog + +router = APIRouter() + + +driver: Driver = nonebot.get_driver() + + +SQL_DICT = {} + + +@driver.on_startup +async def _(): + for matcher in get_matchers(True): + if _plugin := matcher.plugin: + try: + _module = _plugin.module + except AttributeError: + pass + else: + plugin_name = matcher.plugin_name + if plugin_name in SQL_DICT: + raise ValueError(f"{plugin_name} 常用SQL plugin_name 重复") + SqlModel( + name=getattr(_module, "__plugin_name__", None) or plugin_name or "", + plugin_name=plugin_name or "", + sql_list=getattr(_module, "sql_list", []), + ) + SQL_DICT[plugin_name] = SqlModel + + +@router.post("/exec_sql", dependencies=[authentication()], description="执行sql") +async def _(sql: SqlText, request: Request) -> Result: + ip = request.client.host if request.client else "unknown" + try: + if sql.sql.lower().startswith("select"): + db = Tortoise.get_connection("default") + res = await db.execute_query_dict(sql.sql) + return Result.ok(res, "执行成功啦!") + else: + result = await TestSQL.raw(sql.sql) + await SqlLog.add(ip or "0.0.0.0", sql.sql, str(result)) + return Result.ok(info="执行成功啦!") + except OperationalError as e: + await SqlLog.add(ip or "0.0.0.0", sql.sql, str(e), False) + return Result.warning_(f"sql执行错误: {e}") + + +@router.post("/get_sql_log", dependencies=[authentication()], description="sql日志列表") +async def _(query: QueryModel) -> Result: + data = await SqlLog.all().offset((query.index - 1) * query.size).limit(query.size) + return Result.ok(data) + + +@router.get("/get_sql", dependencies=[authentication()], description="常用sql") +async def _(plugin_name: Optional[str] = None) -> Result: + if plugin_name: + return Result.ok(SQL_DICT.get(plugin_name)) + return Result.ok(SQL_DICT) diff --git a/plugins/web_ui/api/tabs/database/models/model.py b/plugins/web_ui/api/tabs/database/models/model.py new file mode 100644 index 00000000..ed78c405 --- /dev/null +++ b/plugins/web_ui/api/tabs/database/models/model.py @@ -0,0 +1,23 @@ +from typing import List + +from pydantic import BaseModel + +from utils.models import CommonSql + + +class SqlText(BaseModel): + """ + sql语句 + """ + + sql: str + + +class SqlModel(BaseModel): + + name: str + """插件中文名称""" + plugin_name: str + """插件名称""" + sql_list: List[CommonSql] + """插件列表""" diff --git a/plugins/web_ui/api/tabs/database/models/sql_log.py b/plugins/web_ui/api/tabs/database/models/sql_log.py new file mode 100644 index 00000000..d58ddd34 --- /dev/null +++ b/plugins/web_ui/api/tabs/database/models/sql_log.py @@ -0,0 +1,40 @@ +from typing import Optional, Union + +from tortoise import fields + +from services.db_context import Model + + +class SqlLog(Model): + + id = fields.IntField(pk=True, generated=True, auto_increment=True) + """自增id""" + ip = fields.CharField(255) + """ip""" + sql = fields.CharField(255) + """sql""" + result = fields.CharField(255, null=True) + """结果""" + is_suc = fields.BooleanField(default=True) + """是否成功""" + create_time = fields.DatetimeField(auto_now_add=True) + """创建时间""" + + class Meta: + table = "sql_log" + table_description = "sql执行日志" + + @classmethod + async def add( + cls, ip: str, sql: str, result: Optional[str] = None, is_suc: bool = True + ): + """ + 说明: + 获取用户在群内的等级 + 参数: + :param ip: ip + :param sql: sql + :param result: 返回结果 + :param is_suc: 是否成功 + """ + await cls.create(ip=ip, sql=sql, result=result, is_suc=is_suc) diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py index 3f2e562f..d9f76531 100644 --- a/plugins/web_ui/base_model.py +++ b/plugins/web_ui/base_model.py @@ -1,14 +1,16 @@ from datetime import datetime from logging import warning -from typing import Any, Dict, List, Optional, TypeVar, Union +from typing import Any, Dict, Generic, List, Optional, TypeVar, Union from nonebot.adapters.onebot.v11 import Bot -from pydantic import BaseModel +from pydantic import BaseModel, validator from configs.utils import Config from utils.manager.models import Plugin as PluginManager from utils.manager.models import PluginBlock, PluginCd, PluginCount, PluginSetting +T = TypeVar("T") + class User(BaseModel): username: str @@ -49,6 +51,31 @@ class Result(BaseModel): return cls(suc=True, info=info, code=code, data=data) +class QueryModel(BaseModel, Generic[T]): + """ + 基本查询条件 + """ + + index: int + """页数""" + size: int + """每页数量""" + data: T + """携带数据""" + + @validator("index") + def index_validator(cls, index): + if index < 1: + raise ValueError("查询下标小于1...") + return index + + @validator("size") + def size_validator(cls, size): + if size < 1: + raise ValueError("每页数量小于1...") + return size + + # class PluginConfig(BaseModel): # """ # 插件配置项