"""Ban management Pydantic models. Request, response, and domain models used by the ban router and service. """ from pydantic import BaseModel, ConfigDict, Field class BanRequest(BaseModel): """Payload for ``POST /api/bans`` (ban an IP).""" model_config = ConfigDict(strict=True) ip: str = Field(..., description="IP address to ban.") jail: str = Field(..., description="Jail in which to apply the ban.") class UnbanRequest(BaseModel): """Payload for ``DELETE /api/bans`` (unban an IP).""" model_config = ConfigDict(strict=True) ip: str = Field(..., description="IP address to unban.") jail: str | None = Field( default=None, description="Jail to remove the ban from. ``null`` means all jails.", ) unban_all: bool = Field( default=False, description="When ``true`` the IP is unbanned from every jail.", ) class Ban(BaseModel): """Domain model representing a single active or historical ban record.""" model_config = ConfigDict(strict=True) ip: str = Field(..., description="Banned IP address.") jail: str = Field(..., description="Jail that issued the ban.") banned_at: str = Field(..., description="ISO 8601 UTC timestamp of the ban.") expires_at: str | None = Field( default=None, description="ISO 8601 UTC expiry timestamp, or ``null`` if permanent.", ) ban_count: int = Field(..., ge=1, description="Number of times this IP was banned.") country: str | None = Field( default=None, description="ISO 3166-1 alpha-2 country code resolved from the IP.", ) class BanResponse(BaseModel): """Response containing a single ban record.""" model_config = ConfigDict(strict=True) ban: Ban class BanListResponse(BaseModel): """Paginated list of ban records.""" model_config = ConfigDict(strict=True) bans: list[Ban] = Field(default_factory=list) total: int = Field(..., ge=0, description="Total number of matching records.") class ActiveBan(BaseModel): """A currently active ban entry returned by ``GET /api/bans/active``.""" model_config = ConfigDict(strict=True) ip: str = Field(..., description="Banned IP address.") jail: str = Field(..., description="Jail holding the ban.") banned_at: str = Field(..., description="ISO 8601 UTC start of the ban.") expires_at: str | None = Field( default=None, description="ISO 8601 UTC expiry, or ``null`` if permanent.", ) ban_count: int = Field(..., ge=1, description="Running ban count for this IP.") class ActiveBanListResponse(BaseModel): """List of all currently active bans across all jails.""" model_config = ConfigDict(strict=True) bans: list[ActiveBan] = Field(default_factory=list) total: int = Field(..., ge=0)