108 lines
3.3 KiB
Python
108 lines
3.3 KiB
Python
"""认证模块请求/响应模型"""
|
|
import re
|
|
|
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
|
|
PASSWORD_RE = re.compile(r"^(?=.*[A-Za-z])(?=.*\d).{8,32}$")
|
|
PHONE_RE = re.compile(r"^1\d{10}$")
|
|
EMAIL_RE = re.compile(r"^[\w.+-]+@[\w-]+\.[\w.]+$")
|
|
|
|
|
|
def _check_password(v: str) -> str:
|
|
if not PASSWORD_RE.match(v):
|
|
raise ValueError("密码须 8-32 位且包含字母和数字")
|
|
return v
|
|
|
|
|
|
class SmsSendReq(BaseModel):
|
|
phone: str
|
|
scene: str = Field(pattern="^(register|login|reset)$")
|
|
|
|
@field_validator("phone")
|
|
@classmethod
|
|
def _v_phone(cls, v: str) -> str:
|
|
if not PHONE_RE.match(v):
|
|
raise ValueError("手机号格式不正确")
|
|
return v
|
|
|
|
|
|
class EmailSendReq(BaseModel):
|
|
email: str
|
|
scene: str = Field(pattern="^(register|login|reset)$")
|
|
|
|
@field_validator("email")
|
|
@classmethod
|
|
def _v_email(cls, v: str) -> str:
|
|
if not EMAIL_RE.match(v):
|
|
raise ValueError("邮箱格式不正确")
|
|
return v
|
|
|
|
|
|
class RegisterReq(BaseModel):
|
|
type: str = Field(pattern="^(phone|email)$")
|
|
phone: str | None = None
|
|
email: str | None = None
|
|
code: str = Field(min_length=4, max_length=8)
|
|
password: str
|
|
nickname: str | None = Field(default=None, max_length=20)
|
|
invite_code: str | None = Field(default=None, max_length=10)
|
|
|
|
@field_validator("password")
|
|
@classmethod
|
|
def _v_pwd(cls, v: str) -> str:
|
|
return _check_password(v)
|
|
|
|
@model_validator(mode="after")
|
|
def _v_target(self):
|
|
if self.type == "phone" and not (self.phone and PHONE_RE.match(self.phone)):
|
|
raise ValueError("手机号格式不正确")
|
|
if self.type == "email" and not (self.email and EMAIL_RE.match(self.email)):
|
|
raise ValueError("邮箱格式不正确")
|
|
return self
|
|
|
|
|
|
class LoginReq(BaseModel):
|
|
type: str = Field(pattern="^(phone_password|phone_code|email_password)$")
|
|
phone: str | None = None
|
|
email: str | None = None
|
|
password: str | None = None
|
|
code: str | None = None
|
|
revoke: bool = False # 注销冻结期内:确认撤销注销
|
|
|
|
@model_validator(mode="after")
|
|
def _v_fields(self):
|
|
if self.type.startswith("phone") and not self.phone:
|
|
raise ValueError("手机号必填")
|
|
if self.type.startswith("email") and not self.email:
|
|
raise ValueError("邮箱必填")
|
|
if self.type.endswith("password") and not self.password:
|
|
raise ValueError("密码必填")
|
|
if self.type.endswith("code") and not self.code:
|
|
raise ValueError("验证码必填")
|
|
return self
|
|
|
|
|
|
class RefreshReq(BaseModel):
|
|
refresh_token: str = Field(min_length=10)
|
|
|
|
|
|
class PasswordResetReq(BaseModel):
|
|
type: str = Field(pattern="^(phone|email)$")
|
|
phone: str | None = None
|
|
email: str | None = None
|
|
code: str = Field(min_length=4, max_length=8)
|
|
new_password: str
|
|
|
|
@field_validator("new_password")
|
|
@classmethod
|
|
def _v_pwd(cls, v: str) -> str:
|
|
return _check_password(v)
|
|
|
|
@model_validator(mode="after")
|
|
def _v_target(self):
|
|
if self.type == "phone" and not (self.phone and PHONE_RE.match(self.phone)):
|
|
raise ValueError("手机号格式不正确")
|
|
if self.type == "email" and not (self.email and EMAIL_RE.match(self.email)):
|
|
raise ValueError("邮箱格式不正确")
|
|
return self
|