refactor(backend): external logging metrics, required mode, health checks

- Add external_logging_init_failures counter
- Add external_log_required flag, raise if init fails and required
- Health endpoint: add external_logging status check
- Blocklist service: enrich with metadata fields, update import logic
- Health check task: add runtime_state dependency, fix return typing
- Metrics: add Histogram for request latencies
- Frontend: align BlocklistImportLogSection props
- Docs: update deployment guide, remove stale tasks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-04 03:45:13 +02:00
parent 42e177e6ea
commit 0a3f9c6c16
15 changed files with 172 additions and 131 deletions

View File

@@ -183,7 +183,7 @@ class PaginationMetadata(BanGuiBaseModel):
description="Opaque cursor token for fetching the next page (cursor pagination only).",
)
pagination_mode: Literal["offset", "cursor"] = Field(
...,
default="offset",
description="Pagination mode used by the endpoint. 'offset' uses page/page_size; 'cursor' uses cursor tokens.",
)
@@ -353,6 +353,7 @@ class ErrorMetadata(TypedDict, total=False):
filter_name: Name of the filter involved in the error.
action_name: Name of the action involved in the error.
source_id: ID of a blocklist source involved in the error.
url: URL involved in a blocklist error.
ip: IP address involved in the error.
pattern: Regex pattern that caused an error.
error: Regex compilation error message.
@@ -371,6 +372,7 @@ class ErrorMetadata(TypedDict, total=False):
filter_name: str
action_name: str
source_id: int
url: str
ip: str
pattern: str
error: str
@@ -412,6 +414,7 @@ class HealthResponse(BanGuiBaseModel):
database: Database connectivity — 'ok' or 'error'.
scheduler: Background scheduler status — 'running', 'stopped', or 'unknown'.
cache: Cache initialization status — 'initialised' or 'uninitialised'.
external_logging: External logging handler status — 'ok', 'error', or 'disabled'.
components: Per-component health detail list (empty when all healthy).
Example:
@@ -423,6 +426,7 @@ class HealthResponse(BanGuiBaseModel):
"database": "ok",
"scheduler": "running",
"cache": "initialised",
"external_logging": "disabled",
"components": []
}
@@ -433,6 +437,7 @@ class HealthResponse(BanGuiBaseModel):
"database": "ok",
"scheduler": "running",
"cache": "initialised",
"external_logging": "ok",
"components": [{"name": "fail2ban", "healthy": false, "message": "Socket not reachable"}]
}
```
@@ -461,6 +466,13 @@ class HealthResponse(BanGuiBaseModel):
...,
description="Cache initialization status: 'initialised' when ready, 'uninitialised' when not.",
)
external_logging: Literal["ok", "error", "disabled"] = Field(
...,
description=(
"External logging handler status: 'ok' when operational, 'error' when "
"initialization failed, 'disabled' when external logging is not configured."
),
)
components: list[ComponentHealth] = Field(
default_factory=list,
description="Per-component health detail list. Empty when status is 'ok'.",