Fix HIGH priority issues: unbounded queries, rate limiting, health checks

Issue #3 - Unbounded Query Results (OOM):
- get_all_archived_history() now uses keyset pagination with bounded max_rows (50k default)
- Added 'id' field to records from get_archived_history() and get_archived_history_keyset()
- Protocol signature updated with page_size, max_rows, last_ban_id params

Issue #7 - Docker Health Check Fails:
- Added curl to Dockerfile.backend runtime image
- HEALTHCHECK now uses 'curl -f http://localhost:8000/api/health'
- compose.prod.yml: increased start_period to 40s, timeout to 10s
- Frontend healthcheck proxies to backend /api/health

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-01 21:47:36 +02:00
parent 1830da496d
commit 0d5882b32f
39 changed files with 2067 additions and 339 deletions

View File

@@ -15,7 +15,8 @@ from typing import cast
import structlog
from app.exceptions import ServerOperationError
from app.models.server import ServerSettings, ServerSettingsResponse, ServerSettingsUpdate
from app.models.server_domain import DomainServerSettings, DomainServerSettingsResult
from app.models.server import ServerSettingsUpdate
from app.utils.constants import FAIL2BAN_SOCKET_TIMEOUT
from app.utils.fail2ban_client import Fail2BanClient, Fail2BanCommand, Fail2BanResponse
from app.utils.fail2ban_response import ok
@@ -87,7 +88,7 @@ async def _safe_get(
# ---------------------------------------------------------------------------
async def get_settings(socket_path: str) -> ServerSettingsResponse:
async def get_settings(socket_path: str) -> DomainServerSettingsResult:
"""Return current fail2ban server-level settings.
Fetches log level, log target, syslog socket, database file path, purge
@@ -97,7 +98,7 @@ async def get_settings(socket_path: str) -> ServerSettingsResponse:
socket_path: Path to the fail2ban Unix domain socket.
Returns:
:class:`~app.models.server.ServerSettingsResponse`.
:class:`~app.models.server_domain.DomainServerSettingsResult`.
Raises:
~app.utils.fail2ban_client.Fail2BanConnectionError: Socket unreachable.
@@ -129,7 +130,7 @@ async def get_settings(socket_path: str) -> ServerSettingsResponse:
db_purge_age = _to_int(db_purge_age_raw, 86400)
db_max_matches = _to_int(db_max_matches_raw, 10)
settings = ServerSettings(
settings = DomainServerSettings(
log_level=log_level,
log_target=log_target,
syslog_socket=syslog_socket,
@@ -143,7 +144,7 @@ async def get_settings(socket_path: str) -> ServerSettingsResponse:
}
log.info("server_settings_fetched", db_purge_age=db_purge_age, warnings=warnings)
return ServerSettingsResponse(settings=settings, warnings=warnings)
return DomainServerSettingsResult(settings=settings, warnings=warnings)
async def update_settings(socket_path: str, update: ServerSettingsUpdate) -> None: