# ============================================================ # app/schemas/auth.py - Authentication DTOs # ============================================================ from pydantic import BaseModel, EmailStr, Field, field_validator from typing import Optional import re class SignupDto(BaseModel): """Signup request DTO""" first_name: str = Field(..., min_length=1, max_length=50) last_name: str = Field(..., min_length=1, max_length=50) email: Optional[EmailStr] = None phone: Optional[str] = None password: str = Field(..., min_length=8, max_length=100) role: str = Field(..., pattern="^(renter|landlord)$") @field_validator("password") @classmethod def validate_password(cls, v): """Validate password strength""" if not re.search(r"[A-Z]", v): raise ValueError("Must contain uppercase letter") if not re.search(r"[a-z]", v): raise ValueError("Must contain lowercase letter") if not re.search(r"[0-9]", v): raise ValueError("Must contain digit") if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", v): raise ValueError("Must contain special character") return v @field_validator("email", "phone") @classmethod def validate_contact(cls, v, info): """At least email or phone required""" if info.data.get("email") or info.data.get("phone"): return v raise ValueError("Email or phone is required") class VerifySignupOtpDto(BaseModel): """Verify signup OTP DTO""" identifier: str = Field(..., min_length=1) code: str = Field(..., min_length=4, max_length=4) class LoginDto(BaseModel): """Login request DTO""" identifier: str = Field(..., min_length=1) password: str = Field(..., min_length=1) class SendPasswordResetOtpDto(BaseModel): """Send password reset OTP DTO""" identifier: str = Field(..., min_length=1) class VerifyPasswordResetOtpDto(BaseModel): """Verify password reset OTP DTO""" identifier: str = Field(..., min_length=1) code: str = Field(..., min_length=4, max_length=4) class ResetPasswordDto(BaseModel): """Reset password DTO""" identifier: str = Field(..., min_length=1) new_password: str = Field(..., min_length=8, max_length=100) @field_validator("new_password") @classmethod def validate_password(cls, v): """Validate password strength""" if not re.search(r"[A-Z]", v): raise ValueError("Must contain uppercase letter") if not re.search(r"[a-z]", v): raise ValueError("Must contain lowercase letter") if not re.search(r"[0-9]", v): raise ValueError("Must contain digit") if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", v): raise ValueError("Must contain special character") return v class ResendOtpDto(BaseModel): """Resend OTP DTO""" identifier: str = Field(..., min_length=1) purpose: str = Field(..., pattern="^(signup|password_reset)$")