from __future__ import annotations from dataclasses import dataclass from typing import Optional from app.config import settings @dataclass class GeoInfo: country: str city: str latitude: Optional[float] longitude: Optional[float] _UNKNOWN = GeoInfo("Unknown", "Unknown", None, None) class GeoResolver: def __init__(self, db_path: str) -> None: self._db_path = db_path self._reader = None def _get_reader(self): if self._reader is None: import geoip2.database # lazy import self._reader = geoip2.database.Reader(self._db_path) return self._reader def resolve(self, ip: str) -> GeoInfo: # Private / loopback addresses have no GeoIP entry if ip in ("127.0.0.1", "::1", "localhost") or ip.startswith("192.168.") or ip.startswith("10.") or ip.startswith("172."): return GeoInfo("Local", "Loopback", None, None) try: reader = self._get_reader() response = reader.city(ip) country = response.country.name or "Unknown" city = response.city.name or "Unknown" lat = response.location.latitude lon = response.location.longitude return GeoInfo(country, city, lat, lon) except Exception: return GeoInfo("Unknown", "Unknown", None, None) geo_resolver = GeoResolver(settings.geoip_db_path)