Two frontend bugs and one mypy false positive fixed: - ActivateJailDialog: Activate button was never disabled when blockingIssues.length > 0 (missing condition in disabled prop). - ActivateJailDialog: handleConfirm called onActivated() even when the backend returned active=false (blocked activation). Dialog now stays open and shows result.message instead. - config.py: Settings() call flagged by mypy --strict because pydantic-settings loads required fields from env vars at runtime; suppressed with a targeted type: ignore[call-arg] comment. Tests: added ActivateJailDialog.test.tsx (5 tests covering button state, backend-rejection handling, success path, and crash detection callback).
89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
"""Application configuration loaded from environment variables and .env file.
|
|
|
|
Follows pydantic-settings patterns: all values are prefixed with BANGUI_
|
|
and validated at startup via the Settings singleton.
|
|
"""
|
|
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""BanGUI runtime configuration.
|
|
|
|
All fields are loaded from environment variables prefixed with ``BANGUI_``
|
|
or from a ``.env`` file located next to the process working directory.
|
|
The application will raise a :class:`pydantic.ValidationError` on startup
|
|
if any required field is missing or has an invalid 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.",
|
|
)
|
|
session_secret: str = Field(
|
|
...,
|
|
description=(
|
|
"Secret key used when generating session tokens. "
|
|
"Must be unique and never committed to source control."
|
|
),
|
|
)
|
|
session_duration_minutes: int = Field(
|
|
default=60,
|
|
ge=1,
|
|
description="Number of minutes a session token remains valid after creation.",
|
|
)
|
|
timezone: str = Field(
|
|
default="UTC",
|
|
description="IANA timezone name used when displaying timestamps in the UI.",
|
|
)
|
|
log_level: str = Field(
|
|
default="info",
|
|
description="Application log level: debug | info | warning | error | critical.",
|
|
)
|
|
geoip_db_path: str | None = Field(
|
|
default=None,
|
|
description=(
|
|
"Optional path to a MaxMind GeoLite2-Country .mmdb file. "
|
|
"When set, failed ip-api.com lookups fall back to local resolution."
|
|
),
|
|
)
|
|
fail2ban_config_dir: str = Field(
|
|
default="/config/fail2ban",
|
|
description=(
|
|
"Path to the fail2ban configuration directory. "
|
|
"Must contain subdirectories jail.d/, filter.d/, and action.d/. "
|
|
"Used for listing, viewing, and editing configuration files through the web UI."
|
|
),
|
|
)
|
|
fail2ban_start_command: str = Field(
|
|
default="fail2ban-client start",
|
|
description=(
|
|
"Shell command used to start (not reload) the fail2ban daemon during "
|
|
"recovery rollback. Split by whitespace to build the argument list — "
|
|
"no shell interpretation is performed. "
|
|
"Example: 'systemctl start fail2ban' or 'fail2ban-client start'."
|
|
),
|
|
)
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_prefix="BANGUI_",
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
case_sensitive=False,
|
|
)
|
|
|
|
|
|
def get_settings() -> Settings:
|
|
"""Return a fresh :class:`Settings` instance loaded from the environment.
|
|
|
|
Returns:
|
|
A validated :class:`Settings` object. Raises :class:`pydantic.ValidationError`
|
|
if required keys are absent or values fail validation.
|
|
"""
|
|
return Settings() # type: ignore[call-arg] # pydantic-settings populates required fields from env vars
|