"""User Pydantic schemas.""" import re from pydantic import BaseModel, ConfigDict, Field, field_validator _PASSWORD_RE = re.compile(r"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&_.-]{8,128}$") class UserBase(BaseModel): """Base user schema.""" username: str = Field(..., min_length=3, max_length=64) role: str = "member" is_active: bool = True @field_validator("role") @classmethod def _validate_role(cls, value: str) -> str: allowed = {"admin", "member"} if value not in allowed: raise ValueError(f"role must be one of {allowed}") return value class UserCreate(UserBase): """User creation schema.""" password: str = Field(..., min_length=8, max_length=128) @field_validator("password") @classmethod def _validate_password_strength(cls, value: str) -> str: if not _PASSWORD_RE.match(value): raise ValueError( "password must be 8-128 characters and contain at least one letter and one number" ) return value class UserOut(UserBase): """User output schema.""" model_config = ConfigDict(from_attributes=True) id: str class UserLogin(BaseModel): """User login schema.""" username: str password: str class TokenResponse(BaseModel): """Token response schema.""" access_token: str refresh_token: str token_type: str = "bearer" class TokenPayload(BaseModel): """JWT token payload.""" sub: str | None = None role: str | None = None jti: str | None = None type: str | None = None exp: int | None = None class RefreshTokenRequest(BaseModel): """Refresh token request schema.""" refresh_token: str