From ce6c9414493bdc9b3480c4630c78d53686d1b90a Mon Sep 17 00:00:00 2001 From: lanthora Date: Tue, 29 Oct 2024 16:05:26 +0800 Subject: [PATCH] feat: support ipinfo location --- candy/location.go | 101 +++++++++++++++++++++++++++++++++++++++++++++ candy/websocket.go | 4 +- go.mod | 5 ++- go.sum | 7 ++++ storage/storage.go | 43 ------------------- 5 files changed, 114 insertions(+), 46 deletions(-) create mode 100644 candy/location.go diff --git a/candy/location.go b/candy/location.go new file mode 100644 index 0000000..e4dc256 --- /dev/null +++ b/candy/location.go @@ -0,0 +1,101 @@ +package candy + +import ( + "crypto/tls" + "net" + "net/http" + "os" + "path" + + "github.com/ipinfo/go/v2/ipinfo" + "github.com/ipinfo/go/v2/ipinfo/cache" + "github.com/lanthora/cacao/argp" + "github.com/lanthora/cacao/logger" + "github.com/oschwald/geoip2-golang" +) + +func GetLocation(ip net.IP) (country, region string) { + if !ip.IsPrivate() { + ok := false + if country, region, ok = ipinfoLocation(ip); !ok { + country, region, _ = mmdbLocation(ip) + } + } + return +} + +type dummyCacheEngine struct { + cache map[string]interface{} +} + +var dummyCache = ipinfo.NewCache(newDummyCacheEngine()) +var httpClient = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}} + +func newDummyCacheEngine() *dummyCacheEngine { + return &dummyCacheEngine{ + cache: make(map[string]interface{}), + } +} + +func (c *dummyCacheEngine) Get(key string) (interface{}, error) { + if v, ok := c.cache[key]; ok { + return v, nil + } + return nil, cache.ErrNotFound +} + +func (c *dummyCacheEngine) Set(key string, value interface{}) error { + c.cache[key] = value + return nil +} + +func ipinfoLocation(ip net.IP) (country, region string, ok bool) { + client := ipinfo.NewClient(httpClient, dummyCache, "") + if info, err := client.GetIPInfo(ip); err == nil { + country = info.Country + region = info.Region + ok = true + } + return +} + +func findFileByExtFromDir(dir string, ext string) (string, error) { + files, err := os.ReadDir(dir) + if err != nil { + return "", err + } + for _, file := range files { + if path.Ext(file.Name()) == ext { + return file.Name(), nil + } + } + return "", os.ErrNotExist +} + +func mmdbLocation(ip net.IP) (country, region string, ok bool) { + storageDir := argp.Get("storage", ".") + filename, err := findFileByExtFromDir(storageDir, ".mmdb") + if err != nil { + logger.Debug("cannot found mmdb file: %v", err) + return + } + db, err := geoip2.Open(path.Join(storageDir, filename)) + if err != nil { + logger.Debug("open mmdb failed: %v", err) + return + } + defer db.Close() + record, err := db.City(ip) + if err != nil { + logger.Debug("get location failed: %v", err) + return + } + + country = record.Country.IsoCode + + if len(record.Subdivisions) > 0 { + region = record.Subdivisions[0].Names["en"] + } + ok = true + return +} diff --git a/candy/websocket.go b/candy/websocket.go index 845e7f7..e7764c2 100644 --- a/candy/websocket.go +++ b/candy/websocket.go @@ -192,7 +192,7 @@ func (ws *candysocket) handleAuthMessage(buffer []byte) error { db.Where(ws.dev.model).First(ws.dev.model) ws.dev.model.IP = uint32ToStrIp(message.IP) ws.dev.model.Online = true - ws.dev.model.Country, ws.dev.model.Region = storage.GetLocation(net.ParseIP(ws.ctx.ClientIP())) + ws.dev.model.Country, ws.dev.model.Region = GetLocation(net.ParseIP(ws.ctx.ClientIP())) ws.dev.model.Save() ws.updateSystemRoute() @@ -357,7 +357,7 @@ func (ws *candysocket) handlePeerConnMessage(buffer []byte) error { ip := make(net.IP, 4) binary.BigEndian.PutUint32(ip, message.IP) - ws.dev.model.Country, ws.dev.model.Region = storage.GetLocation(ip) + ws.dev.model.Country, ws.dev.model.Region = GetLocation(ip) ws.dev.model.Save() return nil diff --git a/go.mod b/go.mod index 37ead47..33c5c6b 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module github.com/lanthora/cacao go 1.22.5 require ( + github.com/deckarep/golang-set/v2 v2.6.0 github.com/gin-gonic/gin v1.10.0 github.com/glebarez/sqlite v1.11.0 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 + github.com/ipinfo/go/v2 v2.10.0 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/oschwald/geoip2-golang v1.11.0 github.com/sirupsen/logrus v1.9.3 @@ -18,7 +20,6 @@ require ( github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -36,6 +37,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/oschwald/maxminddb-golang v1.13.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -43,6 +45,7 @@ require ( golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/protobuf v1.34.1 // indirect diff --git a/go.sum b/go.sum index 48fdcc0..3f685d4 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/ipinfo/go/v2 v2.10.0 h1:v9sFjaxnVVD+JVgpWpjgwols18Tuu4SgBDaHHaw0IXo= +github.com/ipinfo/go/v2 v2.10.0/go.mod h1:tRDkYfM20b1XzNqorn1Q1O6Xtg7uzw3Wn3I2R0SyJh4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -67,6 +69,8 @@ github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -99,6 +103,9 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/storage/storage.go b/storage/storage.go index 54eb12d..da86783 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,14 +1,12 @@ package storage import ( - "net" "os" "path" "github.com/glebarez/sqlite" "github.com/lanthora/cacao/argp" "github.com/lanthora/cacao/logger" - "github.com/oschwald/geoip2-golang" "gorm.io/gorm" gormlogger "gorm.io/gorm/logger" ) @@ -34,44 +32,3 @@ var db *gorm.DB func Get() *gorm.DB { return db } - -func findFileByExtFromDir(dir string, ext string) (string, error) { - files, err := os.ReadDir(dir) - if err != nil { - return "", err - } - for _, file := range files { - if path.Ext(file.Name()) == ext { - return file.Name(), nil - } - } - return "", os.ErrNotExist -} - -func GetLocation(ip net.IP) (country, region string) { - storageDir := argp.Get("storage", ".") - filename, err := findFileByExtFromDir(storageDir, ".mmdb") - if err != nil { - logger.Debug("cannot found mmdb file: %v", err) - return - } - db, err := geoip2.Open(path.Join(storageDir, filename)) - if err != nil { - logger.Debug("open mmdb failed: %v", err) - return - } - defer db.Close() - record, err := db.City(ip) - if err != nil { - logger.Debug("get location failed: %v", err) - return - } - - country = record.Country.IsoCode - - if len(record.Subdivisions) > 0 { - region = record.Subdivisions[0].Names["en"] - } - - return -}