- Add 5-min negative cache (_neg_cache) so failing IPs are throttled rather than hammering the API on every request - Add MaxMind GeoLite2 fallback (init_geoip / _geoip_lookup) that fires when ip-api fails; controlled by BANGUI_GEOIP_DB_PATH env var - Fix lookup_batch bug: failed API results were stored in positive cache - Add _persist_neg_entry: INSERT OR IGNORE into geo_cache with NULL country_code so re-resolve can find historically failed IPs - Add POST /api/geo/re-resolve: clears neg cache, batch-retries all geo_cache rows with country_code IS NULL, returns resolved/total count - BanTable + MapPage: wrap the country — placeholder in a Fluent UI Tooltip explaining the retry behaviour - Add geoip2>=4.8.0 dependency; geoip_db_path config setting - Tests: add TestNegativeCache (4), TestGeoipFallback (4), TestReResolve (4)
72 lines
2.3 KiB
Python
72 lines
2.3 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."
|
|
),
|
|
)
|
|
|
|
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()
|