Task 4 (Better Jail Configuration) implementation:
- Add fail2ban_config_dir setting to app/config.py
- New file_config_service: list/view/edit/create jail.d, filter.d, action.d files
with path-traversal prevention and 512 KB content size limit
- New file_config router: GET/PUT/POST endpoints for jail files, filter files,
and action files; PUT .../enabled for toggle on/off
- Extend config_service with delete_log_path() and add_log_path()
- Add DELETE /api/config/jails/{name}/logpath and POST /api/config/jails/{name}/logpath
- Extend geo router with re-resolve endpoint; add geo_re_resolve background task
- Update blocklist_service with revised scheduling helpers
- Update Docker compose files with BANGUI_FAIL2BAN_CONFIG_DIR env var and
rw volume mount for the fail2ban config directory
- Frontend: new Jail Files, Filters, Actions tabs in ConfigPage; file editor
with accordion-per-file, editable textarea, save/create; add/delete log paths
- Frontend: types in types/config.ts; API calls in api/config.ts and api/endpoints.ts
- 63 new backend tests (test_file_config_service, test_file_config, test_geo_re_resolve)
- 6 new frontend tests in ConfigPageLogPath.test.tsx
- ruff, mypy --strict, tsc --noEmit, eslint: all clean; 617 backend tests pass
67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
"""Geo and IP lookup Pydantic models.
|
|
|
|
Response models for the ``GET /api/geo/lookup/{ip}`` endpoint.
|
|
"""
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
class GeoDetail(BaseModel):
|
|
"""Enriched geolocation data for an IP address.
|
|
|
|
Populated from the ip-api.com free API.
|
|
"""
|
|
|
|
model_config = ConfigDict(strict=True)
|
|
|
|
country_code: str | None = Field(
|
|
default=None,
|
|
description="ISO 3166-1 alpha-2 country code.",
|
|
)
|
|
country_name: str | None = Field(
|
|
default=None,
|
|
description="Human-readable country name.",
|
|
)
|
|
asn: str | None = Field(
|
|
default=None,
|
|
description="Autonomous System Number (e.g. ``'AS3320'``).",
|
|
)
|
|
org: str | None = Field(
|
|
default=None,
|
|
description="Organisation associated with the ASN.",
|
|
)
|
|
|
|
|
|
class GeoCacheStatsResponse(BaseModel):
|
|
"""Response for ``GET /api/geo/stats``.
|
|
|
|
Exposes diagnostic counters of the geo cache subsystem so operators
|
|
can assess resolution health from the UI or CLI.
|
|
"""
|
|
|
|
model_config = ConfigDict(strict=True)
|
|
|
|
cache_size: int = Field(..., description="Number of positive entries in the in-memory cache.")
|
|
unresolved: int = Field(..., description="Number of geo_cache rows with country_code IS NULL.")
|
|
neg_cache_size: int = Field(..., description="Number of entries in the in-memory negative cache.")
|
|
dirty_size: int = Field(..., description="Number of newly resolved entries not yet flushed to disk.")
|
|
|
|
|
|
class IpLookupResponse(BaseModel):
|
|
"""Response for ``GET /api/geo/lookup/{ip}``.
|
|
|
|
Aggregates current ban status and geographical information for an IP.
|
|
"""
|
|
|
|
model_config = ConfigDict(strict=True)
|
|
|
|
ip: str = Field(..., description="The queried IP address.")
|
|
currently_banned_in: list[str] = Field(
|
|
default_factory=list,
|
|
description="Names of jails where this IP is currently banned.",
|
|
)
|
|
geo: GeoDetail | None = Field(
|
|
default=None,
|
|
description="Enriched geographical and network information.",
|
|
)
|