package console // GET /api/cli/apps/{account}/{app}/geo?ip= (geoip spec §11.1): the // platform's GeoIP answer for an arbitrary address, for work that is not // the current request (batch jobs, logs, webhooks). The same fields as the // X-Carlos-Geo-* headers, plus the attribution the answering database's // licence requires. The queried address is never logged. import ( "net" "net/http" "github.com/carlosframework/platform/internal/geoip" ) type geoResponse struct { Found bool `json:"found"` Country string `json:"country,omitempty"` Subdivision string `json:"subdivision,omitempty"` Lat *float64 `json:"lat,omitempty"` Lon *float64 `json:"lon,omitempty"` Source string `json:"source,omitempty"` Attribution string `json:"attribution,omitempty"` AttributionURL string `json:"attribution_url,omitempty"` } func (s *Server) handleAPIGeo(w http.ResponseWriter, r *http.Request) { // The instance family's gate: the app's member, app and instance // tokens, and its per-caller budget of 60 a minute. if _, _, _, ok := s.instanceGateFor(w, r, "geo"); !ok { return } ip := net.ParseIP(r.URL.Query().Get("ip")) if ip == nil { jsonError(w, http.StatusBadRequest, "ip: want an IPv4 or IPv6 address") return } if s.cfg.GeoIPReader == nil { jsonError(w, http.StatusServiceUnavailable, "geo unavailable: this deployment has no GeoIP database") return } // One snapshot: the answer, its source and its attribution all come // from the same reader (spec §5.4). a := geoip.FromSnapshot(s.cfg.GeoIPReader.Lookup(ip)) switch a.State { case geoip.StateOff: jsonError(w, http.StatusServiceUnavailable, "geo unavailable: this deployment has no GeoIP database") return case geoip.StateMiss: writeJSON(w, http.StatusOK, geoResponse{Found: false}) return } out := geoResponse{Found: true, Country: a.Country, Subdivision: a.Subdivision, Source: a.Source} if a.HasPoint { lat, lon := a.Lat, a.Lon out.Lat, out.Lon = &lat, &lon } if at, ok := geoip.AttributionFor(a.Source); ok { out.Attribution, out.AttributionURL = at.Text, at.URL } writeJSON(w, http.StatusOK, out) }