"""Jail management Pydantic models. Request, response, and domain models used by the jails router and service. """ from pydantic import BaseModel, ConfigDict, Field from app.models.config import BantimeEscalation class JailStatus(BaseModel): """Runtime metrics for a single jail.""" model_config = ConfigDict(strict=True) currently_banned: int = Field(..., ge=0) total_banned: int = Field(..., ge=0) currently_failed: int = Field(..., ge=0) total_failed: int = Field(..., ge=0) class Jail(BaseModel): """Domain model for a single fail2ban jail with its full configuration.""" model_config = ConfigDict(strict=True) name: str = Field(..., description="Jail name as configured in fail2ban.") enabled: bool = Field(..., description="Whether the jail is currently active.") running: bool = Field(..., description="Whether the jail backend is running.") idle: bool = Field(default=False, description="Whether the jail is in idle mode.") backend: str = Field(..., description="Log monitoring backend (e.g. polling, systemd).") log_paths: list[str] = Field(default_factory=list, description="Monitored log files.") fail_regex: list[str] = Field(default_factory=list, description="Failure detection regex patterns.") ignore_regex: list[str] = Field(default_factory=list, description="Regex patterns that bypass the ban logic.") ignore_ips: list[str] = Field(default_factory=list, description="IP addresses or CIDRs on the ignore list.") date_pattern: str | None = Field(default=None, description="Custom date pattern for log parsing.") log_encoding: str = Field(default="UTF-8", description="Log file encoding.") find_time: int = Field(..., description="Time window (seconds) for counting failures.") ban_time: int = Field(..., description="Duration (seconds) of a ban. -1 means permanent.") max_retry: int = Field(..., description="Number of failures before a ban is issued.") actions: list[str] = Field(default_factory=list, description="Names of actions attached to this jail.") bantime_escalation: BantimeEscalation | None = Field( default=None, description="Incremental ban-time escalation settings, or None if not configured.", ) status: JailStatus | None = Field(default=None, description="Runtime counters.") class JailSummary(BaseModel): """Lightweight jail entry for the overview list.""" model_config = ConfigDict(strict=True) name: str enabled: bool running: bool idle: bool backend: str find_time: int ban_time: int max_retry: int status: JailStatus | None = None class JailListResponse(BaseModel): """Response for ``GET /api/jails``.""" model_config = ConfigDict(strict=True) jails: list[JailSummary] = Field(default_factory=list) total: int = Field(..., ge=0) class JailDetailResponse(BaseModel): """Response for ``GET /api/jails/{name}``.""" model_config = ConfigDict(strict=True) jail: Jail class JailCommandResponse(BaseModel): """Generic response for jail control commands (start, stop, reload, idle).""" model_config = ConfigDict(strict=True) message: str jail: str class IgnoreIpRequest(BaseModel): """Payload for adding an IP or network to a jail's ignore list.""" model_config = ConfigDict(strict=True) ip: str = Field(..., description="IP address or CIDR network to ignore.")