♻️ refactor api code

This commit is contained in:
felinae98 2022-12-23 21:47:28 +08:00
parent 410cac1af3
commit 0ebbdd2c0d
5 changed files with 189 additions and 193 deletions

View File

@ -30,15 +30,15 @@ export const login = createAsyncThunk(
); );
const setLoginReducer: CaseReducer<AuthStatus, PayloadAction<TokenResp>> = (state, action) => { const setLoginReducer: CaseReducer<AuthStatus, PayloadAction<TokenResp>> = (state, action) => {
if (action.payload.status === 200) { state.login = true;
state.login = true; state.id = action.payload.id;
state.id = action.payload.id; state.userType = action.payload.type;
state.userType = action.payload.type; state.token = action.payload.token;
state.token = action.payload.token; };
} else {
state.login = false; export const setLoginFailedReducer: CaseReducer<AuthStatus> = (state) => {
state.failed = true; state.login = false;
} state.failed = true;
}; };
export const setLogoutReducer: CaseReducer<AuthStatus> = (state) => { export const setLogoutReducer: CaseReducer<AuthStatus> = (state) => {

View File

@ -1,5 +1,4 @@
export interface TokenResp { export interface TokenResp {
status: number;
token: string; token: string;
type: string; type: string;
id: number; id: number;
@ -44,7 +43,7 @@ export interface SubscribeResp {
} }
export interface StatusResp { export interface StatusResp {
status: number; ok: boolean;
msg: string; msg: string;
} }

View File

@ -1,9 +1,9 @@
import os import os
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Union from typing import Union
import socketio import socketio
from fastapi.applications import FastAPI
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from nonebot import get_driver, on_command from nonebot import get_driver, on_command
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
@ -14,30 +14,9 @@ from nonebot.rule import to_me
from nonebot.typing import T_State from nonebot.typing import T_State
from ..plugin_config import plugin_config from ..plugin_config import plugin_config
from ..types import WeightConfig from .api import router as api_router
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 .token_manager import token_manager as tm 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() STATIC_PATH = (Path(__file__).parent / "dist").resolve()
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*") sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
@ -57,90 +36,18 @@ class SinglePageApplication(StaticFiles):
def register_router_fastapi(driver: Driver, socketio): def register_router_fastapi(driver: Driver, socketio):
from fastapi import HTTPException, status static_path = STATIC_PATH
from fastapi.param_functions import Depends nonebot_app = FastAPI(
from fastapi.security import OAuth2PasswordBearer title="nonebot-bison",
description="nonebot-bison webui and api",
oath_scheme = OAuth2PasswordBearer(tokenUrl="token") )
nonebot_app.include_router(api_router)
async def get_jwt_obj(token: str = Depends(oath_scheme)): nonebot_app.mount(
obj = load_jwt(token) "/", SinglePageApplication(directory=static_path), name="bison-frontend"
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]
app = driver.server_app app = driver.server_app
static_path = STATIC_PATH app.mount("/bison", nonebot_app, "nonebot-bison")
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")
def init(): def init():
@ -156,7 +63,7 @@ def init():
host = "localhost" host = "localhost"
logger.opt(colors=True).info( logger.opt(colors=True).info(
f"Nonebot test frontend will be running at: " f"Nonebot test frontend will be running at: "
f"<b><u>http://{host}:{port}{URL_BASE}</u></b>" f"<b><u>http://{host}:{port}/bison</u></b>"
) )

View File

@ -1,4 +1,9 @@
import nonebot 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 nonebot.adapters.onebot.v11.bot import Bot
from ..apis import check_sub_target from ..apis import check_sub_target
@ -12,25 +17,58 @@ from ..config.db_config import SubscribeDupException
from ..platform import platform_manager from ..platform import platform_manager
from ..types import Target as T_Target from ..types import Target as T_Target
from ..types import WeightConfig from ..types import WeightConfig
from .jwt import pack_jwt from .jwt import load_jwt, pack_jwt
from .token_manager import token_manager 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(): async def get_jwt_obj(token: str = Depends(oath_scheme)):
return {"status": 200, "text": "test"} 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 = {} res = {}
for platform_name, platform in platform_manager.items(): for platform_name, platform in platform_manager.items():
res[platform_name] = { res[platform_name] = PlatformConfig(
"platformName": platform_name, platformName=platform_name,
"categories": platform.categories, categories=platform.categories,
"enabledTag": platform.enable_tag, enabledTag=platform.enable_tag,
"name": platform.name, name=platform.name,
"hasTarget": getattr(platform, "has_target"), hasTarget=getattr(platform, "has_target"),
} )
return {"platformConf": res} return GlobalConf(platformConf=res)
async def get_admin_groups(qq: int): async def get_admin_groups(qq: int):
@ -46,7 +84,8 @@ async def get_admin_groups(qq: int):
return res return res
async def auth(token: str): @router.get("/auth")
async def auth(token: str) -> TokenResp:
if qq_tuple := token_manager.get_user(token): if qq_tuple := token_manager.get_user(token):
qq, nickname = qq_tuple qq, nickname = qq_tuple
bot = nonebot.get_bot() bot = nonebot.get_bot()
@ -66,113 +105,112 @@ async def auth(token: str):
) )
), ),
} }
ret_obj = { ret_obj = TokenResp(
"type": "admin", type="admin",
"name": nickname, name=nickname,
"id": qq, id=qq,
"token": pack_jwt(jwt_obj), token=pack_jwt(jwt_obj),
} )
return {"status": 200, **ret_obj} return ret_obj
if admin_groups := await get_admin_groups(int(qq)): if admin_groups := await get_admin_groups(int(qq)):
jwt_obj = {"id": str(qq), "type": "user", "groups": admin_groups} jwt_obj = {"id": str(qq), "type": "user", "groups": admin_groups}
ret_obj = { ret_obj = TokenResp(
"type": "user", type="user",
"name": nickname, name=nickname,
"id": qq, id=qq,
"token": pack_jwt(jwt_obj), token=pack_jwt(jwt_obj),
} )
return {"status": 200, **ret_obj} return ret_obj
else: else:
return {"status": 400, "type": "", "name": "", "id": 0, "token": ""} raise HTTPException(400, "permission denied")
else: 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"] groups = jwt_obj["groups"]
res = {} res: SubscribeResp = {}
for group in groups: for group in groups:
group_id = group["id"] group_id = group["id"]
raw_subs = await config.list_subscribe(group_id, "group") raw_subs = await config.list_subscribe(group_id, "group")
subs = list( subs = list(
map( map(
lambda sub: { lambda sub: SubscribeConfig(
"platformName": sub.target.platform_name, platformName=sub.target.platform_name,
"targetName": sub.target.target_name, targetName=sub.target.target_name,
"cats": sub.categories, cats=sub.categories, # type: ignore
"tags": sub.tags, tags=sub.tags, # type: ignore
"target": sub.target.target, target=sub.target.target,
}, ),
raw_subs, raw_subs,
) )
) )
res[group_id] = {"name": group["name"], "subscribes": subs} res[group_id] = SubscribeGroupDetail(name=group["name"], subscribes=subs)
return res return res
async def get_target_name(platform_name: str, target: str, jwt_obj: dict): @router.get("/target_name", dependencies=[Depends(get_jwt_obj)])
return {"targetName": await check_sub_target(platform_name, target)} async def get_target_name(platformName: str, target: str):
return {"targetName": await check_sub_target(platformName, T_Target(target))}
async def add_group_sub( @router.post("/subs", dependencies=[Depends(check_group_permission)])
group_number: int, async def add_group_sub(groupNumber: int, req: AddSubscribeReq) -> StatusResp:
platform_name: str,
target: str,
target_name: str,
cats: list[int],
tags: list[str],
):
try: try:
await config.add_subscribe( await config.add_subscribe(
int(group_number), int(groupNumber),
"group", "group",
T_Target(target), T_Target(req.target),
target_name, req.targetName,
platform_name, req.platformName,
cats, req.cats,
tags, req.tags,
) )
return {"status": 200, "msg": ""} return StatusResp(ok=True, msg="")
except SubscribeDupException: 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: try:
await config.del_subscribe(int(group_number), "group", target, platform_name) await config.del_subscribe(int(groupNumber), "group", target, platformName)
except (NoSuchUserException, NoSuchSubscribeException): except (NoSuchUserException, NoSuchSubscribeException):
return {"status": 400, "msg": "删除错误"} raise HTTPException(status.HTTP_400_BAD_REQUEST, "no such user or subscribe")
return {"status": 200, "msg": ""} return StatusResp(ok=True, msg="")
async def update_group_sub( @router.patch("/subs", dependencies=[Depends(check_group_permission)])
group_number: int, async def update_group_sub(groupNumber: int, req: AddSubscribeReq):
platform_name: str,
target: str,
target_name: str,
cats: list[int],
tags: list[str],
):
try: try:
await config.update_subscribe( 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): except (NoSuchUserException, NoSuchSubscribeException):
return {"status": 400, "msg": "更新错误"} raise HTTPException(status.HTTP_400_BAD_REQUEST, "no such user or subscribe")
return {"status": 200, "msg": ""} return StatusResp(ok=True, msg="")
@router.get("/weight", dependencies=[Depends(check_is_superuser)])
async def get_weight_config(): async def get_weight_config():
return await config.get_all_weight_config() return await config.get_all_weight_config()
@router.put("/weight", dependencies=[Depends(check_is_superuser)])
async def update_weigth_config( async def update_weigth_config(
platform_name: str, target: str, weight_config: WeightConfig platformName: str, target: str, weight_config: WeightConfig
): ):
try: try:
await config.update_time_weight_config( await config.update_time_weight_config(
T_Target(target), platform_name, weight_config T_Target(target), platformName, weight_config
) )
except NoSuchTargetException: except NoSuchTargetException:
return {"status": 400, "msg": "该订阅不存在"} raise HTTPException(status.HTTP_400_BAD_REQUEST, "no such subscribe")
return {"status": 200, "msg": ""} return StatusResp(ok=True, msg="")

View File

@ -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