feat(ip-location): implement IP location querying and GeoIP database management

This commit is contained in:
Tension
2025-11-23 22:38:55 +08:00
parent e3999ba75f
commit c4166cef6b
10 changed files with 237 additions and 1 deletions
+74
View File
@@ -0,0 +1,74 @@
package svc
import (
"io"
"net/http"
"os"
"path/filepath"
"github.com/oschwald/geoip2-golang"
"github.com/perfect-panel/server/pkg/logger"
)
const GeoIPDBURL = "https://raw.githubusercontent.com/adysec/IP_database/main/geolite/GeoLite2-City.mmdb"
type IPLocation struct {
Path string
DB *geoip2.Reader
}
func NewIPLocation(path string) (*IPLocation, error) {
// 检查文件是否存在
if _, err := os.Stat(path); os.IsNotExist(err) {
logger.Infof("[GeoIP] Database not found, downloading from %s", GeoIPDBURL)
// 文件不存在,下载数据库
err := DownloadGeoIPDatabase(GeoIPDBURL, path)
if err != nil {
logger.Errorf("[GeoIP] Failed to download database: %v", err.Error())
return nil, err
}
logger.Infof("[GeoIP] Database downloaded successfully")
}
db, err := geoip2.Open(path)
if err != nil {
return nil, err
}
return &IPLocation{
Path: path,
DB: db,
}, nil
}
func (ipLoc *IPLocation) Close() error {
return ipLoc.DB.Close()
}
func DownloadGeoIPDatabase(url, path string) error {
// 创建路径, 确保目录存在
err := os.MkdirAll(filepath.Dir(path), 0755)
if err != nil {
logger.Errorf("[GeoIP] Failed to create directory: %v", err.Error())
return err
}
// 创建文件
out, err := os.Create(path)
if err != nil {
return err
}
defer out.Close()
// 请求远程文件
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// 保存文件
_, err = io.Copy(out, resp.Body)
return err
}
+10
View File
@@ -37,6 +37,7 @@ type ServiceContext struct {
Config config.Config
Queue *asynq.Client
ExchangeRate float64
GeoIP *IPLocation
//NodeCache *cache.NodeCacheClient
AuthModel auth.Model
@@ -68,9 +69,17 @@ func NewServiceContext(c config.Config) *ServiceContext {
db, err := orm.ConnectMysql(orm.Mysql{
Config: c.MySQL,
})
if err != nil {
panic(err.Error())
}
// IP location initialize
geoIP, err := NewIPLocation("./cache/GeoLite2-City.mmdb")
if err != nil {
panic(err.Error())
}
rds := redis.NewClient(&redis.Options{
Addr: c.Redis.Host,
Password: c.Redis.Pass,
@@ -89,6 +98,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
Config: c,
Queue: NewAsynqClient(c),
ExchangeRate: 1.0,
GeoIP: geoIP,
//NodeCache: cache.NewNodeCacheClient(rds),
AuthLimiter: authLimiter,
AdsModel: ads.NewModel(db, rds),