feat(core): 添加IP地址检测服务核心功能
- 实现配置文件加载和解析功能,支持服务器、MMDB数据库和日志配置 - 集成GeoLite2城市数据库查询,提供IP地理位置信息查询服务 - 添加私有IP地址检测逻辑,过滤本地网络地址段 - 构建HTTP路由处理器,返回JSON格式的IP位置信息 - 配置默认启动参数和错误处理机制 - 集成日志系统,记录请求处理过程和错误信息
This commit is contained in:
36
cmd/checkip/main.go
Normal file
36
cmd/checkip/main.go
Normal file
@@ -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")
|
||||
}
|
||||
}
|
||||
9
configs/config.yaml
Normal file
9
configs/config.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
server:
|
||||
host: 0.0.0.0
|
||||
port: 8431
|
||||
|
||||
mmdb:
|
||||
filePath: ./db/GeoLite2-City.mmdb
|
||||
|
||||
log:
|
||||
level: debug
|
||||
52
internal/app/router.go
Normal file
52
internal/app/router.go
Normal file
@@ -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
|
||||
}
|
||||
39
internal/config/config.go
Normal file
39
internal/config/config.go
Normal file
@@ -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
|
||||
}
|
||||
49
internal/mmdb/reader.go
Normal file
49
internal/mmdb/reader.go
Normal file
@@ -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
|
||||
}
|
||||
52
internal/network/private.go
Normal file
52
internal/network/private.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user