diff --git a/http/http.go b/http/http.go index fbf4acb..44eda00 100644 --- a/http/http.go +++ b/http/http.go @@ -31,12 +31,15 @@ type Server struct { } type Response struct { - IP net.IP `json:"ip"` - IPDecimal *big.Int `json:"ip_decimal"` - Country string `json:"country,omitempty"` - CountryISO string `json:"country_iso,omitempty"` - City string `json:"city,omitempty"` - Hostname string `json:"hostname,omitempty"` + IP net.IP `json:"ip"` + IPDecimal *big.Int `json:"ip_decimal"` + Country string `json:"country,omitempty"` + CountryISO string `json:"country_iso,omitempty"` + City string `json:"city,omitempty"` + Hostname string `json:"hostname,omitempty"` + IsInEuropeanUnion bool `json:"is_in_european_union,omitempty"` + Latitude float64 `json:"location_latitude,omitempty"` + Longitude float64 `json:"location_longitude,omitempty"` } type PortResponse struct { @@ -95,12 +98,15 @@ func (s *Server) newResponse(r *http.Request) (Response, error) { hostname, _ = s.LookupAddr(ip) } return Response{ - IP: ip, - IPDecimal: ipDecimal, - Country: country.Name, - CountryISO: country.ISO, - City: city, - Hostname: hostname, + IP: ip, + IPDecimal: ipDecimal, + Country: country.Name, + CountryISO: country.ISO, + IsInEuropeanUnion: country.IsInEuropeanUnion, + City: city.Name, + Hostname: hostname, + Latitude: city.Latitude, + Longitude: city.Longitude, }, nil } @@ -158,6 +164,15 @@ func (s *Server) CLICityHandler(w http.ResponseWriter, r *http.Request) *appErro return nil } +func (s *Server) CLICoordinatesHandler(w http.ResponseWriter, r *http.Request) *appError { + response, err := s.newResponse(r) + if err != nil { + return internalServerError(err) + } + fmt.Fprintf(w, "%s, %s\n", formatCoordinate(response.Latitude), formatCoordinate(response.Longitude)) + return nil +} + func (s *Server) JSONHandler(w http.ResponseWriter, r *http.Request) *appError { response, err := s.newResponse(r) if err != nil { @@ -281,6 +296,7 @@ func (s *Server) Handler() http.Handler { r.Route("GET", "/country", s.CLICountryHandler) r.Route("GET", "/country-iso", s.CLICountryISOHandler) r.Route("GET", "/city", s.CLICityHandler) + r.Route("GET", "/coordinates", s.CLICoordinatesHandler) } // Browser @@ -297,3 +313,7 @@ func (s *Server) Handler() http.Handler { func (s *Server) ListenAndServe(addr string) error { return http.ListenAndServe(addr, s.Handler()) } + +func formatCoordinate(c float64) string { + return strconv.FormatFloat(c, 'f', 6, 64) +} diff --git a/http/http_test.go b/http/http_test.go index 9bf8179..107a517 100644 --- a/http/http_test.go +++ b/http/http_test.go @@ -20,7 +20,10 @@ func (t *testDb) Country(net.IP) (geo.Country, error) { return geo.Country{Name: "Elbonia", ISO: "EB"}, nil } -func (t *testDb) City(net.IP) (string, error) { return "Bornyasherk", nil } +func (t *testDb) City(net.IP) (database.City, error) { + return database.City{Name: "Bornyasherk"}, nil +} + func (t *testDb) IsEmpty() bool { return false } func testServer() *Server { diff --git a/iputil/geo/geo.go b/iputil/geo/geo.go index 4f1705f..2b918e1 100644 --- a/iputil/geo/geo.go +++ b/iputil/geo/geo.go @@ -2,19 +2,27 @@ package geo import ( "net" + "math" geoip2 "github.com/oschwald/geoip2-golang" ) type Reader interface { Country(net.IP) (Country, error) - City(net.IP) (string, error) + City(net.IP) (City, error) IsEmpty() bool } type Country struct { - Name string - ISO string + Name string + ISO string + IsInEuropeanUnion bool +} + +type City struct { + Name string + Latitude float64 + Longitude float64 } type geoip struct { @@ -62,21 +70,32 @@ func (g *geoip) Country(ip net.IP) (Country, error) { if record.RegisteredCountry.IsoCode != "" && country.ISO == "" { country.ISO = record.RegisteredCountry.IsoCode } + country.IsInEuropeanUnion = record.Country.IsInEuropeanUnion + if record.RegisteredCountry.IsoCode != "" && country.ISO == "" { + country.IsInEuropeanUnion = record.RegisteredCountry.IsInEuropeanUnion + } return country, nil } -func (g *geoip) City(ip net.IP) (string, error) { +func (g *geoip) City(ip net.IP) (City, error) { + city := City{} if g.city == nil { - return "", nil + return city, nil } record, err := g.city.City(ip) if err != nil { - return "", err + return city, err } - if city, exists := record.City.Names["en"]; exists { - return city, nil + if c, exists := record.City.Names["en"]; exists { + city.Name = c } - return "", nil + if !math.IsNaN(record.Location.Latitude) { + city.Latitude = record.Location.Latitude + } + if !math.IsNaN(record.Location.Longitude) { + city.Longitude = record.Location.Longitude + } + return city, nil } func (g *geoip) IsEmpty() bool {