From 6df8136e1b3f21b36b97424e49452b4bee79657e Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Mon, 2 Feb 2026 17:20:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0IP=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E6=A3=80=E6=B5=8B=E6=9C=8D=E5=8A=A1=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现配置文件加载和解析功能,支持服务器、MMDB数据库和日志配置 - 集成GeoLite2城市数据库查询,提供IP地理位置信息查询服务 - 添加私有IP地址检测逻辑,过滤本地网络地址段 - 构建HTTP路由处理器,返回JSON格式的IP位置信息 - 配置默认启动参数和错误处理机制 - 集成日志系统,记录请求处理过程和错误信息 --- cmd/checkip/main.go | 36 +++++++++++++++++++++++++ configs/config.yaml | 9 +++++++ internal/app/router.go | 52 +++++++++++++++++++++++++++++++++++++ internal/config/config.go | 39 ++++++++++++++++++++++++++++ internal/mmdb/reader.go | 49 ++++++++++++++++++++++++++++++++++ internal/network/private.go | 52 +++++++++++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+) create mode 100644 cmd/checkip/main.go create mode 100644 configs/config.yaml create mode 100644 internal/app/router.go create mode 100644 internal/config/config.go create mode 100644 internal/mmdb/reader.go create mode 100644 internal/network/private.go diff --git a/cmd/checkip/main.go b/cmd/checkip/main.go new file mode 100644 index 0000000..4c18f22 --- /dev/null +++ b/cmd/checkip/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "checkIP/internal/app" + "checkIP/internal/config" + "checkIP/internal/mmdb" + "fmt" + + "github.com/sirupsen/logrus" +) + +func main() { + cfg, err := config.Load("./configs/config.yaml") + if err != nil { + logrus.WithError(err).Fatal("load config failed") + } + + if cfg.Log.Level != "" { + level, err := logrus.ParseLevel(cfg.Log.Level) + if err != nil { + logrus.WithError(err).Fatal("invalid log level") + } + logrus.SetLevel(level) + } + + mmdbReader, err := mmdb.New(cfg.MMDB.FilePath) + if err != nil { + logrus.WithError(err).Fatal("init mmdb reader failed") + } + + router := app.NewRouter(mmdbReader) + addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) + if err := router.Run(addr); err != nil { + logrus.WithError(err).Fatal("http server failed") + } +} diff --git a/configs/config.yaml b/configs/config.yaml new file mode 100644 index 0000000..cf3a213 --- /dev/null +++ b/configs/config.yaml @@ -0,0 +1,9 @@ +server: + host: 0.0.0.0 + port: 8431 + +mmdb: + filePath: ./db/GeoLite2-City.mmdb + +log: + level: debug diff --git a/internal/app/router.go b/internal/app/router.go new file mode 100644 index 0000000..466e782 --- /dev/null +++ b/internal/app/router.go @@ -0,0 +1,52 @@ +package app + +import ( + "net/http" + + "checkIP/internal/mmdb" + "checkIP/internal/network" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +func NewRouter(reader *mmdb.Reader) *gin.Engine { + router := gin.New() + router.Use(gin.Recovery()) + router.Use(gin.LoggerWithWriter(logrus.StandardLogger().Out)) + + router.GET("/", func(c *gin.Context) { + ip := c.ClientIP() + if ip == "" { + c.String(http.StatusBadRequest, "Invalid IP") + return + } + + ipInfo := network.GetIPInformation(ip) + fields := logrus.Fields{ + "ip": ipInfo.IP, + "is_private": ipInfo.IsPrivate, + } + if ipInfo.IsPrivate { + fields["private_cidr"] = ipInfo.PrivateCIDR + } + logrus.WithFields(fields).Info("ip check") + + if ipInfo.IsPrivate { + c.String(http.StatusBadRequest, "Invalid IP") + return + } + + ipLocation, err := reader.QueryIP(ipInfo.IP) + if err != nil { + logrus.WithError(err).Error("mmdb lookup failed") + c.Status(http.StatusInternalServerError) + return + } + + logrus.WithField("ip_info", ipLocation).Info("mmdb lookup") + c.JSON(http.StatusOK, ipLocation) + }) + + return router +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..a641add --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,39 @@ +package config + +import ( + "fmt" + "os" + + "gopkg.in/yaml.v3" +) + +type Config struct { + Server ServerConfig `yaml:"server"` + MMDB MMDBConfig `yaml:"mmdb"` + Log LogConfig `yaml:"log"` +} + +type ServerConfig struct { + Host string `yaml:"host"` + Port int `yaml:"port"` +} + +type MMDBConfig struct { + FilePath string `yaml:"filePath"` +} + +type LogConfig struct { + Level string `yaml:"level"` +} + +func Load(path string) (Config, error) { + var cfg Config + data, err := os.ReadFile(path) + if err != nil { + return cfg, fmt.Errorf("read config: %w", err) + } + if err := yaml.Unmarshal(data, &cfg); err != nil { + return cfg, fmt.Errorf("parse yaml: %w", err) + } + return cfg, nil +} diff --git a/internal/mmdb/reader.go b/internal/mmdb/reader.go new file mode 100644 index 0000000..370aa63 --- /dev/null +++ b/internal/mmdb/reader.go @@ -0,0 +1,49 @@ +package mmdb + +import ( + "fmt" + "net" + + "github.com/IncSW/geoip2" +) + +type IpInfo struct { + Ip string `json:"ip"` + CountryIsoCode string `json:"country_iso_code"` + CountryName string `json:"country_name"` + City string `json:"city"` + TimeZone string `json:"time_zone"` +} + +type Reader struct { + reader *geoip2.CityReader +} + +func New(filePath string) (*Reader, error) { + if filePath == "" { + return nil, fmt.Errorf("mmdb file path is empty") + } + r, err := geoip2.NewCityReaderFromFile(filePath) + if err != nil { + return nil, fmt.Errorf("open mmdb: %w", err) + } + return &Reader{reader: r}, nil +} + +func (r *Reader) QueryIP(ip string) (IpInfo, error) { + if r == nil || r.reader == nil { + return IpInfo{}, fmt.Errorf("mmdb reader not initialized") + } + record, err := r.reader.Lookup(net.ParseIP(ip)) + if err != nil { + return IpInfo{}, fmt.Errorf("lookup ip: %w", err) + } + info := IpInfo{ + Ip: ip, + CountryIsoCode: record.Country.ISOCode, + CountryName: record.Country.Names["en"], + City: record.City.Names["en"], + TimeZone: record.Location.TimeZone, + } + return info, nil +} diff --git a/internal/network/private.go b/internal/network/private.go new file mode 100644 index 0000000..309ef75 --- /dev/null +++ b/internal/network/private.go @@ -0,0 +1,52 @@ +package network + +import ( + "net" + + "github.com/sirupsen/logrus" +) + +type IPInformation struct { + IP string + IsPrivate bool + PrivateCIDR string +} + +func GetIPInformation(ip string) IPInformation { + ipInfo := IPInformation{ + IP: ip, + IsPrivate: false, + PrivateCIDR: "", + } + + cidrs := []*net.IPNet{ + parseCIDR("10.0.0.0/8"), + parseCIDR("172.16.0.0/12"), + parseCIDR("192.168.0.0/16"), + parseCIDR("127.0.0.1/16"), + } + + ipAddr := net.ParseIP(ip) + if ipAddr == nil { + logrus.Warn("invalid ip address") + return ipInfo + } + + for _, cidr := range cidrs { + if cidr.Contains(ipAddr) { + ipInfo.IsPrivate = true + ipInfo.PrivateCIDR = cidr.String() + break + } + } + + return ipInfo +} + +func parseCIDR(cidr string) *net.IPNet { + _, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + return ipNet +}