Files
nl-blogs/server/utils/ip.go
2026-01-20 10:36:27 +08:00

123 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"log"
"net"
"os"
"path/filepath"
"sync"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
)
var (
searcher *xdb.Searcher
once sync.Once
)
// InitIP2Region 初始化 ip2region
// 需要 ip2region.xdb 文件如果不存在则仅支持基本IP解析
// 如果 dbPath 为空,将自动从环境变量或可执行文件目录查找
func InitIP2Region(dbPath string) {
once.Do(func() {
var err error
var finalPath string
// 1. 如果传入了路径,直接使用
if dbPath != "" {
finalPath = dbPath
} else {
// 2. 优先从环境变量读取
finalPath = os.Getenv("IP2REGION_DB_PATH")
if finalPath == "" {
// 3. 尝试在可执行文件同级目录查找
execPath, err := os.Executable()
if err == nil {
execDir := filepath.Dir(execPath)
candidatePath := filepath.Join(execDir, "ip2region.xdb")
if _, err := os.Stat(candidatePath); err == nil {
finalPath = candidatePath
}
}
// 4. 如果还没找到,尝试当前工作目录(兼容开发环境)
if finalPath == "" {
candidatePath := "./ip2region.xdb"
if _, err := os.Stat(candidatePath); err == nil {
finalPath = candidatePath
}
}
}
}
// 如果最终路径为空,说明找不到文件
if finalPath == "" {
log.Printf("IP2Region database file not found. Region lookup will be disabled.")
searcher = nil
return
}
// 5. 尝试加载整个xdb到内存性能最好
cBuff, err := xdb.LoadContentFromFile(finalPath)
if err != nil {
log.Printf("Failed to load ip2region.xdb from %s: %v. Region lookup will be disabled.", finalPath, err)
searcher = nil
return
}
searcher, err = xdb.NewWithBuffer(nil, cBuff)
if err != nil {
log.Printf("Failed to create searcher: %v", err)
searcher = nil
return
}
log.Printf("IP2Region loaded successfully from %s", finalPath)
})
}
// GetRegion 获取IP归属地
// 返回格式: 国家|区域|省份|城市|ISP
func GetRegion(ip string) string {
// 添加 recover 保护,防止 panic 导致整个请求失败
defer func() {
if r := recover(); r != nil {
log.Printf("Panic in GetRegion for IP %s: %v", ip, r)
}
}()
if searcher == nil {
return "Unknown"
}
// 过滤内网IP
if isPrivateIP(ip) {
return "Internal"
}
region, err := searcher.SearchByStr(ip)
if err != nil {
return "Unknown"
}
return region
}
// 简单判断内网IP
func isPrivateIP(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false // 不是有效IP
}
if ip.IsLoopback() {
return true
}
ip4 := ip.To4()
if ip4 == nil {
return false // 暂不处理IPv6内网判断
}
return ip4[0] == 10 ||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) ||
(ip4[0] == 192 && ip4[1] == 168)
}