mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 07:08:02 +00:00
feat(geoip): implement GeoIP functionality with initialization, IP geolocation retrieval, and unit tests
This commit is contained in:
55
pkg/geoip/geoip.go
Normal file
55
pkg/geoip/geoip.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"net/netip"
|
||||
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/oschwald/geoip2-golang/v2"
|
||||
)
|
||||
|
||||
//go:embed resource/*.mmdb
|
||||
var dbFS embed.FS
|
||||
|
||||
var geoipReader *geoip2.Reader
|
||||
|
||||
func Init() error {
|
||||
data, err := dbFS.ReadFile("resource/GeoLite2-Country.mmdb")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
geoipReader, err = geoip2.OpenBytes(data)
|
||||
return err
|
||||
}
|
||||
|
||||
type IpGeoInfo struct {
|
||||
Country *geoip2.Country
|
||||
Emoji string
|
||||
}
|
||||
|
||||
func GetIpGeoInfo(ip string) *IpGeoInfo {
|
||||
if geoipReader == nil || ip == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ipAddr, err := netip.ParseAddr(ip)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
country, err := geoipReader.Country(ipAddr)
|
||||
if err != nil || country.Country.ISOCode == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
emoji, err := emoji.CountryFlag(country.Country.ISOCode)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &IpGeoInfo{
|
||||
Country: country,
|
||||
Emoji: emoji.String(),
|
||||
}
|
||||
}
|
||||
48
pkg/geoip/geoip_test.go
Normal file
48
pkg/geoip/geoip_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func initOrSkip(t *testing.T) {
|
||||
t.Helper()
|
||||
if err := Init(); err != nil {
|
||||
t.Skipf("skipping: geoip database unavailable: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIpGeoInfo_Cloudflare(t *testing.T) {
|
||||
initOrSkip(t)
|
||||
|
||||
ip := "223.167.150.96"
|
||||
city := GetIpGeoInfo(ip)
|
||||
if city == nil {
|
||||
t.Fatalf("GetIpGeoInfo(%q) returned nil, expected a result", ip)
|
||||
}
|
||||
|
||||
t.Logf("Country: %s (%s)", city.Country.Country.Names.English, city.Country.Country.ISOCode)
|
||||
t.Logf("Emoji: %s", city.Emoji)
|
||||
}
|
||||
|
||||
func TestGetIpGeoInfo_EmptyIP(t *testing.T) {
|
||||
initOrSkip(t)
|
||||
|
||||
if city := GetIpGeoInfo(""); city != nil {
|
||||
t.Errorf("GetIpGeoInfo(\"\") = %+v, want nil", city)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIpGeoInfo_InvalidIP(t *testing.T) {
|
||||
initOrSkip(t)
|
||||
|
||||
if city := GetIpGeoInfo("not-an-ip"); city != nil {
|
||||
t.Errorf("GetIpGeoInfo(\"not-an-ip\") = %+v, want nil", city)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIpGeoInfo_UninitializedReader(t *testing.T) {
|
||||
geoipReader = nil
|
||||
if city := GetIpGeoInfo("103.21.244.12"); city != nil {
|
||||
t.Errorf("expected nil when reader is uninitialized, got %+v", city)
|
||||
}
|
||||
}
|
||||
10
pkg/geoip/go.mod
Normal file
10
pkg/geoip/go.mod
Normal file
@@ -0,0 +1,10 @@
|
||||
module pkg/geoip
|
||||
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/enescakir/emoji v1.0.0 // indirect
|
||||
github.com/oschwald/geoip2-golang/v2 v2.1.0 // indirect
|
||||
github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
)
|
||||
8
pkg/geoip/go.sum
Normal file
8
pkg/geoip/go.sum
Normal file
@@ -0,0 +1,8 @@
|
||||
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
|
||||
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
|
||||
github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo=
|
||||
github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
|
||||
github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=
|
||||
github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
BIN
pkg/geoip/resource/GeoLite2-Country.mmdb
Normal file
BIN
pkg/geoip/resource/GeoLite2-Country.mmdb
Normal file
Binary file not shown.
Reference in New Issue
Block a user