package geo import ( "errors" "net" "strings" "github.com/oschwald/geoip2-golang" ) // ErrInvalidIP is returned when an IP cannot be parsed. var ErrInvalidIP = errors.New("invalid ip address") type Resolver struct { db *geoip2.Reader } type Location struct { IP string Country string Region string City string Address string Latitude float64 Longitude float64 } func NewResolver(dbPath string) (*Resolver, error) { if dbPath == "" { return nil, errors.New("db path is required") } db, err := geoip2.Open(dbPath) if err != nil { return nil, err } return &Resolver{db: db}, nil } func (r *Resolver) Close() error { return r.db.Close() } func (r *Resolver) Lookup(ipStr string) (Location, error) { ip := net.ParseIP(ipStr) if ip == nil { return Location{}, ErrInvalidIP } record, err := r.db.City(ip) if err != nil { return Location{}, err } country := record.Country.Names["en"] region := "" if len(record.Subdivisions) > 0 { region = record.Subdivisions[0].Names["en"] } city := record.City.Names["en"] addressParts := make([]string, 0, 3) for _, part := range []string{city, region, country} { if part != "" { addressParts = append(addressParts, part) } } return Location{ IP: ip.String(), Country: country, Region: region, City: city, Address: strings.Join(addressParts, ", "), Latitude: record.Location.Latitude, Longitude: record.Location.Longitude, }, nil }