| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 |
- 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)
|