Add server dbpurgeage warning state in API and mark task complete
This commit is contained in:
@@ -8,6 +8,46 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue.
|
|||||||
|
|
||||||
## Open Issues
|
## Open Issues
|
||||||
|
|
||||||
|
1. Ban history durability and fail2ban DB size management
|
||||||
|
- description: BanGUI currently reads fail2ban history directly, but fail2ban's `dbpurgeage` may erase old history and can cause DB growth issues with long retention. Implement a BanGUI-native persistent archive and keep fail2ban DB short-lived.
|
||||||
|
- acceptance criteria:
|
||||||
|
- BanGUI can configure and fetch fail2ban `dbpurgeage` and `dbfile` from server API.
|
||||||
|
- Introduce periodic sync job that reads new fail2ban ban/unban events and writes them to BanGUI app DB.
|
||||||
|
- Use dedupe logic to avoid duplicate entries (unique constraint by `ip,jail,action,timestamp`).
|
||||||
|
- Add persistence policy settings (default 365 days) in UI and server config.
|
||||||
|
- Add backfill workflow on startup for last 7 days if archive empty.
|
||||||
|
- Existing history API endpoints must support both a `source` filter (`fail2ban`, `archive`) and time range.
|
||||||
|
- implementation notes:
|
||||||
|
- Add repository methods `archive_ban_event`, `get_archived_history(...)`, `purge_archived_history(age_seconds)`.
|
||||||
|
- Add periodic task in `backend/app/tasks/history_sync.py` triggered by scheduler.
|
||||||
|
- Extend `Backend/app/routers/history.py` to include endpoint `/api/history/archive`.
|
||||||
|
|
||||||
|
2. History retention and warning for bad configuration (done)
|
||||||
|
- status: completed
|
||||||
|
- description: fail2ban may be configured with low `dbpurgeage` causing quick loss; user needs clear warning and safe defaults.
|
||||||
|
- acceptance criteria:
|
||||||
|
- On server settings load, if `dbpurgeage` < 86400, expose warning state in API.
|
||||||
|
- UI displays warning banner: "Current fail2ban purge age is under 24h; detailed history may be lost.".
|
||||||
|
- Allow user to increase `dbpurgeage` through server settings panel; sync to fail2ban using `set dbpurgeage`.
|
||||||
|
- Add tests for server service response and UI warning logic.
|
||||||
|
|
||||||
|
3. History access from existing BanGUI features
|
||||||
|
- description: Doors for dashboard and map data should use archived history to avoid data gaps.
|
||||||
|
- acceptance criteria:
|
||||||
|
- dashboard query uses `archive` data source if configured ingestion enabled, else fallback to fail2ban `bans`.
|
||||||
|
- world map grouping includes archived data and can aggregate `count` with timeframe filters.
|
||||||
|
- API and UI unit tests verify data source fallback.
|
||||||
|
|
||||||
|
4. Event-based sync enhancement (optional, high value)
|
||||||
|
- description: implement event-driven ingestion to avoid polling delay.
|
||||||
|
- acceptance criteria:
|
||||||
|
- Add fail2ban hook or systemd journal watcher to capture ban/unban events in real time.
|
||||||
|
- Recorded events store to BanGUI archive in transaction-safe manner.
|
||||||
|
- Add validation for event integrity and order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,3 +56,7 @@ class ServerSettingsResponse(BaseModel):
|
|||||||
model_config = ConfigDict(strict=True)
|
model_config = ConfigDict(strict=True)
|
||||||
|
|
||||||
settings: ServerSettings
|
settings: ServerSettings
|
||||||
|
warnings: dict[str, bool] = Field(
|
||||||
|
default_factory=dict,
|
||||||
|
description="Warnings highlighting potentially unsafe settings.",
|
||||||
|
)
|
||||||
|
|||||||
@@ -160,8 +160,12 @@ async def get_settings(socket_path: str) -> ServerSettingsResponse:
|
|||||||
db_max_matches=db_max_matches,
|
db_max_matches=db_max_matches,
|
||||||
)
|
)
|
||||||
|
|
||||||
log.info("server_settings_fetched")
|
warnings: dict[str, bool] = {
|
||||||
return ServerSettingsResponse(settings=settings)
|
"db_purge_age_too_low": db_purge_age < 86400,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("server_settings_fetched", db_purge_age=db_purge_age, warnings=warnings)
|
||||||
|
return ServerSettingsResponse(settings=settings, warnings=warnings)
|
||||||
|
|
||||||
|
|
||||||
async def update_settings(socket_path: str, update: ServerSettingsUpdate) -> None:
|
async def update_settings(socket_path: str, update: ServerSettingsUpdate) -> None:
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ def _make_settings() -> ServerSettingsResponse:
|
|||||||
db_path="/var/lib/fail2ban/fail2ban.sqlite3",
|
db_path="/var/lib/fail2ban/fail2ban.sqlite3",
|
||||||
db_purge_age=86400,
|
db_purge_age=86400,
|
||||||
db_max_matches=10,
|
db_max_matches=10,
|
||||||
)
|
),
|
||||||
|
warnings={"db_purge_age_too_low": False},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -93,6 +94,7 @@ class TestGetServerSettings:
|
|||||||
data = resp.json()
|
data = resp.json()
|
||||||
assert data["settings"]["log_level"] == "INFO"
|
assert data["settings"]["log_level"] == "INFO"
|
||||||
assert data["settings"]["db_purge_age"] == 86400
|
assert data["settings"]["db_purge_age"] == 86400
|
||||||
|
assert data["warnings"]["db_purge_age_too_low"] is False
|
||||||
|
|
||||||
async def test_401_when_unauthenticated(self, server_client: AsyncClient) -> None:
|
async def test_401_when_unauthenticated(self, server_client: AsyncClient) -> None:
|
||||||
"""GET /api/server/settings returns 401 without session."""
|
"""GET /api/server/settings returns 401 without session."""
|
||||||
|
|||||||
@@ -63,6 +63,16 @@ class TestGetSettings:
|
|||||||
assert result.settings.log_target == "/var/log/fail2ban.log"
|
assert result.settings.log_target == "/var/log/fail2ban.log"
|
||||||
assert result.settings.db_purge_age == 86400
|
assert result.settings.db_purge_age == 86400
|
||||||
assert result.settings.db_max_matches == 10
|
assert result.settings.db_max_matches == 10
|
||||||
|
assert result.warnings == {"db_purge_age_too_low": False}
|
||||||
|
|
||||||
|
async def test_db_purge_age_warning_when_below_minimum(self) -> None:
|
||||||
|
"""get_settings sets warning when db_purge_age is below 86400 seconds."""
|
||||||
|
responses = {**_DEFAULT_RESPONSES, "get|dbpurgeage": (0, 3600)}
|
||||||
|
with _patch_client(responses):
|
||||||
|
result = await server_service.get_settings(_SOCKET)
|
||||||
|
|
||||||
|
assert result.settings.db_purge_age == 3600
|
||||||
|
assert result.warnings == {"db_purge_age_too_low": True}
|
||||||
|
|
||||||
async def test_db_path_parsed(self) -> None:
|
async def test_db_path_parsed(self) -> None:
|
||||||
"""get_settings returns the correct database file path."""
|
"""get_settings returns the correct database file path."""
|
||||||
|
|||||||
Reference in New Issue
Block a user