geo.py 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. from __future__ import annotations
  2. from dataclasses import dataclass
  3. from typing import Optional
  4. from app.config import settings
  5. @dataclass
  6. class GeoInfo:
  7. country: str
  8. city: str
  9. latitude: Optional[float]
  10. longitude: Optional[float]
  11. _UNKNOWN = GeoInfo("Unknown", "Unknown", None, None)
  12. class GeoResolver:
  13. def __init__(self, db_path: str) -> None:
  14. self._db_path = db_path
  15. self._reader = None
  16. def _get_reader(self):
  17. if self._reader is None:
  18. import geoip2.database # lazy import
  19. self._reader = geoip2.database.Reader(self._db_path)
  20. return self._reader
  21. def resolve(self, ip: str) -> GeoInfo:
  22. # Private / loopback addresses have no GeoIP entry
  23. if ip in ("127.0.0.1", "::1", "localhost") or ip.startswith("192.168.") or ip.startswith("10.") or ip.startswith("172."):
  24. return GeoInfo("Local", "Loopback", None, None)
  25. try:
  26. reader = self._get_reader()
  27. response = reader.city(ip)
  28. country = response.country.name or "Unknown"
  29. city = response.city.name or "Unknown"
  30. lat = response.location.latitude
  31. lon = response.location.longitude
  32. return GeoInfo(country, city, lat, lon)
  33. except Exception:
  34. return GeoInfo("Unknown", "Unknown", None, None)
  35. geo_resolver = GeoResolver(settings.geoip_db_path)