diff --git a/cmd/server/qqwry/qqwey.go b/cmd/server/qqwry/qqwey.go deleted file mode 100644 index 94ee4abb8..000000000 --- a/cmd/server/qqwry/qqwey.go +++ /dev/null @@ -1,6 +0,0 @@ -package qqwry - -import _ "embed" - -//go:embed qqwry.dat -var QQwryByte []byte diff --git a/cmd/server/qqwry/qqwry.dat b/cmd/server/qqwry/qqwry.dat deleted file mode 100644 index 0752ec831..000000000 Binary files a/cmd/server/qqwry/qqwry.dat and /dev/null differ diff --git a/core/app/api/v2/auth.go b/core/app/api/v2/auth.go index 1ebc86022..7e621ffb0 100644 --- a/core/app/api/v2/auth.go +++ b/core/app/api/v2/auth.go @@ -2,6 +2,7 @@ package v2 import ( "encoding/base64" + "github.com/1Panel-dev/1Panel/core/utils/geo" "github.com/1Panel-dev/1Panel/core/app/api/v2/helper" "github.com/1Panel-dev/1Panel/core/app/dto" @@ -10,7 +11,6 @@ import ( "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/middleware" "github.com/1Panel-dev/1Panel/core/utils/captcha" - "github.com/1Panel-dev/1Panel/core/utils/qqwry" "github.com/gin-gonic/gin" ) @@ -175,12 +175,15 @@ func saveLoginLogs(c *gin.Context, err error) { logs.Status = constant.StatusSuccess } logs.IP = c.ClientIP() - qqWry, err := qqwry.NewQQwry() - if err != nil { - global.LOG.Errorf("load qqwry datas failed: %s", err) + lang := c.GetHeader("Accept-Language") + if lang == "" { + lang = "zh" + } + address, err := geo.GetIPLocation(logs.IP, lang) + if err != nil { + global.LOG.Errorf("get ip location failed: %s", err) } - res := qqWry.Find(logs.IP) logs.Agent = c.GetHeader("User-Agent") - logs.Address = res.Area + logs.Address = address _ = logService.CreateLoginLog(logs) } diff --git a/core/utils/geo/geo.go b/core/utils/geo/geo.go new file mode 100644 index 000000000..b55f46f99 --- /dev/null +++ b/core/utils/geo/geo.go @@ -0,0 +1,39 @@ +package geo + +import ( + "github.com/1Panel-dev/1Panel/core/global" + "github.com/oschwald/maxminddb-golang" + "net" + "path" +) + +type Location struct { + En string `maxminddb:"en"` + Zh string `maxminddb:"zh"` +} + +type LocationRes struct { + Iso string `maxminddb:"iso"` + Country Location `maxminddb:"country"` + Latitude float64 `maxminddb:"latitude"` + Longitude float64 `maxminddb:"longitude"` + Province Location `maxminddb:"province"` +} + +func GetIPLocation(ip, lang string) (string, error) { + geoPath := path.Join(global.CONF.System.BaseDir, "1panel", "geo", "GeoIP.mmdb") + reader, err := maxminddb.Open(geoPath) + if err != nil { + return "", err + } + var geoLocation LocationRes + ipNet := net.ParseIP(ip) + err = reader.Lookup(ipNet, &geoLocation) + if err != nil { + return "", err + } + if lang == "en" { + return geoLocation.Country.En + geoLocation.Province.En, nil + } + return geoLocation.Country.Zh + geoLocation.Province.Zh, nil +} diff --git a/core/utils/qqwry/qqwry.go b/core/utils/qqwry/qqwry.go deleted file mode 100644 index bd2b3d46a..000000000 --- a/core/utils/qqwry/qqwry.go +++ /dev/null @@ -1,165 +0,0 @@ -package qqwry - -import ( - "encoding/binary" - "net" - "strings" - - "github.com/1Panel-dev/1Panel/cmd/server/qqwry" - "golang.org/x/text/encoding/simplifiedchinese" -) - -const ( - indexLen = 7 - redirectMode1 = 0x01 - redirectMode2 = 0x02 -) - -var IpCommonDictionary []byte - -type QQwry struct { - Data []byte - Offset int64 -} - -func NewQQwry() (*QQwry, error) { - IpCommonDictionary := qqwry.QQwryByte - return &QQwry{Data: IpCommonDictionary}, nil -} - -// readData 从文件中读取数据 -func (q *QQwry) readData(num int, offset ...int64) (rs []byte) { - if len(offset) > 0 { - q.setOffset(offset[0]) - } - nums := int64(num) - end := q.Offset + nums - dataNum := int64(len(q.Data)) - if q.Offset > dataNum { - return nil - } - - if end > dataNum { - end = dataNum - } - rs = q.Data[q.Offset:end] - q.Offset = end - return -} - -// setOffset 设置偏移量 -func (q *QQwry) setOffset(offset int64) { - q.Offset = offset -} - -// Find ip地址查询对应归属地信息 -func (q *QQwry) Find(ip string) (res ResultQQwry) { - res = ResultQQwry{} - res.IP = ip - if strings.Count(ip, ".") != 3 { - return res - } - offset := q.searchIndex(binary.BigEndian.Uint32(net.ParseIP(ip).To4())) - if offset <= 0 { - return - } - - var area []byte - mode := q.readMode(offset + 4) - if mode == redirectMode1 { - countryOffset := q.readUInt24() - mode = q.readMode(countryOffset) - if mode == redirectMode2 { - c := q.readUInt24() - area = q.readString(c) - } else { - area = q.readString(countryOffset) - } - } else if mode == redirectMode2 { - countryOffset := q.readUInt24() - area = q.readString(countryOffset) - } else { - area = q.readString(offset + 4) - } - - enc := simplifiedchinese.GBK.NewDecoder() - res.Area, _ = enc.String(string(area)) - - return -} - -type ResultQQwry struct { - IP string `json:"ip"` - Area string `json:"area"` -} - -// readMode 获取偏移值类型 -func (q *QQwry) readMode(offset uint32) byte { - mode := q.readData(1, int64(offset)) - return mode[0] -} - -// readString 获取字符串 -func (q *QQwry) readString(offset uint32) []byte { - q.setOffset(int64(offset)) - data := make([]byte, 0, 30) - for { - buf := q.readData(1) - if buf[0] == 0 { - break - } - data = append(data, buf[0]) - } - return data -} - -// searchIndex 查找索引位置 -func (q *QQwry) searchIndex(ip uint32) uint32 { - header := q.readData(8, 0) - - start := binary.LittleEndian.Uint32(header[:4]) - end := binary.LittleEndian.Uint32(header[4:]) - - for { - mid := q.getMiddleOffset(start, end) - buf := q.readData(indexLen, int64(mid)) - _ip := binary.LittleEndian.Uint32(buf[:4]) - - if end-start == indexLen { - offset := byteToUInt32(buf[4:]) - buf = q.readData(indexLen) - if ip < binary.LittleEndian.Uint32(buf[:4]) { - return offset - } - return 0 - } - - if _ip > ip { - end = mid - } else if _ip < ip { - start = mid - } else if _ip == ip { - return byteToUInt32(buf[4:]) - } - } -} - -// readUInt24 -func (q *QQwry) readUInt24() uint32 { - buf := q.readData(3) - return byteToUInt32(buf) -} - -// getMiddleOffset -func (q *QQwry) getMiddleOffset(start uint32, end uint32) uint32 { - records := ((end - start) / indexLen) >> 1 - return start + records*indexLen -} - -// byteToUInt32 将 byte 转换为uint32 -func byteToUInt32(data []byte) uint32 { - i := uint32(data[0]) & 0xff - i |= (uint32(data[1]) << 8) & 0xff00 - i |= (uint32(data[2]) << 16) & 0xff0000 - return i -} diff --git a/go.mod b/go.mod index 1de3ae25e..835d325d9 100644 --- a/go.mod +++ b/go.mod @@ -99,6 +99,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mozillazg/go-httpheader v0.2.1 // indirect + github.com/oschwald/maxminddb-golang v1.13.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/xid v1.5.0 // indirect diff --git a/go.sum b/go.sum index c72fe8ebc..15345a83a 100644 --- a/go.sum +++ b/go.sum @@ -300,6 +300,8 @@ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISe github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= +github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= 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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=