mirror of
https://github.com/Li4n0/revsuit.git
synced 2026-09-27 00:51:55 +08:00
feat(ipinfo): support GeoIP (#26)
Co-authored-by: E99p1ant <[email protected]>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
Url = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz"
|
||||
)
|
||||
|
||||
func get(url string) (b []byte, err error) {
|
||||
client := http.Client{
|
||||
Timeout: 90 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // disable verify
|
||||
}}
|
||||
request, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
request.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36")
|
||||
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func extractTarGz(gzipStream io.Reader) error {
|
||||
uncompressedStream, err := gzip.NewReader(gzipStream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(uncompressedStream)
|
||||
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeReg:
|
||||
log.Trace(header.Name)
|
||||
if !strings.HasSuffix(header.Name, "GeoLite2-City.mmdb") {
|
||||
continue
|
||||
}
|
||||
outFile, err := os.Create("GeoLite2-City.mmdb")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func download(licenseKey string) (err error) {
|
||||
var GeoLite2TarGz []byte
|
||||
log.Info("Downloading GeoLite2-City.mmdb...")
|
||||
if GeoLite2TarGz, err = get(fmt.Sprintf(Url, licenseKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return extractTarGz(bytes.NewReader(GeoLite2TarGz))
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
geo *geoip2.Reader
|
||||
}
|
||||
|
||||
var geo *geoip2.Reader
|
||||
var once sync.Once
|
||||
|
||||
// Area returns IpArea according to ip
|
||||
func (db *Database) Area(ip string) string {
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
record, err := db.geo.City(net.ParseIP(ip))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
country := record.Country.Names["en"]
|
||||
city := record.City.Names["en"]
|
||||
if city == "" {
|
||||
city = record.Location.TimeZone
|
||||
}
|
||||
return fmt.Sprintf("%s %s", country, city)
|
||||
}
|
||||
|
||||
func checkUpdate(licenseKey string) {
|
||||
info, err := os.Stat("GeoLite2-City.mmdb")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err := download(licenseKey)
|
||||
if err != nil {
|
||||
log.Warn("Download GeoLite2-City.mmdb failed, caused by:%v, recommend to download it by yourself otherwise the `IpArea` will be null", err)
|
||||
}
|
||||
}
|
||||
} else if -time.Until(info.ModTime()) > 7*24*time.Hour {
|
||||
log.Info("Updating GeoLite2-City.mmdb...")
|
||||
err := download(licenseKey)
|
||||
if err != nil {
|
||||
log.Warn("Update GeoLite2-City.mmdb failed, please download GeoLite2-City.mmdb by yourself")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func New(licenseKey string) *Database {
|
||||
once.Do(func() {
|
||||
var err error
|
||||
checkUpdate(licenseKey)
|
||||
geo, err = geoip2.Open("GeoLite2-City.mmdb")
|
||||
if err != nil {
|
||||
log.Error("Load GeoLite2-City.mmdb failed, `IpArea` will be null")
|
||||
}
|
||||
})
|
||||
return &Database{geo: geo}
|
||||
}
|
||||
Reference in New Issue
Block a user