81 lines
1.4 KiB
Go
81 lines
1.4 KiB
Go
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
|
|
}
|