Skip to content

feat: add IP geolocation via ipapi #23

@GRACENOBLE

Description

@GRACENOBLE

Summary

Integrate ipapi.co (or ipapi.is) to resolve the requester's IP address to geographic metadata — country, region, city, timezone, and currency. Used for locale-aware UX, fraud signals, and rate-limiting by region.

Scope

Backend (Go)

  • Add a Geolocator interface in usecase/ with a Lookup(ip string) (*domain.GeoLocation, error) method
  • Add domain/geolocation.go with a GeoLocation entity (country code, country name, region, city, timezone, currency, is_eu)
  • Implement in internal/infrastructure/geo/ipapi/ using the ipapi HTTP API
  • Cache results in Redis (TTL ~24h) by IP to avoid hammering the API rate limit — reuses the existing Redis client
  • Expose a middleware helper GeoFromRequest() that extracts the real IP (respecting X-Forwarded-For from Railway's proxy) and attaches a *domain.GeoLocation to the Gin context
  • Wire the client in server.go using IPAPI_KEY env var (optional — ipapi has a free tier without a key)
  • Add IPAPI_KEY to backend/.env.example

Usage points

Acceptance criteria

  • GeoLocator interface and GeoLocation domain entity exist
  • ipapi implementation returns correct country/city/timezone for a known IP
  • Results are cached in Redis; second lookup for the same IP does not hit the API
  • GeoFromRequest() middleware correctly extracts IP behind Railway's proxy
  • Unit tests mock the ipapi HTTP response; integration test uses Testcontainers Redis
  • IPAPI_KEY in backend/.env.example (marked optional with comment)
  • Docs added to backend/docs/geolocation.md

Dependencies

  • Existing Redis infrastructure — caching layer

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions