import re from typing import Optional from pydantic import BaseModel, Field, field_validator from app.core.dto.user import UserRole EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") def _normalize_email(value: str) -> str: value = value.strip().lower() if not EMAIL_PATTERN.match(value) or len(value) > 254: raise ValueError("invalid email") return value class UserCreate(BaseModel): email: str password: str = Field(min_length=8, max_length=128) first_name: str = Field(default="", max_length=100) last_name: str = Field(default="", max_length=100) role: UserRole = UserRole.trainee _email = field_validator("email")(_normalize_email) class UserUpdate(BaseModel): """Partial update: only the fields that were sent are changed.""" email: Optional[str] = None password: Optional[str] = Field(default=None, min_length=8, max_length=128) first_name: Optional[str] = Field(default=None, max_length=100) last_name: Optional[str] = Field(default=None, max_length=100) role: Optional[UserRole] = None is_active: Optional[bool] = None @field_validator("email") @classmethod def normalize_email(cls, value: Optional[str]) -> Optional[str]: return None if value is None else _normalize_email(value)