Merge pull request #4 from mizhexiaoxiao/pydantic_v2

Upgrade to pydantic2.0
This commit is contained in:
mizhexiaoxiao 2023-09-19 11:26:23 +08:00 committed by GitHub
commit ebd80fd532
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 384 additions and 245 deletions

View File

@ -26,12 +26,9 @@ async def list_api(
q &= Q(summary__contains=summary) q &= Q(summary__contains=summary)
if tags: if tags:
q &= Q(tags__contains=tags) q &= Q(tags__contains=tags)
total, api_objs = await api_controller.list(page=page, page_size=page_size, search=q, order=["id"]) total, api_objs = await api_controller.list(page=page, page_size=page_size, search=q, order=["tags", "id"])
result = [] data = [await obj.to_dict() for obj in api_objs]
for api in api_objs: return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
api_dict = await api.to_dict(m2m=False)
result.append(api_dict)
return SuccessExtra(data=result, total=total, page=page, page_size=page_size)
@router.get("/get", summary="查看Api") @router.get("/get", summary="查看Api")
@ -39,16 +36,16 @@ async def get_api(
id: int = Query(..., description="Api"), id: int = Query(..., description="Api"),
): ):
api_obj = await api_controller.get(id=id) api_obj = await api_controller.get(id=id)
api_dict = await api_obj.to_dict() data = await api_obj.to_dict()
return Success(code=200, data=api_dict) return Success(data=data)
@router.post("/create", summary="创建Api") @router.post("/create", summary="创建Api")
async def create_api( async def create_api(
api_in: ApiCreate, api_in: ApiCreate,
): ):
new_api = await api_controller.create(obj_in=api_in) await api_controller.create(obj_in=api_in)
return Success(msg="Created Successfully", data=new_api) return Success(msg="Created Successfully")
@router.post("/update", summary="更新Api") @router.post("/update", summary="更新Api")

View File

@ -6,7 +6,7 @@ from app.controllers.user import UserController, user_controller
from app.core.ctx import CTX_USER_ID from app.core.ctx import CTX_USER_ID
from app.core.dependency import DependAuth from app.core.dependency import DependAuth
from app.models.admin import Api, Menu, Role, User from app.models.admin import Api, Menu, Role, User
from app.schemas.base import BaseResponse, Fail, Success from app.schemas.base import Fail, Success
from app.schemas.login import * from app.schemas.login import *
from app.schemas.users import UpdatePassword from app.schemas.users import UpdatePassword
from app.settings import settings from app.settings import settings
@ -17,13 +17,13 @@ router = APIRouter()
@router.post("/access_token", summary="获取token") @router.post("/access_token", summary="获取token")
async def login_access_token(credentials: CredentialsSchema) -> BaseResponse: async def login_access_token(credentials: CredentialsSchema):
user: User = await user_controller.authenticate(credentials) user: User = await user_controller.authenticate(credentials)
await user_controller.update_last_login(user.id) await user_controller.update_last_login(user.id)
access_token_expires = timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES) access_token_expires = timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
expire = datetime.utcnow() + access_token_expires expire = datetime.utcnow() + access_token_expires
result = JWTOut( data = JWTOut(
access_token=create_access_token( access_token=create_access_token(
data=JWTPayload( data=JWTPayload(
user_id=user.id, user_id=user.id,
@ -34,23 +34,21 @@ async def login_access_token(credentials: CredentialsSchema) -> BaseResponse:
), ),
username=user.username, username=user.username,
) )
return Success(data=data.model_dump())
return BaseResponse(code=200, data=result)
@router.get("/userinfo", summary="查看用户信息", dependencies=[DependAuth]) @router.get("/userinfo", summary="查看用户信息", dependencies=[DependAuth])
async def get_userinfo(): async def get_userinfo():
user_id = CTX_USER_ID.get() user_id = CTX_USER_ID.get()
user_obj = await user_controller.get(id=user_id) user_obj = await user_controller.get(id=user_id)
to_dict = await user_obj.to_dict() data = await user_obj.to_dict()
to_dict.pop("password") data.pop("password")
to_dict["avatar"] = "https://avatars.githubusercontent.com/u/54677442?v=4" data["avatar"] = "https://avatars.githubusercontent.com/u/54677442?v=4"
return Success(data=data)
return BaseResponse(code=200, data=to_dict)
@router.get("/usermenu", summary="查看用户菜单", dependencies=[DependAuth]) @router.get("/usermenu", summary="查看用户菜单", dependencies=[DependAuth])
async def get_user_menu() -> BaseResponse: async def get_user_menu():
user_id = CTX_USER_ID.get() user_id = CTX_USER_ID.get()
user_obj = await User.filter(id=user_id).first() user_obj = await User.filter(id=user_id).first()
menus: list[Menu] = [] menus: list[Menu] = []
@ -78,7 +76,7 @@ async def get_user_menu() -> BaseResponse:
@router.get("/userapi", summary="查看用户API", dependencies=[DependAuth]) @router.get("/userapi", summary="查看用户API", dependencies=[DependAuth])
async def get_user_api() -> BaseResponse: async def get_user_api():
user_id = CTX_USER_ID.get() user_id = CTX_USER_ID.get()
user_obj = await User.filter(id=user_id).first() user_obj = await User.filter(id=user_id).first()
if user_obj.is_superuser: if user_obj.is_superuser:
@ -95,7 +93,7 @@ async def get_user_api() -> BaseResponse:
@router.post("/update_password", summary="更新用户密码", dependencies=[DependAuth]) @router.post("/update_password", summary="更新用户密码", dependencies=[DependAuth])
async def update_user_password(req_in: UpdatePassword) -> BaseResponse: async def update_user_password(req_in: UpdatePassword):
user_controller = UserController() user_controller = UserController()
user = await user_controller.get(req_in.id) user = await user_controller.get(req_in.id)
verified = verify_password(req_in.old_password, user.password) verified = verify_password(req_in.old_password, user.password)

View File

@ -3,7 +3,7 @@ import logging
from fastapi import APIRouter, Query from fastapi import APIRouter, Query
from app.controllers.menu import menu_controller from app.controllers.menu import menu_controller
from app.schemas.base import BaseResponse, Fail, Success from app.schemas.base import Fail, Success, SuccessExtra
from app.schemas.menus import * from app.schemas.menus import *
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -15,13 +15,13 @@ router = APIRouter()
async def list_menu( async def list_menu(
page: int = Query(1, description="页码"), page: int = Query(1, description="页码"),
page_size: int = Query(10, description="每页数量"), page_size: int = Query(10, description="每页数量"),
) -> MenuOutList: ):
parent_menus = await menu_controller.model.filter(parent_id=0).order_by("order") parent_menus = await menu_controller.model.filter(parent_id=0).order_by("order")
res_menu = [] res_menu = []
for menu in parent_menus: for menu in parent_menus:
child_menu = await menu_controller.model.filter(parent_id=menu.id).order_by("order") child_menu = await menu_controller.model.filter(parent_id=menu.id).order_by("order")
menu_dict = await menu.to_dict() menu_dict = await menu.to_dict()
menu_dict["children"] = child_menu menu_dict["children"] = [await obj.to_dict() for obj in child_menu]
res_menu.append(menu_dict) res_menu.append(menu_dict)
return SuccessExtra(data=res_menu, total=len(res_menu), page=page, page_size=page_size) return SuccessExtra(data=res_menu, total=len(res_menu), page=page, page_size=page_size)
@ -29,7 +29,7 @@ async def list_menu(
@router.get("/get", summary="查看菜单") @router.get("/get", summary="查看菜单")
async def get_menu( async def get_menu(
menu_id: int = Query(..., description="菜单id"), menu_id: int = Query(..., description="菜单id"),
) -> BaseResponse: ):
result = await menu_controller.get(id=menu_id) result = await menu_controller.get(id=menu_id)
return Success(data=result) return Success(data=result)
@ -37,7 +37,7 @@ async def get_menu(
@router.post("/create", summary="创建菜单") @router.post("/create", summary="创建菜单")
async def create_menu( async def create_menu(
menu_in: MenuCreate, menu_in: MenuCreate,
) -> BaseResponse: ):
await menu_controller.create(obj_in=menu_in) await menu_controller.create(obj_in=menu_in)
return Success(msg="Created Success") return Success(msg="Created Success")
@ -45,7 +45,7 @@ async def create_menu(
@router.post("/update", summary="更新菜单") @router.post("/update", summary="更新菜单")
async def update_menu( async def update_menu(
menu_in: MenuUpdate, menu_in: MenuUpdate,
) -> BaseResponse: ):
await menu_controller.update(id=menu_in.id, obj_in=menu_in.update_dict()) await menu_controller.update(id=menu_in.id, obj_in=menu_in.update_dict())
return Success(msg="Updated Success") return Success(msg="Updated Success")
@ -53,7 +53,7 @@ async def update_menu(
@router.delete("/delete", summary="删除菜单") @router.delete("/delete", summary="删除菜单")
async def delete_menu( async def delete_menu(
id: int = Query(..., description="菜单id"), id: int = Query(..., description="菜单id"),
) -> BaseResponse: ):
child_menu_count = await menu_controller.model.filter(parent_id=id).count() child_menu_count = await menu_controller.model.filter(parent_id=id).count()
if child_menu_count > 0: if child_menu_count > 0:
return Fail(msg="Cannot delete a menu with child menus") return Fail(msg="Cannot delete a menu with child menus")

View File

@ -5,7 +5,7 @@ from fastapi.exceptions import HTTPException
from tortoise.expressions import Q from tortoise.expressions import Q
from app.controllers import role_controller from app.controllers import role_controller
from app.schemas.base import BaseResponse, Success, SuccessExtra from app.schemas.base import Success, SuccessExtra
from app.schemas.roles import * from app.schemas.roles import *
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -17,26 +17,27 @@ async def list_role(
page: int = Query(1, description="页码"), page: int = Query(1, description="页码"),
page_size: int = Query(10, description="每页数量"), page_size: int = Query(10, description="每页数量"),
role_name: str = Query("", description="角色名称,用于查询"), role_name: str = Query("", description="角色名称,用于查询"),
) -> RoleOutList: ):
q = Q() q = Q()
if role_name: if role_name:
q = Q(name__contains=role_name) q = Q(name__contains=role_name)
total, result = await role_controller.list(page=page, page_size=page_size, search=q) total, role_objs = await role_controller.list(page=page, page_size=page_size, search=q)
return SuccessExtra(data=result, total=total, page=page, page_size=page_size) data = [await obj.to_dict() for obj in role_objs]
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
@router.get("/get", summary="查看角色") @router.get("/get", summary="查看角色")
async def get_role( async def get_role(
role_id: int = Query(..., description="角色ID"), role_id: int = Query(..., description="角色ID"),
) -> RoleOut: ):
role_obj = await role_controller.get(id=role_id) role_obj = await role_controller.get(id=role_id)
return Success(data=role_obj) return Success(data=await role_obj.to_dict())
@router.post("/create", summary="创建角色") @router.post("/create", summary="创建角色")
async def create_role( async def create_role(
role_in: RoleCreate, role_in: RoleCreate,
) -> BaseResponse: ):
if await role_controller.is_exist(name=role_in.name): if await role_controller.is_exist(name=role_in.name):
raise HTTPException( raise HTTPException(
status_code=400, status_code=400,
@ -47,7 +48,7 @@ async def create_role(
@router.post("/update", summary="更新角色") @router.post("/update", summary="更新角色")
async def update_role(role_in: RoleUpdate) -> BaseResponse: async def update_role(role_in: RoleUpdate):
await role_controller.update(id=role_in.id, obj_in=role_in.update_dict()) await role_controller.update(id=role_in.id, obj_in=role_in.update_dict())
return Success(msg="Updated Successfully") return Success(msg="Updated Successfully")
@ -55,20 +56,20 @@ async def update_role(role_in: RoleUpdate) -> BaseResponse:
@router.delete("/delete", summary="删除角色") @router.delete("/delete", summary="删除角色")
async def delete_role( async def delete_role(
role_id: int = Query(..., description="角色ID"), role_id: int = Query(..., description="角色ID"),
) -> BaseResponse: ):
await role_controller.remove(id=role_id) await role_controller.remove(id=role_id)
return Success(msg="Deleted Success") return Success(msg="Deleted Success")
@router.get("/authorized", summary="查看角色权限") @router.get("/authorized", summary="查看角色权限")
async def get_role_authorized(id: int = Query(..., description="角色ID")) -> BaseResponse: async def get_role_authorized(id: int = Query(..., description="角色ID")):
role_obj = await role_controller.get(id=id) role_obj = await role_controller.get(id=id)
role_dict = await role_obj.to_dict() data = await role_obj.to_dict(m2m=True)
return Success(data=role_dict) return Success(data=data)
@router.post("/authorized", summary="更新角色权限") @router.post("/authorized", summary="更新角色权限")
async def update_role_authorized(role_in: RoleUpdateMenusApis) -> BaseResponse: async def update_role_authorized(role_in: RoleUpdateMenusApis):
role_obj = await role_controller.get(id=role_in.id) role_obj = await role_controller.get(id=role_in.id)
await role_controller.update_roles(role=role_obj, menu_ids=role_in.menu_ids, api_infos=role_in.api_infos) await role_controller.update_roles(role=role_obj, menu_ids=role_in.menu_ids, api_infos=role_in.api_infos)
return Success(msg="Updated Successfully") return Success(msg="Updated Successfully")

View File

@ -6,7 +6,7 @@ from tortoise.expressions import Q
from app.controllers.user import UserController from app.controllers.user import UserController
from app.core.dependency import DependPermisson from app.core.dependency import DependPermisson
from app.schemas.base import BaseResponse, Success, SuccessExtra from app.schemas.base import Success, SuccessExtra
from app.schemas.users import * from app.schemas.users import *
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -20,7 +20,7 @@ async def list_user(
page_size: int = Query(10, description="每页数量"), page_size: int = Query(10, description="每页数量"),
username: str = Query("", description="用户名称,用于搜索"), username: str = Query("", description="用户名称,用于搜索"),
email: str = Query("", description="邮箱地址"), email: str = Query("", description="邮箱地址"),
) -> UserOutList: ):
user_controller = UserController() user_controller = UserController()
q = Q() q = Q()
if username: if username:
@ -28,27 +28,24 @@ async def list_user(
if email: if email:
q &= Q(email__contains=email) q &= Q(email__contains=email)
total, user_objs = await user_controller.list(page=page, page_size=page_size, search=q) total, user_objs = await user_controller.list(page=page, page_size=page_size, search=q)
result = [] data = [await obj.to_dict(m2m=True) for obj in user_objs]
for user in user_objs: return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
user_dict = await user.to_dict()
result.append(user_dict)
return SuccessExtra(data=result, total=total, page=page, page_size=page_size)
@router.get("/get", summary="查看用户", dependencies=[DependPermisson]) @router.get("/get", summary="查看用户", dependencies=[DependPermisson])
async def get_user( async def get_user(
user_id: int = Query(..., description="用户ID"), user_id: int = Query(..., description="用户ID"),
) -> UserOut: ):
user_controller = UserController() user_controller = UserController()
user_obj = await user_controller.get(id=user_id) user_obj = await user_controller.get(id=user_id)
user_dict = await user_obj.to_dict() user_dict = await user_obj.to_dict()
return Success(code=200, data=user_dict) return Success(data=user_dict)
@router.post("/create", summary="创建用户", dependencies=[DependPermisson]) @router.post("/create", summary="创建用户", dependencies=[DependPermisson])
async def create_user( async def create_user(
user_in: UserCreate, user_in: UserCreate,
) -> BaseResponse: ):
user_controller = UserController() user_controller = UserController()
user = await user_controller.get_by_email(user_in.email) user = await user_controller.get_by_email(user_in.email)
if user: if user:
@ -64,7 +61,7 @@ async def create_user(
@router.post("/update", summary="更新用户", dependencies=[DependPermisson]) @router.post("/update", summary="更新用户", dependencies=[DependPermisson])
async def update_user( async def update_user(
user_in: UserUpdate, user_in: UserUpdate,
) -> BaseResponse: ):
user_controller = UserController() user_controller = UserController()
user = await user_controller.update(obj_in=user_in) user = await user_controller.update(obj_in=user_in)
await user_controller.update_roles(user, user_in.roles) await user_controller.update_roles(user, user_in.roles)
@ -74,7 +71,7 @@ async def update_user(
@router.delete("/delete", summary="删除用户", dependencies=[DependPermisson]) @router.delete("/delete", summary="删除用户", dependencies=[DependPermisson])
async def delete_user( async def delete_user(
user_id: int = Query(..., description="用户ID"), user_id: int = Query(..., description="用户ID"),
) -> BaseResponse: ):
user_controller = UserController() user_controller = UserController()
await user_controller.remove(id=user_id) await user_controller.remove(id=user_id)
return Success(msg="Deleted Successfully") return Success(msg="Deleted Successfully")

View File

@ -25,7 +25,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
if isinstance(obj_in, Dict): if isinstance(obj_in, Dict):
obj_dict = obj_in obj_dict = obj_in
else: else:
obj_dict = obj_in.dict() obj_dict = obj_in.model_dump()
obj = self.model(**obj_dict) obj = self.model(**obj_dict)
await obj.save() await obj.save()
return obj return obj
@ -34,7 +34,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
if isinstance(obj_in, Dict): if isinstance(obj_in, Dict):
obj_dict = obj_in obj_dict = obj_in
else: else:
obj_dict = obj_in.dict(exclude_unset=True) obj_dict = obj_in.model_dump(exclude_unset=True)
obj = await self.get(id=id) obj = await self.get(id=id)
obj = obj.update_from_dict(obj_dict) obj = obj.update_from_dict(obj_dict)
await obj.save() await obj.save()

View File

@ -2,7 +2,7 @@ from tortoise import fields
from app.schemas.menus import MenuType from app.schemas.menus import MenuType
from .base import BaseModel, TimestampMixin from .base import BaseModel, CustomDatetimeField, TimestampMixin
from .enums import MethodType from .enums import MethodType
@ -14,7 +14,7 @@ class User(BaseModel, TimestampMixin):
password = fields.CharField(max_length=128, null=True, description="密码") password = fields.CharField(max_length=128, null=True, description="密码")
is_active = fields.BooleanField(default=True, description="是否激活") is_active = fields.BooleanField(default=True, description="是否激活")
is_superuser = fields.BooleanField(default=False, description="是否为超级管理员") is_superuser = fields.BooleanField(default=False, description="是否为超级管理员")
last_login = fields.DatetimeField(null=True, description="最后登录时间") last_login = CustomDatetimeField(null=True, description="最后登录时间")
roles = fields.ManyToManyField("models.Role", related_name="user_roles") roles = fields.ManyToManyField("models.Role", related_name="user_roles")
class Meta: class Meta:

View File

@ -1,16 +1,29 @@
from tortoise import fields, models import datetime
import functools
from typing import Any, Optional
from tortoise import fields, models, timezone
try:
from ciso8601 import parse_datetime
except ImportError: # pragma: nocoverage
from iso8601 import parse_date
parse_datetime = functools.partial(parse_date, default_timezone=None)
from tortoise.timezone import get_timezone, localtime
class BaseModel(models.Model): class BaseModel(models.Model):
id = fields.BigIntField(pk=True, index=True) id = fields.BigIntField(pk=True, index=True)
async def to_dict(self, m2m=True): async def to_dict(self, m2m=False):
d = {} d = {}
for field in self._meta.db_fields: for field in self._meta.db_fields:
d[field] = getattr(self, field) d[field] = getattr(self, field)
if m2m: if m2m:
for field in self._meta.m2m_fields: for field in self._meta.m2m_fields:
d[field] = await getattr(self, field).all().values() values = await getattr(self, field).all().values()
d[field] = values
return d return d
class Meta: class Meta:
@ -21,6 +34,28 @@ class UUIDModel:
uuid = fields.UUIDField(unique=True, pk=False) uuid = fields.UUIDField(unique=True, pk=False)
class CustomDatetimeField(fields.DatetimeField):
def to_python_value(self, value: Any) -> Optional[datetime.datetime]:
if value is None:
value = None
else:
if isinstance(value, datetime.datetime):
value = value.strftime("%Y-%m-%d %H:%M:%S")
elif isinstance(value, int):
value = datetime.datetime.fromtimestamp(value)
value = value.strftime("%Y-%m-%d %H:%M:%S")
else:
value = parse_datetime(value)
if timezone.is_naive(value):
value = timezone.make_aware(value, get_timezone())
value = value.strftime("%Y-%m-%d %H:%M:%S")
else:
value = localtime(value)
value = value.strftime("%Y-%m-%d %H:%M:%S")
self.validate(value)
return value
class TimestampMixin: class TimestampMixin:
created_at = fields.DatetimeField(auto_now_add=True) created_at = CustomDatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True) updated_at = CustomDatetimeField(auto_now=True)

View File

@ -18,4 +18,4 @@ class ApiUpdate(BaseApi):
id: int id: int
def update_dict(self): def update_dict(self):
return self.dict(exclude_unset=True, exclude={"id"}) return self.model_dump(exclude_unset=True, exclude={"id"})

View File

@ -1,26 +1,52 @@
from typing import Generic, Optional, TypeVar from typing import Any, Optional
from pydantic import BaseModel from fastapi.responses import JSONResponse
from pydantic.generics import GenericModel
DataT = TypeVar("DataT")
class BaseResponse(GenericModel, BaseModel, Generic[DataT]): class Success(JSONResponse):
code: int def __init__(
msg: str = "" self,
data: Optional[DataT] = None code: int = 200,
msg: Optional[str] = "OK",
data: Optional[Any] = None,
**kwargs,
):
content = {"code": code, "msg": msg, "data": data}
content.update(kwargs)
super().__init__(content=content, status_code=code)
class Success(BaseResponse): class Fail(JSONResponse):
code: int = 200 def __init__(
self,
code: int = 400,
msg: Optional[str] = None,
data: Optional[Any] = None,
**kwargs,
):
content = {"code": code, "msg": msg, "data": data}
content.update(kwargs)
super().__init__(content=content, status_code=code)
class Fail(BaseResponse): class SuccessExtra(JSONResponse):
code: int = -1 def __init__(
self,
code: int = 200,
class SuccessExtra(Success): msg: Optional[str] = None,
total: int data: Optional[Any] = None,
page: int total: int = 0,
page_size: int page: int = 1,
page_size: int = 20,
**kwargs,
):
content = {
"code": code,
"msg": msg,
"data": data,
"total": total,
"page": page,
"page_size": page_size,
}
content.update(kwargs)
super().__init__(content=content, status_code=code)

View File

@ -1,7 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Optional
from pydantic import BaseModel, EmailStr, Field from pydantic import BaseModel, Field
class CredentialsSchema(BaseModel): class CredentialsSchema(BaseModel):

View File

@ -1,10 +1,8 @@
from enum import Enum from enum import Enum
from typing import List, Optional from typing import Optional
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from .base import BaseResponse, SuccessExtra
class MenuType(str, Enum): class MenuType(str, Enum):
CATALOG = "catalog" # 目录 CATALOG = "catalog" # 目录
@ -30,7 +28,6 @@ class BaseMenu(BaseModel):
class MenuCreate(BaseModel): class MenuCreate(BaseModel):
menu_type: MenuType = Field(default=MenuType.CATALOG.value) menu_type: MenuType = Field(default=MenuType.CATALOG.value)
name: str = Field(example="用户管理") name: str = Field(example="用户管理")
remark: Optional[dict] = Field(example={})
icon: Optional[str] = "ph:user-list-bold" icon: Optional[str] = "ph:user-list-bold"
path: str = Field(example="/system/user") path: str = Field(example="/system/user")
order: Optional[int] = Field(example=1) order: Optional[int] = Field(example=1)
@ -45,7 +42,6 @@ class MenuUpdate(BaseModel):
id: int id: int
menu_type: Optional[MenuType] = Field(example=MenuType.CATALOG.value) menu_type: Optional[MenuType] = Field(example=MenuType.CATALOG.value)
name: Optional[str] = Field(example="用户管理") name: Optional[str] = Field(example="用户管理")
remark: Optional[dict] = Field(example={})
icon: Optional[str] = "ph:user-list-bold" icon: Optional[str] = "ph:user-list-bold"
path: Optional[str] = Field(example="/system/user") path: Optional[str] = Field(example="/system/user")
order: Optional[int] = Field(example=1) order: Optional[int] = Field(example=1)
@ -56,15 +52,4 @@ class MenuUpdate(BaseModel):
redirect: Optional[str] = "" redirect: Optional[str] = ""
def update_dict(self): def update_dict(self):
return self.dict(exclude_unset=True, exclude={"id"}) return self.model_dump(exclude_unset=True, exclude={"id"})
"""Response"""
class MenuOutList(SuccessExtra):
data: Optional[List[BaseMenu]]
class MenuOut(BaseResponse):
data: Optional[BaseMenu]

View File

@ -1,10 +1,8 @@
from datetime import datetime from datetime import datetime
from typing import List, Optional from typing import Optional
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from .base import BaseResponse, SuccessExtra
class BaseRole(BaseModel): class BaseRole(BaseModel):
id: int id: int
@ -28,21 +26,10 @@ class RoleUpdate(BaseModel):
desc: Optional[str] = Field(example="管理员角色") desc: Optional[str] = Field(example="管理员角色")
def update_dict(self): def update_dict(self):
return self.dict(exclude_unset=True, exclude={"id"}) return self.model_dump(exclude_unset=True, exclude={"id"})
class RoleUpdateMenusApis(BaseModel): class RoleUpdateMenusApis(BaseModel):
id: int id: int
menu_ids: List[int] = [] menu_ids: list[int] = []
api_infos: List[dict] = [] api_infos: list[dict] = []
"""Response"""
class RoleOutList(SuccessExtra):
data: Optional[List[BaseRole]]
class RoleOut(BaseResponse):
data: Optional[BaseRole]

View File

@ -3,8 +3,6 @@ from typing import List, Optional
from pydantic import BaseModel, EmailStr, Field from pydantic import BaseModel, EmailStr, Field
from app.schemas.base import BaseResponse, SuccessExtra
class BaseUser(BaseModel): class BaseUser(BaseModel):
id: int id: int
@ -27,7 +25,7 @@ class UserCreate(BaseModel):
roles: Optional[List[int]] = [] roles: Optional[List[int]] = []
def create_dict(self): def create_dict(self):
return self.dict(exclude_unset=True, exclude={"roles"}) return self.model_dump(exclude_unset=True, exclude={"roles"})
class UserUpdate(BaseModel): class UserUpdate(BaseModel):
@ -40,21 +38,10 @@ class UserUpdate(BaseModel):
roles: Optional[List[int]] = [] roles: Optional[List[int]] = []
def update_dict(self): def update_dict(self):
return self.dict(exclude_unset=True, exclude={"roles", "id"}) return self.model_dump(exclude_unset=True, exclude={"roles", "id"})
class UpdatePassword(BaseModel): class UpdatePassword(BaseModel):
id: int = Field(description="用户ID") id: int = Field(description="用户ID")
old_password: str = Field(description="旧密码") old_password: str = Field(description="旧密码")
new_password: str = Field(description="新密码") new_password: str = Field(description="新密码")
"""Response"""
class UserOutList(SuccessExtra):
data: Optional[List[BaseUser]]
class UserOut(BaseResponse):
data: Optional[BaseUser]

View File

@ -1,7 +1,7 @@
import os import os
import typing import typing
from pydantic import BaseSettings from pydantic_settings import BaseSettings
class Settings(BaseSettings): class Settings(BaseSettings):
@ -15,7 +15,7 @@ class Settings(BaseSettings):
CORS_ALLOW_METHODS: typing.List = ["*"] CORS_ALLOW_METHODS: typing.List = ["*"]
CORS_ALLOW_HEADERS: typing.List = ["*"] CORS_ALLOW_HEADERS: typing.List = ["*"]
DEBUG = True DEBUG: bool = True
DB_URL: str = "sqlite://db.sqlite3" DB_URL: str = "sqlite://db.sqlite3"
DB_CONNECTIONS: dict = { DB_CONNECTIONS: dict = {
"default": { "default": {
@ -37,7 +37,7 @@ class Settings(BaseSettings):
SECRET_KEY: str = "3488a63e1765035d386f05409663f55c83bfae3b3c61a932744b20ad14244dcf" # openssl rand -hex 32 SECRET_KEY: str = "3488a63e1765035d386f05409663f55c83bfae3b3c61a932744b20ad14244dcf" # openssl rand -hex 32
JWT_ALGORITHM: str = "HS256" JWT_ALGORITHM: str = "HS256"
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 day JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 day
TORTOISE_ORM = { TORTOISE_ORM: dict = {
"connections": { "connections": {
"sqlite": { "sqlite": {
"engine": "tortoise.backends.sqlite", "engine": "tortoise.backends.sqlite",

311
poetry.lock generated
View File

@ -20,14 +20,30 @@ url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua" reference = "tsinghua"
[[package]] [[package]]
name = "anyio" name = "annotated-types"
version = "3.7.1" version = "0.5.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations" description = "Reusable constraint types to use with typing.Annotated"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"},
{file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"},
]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "anyio"
version = "4.0.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.8"
files = [
{file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"},
{file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"},
] ]
[package.dependencies] [package.dependencies]
@ -35,9 +51,9 @@ idna = ">=2.8"
sniffio = ">=1.1" sniffio = ">=1.1"
[package.extras] [package.extras]
doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"]
test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
trio = ["trio (<0.22)"] trio = ["trio (>=0.22)"]
[package.source] [package.source]
type = "legacy" type = "legacy"
@ -85,33 +101,33 @@ reference = "tsinghua"
[[package]] [[package]]
name = "black" name = "black"
version = "23.7.0" version = "23.9.1"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
] ]
[package.dependencies] [package.dependencies]
@ -134,13 +150,13 @@ reference = "tsinghua"
[[package]] [[package]]
name = "click" name = "click"
version = "8.1.6" version = "8.1.7"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
] ]
[package.dependencies] [package.dependencies]
@ -307,13 +323,13 @@ reference = "tsinghua"
[[package]] [[package]]
name = "loguru" name = "loguru"
version = "0.7.0" version = "0.7.2"
description = "Python logging made (stupidly) simple" description = "Python logging made (stupidly) simple"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
{file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"}, {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
{file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"}, {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
] ]
[package.dependencies] [package.dependencies]
@ -321,7 +337,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras] [package.extras]
dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"] dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
[package.source] [package.source]
type = "legacy" type = "legacy"
@ -420,55 +436,165 @@ reference = "tsinghua"
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "1.10.12" version = "2.3.0"
description = "Data validation and settings management using python type hints" description = "Data validation using Python type hints"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pydantic-1.10.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718"}, {file = "pydantic-2.3.0-py3-none-any.whl", hash = "sha256:45b5e446c6dfaad9444819a293b921a40e1db1aa61ea08aede0522529ce90e81"},
{file = "pydantic-1.10.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe"}, {file = "pydantic-2.3.0.tar.gz", hash = "sha256:1607cc106602284cd4a00882986570472f193fde9cb1259bceeaedb26aa79a6d"},
{file = "pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b"},
{file = "pydantic-1.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d"},
{file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09"},
{file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed"},
{file = "pydantic-1.10.12-cp310-cp310-win_amd64.whl", hash = "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a"},
{file = "pydantic-1.10.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc"},
{file = "pydantic-1.10.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405"},
{file = "pydantic-1.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62"},
{file = "pydantic-1.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494"},
{file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246"},
{file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33"},
{file = "pydantic-1.10.12-cp311-cp311-win_amd64.whl", hash = "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f"},
{file = "pydantic-1.10.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a"},
{file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565"},
{file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350"},
{file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303"},
{file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5"},
{file = "pydantic-1.10.12-cp37-cp37m-win_amd64.whl", hash = "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8"},
{file = "pydantic-1.10.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62"},
{file = "pydantic-1.10.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb"},
{file = "pydantic-1.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0"},
{file = "pydantic-1.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c"},
{file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d"},
{file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33"},
{file = "pydantic-1.10.12-cp38-cp38-win_amd64.whl", hash = "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47"},
{file = "pydantic-1.10.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6"},
{file = "pydantic-1.10.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523"},
{file = "pydantic-1.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86"},
{file = "pydantic-1.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1"},
{file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe"},
{file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb"},
{file = "pydantic-1.10.12-cp39-cp39-win_amd64.whl", hash = "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d"},
{file = "pydantic-1.10.12-py3-none-any.whl", hash = "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942"},
{file = "pydantic-1.10.12.tar.gz", hash = "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303"},
] ]
[package.dependencies] [package.dependencies]
typing-extensions = ">=4.2.0" annotated-types = ">=0.4.0"
pydantic-core = "2.6.3"
typing-extensions = ">=4.6.1"
[package.extras] [package.extras]
dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=2.0.0)"]
email = ["email-validator (>=1.0.3)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "pydantic-core"
version = "2.6.3"
description = ""
optional = false
python-versions = ">=3.7"
files = [
{file = "pydantic_core-2.6.3-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1a0ddaa723c48af27d19f27f1c73bdc615c73686d763388c8683fe34ae777bad"},
{file = "pydantic_core-2.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5cfde4fab34dd1e3a3f7f3db38182ab6c95e4ea91cf322242ee0be5c2f7e3d2f"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5493a7027bfc6b108e17c3383959485087d5942e87eb62bbac69829eae9bc1f7"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84e87c16f582f5c753b7f39a71bd6647255512191be2d2dbf49458c4ef024588"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:522a9c4a4d1924facce7270c84b5134c5cabcb01513213662a2e89cf28c1d309"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaafc776e5edc72b3cad1ccedb5fd869cc5c9a591f1213aa9eba31a781be9ac1"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a750a83b2728299ca12e003d73d1264ad0440f60f4fc9cee54acc489249b728"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e8b374ef41ad5c461efb7a140ce4730661aadf85958b5c6a3e9cf4e040ff4bb"},
{file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b594b64e8568cf09ee5c9501ede37066b9fc41d83d58f55b9952e32141256acd"},
{file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2a20c533cb80466c1d42a43a4521669ccad7cf2967830ac62c2c2f9cece63e7e"},
{file = "pydantic_core-2.6.3-cp310-none-win32.whl", hash = "sha256:04fe5c0a43dec39aedba0ec9579001061d4653a9b53a1366b113aca4a3c05ca7"},
{file = "pydantic_core-2.6.3-cp310-none-win_amd64.whl", hash = "sha256:6bf7d610ac8f0065a286002a23bcce241ea8248c71988bda538edcc90e0c39ad"},
{file = "pydantic_core-2.6.3-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:6bcc1ad776fffe25ea5c187a028991c031a00ff92d012ca1cc4714087e575973"},
{file = "pydantic_core-2.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df14f6332834444b4a37685810216cc8fe1fe91f447332cd56294c984ecbff1c"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b7486d85293f7f0bbc39b34e1d8aa26210b450bbd3d245ec3d732864009819"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a892b5b1871b301ce20d40b037ffbe33d1407a39639c2b05356acfef5536d26a"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:883daa467865e5766931e07eb20f3e8152324f0adf52658f4d302242c12e2c32"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4eb77df2964b64ba190eee00b2312a1fd7a862af8918ec70fc2d6308f76ac64"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce8c84051fa292a5dc54018a40e2a1926fd17980a9422c973e3ebea017aa8da"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22134a4453bd59b7d1e895c455fe277af9d9d9fbbcb9dc3f4a97b8693e7e2c9b"},
{file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:02e1c385095efbd997311d85c6021d32369675c09bcbfff3b69d84e59dc103f6"},
{file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d79f1f2f7ebdb9b741296b69049ff44aedd95976bfee38eb4848820628a99b50"},
{file = "pydantic_core-2.6.3-cp311-none-win32.whl", hash = "sha256:430ddd965ffd068dd70ef4e4d74f2c489c3a313adc28e829dd7262cc0d2dd1e8"},
{file = "pydantic_core-2.6.3-cp311-none-win_amd64.whl", hash = "sha256:84f8bb34fe76c68c9d96b77c60cef093f5e660ef8e43a6cbfcd991017d375950"},
{file = "pydantic_core-2.6.3-cp311-none-win_arm64.whl", hash = "sha256:5a2a3c9ef904dcdadb550eedf3291ec3f229431b0084666e2c2aa8ff99a103a2"},
{file = "pydantic_core-2.6.3-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:8421cf496e746cf8d6b677502ed9a0d1e4e956586cd8b221e1312e0841c002d5"},
{file = "pydantic_core-2.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bb128c30cf1df0ab78166ded1ecf876620fb9aac84d2413e8ea1594b588c735d"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a822f630712817b6ecc09ccc378192ef5ff12e2c9bae97eb5968a6cdf3b862"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240a015102a0c0cc8114f1cba6444499a8a4d0333e178bc504a5c2196defd456"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f90e5e3afb11268628c89f378f7a1ea3f2fe502a28af4192e30a6cdea1e7d5e"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:340e96c08de1069f3d022a85c2a8c63529fd88709468373b418f4cf2c949fb0e"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1480fa4682e8202b560dcdc9eeec1005f62a15742b813c88cdc01d44e85308e5"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f14546403c2a1d11a130b537dda28f07eb6c1805a43dae4617448074fd49c282"},
{file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a87c54e72aa2ef30189dc74427421e074ab4561cf2bf314589f6af5b37f45e6d"},
{file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f93255b3e4d64785554e544c1c76cd32f4a354fa79e2eeca5d16ac2e7fdd57aa"},
{file = "pydantic_core-2.6.3-cp312-none-win32.whl", hash = "sha256:f70dc00a91311a1aea124e5f64569ea44c011b58433981313202c46bccbec0e1"},
{file = "pydantic_core-2.6.3-cp312-none-win_amd64.whl", hash = "sha256:23470a23614c701b37252618e7851e595060a96a23016f9a084f3f92f5ed5881"},
{file = "pydantic_core-2.6.3-cp312-none-win_arm64.whl", hash = "sha256:1ac1750df1b4339b543531ce793b8fd5c16660a95d13aecaab26b44ce11775e9"},
{file = "pydantic_core-2.6.3-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:a53e3195f134bde03620d87a7e2b2f2046e0e5a8195e66d0f244d6d5b2f6d31b"},
{file = "pydantic_core-2.6.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:f2969e8f72c6236c51f91fbb79c33821d12a811e2a94b7aa59c65f8dbdfad34a"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:672174480a85386dd2e681cadd7d951471ad0bb028ed744c895f11f9d51b9ebe"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:002d0ea50e17ed982c2d65b480bd975fc41086a5a2f9c924ef8fc54419d1dea3"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ccc13afee44b9006a73d2046068d4df96dc5b333bf3509d9a06d1b42db6d8bf"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:439a0de139556745ae53f9cc9668c6c2053444af940d3ef3ecad95b079bc9987"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63b7545d489422d417a0cae6f9898618669608750fc5e62156957e609e728a5"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b44c42edc07a50a081672e25dfe6022554b47f91e793066a7b601ca290f71e42"},
{file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1c721bfc575d57305dd922e6a40a8fe3f762905851d694245807a351ad255c58"},
{file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5e4a2cf8c4543f37f5dc881de6c190de08096c53986381daebb56a355be5dfe6"},
{file = "pydantic_core-2.6.3-cp37-none-win32.whl", hash = "sha256:d9b4916b21931b08096efed090327f8fe78e09ae8f5ad44e07f5c72a7eedb51b"},
{file = "pydantic_core-2.6.3-cp37-none-win_amd64.whl", hash = "sha256:a8acc9dedd304da161eb071cc7ff1326aa5b66aadec9622b2574ad3ffe225525"},
{file = "pydantic_core-2.6.3-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:5e9c068f36b9f396399d43bfb6defd4cc99c36215f6ff33ac8b9c14ba15bdf6b"},
{file = "pydantic_core-2.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e61eae9b31799c32c5f9b7be906be3380e699e74b2db26c227c50a5fc7988698"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85463560c67fc65cd86153a4975d0b720b6d7725cf7ee0b2d291288433fc21b"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9616567800bdc83ce136e5847d41008a1d602213d024207b0ff6cab6753fe645"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e9b65a55bbabda7fccd3500192a79f6e474d8d36e78d1685496aad5f9dbd92c"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f468d520f47807d1eb5d27648393519655eadc578d5dd862d06873cce04c4d1b"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9680dd23055dd874173a3a63a44e7f5a13885a4cfd7e84814be71be24fba83db"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a718d56c4d55efcfc63f680f207c9f19c8376e5a8a67773535e6f7e80e93170"},
{file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8ecbac050856eb6c3046dea655b39216597e373aa8e50e134c0e202f9c47efec"},
{file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:788be9844a6e5c4612b74512a76b2153f1877cd845410d756841f6c3420230eb"},
{file = "pydantic_core-2.6.3-cp38-none-win32.whl", hash = "sha256:07a1aec07333bf5adebd8264047d3dc518563d92aca6f2f5b36f505132399efc"},
{file = "pydantic_core-2.6.3-cp38-none-win_amd64.whl", hash = "sha256:621afe25cc2b3c4ba05fff53525156d5100eb35c6e5a7cf31d66cc9e1963e378"},
{file = "pydantic_core-2.6.3-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:813aab5bfb19c98ae370952b6f7190f1e28e565909bfc219a0909db168783465"},
{file = "pydantic_core-2.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:50555ba3cb58f9861b7a48c493636b996a617db1a72c18da4d7f16d7b1b9952b"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e20f8baedd7d987bd3f8005c146e6bcbda7cdeefc36fad50c66adb2dd2da48"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b0a5d7edb76c1c57b95df719af703e796fc8e796447a1da939f97bfa8a918d60"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f06e21ad0b504658a3a9edd3d8530e8cea5723f6ea5d280e8db8efc625b47e49"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea053cefa008fda40f92aab937fb9f183cf8752e41dbc7bc68917884454c6362"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:171a4718860790f66d6c2eda1d95dd1edf64f864d2e9f9115840840cf5b5713f"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ed7ceca6aba5331ece96c0e328cd52f0dcf942b8895a1ed2642de50800b79d3"},
{file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:acafc4368b289a9f291e204d2c4c75908557d4f36bd3ae937914d4529bf62a76"},
{file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1aa712ba150d5105814e53cb141412217146fedc22621e9acff9236d77d2a5ef"},
{file = "pydantic_core-2.6.3-cp39-none-win32.whl", hash = "sha256:44b4f937b992394a2e81a5c5ce716f3dcc1237281e81b80c748b2da6dd5cf29a"},
{file = "pydantic_core-2.6.3-cp39-none-win_amd64.whl", hash = "sha256:9b33bf9658cb29ac1a517c11e865112316d09687d767d7a0e4a63d5c640d1b17"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d7050899026e708fb185e174c63ebc2c4ee7a0c17b0a96ebc50e1f76a231c057"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99faba727727b2e59129c59542284efebbddade4f0ae6a29c8b8d3e1f437beb7"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fa159b902d22b283b680ef52b532b29554ea2a7fc39bf354064751369e9dbd7"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:046af9cfb5384f3684eeb3f58a48698ddab8dd870b4b3f67f825353a14441418"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:930bfe73e665ebce3f0da2c6d64455098aaa67e1a00323c74dc752627879fc67"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:85cc4d105747d2aa3c5cf3e37dac50141bff779545ba59a095f4a96b0a460e70"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b25afe9d5c4f60dcbbe2b277a79be114e2e65a16598db8abee2a2dcde24f162b"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e49ce7dc9f925e1fb010fc3d555250139df61fa6e5a0a95ce356329602c11ea9"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2dd50d6a1aef0426a1d0199190c6c43ec89812b1f409e7fe44cb0fbf6dfa733c"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6595b0d8c8711e8e1dc389d52648b923b809f68ac1c6f0baa525c6440aa0daa"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef724a059396751aef71e847178d66ad7fc3fc969a1a40c29f5aac1aa5f8784"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3c8945a105f1589ce8a693753b908815e0748f6279959a4530f6742e1994dcb6"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c8c6660089a25d45333cb9db56bb9e347241a6d7509838dbbd1931d0e19dbc7f"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:692b4ff5c4e828a38716cfa92667661a39886e71136c97b7dac26edef18767f7"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f1a5d8f18877474c80b7711d870db0eeef9442691fcdb00adabfc97e183ee0b0"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3796a6152c545339d3b1652183e786df648ecdf7c4f9347e1d30e6750907f5bb"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b962700962f6e7a6bd77e5f37320cabac24b4c0f76afeac05e9f93cf0c620014"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56ea80269077003eaa59723bac1d8bacd2cd15ae30456f2890811efc1e3d4413"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c0ebbebae71ed1e385f7dfd9b74c1cff09fed24a6df43d326dd7f12339ec34"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:252851b38bad3bfda47b104ffd077d4f9604a10cb06fe09d020016a25107bf98"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6656a0ae383d8cd7cc94e91de4e526407b3726049ce8d7939049cbfa426518c8"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9140ded382a5b04a1c030b593ed9bf3088243a0a8b7fa9f071a5736498c5483"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d38bbcef58220f9c81e42c255ef0bf99735d8f11edef69ab0b499da77105158a"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c9d469204abcca28926cbc28ce98f28e50e488767b084fb3fbdf21af11d3de26"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48c1ed8b02ffea4d5c9c220eda27af02b8149fe58526359b3c07eb391cb353a2"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b2b1bfed698fa410ab81982f681f5b1996d3d994ae8073286515ac4d165c2e7"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf9d42a71a4d7a7c1f14f629e5c30eac451a6fc81827d2beefd57d014c006c4a"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4292ca56751aebbe63a84bbfc3b5717abb09b14d4b4442cc43fd7c49a1529efd"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc2ce039c7290b4ef64334ec7e6ca6494de6eecc81e21cb4f73b9b39991408c"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:615a31b1629e12445c0e9fc8339b41aaa6cc60bd53bf802d5fe3d2c0cda2ae8d"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1fa1f6312fb84e8c281f32b39affe81984ccd484da6e9d65b3d18c202c666149"},
{file = "pydantic_core-2.6.3.tar.gz", hash = "sha256:1508f37ba9e3ddc0189e6ff4e2228bd2d3c3a4641cbe8c07177162f76ed696c7"},
]
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "pydantic-settings"
version = "2.0.3"
description = "Settings management using Pydantic"
optional = false
python-versions = ">=3.7"
files = [
{file = "pydantic_settings-2.0.3-py3-none-any.whl", hash = "sha256:ddd907b066622bd67603b75e2ff791875540dc485b7307c4fffc015719da8625"},
{file = "pydantic_settings-2.0.3.tar.gz", hash = "sha256:962dc3672495aad6ae96a4390fac7e593591e144625e5112d359f8f67fb75945"},
]
[package.dependencies]
pydantic = ">=2.0.1"
python-dotenv = ">=0.21.0"
[package.source] [package.source]
type = "legacy" type = "legacy"
@ -513,15 +639,34 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua" reference = "tsinghua"
[[package]]
name = "python-dotenv"
version = "1.0.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
python-versions = ">=3.8"
files = [
{file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
{file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
]
[package.extras]
cli = ["click (>=5.0)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]] [[package]]
name = "pytz" name = "pytz"
version = "2023.3" version = "2023.3.post1"
description = "World timezone definitions, modern and historical" description = "World timezone definitions, modern and historical"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
{file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
] ]
[package.source] [package.source]
@ -689,4 +834,4 @@ reference = "tsinghua"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "59b09802b9b8df810377480c8da534798638993c3441bb55814f0e41e33bd054" content-hash = "e66116a325977520e14789d0c4e84ed9ac2267c8a5f8dc3c5f245794dd3a6083"

View File

@ -10,7 +10,7 @@ python = "^3.11"
fastapi = "^0.100.0" fastapi = "^0.100.0"
uvicorn = "^0.23.1" uvicorn = "^0.23.1"
tortoise-orm = "^0.19.3" tortoise-orm = "^0.19.3"
pydantic = "^1.10.5" pydantic = "^2.3.0"
email-validator = "^2.0.0.post2" email-validator = "^2.0.0.post2"
passlib = "^1.7.4" passlib = "^1.7.4"
pyjwt = "^2.7.0" pyjwt = "^2.7.0"
@ -19,6 +19,7 @@ black = "^23.7.0"
isort = "^5.12.0" isort = "^5.12.0"
ruff = "^0.0.281" ruff = "^0.0.281"
loguru = "^0.7.0" loguru = "^0.7.0"
pydantic-settings = "^2.0.3"
[tool.black] [tool.black]
line-length = 120 line-length = 120

View File

@ -1,5 +1,6 @@
import { getToken } from '@/utils' import { getToken } from '@/utils'
import { resolveResError } from './helpers' import { resolveResError } from './helpers'
import { useUserStore } from '@/store'
export function reqResolve(config) { export function reqResolve(config) {
// 处理不需要token的请求 // 处理不需要token的请求
@ -31,8 +32,6 @@ export function resResolve(response) {
return Promise.resolve(data) return Promise.resolve(data)
} }
let isDialogShow = false //解决多个请求弹出多个dialog
export async function resReject(error) { export async function resReject(error) {
if (!error || !error.response) { if (!error || !error.response) {
const code = error?.code const code = error?.code
@ -42,30 +41,12 @@ export async function resReject(error) {
return Promise.reject({ code, message, error }) return Promise.reject({ code, message, error })
} }
const { data, status } = error.response const { data, status } = error.response
if (data?.code === 401) { if (data?.code === 401) {
if (isDialogShow) return
try { try {
isDialogShow = true const userStore = useUserStore()
await new Promise((resolve, reject) => { userStore.logout()
$dialog.confirm({
title: '系统提示',
type: 'warning',
content: '账号登录已过期,您可以继续留在该页面,或者重新登录',
positiveText: '重新登录',
negativeText: '取消',
confirm() {
isDialogShow = false
location.reload()
resolve() // 解决 Promise 以继续执行
},
cancel() {
isDialogShow = false
reject(new Error('对话框已取消')) // 拒绝 Promise 以停止执行
},
})
})
} catch (error) { } catch (error) {
isDialogShow = false
console.log('resReject error', error) console.log('resReject error', error)
return return
} }