"""Setup wizard Pydantic models. Request, response, and domain models for the first-run configuration wizard. """ from pydantic import BaseModel, ConfigDict, Field, field_validator class SetupRequest(BaseModel): """Payload for ``POST /api/setup``.""" model_config = ConfigDict(strict=True) master_password: str = Field( ..., min_length=8, max_length=72, description="Master password that protects the BanGUI interface (max 72 bytes due to bcrypt truncation).", ) @field_validator("master_password") @classmethod def validate_master_password(cls, value: str) -> str: if len(value) < 8: raise ValueError("Password must be at least 8 characters long.") if len(value) > 72: raise ValueError("Password must not exceed 72 bytes (bcrypt limitation).") if not any(char.isupper() for char in value): raise ValueError("Password must include at least one uppercase letter.") if not any(char.isdigit() for char in value): raise ValueError("Password must include at least one number.") if not any(char in "!@#$%^&*()" for char in value): raise ValueError( "Password must include at least one special character (!@#$%^&*())." ) return value database_path: str = Field( default="bangui.db", description="Filesystem path to the BanGUI SQLite application database.", ) fail2ban_socket: str = Field( default="/var/run/fail2ban/fail2ban.sock", description="Path to the fail2ban Unix domain socket.", ) timezone: str = Field( default="UTC", description="IANA timezone name used when displaying timestamps.", ) session_duration_minutes: int = Field( default=60, ge=1, description="Number of minutes a user session remains valid.", ) class SetupResponse(BaseModel): """Response returned after a successful initial setup.""" model_config = ConfigDict(strict=True) message: str = Field( default="Setup completed successfully. Please log in.", ) class SetupTimezoneResponse(BaseModel): """Response for ``GET /api/setup/timezone``.""" model_config = ConfigDict(strict=True) timezone: str = Field(..., description="Configured IANA timezone identifier.") class SetupStatusResponse(BaseModel): """Response indicating whether setup has been completed.""" model_config = ConfigDict(strict=True) completed: bool = Field( ..., description="``True`` if the initial setup has already been performed.", )