""" Pydantic schemas for the User resource. Schemas vs. models ------------------- SQLAlchemy *models* define how data is stored in the database. Pydantic *schemas* define what comes in over the API (request bodies) and what goes out (response shapes). Keeping them separate means we can expose only the fields we want and add validation logic without touching the DB layer. """ from datetime import datetime from pydantic import BaseModel, ConfigDict, EmailStr, field_validator class UserCreate(BaseModel): """Request body for POST /auth/register.""" email: EmailStr display_name: str password: str @field_validator("password") @classmethod def password_min_length(cls, v: str) -> str: if len(v) < 8: raise ValueError("Password must be at least 8 characters long") return v @field_validator("display_name") @classmethod def display_name_not_empty(cls, v: str) -> str: v = v.strip() if not v: raise ValueError("Display name must not be empty") return v class UserRead(BaseModel): """Response schema – safe to expose publicly (no password hash).""" model_config = ConfigDict(from_attributes=True) id: int email: EmailStr display_name: str is_admin: bool is_active: bool created_at: datetime class UserUpdate(BaseModel): """Request body for PATCH /users/{id} – all fields optional.""" display_name: str | None = None is_active: bool | None = None # Admins can promote/demote other users here; the business rule about # settings.admin_email always being admin is enforced in the router. is_admin: bool | None = None class TokenResponse(BaseModel): """Response body for POST /auth/login.""" access_token: str token_type: str = "bearer" user: UserRead