Refactor geo re-resolve endpoint into geo_service and add typed response

This commit is contained in:
2026-04-15 08:56:37 +02:00
parent 2451ec77b2
commit a8f2d2d7b9
7 changed files with 115 additions and 31 deletions

View File

@@ -57,6 +57,19 @@ class GeoCacheStatsResponse(BaseModel):
dirty_size: int = Field(..., description="Number of newly resolved entries not yet flushed to disk.")
class GeoReResolveResponse(BaseModel):
"""Response for ``POST /api/geo/re-resolve``.
Reports how many previously unresolved IPs were retried and how many
gained a resolved country code after the re-resolve operation.
"""
model_config = ConfigDict(strict=True)
resolved: int = Field(..., description="Number of IPs successfully resolved.")
total: int = Field(..., description="Number of IPs retried.")
class IpLookupResponse(BaseModel):
"""Response for ``GET /api/geo/lookup/{ip}``.

View File

@@ -11,8 +11,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Annotated
if TYPE_CHECKING:
import aiohttp
from app.services.jail_service import IpLookupResult
import aiosqlite
@@ -20,12 +18,11 @@ from fastapi import APIRouter, Depends, HTTPException, Path, status
from app.dependencies import (
AuthDep,
DbDep,
Fail2BanSocketDep,
HttpSessionDep,
get_db,
)
from app.models.geo import GeoCacheStatsResponse, GeoDetail, GeoInfo, IpLookupResponse
from app.models.geo import GeoCacheStatsResponse, GeoDetail, GeoInfo, GeoReResolveResponse, IpLookupResponse
from app.services import geo_service, jail_service
from app.utils.fail2ban_client import Fail2BanConnectionError
@@ -136,12 +133,13 @@ async def geo_stats(
@router.post(
"/re-resolve",
summary="Re-resolve all IPs whose country could not be determined",
response_model=GeoReResolveResponse,
)
async def re_resolve_geo(
_auth: AuthDep,
db: Annotated[aiosqlite.Connection, Depends(get_db)],
http_session: HttpSessionDep,
) -> dict[str, int]:
) -> GeoReResolveResponse:
"""Retry geo resolution for every IP in ``geo_cache`` with a null country.
Clears the in-memory negative cache first so that previously failing IPs
@@ -153,22 +151,6 @@ async def re_resolve_geo(
http_session: Shared HTTP session for geo lookups.
Returns:
JSON object ``{"resolved": N, "total": M}`` where *N* is the number
of IPs that gained a country code and *M* is the total number of IPs
that were retried.
A :class:`~app.models.geo.GeoReResolveResponse` with retry counts.
"""
# Collect all IPs in geo_cache that still lack a country code.
unresolved = await geo_service.get_unresolved_ips(db)
if not unresolved:
return {"resolved": 0, "total": 0}
# Clear negative cache so these IPs bypass the TTL check.
geo_service.clear_neg_cache()
geo_map = await geo_service.lookup_batch(unresolved, http_session, db=db)
resolved_count = sum(
1 for info in geo_map.values() if info.country_code is not None
)
return {"resolved": resolved_count, "total": len(unresolved)}
return await geo_service.re_resolve_all(db, http_session)

View File

@@ -187,6 +187,41 @@ async def get_unresolved_ips(db: aiosqlite.Connection) -> list[str]:
return await geo_cache_repo.get_unresolved_ips(db)
async def re_resolve_all(
db: aiosqlite.Connection,
http_session: aiohttp.ClientSession,
) -> dict[str, int]:
"""Retry geo resolution for all unresolved cache entries.
This helper clears the in-memory negative cache before attempting a
fresh batch lookup, then returns counters for how many IPs were retried
and how many gained a resolved country code.
Args:
db: BanGUI application database connection.
http_session: Shared aiohttp client session.
Returns:
A dict with ``resolved`` and ``total`` counts.
"""
unresolved = await get_unresolved_ips(db)
if not unresolved:
return {"resolved": 0, "total": 0}
clear_neg_cache()
geo_map = await lookup_batch(unresolved, http_session, db=db)
resolved_count = sum(
1 for info in geo_map.values() if info.country_code is not None
)
log.info(
"geo_re_resolve_complete",
total=len(unresolved),
resolved=resolved_count,
)
return {"resolved": resolved_count, "total": len(unresolved)}
def init_geoip(mmdb_path: str | None) -> None:
"""Initialise the MaxMind GeoLite2-Country database reader.