版本升级:

1. 新增(kinit-api,kinit-admin,kinit-uni):新增JWT刷新字段中间件,默认将refresh字段加入到response headers中存储
2. 更新(kinit-api):更新接口依赖库到最新版本
2. 优化(kinit-api):更新 OpenAuth 认证依赖项
This commit is contained in:
ktianc 2023-03-20 14:11:25 +08:00
parent e8979577bd
commit a2bab19881
13 changed files with 123 additions and 183 deletions

View File

@ -66,13 +66,13 @@ service.interceptors.response.use(
// 这个状态码是和后端约定好的
const code = response.data.code || unauthorized_code
const message = response.data.message || '后端接口无返回内容'
const refresh = response.data.refresh || false
const refresh = response.headers.refresh
if (response.config.responseType === 'blob') {
// 如果是文件流,直接过
return response
} else if (code === result_code) {
if (refresh) {
if (refresh === '1') {
// 因token快过期刷新token
refreshToken().then((res) => {
const appStore = useAppStore()

View File

@ -43,7 +43,7 @@ Typer 官方文档https://typer.tiangolo.com/
开发语言Python 3.10
开发框架Fastapi 0.94.1
开发框架Fastapi 0.95.0
## 开发工具

View File

@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
"""
系统版本
"""
VERSION = "1.6.4"
VERSION = "1.7.0"
"""安全警告: 不要在生产中打开调试运行!"""
DEBUG = True
@ -132,4 +132,5 @@ MIDDLEWARES = [
"core.middleware.register_request_log_middleware" if REQUEST_LOG_RECORD else None,
"core.middleware.register_operation_record_middleware" if OPERATION_LOG_RECORD and MONGO_DB_ENABLE else None,
"core.middleware.register_demo_env_middleware" if DEMO else None,
"core.middleware.register_jwt_refresh_middleware"
]

View File

@ -30,7 +30,7 @@ async def get_banners(auth: Auth = Depends(AllUserAuth())):
"id": 3, "image": "https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-09/banner3.png"
},
]
return SuccessResponse(data, refresh=auth.refresh)
return SuccessResponse(data)
@app.get("/user/access/source/", summary="用户来源")
@ -42,7 +42,7 @@ async def get_user_access_source(auth: Auth = Depends(AllUserAuth())):
{"value": 135, "name": 'analysis.videoAdvertising'},
{"value": 1548, "name": 'analysis.searchEngines'}
]
return SuccessResponse(data, refresh=auth.refresh)
return SuccessResponse(data)
@app.get("/weekly/user/activity/", summary="每周用户活跃量")
@ -56,7 +56,7 @@ async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())):
{"value": 1322, "name": 'analysis.saturday'},
{"value": 1324, "name": 'analysis.sunday'}
]
return SuccessResponse(data, refresh=auth.refresh)
return SuccessResponse(data)
@app.get("/monthly/sales/", summary="每月销售额")
@ -75,4 +75,4 @@ async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())):
{"estimate": 118, "actual": 99, "name": 'analysis.november'},
{"estimate": 123, "actual": 123, "name": 'analysis.december'}
]
return SuccessResponse(data, refresh=auth.refresh)
return SuccessResponse(data)

View File

@ -4,9 +4,8 @@
# @File : current.py
# @IDE : PyCharm
# @desc : 获取认证后的信息工具
from datetime import datetime, timedelta
from typing import List, Optional
import jwt
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from apps.vadmin.auth.crud import UserDal
@ -20,20 +19,6 @@ from core.database import db_getter
from .validation.auth import Auth
def get_user_permissions(user: VadminUser) -> set:
"""
获取员工用户所有权限列表
"""
if any([role.is_admin for role in user.roles]):
return {'*.*.*'}
permissions = set()
for role_obj in user.roles:
for menu in role_obj.menus:
if menu.perms and not menu.disabled:
permissions.add(menu.perms)
return permissions
class OpenAuth(AuthValidation):
"""
@ -41,50 +26,6 @@ class OpenAuth(AuthValidation):
认证了以后可以获取到用户信息无认证则获取不到
"""
@classmethod
def validate_token(cls, request: Request, token: str | None) -> str | None:
"""
验证用户 token没有则返回 None
"""
if not token:
return None
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
telephone: str = payload.get("sub")
exp: int = payload.get("exp")
is_refresh: bool = payload.get("is_refresh")
if telephone is None or is_refresh:
return None
# 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间
buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp()
if buffer_time >= exp:
request.scope["refresh"] = True
else:
request.scope["refresh"] = False
except jwt.exceptions.InvalidSignatureError:
return None
except jwt.exceptions.ExpiredSignatureError:
return None
return telephone
@classmethod
async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession) -> Auth:
"""
验证用户信息
"""
if user is None:
return Auth(db=db)
elif not user.is_active:
return Auth(db=db)
request.scope["user_id"] = user.id
request.scope["user_name"] = user.name
request.scope["telephone"] = user.telephone
try:
request.scope["body"] = await request.body()
except RuntimeError:
request.scope["body"] = "获取失败"
return Auth(user=user, db=db)
async def __call__(
self,
request: Request,
@ -94,10 +35,13 @@ class OpenAuth(AuthValidation):
"""
每次调用依赖此类的接口会执行该方法
"""
if not settings.OAUTH_ENABLE:
return Auth(db=db)
try:
telephone = self.validate_token(request, token)
if telephone:
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
return await self.validate_user(request, user, db)
except CustomException:
return Auth(db=db)
@ -120,8 +64,6 @@ class AllUserAuth(AuthValidation):
if not settings.OAUTH_ENABLE:
return Auth(db=db)
telephone = self.validate_token(request, token)
if isinstance(telephone, Auth):
return telephone
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
return await self.validate_user(request, user, db)
@ -152,12 +94,10 @@ class FullAdminAuth(AuthValidation):
if not settings.OAUTH_ENABLE:
return Auth(db=db)
telephone = self.validate_token(request, token)
if isinstance(telephone, Auth):
return telephone
options = [joinedload(VadminUser.roles), joinedload("roles.menus")]
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True, v_options=options, is_staff=True)
result = await self.validate_user(request, user, db)
permissions = get_user_permissions(user)
permissions = self.get_user_permissions(user)
if permissions != {'*.*.*'} and self.permissions:
if not (self.permissions & permissions):
raise CustomException(msg="无权限操作", code=status.HTTP_403_FORBIDDEN)

View File

@ -4,20 +4,20 @@
# @File : auth.py
# @IDE : PyCharm
# @desc : 用户凭证验证装饰器
from datetime import datetime, timedelta
from fastapi import Request
import jwt
from pydantic import BaseModel
from application import settings
from sqlalchemy.ext.asyncio import AsyncSession
from apps.vadmin.auth import models
from apps.vadmin.auth.models import VadminUser
from core.exception import CustomException
from utils import status
from datetime import timedelta, datetime
class Auth(BaseModel):
user: models.VadminUser = None
refresh: bool = False
user: VadminUser = None
db: AsyncSession
class Config:
@ -33,7 +33,7 @@ class AuthValidation:
error_code = status.HTTP_401_UNAUTHORIZED
@classmethod
def validate_token(cls, request: Request, token: str) -> str:
def validate_token(cls, request: Request, token: str | None) -> str:
"""
验证用户 token
"""
@ -52,9 +52,9 @@ class AuthValidation:
# print("当前时间", buffer_time, datetime.fromtimestamp(buffer_time))
# print("剩余时间", exp - buffer_time)
if buffer_time >= exp:
request.scope["refresh"] = True
request.scope["refresh"] = 1
else:
request.scope["refresh"] = False
request.scope["refresh"] = 0
except jwt.exceptions.InvalidSignatureError:
raise CustomException(msg="无效认证,请您重新登录", code=cls.error_code)
except jwt.exceptions.ExpiredSignatureError:
@ -62,7 +62,7 @@ class AuthValidation:
return telephone
@classmethod
async def validate_user(cls, request: Request, user: models.VadminUser, db: AsyncSession) -> Auth:
async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession) -> Auth:
"""
验证用户信息
"""
@ -70,12 +70,25 @@ class AuthValidation:
raise CustomException(msg="未认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code)
elif not user.is_active:
raise CustomException(msg="用户已被冻结!", code=cls.error_code, status_code=cls.error_code)
request.scope["telephone"] = user.telephone
request.scope["user_id"] = user.id
request.scope["user_name"] = user.name
request.scope["telephone"] = user.telephone
try:
request.scope["body"] = await request.body()
except RuntimeError:
request.scope["body"] = "获取失败"
refresh = request.scope.get("refresh", False)
return Auth(user=user, db=db, refresh=refresh)
return Auth(user=user, db=db)
@classmethod
def get_user_permissions(cls, user: VadminUser) -> set:
"""
获取员工用户所有权限列表
"""
if any([role.is_admin for role in user.roles]):
return {'*.*.*'}
permissions = set()
for role_obj in user.roles:
for menu in role_obj.menus:
if menu.perms and not menu.disabled:
permissions.add(menu.perms)
return permissions

View File

@ -11,7 +11,7 @@ from sqlalchemy.orm import joinedload
from utils.response import SuccessResponse, ErrorResponse
from . import schemas, crud, models
from core.dependencies import IdList
from apps.vadmin.auth.utils.current import AllUserAuth, get_user_permissions, FullAdminAuth
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth
from apps.vadmin.auth.utils.validation.auth import Auth
from .params import UserParams, RoleParams
@ -31,12 +31,12 @@ async def get_users(
schema = schemas.UserOut
datas = await crud.UserDal(auth.db).get_datas(**params.dict(), v_options=options, v_schema=schema)
count = await crud.UserDal(auth.db).get_count(**params.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.post("/users/", summary="创建用户")
async def create_user(data: schemas.UserIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create"]))):
return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data))
@app.delete("/users/", summary="批量删除用户", description="软删除,删除后清空所关联的角色")
@ -46,7 +46,7 @@ async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
elif 1 in ids.ids:
return ErrorResponse("不能删除超级管理员用户")
await crud.UserDal(auth.db).delete_datas(ids=ids.ids, v_soft=True, is_active=False)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.put("/users/{data_id}/", summary="更新用户信息")
@ -55,7 +55,7 @@ async def put_user(
data: schemas.UserUpdate,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.update"]))
):
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data))
@app.get("/users/{data_id}/", summary="获取用户信息")
@ -66,32 +66,29 @@ async def get_user(
model = models.VadminUser
options = [joinedload(model.roles)]
schema = schemas.UserOut
return SuccessResponse(
await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema),
refresh=auth.refresh
)
return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema))
@app.post("/user/current/reset/password/", summary="重置当前用户密码")
async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data))
@app.post("/user/current/update/info/", summary="更新当前用户基本信息")
async def post_user_current_update_info(data: schemas.UserUpdateBaseInfo, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data))
@app.post("/user/current/update/avatar/", summary="更新当前用户头像")
async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file))
@app.get("/user/admin/current/info/", summary="获取当前管理员信息")
async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())):
result = schemas.UserOut.from_orm(auth.user).dict()
result["permissions"] = list(get_user_permissions(auth.user))
return SuccessResponse(result, refresh=auth.refresh)
result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user))
return SuccessResponse(result)
@app.post("/user/export/query/list/to/excel/", summary="导出用户查询列表为excel")
@ -100,17 +97,17 @@ async def post_user_export_query_list(
params: UserParams = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.export"]))
):
return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params))
@app.get("/user/download/import/template/", summary="下载最新批量导入用户模板")
async def get_user_download_new_import_template(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.UserDal(auth.db).download_import_template(), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).download_import_template())
@app.post("/import/users/", summary="批量导入用户")
async def post_import_users(file: UploadFile, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.import"]))):
return SuccessResponse(await crud.UserDal(auth.db).import_users(file), refresh=auth.refresh)
return SuccessResponse(await crud.UserDal(auth.db).import_users(file))
@app.post("/users/init/password/send/sms/", summary="初始化所选用户密码并发送通知短信")
@ -119,16 +116,13 @@ async def post_users_init_password(
ids: IdList = Depends(),
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.reset"]))
):
return SuccessResponse(
await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis),
refresh=auth.refresh
)
return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis))
@app.put("/users/wx/server/openid/", summary="更新当前用户服务端微信平台openid")
async def put_user_wx_server_openid(request: Request, code: str, auth: Auth = Depends(AllUserAuth())):
result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, request.app.state.redis)
return SuccessResponse(result, refresh=auth.refresh)
return SuccessResponse(result)
###########################################################
@ -141,12 +135,12 @@ async def get_roles(
):
datas = await crud.RoleDal(auth.db).get_datas(**params.dict())
count = await crud.RoleDal(auth.db).get_count(**params.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.post("/roles/", summary="创建角色信息")
async def create_role(role: schemas.RoleIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create"]))):
return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role), refresh=auth.refresh)
return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role))
@app.delete("/roles/", summary="批量删除角色", description="硬删除, 如果存在用户关联则无法删除")
@ -154,7 +148,7 @@ async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu
if 1 in ids.ids:
return ErrorResponse("不能删除管理员角色")
await crud.RoleDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.put("/roles/{data_id}/", summary="更新角色信息")
@ -165,12 +159,12 @@ async def put_role(
):
if 1 == data_id:
return ErrorResponse("不能修改管理员角色")
return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data))
@app.get("/roles/options/", summary="获取角色选择项")
async def get_role_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create", "auth.user.update"]))):
return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas(), refresh=auth.refresh)
return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas())
@app.get("/roles/{data_id}/", summary="获取角色信息")
@ -181,10 +175,7 @@ async def get_role(
model = models.VadminRole
options = [joinedload(model.menus)]
schema = schemas.RoleOut
return SuccessResponse(
await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema),
refresh=auth.refresh
)
return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema))
###########################################################
@ -193,33 +184,33 @@ async def get_role(
@app.get("/menus/", summary="获取菜单列表")
async def get_menus(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.list"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=1)
return SuccessResponse(datas, refresh=auth.refresh)
return SuccessResponse(datas)
@app.get("/menus/tree/options/", summary="获取菜单树选择项,添加/修改菜单时使用")
async def get_menus_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create", "auth.menu.update"]))):
datas = await crud.MenuDal(auth.db).get_tree_list(mode=2)
return SuccessResponse(datas, refresh=auth.refresh)
return SuccessResponse(datas)
@app.get("/menus/role/tree/options/", summary="获取菜单列表树信息,角色权限使用")
async def get_menus_treeselect(
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
):
return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3), refresh=auth.refresh)
return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3))
@app.post("/menus/", summary="创建菜单信息")
async def create_menu(menu: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create"]))):
if menu.parent_id:
menu.alwaysShow = False
return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu), refresh=auth.refresh)
return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu))
@app.delete("/menus/", summary="批量删除菜单", description="硬删除, 如果存在角色关联则无法删除")
async def delete_menus(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.delete"]))):
await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.put("/menus/{data_id}/", summary="更新菜单信息")
@ -227,7 +218,7 @@ async def put_menus(
data_id: int,
data: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.update"]))
):
return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data))
@app.get("/menus/{data_id}/", summary="获取菜单信息")
@ -236,7 +227,7 @@ async def put_menus(
auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.view", "auth.menu.update"]))
):
schema = schemas.MenuSimpleOut
return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema), refresh=auth.refresh)
return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema))
@app.get("/role/menus/tree/{role_id}/", summary="获取菜单列表树信息以及角色菜单权限ID角色权限使用")
@ -246,4 +237,4 @@ async def get_role_menu_tree(
):
treeselect = await crud.MenuDal(auth.db).get_tree_list(mode=3)
role_menu_tree = await crud.RoleDal(auth.db).get_role_menu_tree(role_id)
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect}, refresh=auth.refresh)
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect})

View File

@ -29,42 +29,36 @@ async def get_issue_categorys(p: params.IssueCategoryParams = Depends(), auth: A
schema = schemas.IssueCategoryListOut
datas = await crud.IssueCategoryDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema)
count = await crud.IssueCategoryDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.get("/issue/categorys/options/", summary="获取类别选择项")
async def get_issue_categorys_options(auth: Auth = Depends(AllUserAuth())):
schema = schemas.IssueCategoryOptionsOut
return SuccessResponse(
await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema),
refresh=auth.refresh
)
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema))
@app.post("/issue/categorys/", summary="创建类别")
async def create_issue_category(data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
data.user_id = auth.user.id
return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data), refresh=auth.refresh)
return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data))
@app.delete("/issue/categorys/", summary="批量删除类别", description="硬删除")
async def delete_issue_categorys(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.put("/issue/categorys/{data_id}/", summary="更新类别信息")
async def put_issue_category(data_id: int, data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data))
@app.get("/issue/categorys/{data_id}/", summary="获取类别信息")
async def get_issue_category(data_id: int, auth: Auth = Depends(AllUserAuth())):
schema = schemas.IssueCategorySimpleOut
return SuccessResponse(
await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema),
refresh=auth.refresh
)
return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema))
@app.get("/issue/categorys/platform/{platform}/", summary="获取平台中的常见问题类别列表")
@ -87,24 +81,24 @@ async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(All
schema = schemas.IssueListOut
datas = await crud.IssueDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema)
count = await crud.IssueDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.post("/issues/", summary="创建问题")
async def create_issue(data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
data.user_id = auth.user.id
return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data), refresh=auth.refresh)
return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data))
@app.delete("/issues/", summary="批量删除问题", description="硬删除")
async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.put("/issues/{data_id}/", summary="更新问题信息")
async def put_issue(data_id: int, data: schemas.Issue, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data))
@app.get("/issues/{data_id}/", summary="获取问题信息")

View File

@ -23,22 +23,25 @@ app = APIRouter()
async def get_record_login(p: LoginParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict())
count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.get("/operations/", summary="获取操作日志列表")
async def get_record_operation(p: OperationParams = Depends(), db: DatabaseManage = Depends(get_database),
auth: Auth = Depends(AllUserAuth())):
async def get_record_operation(
p: OperationParams = Depends(),
db: DatabaseManage = Depends(get_database),
auth: Auth = Depends(AllUserAuth())
):
count = await db.get_count("operation_record", **p.to_count())
datas = await db.get_datas("operation_record", v_schema=schemas.OpertionRecordSimpleOut, **p.dict())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.get("/sms/send/list/", summary="获取短信发送列表")
async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict())
count = await crud.SMSSendRecordDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
###########################################################
@ -46,4 +49,4 @@ async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUs
###########################################################
@app.get("/analysis/user/login/distribute/", summary="获取用户登录分布情况列表")
async def get_user_login_distribute(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute(), refresh=auth.refresh)
return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute())

View File

@ -31,18 +31,18 @@ app = APIRouter()
async def get_dict_types(p: DictTypeParams = Depends(), auth: Auth = Depends(AllUserAuth())):
datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict())
count = await crud.DictTypeDal(auth.db).get_count(**p.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.post("/dict/types/", summary="创建字典类型")
async def create_dict_types(data: schemas.DictType, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data), refresh=auth.refresh)
return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data))
@app.delete("/dict/types/", summary="批量删除字典类型")
async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.DictTypeDal(auth.db).delete_datas(ids=ids.ids)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.post("/dict/types/details/", summary="获取多个字典类型下的字典元素列表")
@ -51,17 +51,17 @@ async def post_dicts_details(
dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表")
):
datas = await crud.DictTypeDal(auth.db).get_dicts_details(dict_types)
return SuccessResponse(datas, refresh=auth.refresh)
return SuccessResponse(datas)
@app.get("/dict/types/options/", summary="获取字典类型选择项")
async def get_dicts_options(auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas(), refresh=auth.refresh)
return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas())
@app.put("/dict/types/{data_id}/", summary="更新字典类型")
async def put_dict_types(data_id: int, data: schemas.DictType, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data))
@app.get("/dict/types/{data_id}/", summary="获取字典类型详细")
@ -78,7 +78,7 @@ async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())):
###########################################################
@app.post("/dict/details/", summary="创建字典元素")
async def create_dict_details(data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data), refresh=auth.refresh)
return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data))
@app.get("/dict/details/", summary="获取单个字典类型下的字典元素列表,分页")
@ -87,18 +87,18 @@ async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = De
return ErrorResponse(msg="未获取到字典类型!")
datas = await crud.DictDetailsDal(auth.db).get_datas(**params.dict())
count = await crud.DictDetailsDal(auth.db).get_count(**params.to_count())
return SuccessResponse(datas, count=count, refresh=auth.refresh)
return SuccessResponse(datas, count=count)
@app.delete("/dict/details/", summary="批量删除字典元素", description="硬删除")
async def delete_dict_details(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())):
await crud.DictDetailsDal(auth.db).delete_datas(ids.ids, v_soft=False)
return SuccessResponse("删除成功", refresh=auth.refresh)
return SuccessResponse("删除成功")
@app.put("/dict/details/{data_id}/", summary="更新字典元素")
async def put_dict_details(data_id: int, data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())):
return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data), refresh=auth.refresh)
return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data))
@app.get("/dict/details/{data_id}/", summary="获取字典元素详情")
@ -142,23 +142,17 @@ async def sms_send(request: Request, telephone: str):
###########################################################
@app.get("/settings/tabs/", summary="获取系统配置标签列表")
async def get_settings_tabs(classify: str, auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(
await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify),
refresh=auth.refresh
)
return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify))
@app.get("/settings/tabs/values/", summary="获取系统配置标签下的信息")
async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id), refresh=auth.refresh)
return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id))
@app.put("/settings/tabs/values/", summary="更新系统配置信息")
async def put_settings_tabs_values(request: Request, datas: dict = Body(...), auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(
await crud.SettingsDal(auth.db).update_datas(datas, request.app.state.redis),
refresh=auth.refresh
)
return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, request.app.state.redis))
@app.get("/settings/base/config/", summary="获取系统基础配置", description="每次进入系统中时使用")
@ -168,15 +162,9 @@ async def get_setting_base_config(db: AsyncSession = Depends(db_getter)):
@app.get("/settings/privacy/", summary="获取隐私协议")
async def get_settings_privacy(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(
(await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value,
refresh=auth.refresh
)
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value)
@app.get("/settings/agreement/", summary="获取用户协议")
async def get_settings_agreement(auth: Auth = Depends(FullAdminAuth())):
return SuccessResponse(
(await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value,
refresh=auth.refresh
)
return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value)

View File

@ -13,7 +13,6 @@ import datetime
import json
import time
from fastapi import Request, Response
from core.exception import CustomException
from core.logger import logger
from fastapi import FastAPI
from fastapi.routing import APIRoute
@ -21,10 +20,6 @@ from user_agents import parse
from application.settings import OPERATION_RECORD_METHOD, MONGO_DB_ENABLE, IGNORE_OPERATION_FUNCTION,\
DEMO_WHITE_LIST_PATH, DEMO
from core.mongo import get_database
# 记录请求日志
from utils import status
from utils.response import ErrorResponse
@ -137,3 +132,18 @@ def register_demo_env_middleware(app: FastAPI):
if DEMO and request.method != "GET" and path not in DEMO_WHITE_LIST_PATH:
return ErrorResponse(msg="演示环境,禁止操作")
return await call_next(request)
def register_jwt_refresh_middleware(app: FastAPI):
"""
JWT刷新中间件
:param app:
:return:
"""
@app.middleware("http")
async def jwt_refresh_middleware(request: Request, call_next):
response = await call_next(request)
refresh = request.scope.get('refresh', 0)
response.headers["refresh"] = str(refresh)
return response

View File

@ -29,7 +29,7 @@ cryptography==38.0.3
dnspython==2.2.1
ecdsa==0.18.0
et-xmlfile==1.1.0
fastapi==0.94.1
fastapi==0.95.0
frozenlist==1.3.3
greenlet==2.0.1
gunicorn==20.1.0

View File

@ -42,7 +42,7 @@ http.interceptors.response.use(
// 获取错误信息
const msg = res.data.message || errorCode[code] || errorCode['default']
// 是否刷新token
const refresh = res.data.refresh || false
const refresh = res.header.refresh
if (code === 500) {
toast(msg)
return Promise.reject(new Error(msg))
@ -58,7 +58,7 @@ http.interceptors.response.use(
toast(msg)
return Promise.reject('error')
} else if (code === 200) {
if (refresh) {
if (refresh === '1') {
// 因token快过期刷新token
refreshToken().then((res) => {
setToken(`${res.data.token_type} ${res.data.access_token}`)