From 0ebbdd2c0df5152efca27188620d696d1bbae2f2 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Fri, 23 Dec 2022 21:47:28 +0800 Subject: [PATCH] :recycle: refactor api code --- admin-frontend/src/features/auth/authSlice.ts | 18 +- admin-frontend/src/utils/type.ts | 3 +- .../nonebot_bison/admin_page/__init__.py | 119 ++--------- src/plugins/nonebot_bison/admin_page/api.py | 190 +++++++++++------- src/plugins/nonebot_bison/admin_page/types.py | 52 +++++ 5 files changed, 189 insertions(+), 193 deletions(-) create mode 100644 src/plugins/nonebot_bison/admin_page/types.py diff --git a/admin-frontend/src/features/auth/authSlice.ts b/admin-frontend/src/features/auth/authSlice.ts index 7f6c4d6..cdafcf2 100644 --- a/admin-frontend/src/features/auth/authSlice.ts +++ b/admin-frontend/src/features/auth/authSlice.ts @@ -30,15 +30,15 @@ export const login = createAsyncThunk( ); const setLoginReducer: CaseReducer> = (state, action) => { - if (action.payload.status === 200) { - state.login = true; - state.id = action.payload.id; - state.userType = action.payload.type; - state.token = action.payload.token; - } else { - state.login = false; - state.failed = true; - } + state.login = true; + state.id = action.payload.id; + state.userType = action.payload.type; + state.token = action.payload.token; +}; + +export const setLoginFailedReducer: CaseReducer = (state) => { + state.login = false; + state.failed = true; }; export const setLogoutReducer: CaseReducer = (state) => { diff --git a/admin-frontend/src/utils/type.ts b/admin-frontend/src/utils/type.ts index 8b2ea60..6f877f1 100644 --- a/admin-frontend/src/utils/type.ts +++ b/admin-frontend/src/utils/type.ts @@ -1,5 +1,4 @@ export interface TokenResp { - status: number; token: string; type: string; id: number; @@ -44,7 +43,7 @@ export interface SubscribeResp { } export interface StatusResp { - status: number; + ok: boolean; msg: string; } diff --git a/src/plugins/nonebot_bison/admin_page/__init__.py b/src/plugins/nonebot_bison/admin_page/__init__.py index 8087ff5..199f7ba 100644 --- a/src/plugins/nonebot_bison/admin_page/__init__.py +++ b/src/plugins/nonebot_bison/admin_page/__init__.py @@ -1,9 +1,9 @@ import os -from dataclasses import dataclass from pathlib import Path from typing import Union import socketio +from fastapi.applications import FastAPI from fastapi.staticfiles import StaticFiles from nonebot import get_driver, on_command from nonebot.adapters.onebot.v11 import Bot @@ -14,30 +14,9 @@ from nonebot.rule import to_me from nonebot.typing import T_State from ..plugin_config import plugin_config -from ..types import WeightConfig -from .api import ( - add_group_sub, - auth, - del_group_sub, - get_global_conf, - get_subs_info, - get_target_name, - get_weight_config, - test, - update_group_sub, - update_weigth_config, -) -from .jwt import load_jwt +from .api import router as api_router from .token_manager import token_manager as tm -URL_BASE = "/bison/" -GLOBAL_CONF_URL = f"{URL_BASE}api/global_conf" -AUTH_URL = f"{URL_BASE}api/auth" -SUBSCRIBE_URL = f"{URL_BASE}api/subs" -GET_TARGET_NAME_URL = f"{URL_BASE}api/target_name" -WEIGHT_URL = f"{URL_BASE}api/weight" -TEST_URL = f"{URL_BASE}test" - STATIC_PATH = (Path(__file__).parent / "dist").resolve() sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*") @@ -57,90 +36,18 @@ class SinglePageApplication(StaticFiles): def register_router_fastapi(driver: Driver, socketio): - from fastapi import HTTPException, status - from fastapi.param_functions import Depends - from fastapi.security import OAuth2PasswordBearer - - oath_scheme = OAuth2PasswordBearer(tokenUrl="token") - - async def get_jwt_obj(token: str = Depends(oath_scheme)): - obj = load_jwt(token) - if not obj: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) - return obj - - async def check_group_permission( - groupNumber: int, token_obj: dict = Depends(get_jwt_obj) - ): - groups = token_obj["groups"] - for group in groups: - if int(groupNumber) == group["id"]: - return - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) - - async def check_is_superuser(token_obj: dict = Depends(get_jwt_obj)): - if token_obj.get("type") != "admin": - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) - - @dataclass - class AddSubscribeReq: - platformName: str - target: str - targetName: str - cats: list[int] - tags: list[str] + static_path = STATIC_PATH + nonebot_app = FastAPI( + title="nonebot-bison", + description="nonebot-bison webui and api", + ) + nonebot_app.include_router(api_router) + nonebot_app.mount( + "/", SinglePageApplication(directory=static_path), name="bison-frontend" + ) app = driver.server_app - static_path = STATIC_PATH - app.get(TEST_URL)(test) - app.get(GLOBAL_CONF_URL)(get_global_conf) - app.get(AUTH_URL)(auth) - - @app.get(SUBSCRIBE_URL) - async def subs(jwt_obj: dict = Depends(get_jwt_obj)): - return await get_subs_info(jwt_obj) - - @app.get(GET_TARGET_NAME_URL) - async def _get_target_name( - platformName: str, target: str, jwt_obj: dict = Depends(get_jwt_obj) - ): - return await get_target_name(platformName, target, jwt_obj) - - @app.post(SUBSCRIBE_URL, dependencies=[Depends(check_group_permission)]) - async def _add_group_subs(groupNumber: int, req: AddSubscribeReq): - return await add_group_sub( - group_number=groupNumber, - platform_name=req.platformName, - target=req.target, - target_name=req.targetName, - cats=req.cats, - tags=req.tags, - ) - - @app.patch(SUBSCRIBE_URL, dependencies=[Depends(check_group_permission)]) - async def _update_group_subs(groupNumber: int, req: AddSubscribeReq): - return await update_group_sub( - group_number=groupNumber, - platform_name=req.platformName, - target=req.target, - target_name=req.targetName, - cats=req.cats, - tags=req.tags, - ) - - @app.delete(SUBSCRIBE_URL, dependencies=[Depends(check_group_permission)]) - async def _del_group_subs(groupNumber: int, target: str, platformName: str): - return await del_group_sub(groupNumber, platformName, target) - - @app.get(WEIGHT_URL, dependencies=[Depends(check_is_superuser)]) - async def _get_weight_config(): - return await get_weight_config() - - @app.put(WEIGHT_URL, dependencies=[Depends(check_is_superuser)]) - async def _update_weight_config(platform_name: str, target: str, req: WeightConfig): - return await update_weigth_config(platform_name, target, req) - - app.mount(URL_BASE, SinglePageApplication(directory=static_path), name="bison") + app.mount("/bison", nonebot_app, "nonebot-bison") def init(): @@ -156,7 +63,7 @@ def init(): host = "localhost" logger.opt(colors=True).info( f"Nonebot test frontend will be running at: " - f"http://{host}:{port}{URL_BASE}" + f"http://{host}:{port}/bison" ) diff --git a/src/plugins/nonebot_bison/admin_page/api.py b/src/plugins/nonebot_bison/admin_page/api.py index 19ec3a5..be4ab0a 100644 --- a/src/plugins/nonebot_bison/admin_page/api.py +++ b/src/plugins/nonebot_bison/admin_page/api.py @@ -1,4 +1,9 @@ import nonebot +from fastapi import status +from fastapi.exceptions import HTTPException +from fastapi.param_functions import Depends +from fastapi.routing import APIRouter +from fastapi.security.oauth2 import OAuth2PasswordBearer from nonebot.adapters.onebot.v11.bot import Bot from ..apis import check_sub_target @@ -12,25 +17,58 @@ from ..config.db_config import SubscribeDupException from ..platform import platform_manager from ..types import Target as T_Target from ..types import WeightConfig -from .jwt import pack_jwt +from .jwt import load_jwt, pack_jwt from .token_manager import token_manager +from .types import ( + AddSubscribeReq, + GlobalConf, + PlatformConfig, + StatusResp, + SubscribeConfig, + SubscribeGroupDetail, + SubscribeResp, + TokenResp, +) + +router = APIRouter(prefix="/api", tags=["api"]) + +oath_scheme = OAuth2PasswordBearer(tokenUrl="token") -async def test(): - return {"status": 200, "text": "test"} +async def get_jwt_obj(token: str = Depends(oath_scheme)): + obj = load_jwt(token) + if not obj: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) + return obj -async def get_global_conf(): +async def check_group_permission( + groupNumber: int, token_obj: dict = Depends(get_jwt_obj) +): + groups = token_obj["groups"] + for group in groups: + if int(groupNumber) == group["id"]: + return + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + + +async def check_is_superuser(token_obj: dict = Depends(get_jwt_obj)): + if token_obj.get("type") != "admin": + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) + + +@router.get("/global_conf") +async def get_global_conf() -> GlobalConf: res = {} for platform_name, platform in platform_manager.items(): - res[platform_name] = { - "platformName": platform_name, - "categories": platform.categories, - "enabledTag": platform.enable_tag, - "name": platform.name, - "hasTarget": getattr(platform, "has_target"), - } - return {"platformConf": res} + res[platform_name] = PlatformConfig( + platformName=platform_name, + categories=platform.categories, + enabledTag=platform.enable_tag, + name=platform.name, + hasTarget=getattr(platform, "has_target"), + ) + return GlobalConf(platformConf=res) async def get_admin_groups(qq: int): @@ -46,7 +84,8 @@ async def get_admin_groups(qq: int): return res -async def auth(token: str): +@router.get("/auth") +async def auth(token: str) -> TokenResp: if qq_tuple := token_manager.get_user(token): qq, nickname = qq_tuple bot = nonebot.get_bot() @@ -66,113 +105,112 @@ async def auth(token: str): ) ), } - ret_obj = { - "type": "admin", - "name": nickname, - "id": qq, - "token": pack_jwt(jwt_obj), - } - return {"status": 200, **ret_obj} + ret_obj = TokenResp( + type="admin", + name=nickname, + id=qq, + token=pack_jwt(jwt_obj), + ) + return ret_obj if admin_groups := await get_admin_groups(int(qq)): jwt_obj = {"id": str(qq), "type": "user", "groups": admin_groups} - ret_obj = { - "type": "user", - "name": nickname, - "id": qq, - "token": pack_jwt(jwt_obj), - } - return {"status": 200, **ret_obj} + ret_obj = TokenResp( + type="user", + name=nickname, + id=qq, + token=pack_jwt(jwt_obj), + ) + return ret_obj else: - return {"status": 400, "type": "", "name": "", "id": 0, "token": ""} + raise HTTPException(400, "permission denied") else: - return {"status": 400, "type": "", "name": "", "id": 0, "token": ""} + raise HTTPException(400, "code error") -async def get_subs_info(jwt_obj: dict): +@router.get("/subs") +async def get_subs_info(jwt_obj: dict = Depends(get_jwt_obj)) -> SubscribeResp: groups = jwt_obj["groups"] - res = {} + res: SubscribeResp = {} for group in groups: group_id = group["id"] raw_subs = await config.list_subscribe(group_id, "group") subs = list( map( - lambda sub: { - "platformName": sub.target.platform_name, - "targetName": sub.target.target_name, - "cats": sub.categories, - "tags": sub.tags, - "target": sub.target.target, - }, + lambda sub: SubscribeConfig( + platformName=sub.target.platform_name, + targetName=sub.target.target_name, + cats=sub.categories, # type: ignore + tags=sub.tags, # type: ignore + target=sub.target.target, + ), raw_subs, ) ) - res[group_id] = {"name": group["name"], "subscribes": subs} + res[group_id] = SubscribeGroupDetail(name=group["name"], subscribes=subs) return res -async def get_target_name(platform_name: str, target: str, jwt_obj: dict): - return {"targetName": await check_sub_target(platform_name, target)} +@router.get("/target_name", dependencies=[Depends(get_jwt_obj)]) +async def get_target_name(platformName: str, target: str): + return {"targetName": await check_sub_target(platformName, T_Target(target))} -async def add_group_sub( - group_number: int, - platform_name: str, - target: str, - target_name: str, - cats: list[int], - tags: list[str], -): +@router.post("/subs", dependencies=[Depends(check_group_permission)]) +async def add_group_sub(groupNumber: int, req: AddSubscribeReq) -> StatusResp: try: await config.add_subscribe( - int(group_number), + int(groupNumber), "group", - T_Target(target), - target_name, - platform_name, - cats, - tags, + T_Target(req.target), + req.targetName, + req.platformName, + req.cats, + req.tags, ) - return {"status": 200, "msg": ""} + return StatusResp(ok=True, msg="") except SubscribeDupException: - return {"status": 403, "msg": ""} + raise HTTPException(status.HTTP_400_BAD_REQUEST, "subscribe duplicated") -async def del_group_sub(group_number: int, platform_name: str, target: str): +@router.delete("/subs", dependencies=[Depends(check_group_permission)]) +async def del_group_sub(groupNumber: int, platformName: str, target: str): try: - await config.del_subscribe(int(group_number), "group", target, platform_name) + await config.del_subscribe(int(groupNumber), "group", target, platformName) except (NoSuchUserException, NoSuchSubscribeException): - return {"status": 400, "msg": "删除错误"} - return {"status": 200, "msg": ""} + raise HTTPException(status.HTTP_400_BAD_REQUEST, "no such user or subscribe") + return StatusResp(ok=True, msg="") -async def update_group_sub( - group_number: int, - platform_name: str, - target: str, - target_name: str, - cats: list[int], - tags: list[str], -): +@router.patch("/subs", dependencies=[Depends(check_group_permission)]) +async def update_group_sub(groupNumber: int, req: AddSubscribeReq): try: await config.update_subscribe( - int(group_number), "group", target, target_name, platform_name, cats, tags + int(groupNumber), + "group", + req.target, + req.targetName, + req.platformName, + req.cats, + req.tags, ) except (NoSuchUserException, NoSuchSubscribeException): - return {"status": 400, "msg": "更新错误"} - return {"status": 200, "msg": ""} + raise HTTPException(status.HTTP_400_BAD_REQUEST, "no such user or subscribe") + return StatusResp(ok=True, msg="") +@router.get("/weight", dependencies=[Depends(check_is_superuser)]) async def get_weight_config(): return await config.get_all_weight_config() +@router.put("/weight", dependencies=[Depends(check_is_superuser)]) async def update_weigth_config( - platform_name: str, target: str, weight_config: WeightConfig + platformName: str, target: str, weight_config: WeightConfig ): try: await config.update_time_weight_config( - T_Target(target), platform_name, weight_config + T_Target(target), platformName, weight_config ) except NoSuchTargetException: - return {"status": 400, "msg": "该订阅不存在"} - return {"status": 200, "msg": ""} + raise HTTPException(status.HTTP_400_BAD_REQUEST, "no such subscribe") + return StatusResp(ok=True, msg="") diff --git a/src/plugins/nonebot_bison/admin_page/types.py b/src/plugins/nonebot_bison/admin_page/types.py new file mode 100644 index 0000000..996d303 --- /dev/null +++ b/src/plugins/nonebot_bison/admin_page/types.py @@ -0,0 +1,52 @@ +from pydantic import BaseModel + + +class PlatformConfig(BaseModel): + name: str + categories: dict[int, str] + enabledTag: bool + platformName: str + hasTarget: bool + + +AllPlatformConf = dict[str, PlatformConfig] + + +class GlobalConf(BaseModel): + platformConf: AllPlatformConf + + +class TokenResp(BaseModel): + token: str + type: str + id: int + name: str + + +class SubscribeConfig(BaseModel): + platformName: str + target: str + targetName: str + cats: list[int] + tags: list[str] + + +class SubscribeGroupDetail(BaseModel): + name: str + subscribes: list[SubscribeConfig] + + +SubscribeResp = dict[str, SubscribeGroupDetail] + + +class AddSubscribeReq(BaseModel): + platformName: str + target: str + targetName: str + cats: list[int] + tags: list[str] + + +class StatusResp(BaseModel): + ok: bool + msg: str