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

@@ -26,12 +26,14 @@ if TYPE_CHECKING:
ScheduleConfig,
ScheduleInfo,
)
from app.models.config_domain import (
DomainGlobalConfig,
DomainJailConfig,
DomainJailConfigList,
)
from app.models.config import (
AddLogPathRequest,
GlobalConfigResponse,
GlobalConfigUpdate,
JailConfigListResponse,
JailConfigResponse,
JailConfigUpdate,
LogPreviewRequest,
LogPreviewResponse,
@@ -40,9 +42,9 @@ if TYPE_CHECKING:
RegexTestResponse,
)
from app.models.geo import GeoEnricher, GeoInfo
from app.models.history import HistoryListResponse, IpDetailResponse
from app.models.jail import JailDetailResponse, JailListResponse
from app.models.server import ServerSettingsResponse, ServerSettingsUpdate, ServerStatus
from app.models.history_domain import DomainHistoryList, DomainIpDetail
from app.models.jail_domain import DomainJailBannedIps, DomainJailDetail, DomainJailList
from app.models.server_domain import DomainServerSettingsResult
from app.services.geo_cache import GeoCache
@@ -81,10 +83,10 @@ class AuthService(Protocol):
class JailService(Protocol):
"""Protocol for jail management service operations."""
async def list_jails(self, socket_path: str) -> JailListResponse:
async def list_jails(self, socket_path: str) -> DomainJailList:
...
async def get_jail(self, socket_path: str, name: str) -> JailDetailResponse:
async def get_jail(self, socket_path: str, name: str) -> DomainJailDetail:
...
async def reload_all(self, socket_path: str) -> None:
@@ -125,7 +127,7 @@ class JailService(Protocol):
geo_batch_lookup: object,
http_session: object,
app_db: aiosqlite.Connection,
) -> JailBannedIpsResponse:
) -> DomainJailBannedIps:
...
async def lookup_ip(
@@ -233,10 +235,10 @@ class BlocklistService(Protocol):
@runtime_checkable
class ConfigService(Protocol):
async def get_jail_config(self, socket_path: str, name: str) -> JailConfigResponse:
async def get_jail_config(self, socket_path: str, name: str) -> DomainJailConfig:
...
async def list_jail_configs(self, socket_path: str) -> JailConfigListResponse:
async def list_jail_configs(self, socket_path: str) -> DomainJailConfigList:
...
async def update_jail_config(
@@ -247,7 +249,7 @@ class ConfigService(Protocol):
) -> None:
...
async def get_global_config(self, socket_path: str) -> GlobalConfigResponse:
async def get_global_config(self, socket_path: str) -> DomainGlobalConfig:
...
async def update_global_config(
@@ -305,7 +307,7 @@ class HistoryService(Protocol):
http_session: aiohttp.ClientSession | None = None,
geo_enricher: GeoEnricher | None = None,
db: aiosqlite.Connection | None = None,
) -> HistoryListResponse:
) -> DomainHistoryList:
...
async def get_ip_detail(
@@ -315,7 +317,7 @@ class HistoryService(Protocol):
*,
http_session: aiohttp.ClientSession | None = None,
geo_enricher: GeoEnricher | None = None,
) -> IpDetailResponse | None:
) -> DomainIpDetail | None:
...
@@ -394,7 +396,7 @@ class HealthProbe(Protocol):
@runtime_checkable
class ServerService(Protocol):
async def get_settings(self, socket_path: str) -> ServerSettingsResponse:
async def get_settings(self, socket_path: str) -> DomainServerSettingsResult:
...
async def update_settings(