Fix country not shown in ban list due to geo rate limiting

list_bans() was calling geo_service.lookup() once per IP on the
page (e.g. 100 sequential HTTP requests), hitting the ip-api.com
free-tier single-IP limit of 45 req/min.  IPs beyond the ~45th
were added to the in-process negative cache (5 min TTL) and showed
as no country until the TTL expired.  The map endpoint never had
this problem because it used lookup_batch (100 IPs per POST).

Add http_session and app_db params to list_bans().  When
http_session is provided (production path), the entire page is
resolved in one lookup_batch() call instead of N individual ones.
The legacy geo_enricher callback is kept for test compatibility.
Update the dashboard router to use the batch path directly.

Adds 3 tests covering the batch geo path, failure resilience, and
http_session priority over geo_enricher.
This commit is contained in:
2026-03-10 17:20:13 +01:00
parent 6877637507
commit 0225f32901
4 changed files with 159 additions and 408 deletions

View File

@@ -26,7 +26,7 @@ from app.models.ban import (
TimeRange,
)
from app.models.server import ServerStatus, ServerStatusResponse
from app.services import ban_service, geo_service
from app.services import ban_service
router: APIRouter = APIRouter(prefix="/api/dashboard", tags=["Dashboard"])
@@ -109,15 +109,13 @@ async def get_dashboard_bans(
socket_path: str = request.app.state.settings.fail2ban_socket
http_session: aiohttp.ClientSession = request.app.state.http_session
async def _enricher(ip: str) -> geo_service.GeoInfo | None:
return await geo_service.lookup(ip, http_session, db=db)
return await ban_service.list_bans(
socket_path,
range,
page=page,
page_size=page_size,
geo_enricher=_enricher,
http_session=http_session,
app_db=db,
origin=origin,
)