diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index d1e1fd4..2851243 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -34,7 +34,7 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Build run: | - CGO_ENABLED=0 go build -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go + CGO_ENABLED=0 go build -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} . - name: Package run: | tar -czvf ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}.tar.gz ./${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d082ad8..b76b70c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Build run: | - CGO_ENABLED=0 go build -ldflags "-s -w" -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go + CGO_ENABLED=0 go build -ldflags "-s -w" -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} . - name: Package run: | tar -czvf ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}.tar.gz ./${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} diff --git a/CHANGELOG.md b/CHANGELOG.md index b29c91b..16be201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 更新日志 +3.0.0-beta.0 +--- +- 使用Touka框架重构, 去除caddy依赖, 大幅减小体积和额外的开销 + v2.1.4 --- - UPDATE: 安全性更新 diff --git a/DEV-VERSION b/DEV-VERSION index a33e211..f48b9c8 100644 --- a/DEV-VERSION +++ b/DEV-VERSION @@ -1 +1 @@ -25w03b \ No newline at end of file +3.0.0-beta.0 \ No newline at end of file diff --git a/api/api.go b/api/api.go index 4c70b2e..6bf1c43 100644 --- a/api/api.go +++ b/api/api.go @@ -1,29 +1,29 @@ package api import ( - "ip/bilibili" + "ip/bapi" "ip/config" "ip/ip" - "github.com/gin-gonic/gin" + "github.com/infinite-iroha/touka" ) -func InitHandleRouter(cfg *config.Config, router *gin.Engine) { +func InitHandleRouter(cfg *config.Config, router *touka.Engine) { apiRouter := router.Group("api") { - apiRouter.GET("/healthcheck", func(c *gin.Context) { - c.JSON(200, gin.H{ + apiRouter.GET("/healthcheck", func(c *touka.Context) { + c.JSON(200, touka.H{ "status": "ok", }) }) - apiRouter.Any("/ip-lookup", func(c *gin.Context) { + apiRouter.GET("/ip-lookup", func(c *touka.Context) { ip.IPHandler(c) }) - apiRouter.Any("/ip", func(c *gin.Context) { + apiRouter.GET("/ip", func(c *touka.Context) { ip.IPPureHandler(c) }) - apiRouter.Any("/bilibili", func(c *gin.Context) { - bilibili.Bilibili(c) + apiRouter.GET("/bilibili", func(c *touka.Context) { + bapi.Bilibili(c) }) } } diff --git a/bapi/bapi.go b/bapi/bapi.go new file mode 100644 index 0000000..cf5f027 --- /dev/null +++ b/bapi/bapi.go @@ -0,0 +1,71 @@ +package bapi + +import ( + "net/http" + "net/netip" + "net/url" + + "github.com/infinite-iroha/touka" +) + +// 使用req库处理 /bilibili 路由的请求并使用chrome的TLS指纹 +func Bilibili(c *touka.Context) { + + // 设置响应头 + c.Header("Access-Control-Allow-Origin", "*") // 允许跨域请求 + c.Header("Content-Type", "application/json") // 内容类型 + + // 从请求中获取ip参数 + ip := c.Query("ip") + if ip == "" { + c.JSON(http.StatusBadRequest, touka.H{ + "error": "Missing ip parameter", + }) + // IP METHOD URL UA PROTOCAL + c.Warnf("%s %s %s %s %s Missing ip parameter", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) + return + } + + _, err := netip.ParseAddr(ip) + if err != nil { + c.JSON(http.StatusBadRequest, touka.H{ + "error": "Invalid ip parameter", + }) + // IP METHOD URL UA PROTOCAL + c.Warnf("%s %s %s %s %s Invalid ip parameter", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) + return + } + + // 定义源API的URL并添加ip参数 + apiURL := "https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr?ip=" + url.QueryEscape(ip) + + // 使用req库发送请求并使用chrome的TLS指纹 + client := c.GetHTTPC() + rb := client.NewRequestBuilder("GET", apiURL) + rb.NoDefaultHeaders() + rb.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36") + + body, err := rb.Bytes() + if err != nil { + c.JSON(http.StatusInternalServerError, touka.H{ + "error": "Failed to get response from source API", + }) + // IP METHOD URL UA PROTOCAL ERROR + c.Errorf("%s %s %s %s %s Failed to get response from source API: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto, err) + return + } + + if len(body) == 0 { + c.JSON(http.StatusInternalServerError, touka.H{ + "error": "Failed to get response from source API", + }) + // IP METHOD URL UA PROTOCAL ERROR + c.Errorf("%s %s %s %s %s Failed to get response from source API: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto, err) + return + } + + // 返回上游API的响应内容 + c.Raw(http.StatusOK, "application/json", body) + // IP METHOD URL UA PROTOCAL + c.Infof("%s %s %s %s %s Success", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) +} diff --git a/bilibili/bilibili.go b/bilibili/bilibili.go deleted file mode 100644 index 3fc9e24..0000000 --- a/bilibili/bilibili.go +++ /dev/null @@ -1,105 +0,0 @@ -package bilibili - -import ( - "io" - "ip/logger" - "net/http" - "net/netip" - "net/url" - - "github.com/gin-gonic/gin" - "github.com/imroc/req/v3" -) - -var ( - logw = logger.Logw - logInfo = logger.LogInfo - logWarning = logger.LogWarning - logError = logger.LogError -) - -// 使用req库处理 /bilibili 路由的请求并使用chrome的TLS指纹 -func Bilibili(c *gin.Context) { - - // 设置响应头 - c.Header("Access-Control-Allow-Origin", "*") // 允许跨域请求 - c.Header("Content-Type", "application/json") // 内容类型 - - // 从请求中获取ip参数 - ip := c.Query("ip") - if ip == "" { - c.JSON(http.StatusBadRequest, gin.H{ - "error": "Missing ip parameter", - }) - // IP METHOD URL UA PROTOCAL - logWarning("%s %s %s %s %s Missing ip parameter", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) - return - } - - // 验证IP是否正确 - /* - if net.ParseIP(ip) == nil { - c.JSON(http.StatusBadRequest, gin.H{ - "error": "Invalid ip parameter", - }) - // IP METHOD URL UA PROTOCAL - logWarning("%s %s %s %s %s Invalid ip parameter", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) - return - } - */ - _, err := netip.ParseAddr(ip) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "error": "Invalid ip parameter", - }) - // IP METHOD URL UA PROTOCAL - logWarning("%s %s %s %s %s Invalid ip parameter", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) - return - } - - // 定义源API的URL并添加ip参数 - apiURL := "https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr?ip=" + url.QueryEscape(ip) - - // 使用req库发送请求并使用chrome的TLS指纹 - client := req.C(). - //SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"). - SetTLSFingerprintChrome(). - ImpersonateChrome() - - resp, err := client.R().Get(apiURL) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "error": "Failed to get response from source API", - }) - // IP METHOD URL UA PROTOCAL ERROR - logError("%s %s %s %s %s Failed to get response from source API: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto, err) - return - } - defer resp.Body.Close() - - // 检查源API的响应状态码 - if resp.StatusCode != http.StatusOK { - c.JSON(http.StatusInternalServerError, gin.H{ - "error": "Source API returned non-OK status", - }) - // IP METHOD URL UA PROTOCAL ERROR - logError("%s %s %s %s %s Source API returned non-OK status: %d", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto, resp.StatusCode) - return - } - - // 读取源API返回的内容 - body, err := io.ReadAll(resp.Body) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "error": "Failed to read response body", - }) - // IP METHOD URL UA PROTOCAL ERROR - logError("%s %s %s %s %s Failed to read response body: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto, err) - return - } - - // 返回上游API的响应内容 - c.Data(http.StatusOK, "application/json", body) - // IP METHOD URL UA PROTOCAL - logInfo("%s %s %s %s %s Success", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.Header.Get("User-Agent"), c.Request.Proto) -} diff --git a/config/config.go b/config/config.go index 10aec45..2b45d05 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,8 @@ package config import ( + "os" + "github.com/BurntSushi/toml" ) @@ -30,9 +32,73 @@ type MmdbConfig struct { // LoadConfig 从 TOML 配置文件加载配置 func LoadConfig(filePath string) (*Config, error) { + if !FileExists(filePath) { + // 楔入配置文件 + err := DefaultConfig().WriteConfig(filePath) + if err != nil { + return nil, err + } + return DefaultConfig(), nil + } + var config Config if _, err := toml.DecodeFile(filePath, &config); err != nil { return nil, err } return &config, nil } + +// 写入配置文件 +func (c *Config) WriteConfig(filePath string) error { + file, err := os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + + encoder := toml.NewEncoder(file) + return encoder.Encode(c) +} + +// 检测文件是否存在 +func FileExists(filename string) bool { + _, err := os.Stat(filename) + return !os.IsNotExist(err) +} + +/* +[server] +host = "127.0.0.1" +port = 8080 + +[mmdb] +mmdbpath = "/data/ip/db" +asndbpath = "/data/ip/db/asn.mmdb" +countrydbpath = "/data/ip/db/country.mmdb" +ipinfoKey = "" +updateFreq = 24 # hours + +[log] +logfilepath = "/data/ip/log/ip.log" +maxlogsize = 5 # MB +*/ + +func DefaultConfig() *Config { + return &Config{ + Server: ServerConfig{ + Host: "0.0.0.0", + Port: 8080, + }, + Log: LogConfig{ + LogFilePath: "./log/ip.log", + MaxLogSize: 5, // MB + }, + Mmdb: MmdbConfig{ + MmDBPath: "./data", + ASNDBPath: "./data/asn.mmdb", + CountryDBPath: "./data/country.mmdb", + IPinfoKey: "", + UpdateFreq: 24, // hours + }, + } +} diff --git a/db/db.go b/db/db.go index f8feb58..7cb4b3a 100644 --- a/db/db.go +++ b/db/db.go @@ -3,17 +3,18 @@ package db import ( "fmt" "ip/config" - "ip/logger" - "net" + "net/netip" - "github.com/oschwald/maxminddb-golang" + "github.com/fenthope/reco" + + "github.com/oschwald/maxminddb-golang/v2" ) var ( - logw = logger.Logw - logInfo = logger.LogInfo - logWarning = logger.LogWarning - logError = logger.LogError + logger *reco.Logger + logError = logger.Errorf + logWarning = logger.Warnf + logInfo = logger.Infof ) var ( @@ -48,6 +49,13 @@ func openDB(db **maxminddb.Reader, path string) { } } +func LoggerInit(recorder *reco.Logger) { + logger = recorder + logError = logger.Errorf + logWarning = logger.Warnf + logInfo = logger.Infof +} + // DBinit 初始化日志文件和数据库 func DBinit(cfg *config.Config) { ASNDB_Path = cfg.Mmdb.ASNDBPath @@ -86,21 +94,24 @@ func CloseDB() { } // SearchDB 根据 IP 地址查询 ASN 和国家信息 -func SearchDB(ip net.IP) (results []string, err error) { +func SearchDB(ip netip.Addr) (results []string, err error) { var ( asn ASNRecord country CountryRecord ) // 查询 ASN 信息 - err = asnDB.Lookup(ip, &asn) + // v1 func (r *maxminddb.Reader) Lookup(ip net.IP, result any) error + // v2 func (r *maxminddb.Reader) Lookup(ip netip.Addr) maxminddb.Result + err = asnDB.Lookup(ip).Decode(&asn) if err != nil { logError("ASN Lookup failed: %v", err) return nil, fmt.Errorf("ASN Lookup failed: %v", err) } // 查询国家信息 - err = countryDB.Lookup(ip, &country) + //err = countryDB.Lookup(ip, &country) + err = countryDB.Lookup(ip).Decode(&country) if err != nil { logError("Country Lookup failed: %v", err) return nil, fmt.Errorf("Country Lookup failed: %v", err) diff --git a/db/loop.go b/db/loop.go index f34c91d..2716072 100644 --- a/db/loop.go +++ b/db/loop.go @@ -9,10 +9,10 @@ func LoopForUpdate(cfg *config.Config) { var err error for { if Is2Update(cfg) { - logInfo("Updating database...") + logger.Info("Updating database...") err = GetNewDB(cfg) if err != nil { - logWarning("Failed to update database: %s", err) + logger.Warnf("Failed to update database: %s", err) } } diff --git a/docker/dockerfile/dev/Dockerfile b/docker/dockerfile/dev/Dockerfile index 0f86ccc..3f46c1b 100644 --- a/docker/dockerfile/dev/Dockerfile +++ b/docker/dockerfile/dev/Dockerfile @@ -1,4 +1,4 @@ -FROM wjqserver/caddy:alpine AS builder +FROM alpine:latest AS builder ARG USER=WJQSERVER-STUDIO ARG REPO=ip @@ -16,35 +16,22 @@ RUN mkdir -p /data/${APPLICATION}/config RUN mkdir -p /data/${APPLICATION}/log RUN mkdir -p /data/${APPLICATION}/db -# 前端 -RUN wget -O /data/www/index.html https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/index.html -RUN wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico - -# Caddyfile -RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/release/Caddyfile - # 后端 RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/DEV-VERSION) && \ wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH} -RUN wget -O /data/${APPLICATION}/config.toml https://raw.githubusercontent.com/${USER}/${REPO}/main/config/config.toml -RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/dev/init.sh # 权限 RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh -FROM wjqserver/caddy:alpine +FROM alpine:latest COPY --from=builder /data/www /data/www -COPY --from=builder /data/caddy /data/caddy COPY --from=builder /data/${APPLICATION} /data/${APPLICATION} -COPY --from=builder /usr/local/bin/init.sh /usr/local/bin/init.sh RUN mkdir -p /data/${APPLICATION}/db # 权限 RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh -CMD ["/usr/local/bin/init.sh"] +CMD ["/data/ip/ip"] diff --git a/docker/dockerfile/release/Dockerfile b/docker/dockerfile/release/Dockerfile index 2fa20fb..423afc1 100644 --- a/docker/dockerfile/release/Dockerfile +++ b/docker/dockerfile/release/Dockerfile @@ -1,4 +1,4 @@ -FROM wjqserver/caddy:alpine AS builder +FROM alpine:latest AS builder ARG USER=WJQSERVER-STUDIO ARG REPO=ip @@ -16,34 +16,22 @@ RUN mkdir -p /data/${APPLICATION}/config RUN mkdir -p /data/${APPLICATION}/log RUN mkdir -p /data/${APPLICATION}/db -# 前端 -RUN wget -O /data/www/index.html https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/index.html -RUN wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico - -# Caddyfile -RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/release/Caddyfile - # 后端 RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/VERSION) && \ wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH} -RUN wget -O /data/${APPLICATION}/config.toml https://raw.githubusercontent.com/${USER}/${REPO}/main/config/config.toml -RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/release/init.sh # 权限 RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh -FROM wjqserver/caddy:alpine +FROM alpine:latest COPY --from=builder /data/www /data/www -COPY --from=builder /data/caddy /data/caddy COPY --from=builder /data/${APPLICATION} /data/${APPLICATION} -COPY --from=builder /usr/local/bin/init.sh /usr/local/bin/init.sh RUN mkdir -p /data/${APPLICATION}/db # 权限 RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh -CMD ["/usr/local/bin/init.sh"] +CMD ["/data/ip/ip"] + diff --git a/go.mod b/go.mod index da43ad8..dc2c985 100644 --- a/go.mod +++ b/go.mod @@ -1,55 +1,19 @@ module ip -go 1.24.3 +go 1.24.5 require ( github.com/BurntSushi/toml v1.5.0 - github.com/gin-gonic/gin v1.10.1 - github.com/imroc/req/v3 v3.52.2 - github.com/oschwald/maxminddb-golang v1.13.1 + github.com/fenthope/reco v0.0.3 + github.com/infinite-iroha/touka v0.3.1 + github.com/oschwald/maxminddb-golang/v2 v2.0.0-beta.8 ) require ( - github.com/andybalholm/brotli v1.1.1 // indirect - github.com/bytedance/sonic v1.13.3 // indirect - github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cloudflare/circl v1.6.1 // indirect - github.com/cloudwego/base64x v0.1.5 // indirect - github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.26.0 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/icholy/digest v1.1.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/onsi/ginkgo/v2 v2.23.4 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.52.0 // indirect - github.com/refraction-networking/utls v1.7.3 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.14 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/mock v0.5.2 // indirect - golang.org/x/arch v0.18.0 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/sync v0.15.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.26.0 // indirect - golang.org/x/tools v0.34.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.6 // indirect + github.com/WJQSERVER-STUDIO/httpc v0.8.1 // indirect + github.com/go-json-experiment/json v0.0.0-20250714165856-be8212f5270d // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect ) diff --git a/go.sum b/go.sum index e460a21..a143080 100644 --- a/go.sum +++ b/go.sum @@ -1,138 +1,28 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= -github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.6 h1:/50VJYXd6jcu+p5BnEBDyiX0nAyGxas1W3DCnrYMxMY= +github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.6/go.mod h1:FZ6XE+4TKy4MOfX1xWKe6Rwsg0ucYFCdNh1KLvyKTfc= +github.com/WJQSERVER-STUDIO/httpc v0.8.1 h1:/eG8aYKL3WfQILIRbG+cbzQjPkNHEPTqfGUdQS5rtI4= +github.com/WJQSERVER-STUDIO/httpc v0.8.1/go.mod h1:mxXBf2hqbQGNHkVy/7wfU7Xi2s09MyZpbY2hyR+4uD4= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= -github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= -github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4= -github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y= -github.com/imroc/req/v3 v3.52.2 h1:xJocr1aIv0a2K9knfBQ4JnZHk+kWTITdjf0mgDg229I= -github.com/imroc/req/v3 v3.52.2/go.mod h1:dBGsDloOSZJcFs6PnTjZXYBJK70OXbZpizHBLNqcH2k= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= -github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= -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.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/fenthope/reco v0.0.3 h1:RmnQ0D9a8PWtwOODawitTe4BztTnS9wYwrDbipISNq4= +github.com/fenthope/reco v0.0.3/go.mod h1:mDkGLHte5udWTIcjQTxrABRcf56SSdxBOCLgrRDwI/Y= +github.com/go-json-experiment/json v0.0.0-20250714165856-be8212f5270d h1:+d6m5Bjvv0/RJct1VcOw2P5bvBOGjENmxORJYnSYDow= +github.com/go-json-experiment/json v0.0.0-20250714165856-be8212f5270d/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= +github.com/infinite-iroha/touka v0.3.1 h1:djR9hg5MbVpT1dIz2GWo4MZ/kx3l6bJ4nrpzpvdi3uk= +github.com/infinite-iroha/touka v0.3.1/go.mod h1:pHOYHE4AKoQ1KikHF9JYKIJ4he8um1MzgcddscjCeyg= +github.com/oschwald/maxminddb-golang/v2 v2.0.0-beta.8 h1:aM1/rO6p+XV+l+seD7UCtFZgsOefDTrFVLvPoZWjXZs= +github.com/oschwald/maxminddb-golang/v2 v2.0.0-beta.8/go.mod h1:Jts8ztuE0PkUwY7VCJyp6B68ujQfr6G9P5Dn3Yx9u6w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= -github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= -github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo= -github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.14 h1:yOQvXCBc3Ij46LRkRoh4Yd5qK6LVOgi0bYOXfb7ifjw= -github.com/ugorji/go/codec v1.2.14/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= -golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc= -golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/ip/ip.go b/ip/ip.go index fba1da4..376722d 100644 --- a/ip/ip.go +++ b/ip/ip.go @@ -2,17 +2,10 @@ package ip import ( "ip/db" - "ip/logger" "net" + "net/netip" - "github.com/gin-gonic/gin" -) - -var ( - logw = logger.Logw - logInfo = logger.LogInfo - logWarning = logger.LogWarning - logError = logger.LogError + "github.com/infinite-iroha/touka" ) // 构造响应json @@ -28,7 +21,7 @@ type Response struct { UserAgent string `json:"user_agent"` } -func IPHandler(c *gin.Context) { +func IPHandler(c *touka.Context) { var ( ip string @@ -40,36 +33,27 @@ func IPHandler(c *gin.Context) { if ipStr != "" && net.ParseIP(ipStr) != nil { ip = ipStr } else { - // 优先获取X-Forwarded-For,其次是X-Real-IP,最后是c.clientIP() - fwdIP := c.GetHeader("X-Forwarded-For") - realIP := c.GetHeader("X-Real-IP") - if fwdIP != "" { - ip = fwdIP - } else if realIP != "" { - ip = realIP - } else { - ip = c.ClientIP() - } + ip = c.ClientIP() } // 预处理IP地址,转为net.IP类型 - netIP := net.ParseIP(ip) - if netIP == nil { - logWarning("Invalid IP address: ", ip) - c.JSON(400, gin.H{"error": "Invalid IP address"}) + netIP, err := netip.ParseAddr(ip) + if err != nil { + c.Warnf("Invalid IP address: %s", ip) + c.JSON(400, touka.H{"error": "Invalid IP address"}) return } // call DB 获取 GeoIP 数据 results, err := db.SearchDB(netIP) if err != nil { - logError("SearchDB error: ", err) - c.JSON(500, gin.H{"error": "Internal Server Error"}) + c.Errorf("SearchDB error: %s", err) + c.JSON(500, touka.H{"error": "Internal Server Error"}) return } // 获取User-Agent - ua = c.GetHeader("User-Agent") + ua = c.UserAgent() c.Header("Access-Control-Allow-Origin", "*") // 允许跨域请求 c.Header("Content-Type", "application/json") // 内容类型 @@ -91,29 +75,19 @@ func IPHandler(c *gin.Context) { c.JSON(200, response) } -func IPPureHandler(c *gin.Context) { - var ip string - // 优先获取X-Forwarded-For,其次是X-Real-IP,最后是c.clientIP() - fwdIP := c.GetHeader("X-Forwarded-For") - realIP := c.GetHeader("X-Real-IP") - if fwdIP != "" { - ip = fwdIP - } else if realIP != "" { - ip = realIP - } else { - ip = c.ClientIP() - } +func IPPureHandler(c *touka.Context) { + ip := c.ClientIP() // 预处理IP地址,转为net.IP类型 netIP := net.ParseIP(ip) if netIP == nil { - logWarning("Invalid IP address: ", ip) - c.JSON(400, gin.H{"error": "Invalid IP address"}) + c.Warnf("Invalid IP address: %s", ip) + c.JSON(400, touka.H{"error": "Invalid IP address"}) return } c.Header("Access-Control-Allow-Origin", "*") // 允许跨域请求 // 纯IP响应,非json格式 - c.String(200, ip) + c.Text(200, ip) } diff --git a/logger/logger.go b/logger/logger.go deleted file mode 100644 index 0181014..0000000 --- a/logger/logger.go +++ /dev/null @@ -1,220 +0,0 @@ -/* -Copyright 2024 WJQserver Studio. Open source WSL 1.2 License. -*/ - -package logger - -import ( - "archive/tar" - "compress/gzip" - "fmt" - "io" - "log" - "os" - "path/filepath" - "sync" - "time" -) - -const ( - timeFormat = "02/Jan/2006:15:04:05 -0700" - defaultBufSize = 1000 -) - -var ( - Logw = Logf - logf = Logf - logFile *os.File - logger *log.Logger - logChannel = make(chan string, defaultBufSize) - quitChannel = make(chan struct{}) - logFileMutex sync.Mutex - wg sync.WaitGroup -) - -// Init 初始化日志记录器 -func Init(logFilePath string, maxLogSizeMB int) error { - logFileMutex.Lock() - defer logFileMutex.Unlock() - - var err error - logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return fmt.Errorf("failed to open log file: %w", err) - } - - logger = log.New(logFile, "", 0) - go logWorker() - go monitorLogSize(logFilePath, int64(maxLogSizeMB)*1024*1024) - return nil -} - -func logWorker() { - wg.Add(1) - defer wg.Done() - - for { - select { - case msg := <-logChannel: - logFileMutex.Lock() - logger.Printf("%s - %s\n", time.Now().Format(timeFormat), msg) - logFileMutex.Unlock() - case <-quitChannel: - // 处理剩余日志 - for { - select { - case msg := <-logChannel: - logFileMutex.Lock() - logger.Printf("%s - %s\n", time.Now().Format(timeFormat), msg) - logFileMutex.Unlock() - default: - return - } - } - } - } -} - -// Log 记录日志 -func Log(msg string) { - select { - case logChannel <- msg: - default: - // 日志队列满时丢弃日志并通知 - fmt.Fprintf(os.Stderr, "Log queue full, dropping message: %s\n", msg) - } -} - -// Logf 格式化日志 -func Logf(format string, args ...interface{}) { - Log(fmt.Sprintf(format, args...)) -} - -// LogInfo 信息级别日志 -func LogInfo(format string, args ...interface{}) { - Logf("[INFO] "+format, args...) -} - -// LogWarning 警告级别日志 -func LogWarning(format string, args ...interface{}) { - Logf("[WARNING] "+format, args...) -} - -// LogError 错误级别日志 -func LogError(format string, args ...interface{}) { - Logf("[ERROR] "+format, args...) -} - -// Close 关闭日志系统 -func Close() { - close(quitChannel) - wg.Wait() - - logFileMutex.Lock() - defer logFileMutex.Unlock() - if logFile != nil { - if err := logFile.Close(); err != nil { - fmt.Fprintf(os.Stderr, "Error closing log file: %v\n", err) - } - } -} - -func monitorLogSize(logFilePath string, maxBytes int64) { - ticker := time.NewTicker(15 * time.Minute) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - logFileMutex.Lock() - info, err := logFile.Stat() - logFileMutex.Unlock() - - if err == nil && info.Size() > maxBytes { - if err := rotateLogFile(logFilePath); err != nil { - LogError("Log rotation failed: %v", err) - } - } - case <-quitChannel: - return - } - } -} - -func rotateLogFile(logFilePath string) error { - logFileMutex.Lock() - defer logFileMutex.Unlock() - - // 关闭当前日志文件 - if logFile != nil { - if err := logFile.Close(); err != nil { - return fmt.Errorf("error closing log file: %w", err) - } - } - - // 重命名原日志文件 - backupPath := fmt.Sprintf("%s.%s", logFilePath, time.Now().Format("20060102-150405")) - if err := os.Rename(logFilePath, backupPath); err != nil { - return fmt.Errorf("error renaming log file: %w", err) - } - - // 创建新日志文件 - newFile, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return fmt.Errorf("error creating new log file: %w", err) - } - logFile = newFile - logger.SetOutput(logFile) - - // 异步压缩旧日志 - go func() { - if err := compressLog(backupPath); err != nil { - LogError("Compression failed: %v", err) - } - os.Remove(backupPath) - }() - - return nil -} - -func compressLog(srcPath string) error { - srcFile, err := os.Open(srcPath) - if err != nil { - return err - } - defer srcFile.Close() - - dstFile, err := os.Create(srcPath + ".tar.gz") - if err != nil { - return err - } - defer dstFile.Close() - - gzWriter := gzip.NewWriter(dstFile) - defer gzWriter.Close() - - tarWriter := tar.NewWriter(gzWriter) - defer tarWriter.Close() - - info, err := srcFile.Stat() - if err != nil { - return err - } - - header := &tar.Header{ - Name: filepath.Base(srcPath), - Size: info.Size(), - Mode: int64(info.Mode()), - ModTime: info.ModTime(), - } - - if err := tarWriter.WriteHeader(header); err != nil { - return err - } - - if _, err := io.Copy(tarWriter, srcFile); err != nil { - return err - } - - return nil -} diff --git a/main.go b/main.go index c2b4b35..4359162 100644 --- a/main.go +++ b/main.go @@ -1,34 +1,33 @@ package main import ( + "embed" "flag" "fmt" + "io/fs" "log" + "net/http" + "os" "ip/api" "ip/config" "ip/db" - "ip/logger" - "github.com/gin-gonic/gin" + "github.com/fenthope/reco" + "github.com/infinite-iroha/touka" ) -var ( - cfg *config.Config - configfile = "/data/go/config/config.toml" - router *gin.Engine -) +//go:embed pages +var pagesFS embed.FS -// 日志模块 var ( - logw = logger.Logw - logInfo = logger.LogInfo - logWarning = logger.LogWarning - logError = logger.LogError + cfg *config.Config + configfile = "/data/ip/config/config.toml" + router *touka.Engine ) func ReadFlag() { - cfgfile := flag.String("cfg", configfile, "config file path") + cfgfile := flag.String("c", configfile, "config file path") flag.Parse() configfile = *cfgfile } @@ -43,46 +42,59 @@ func loadConfig() { fmt.Printf("Loaded config: %v\n", cfg) } -func setupLogger() { - // 初始化日志模块 - var err error - err = logger.Init(cfg.Log.LogFilePath, cfg.Log.MaxLogSize) // 传递日志文件路径 - if err != nil { - log.Fatalf("Failed to initialize logger: %v", err) - } - logw("Logger initialized") - logw("Init Completed") -} - -func updateDB() { +func updateDB(cfg *config.Config) { go db.LoopForUpdate(cfg) } -func setupDB() { +func setupDB(cfg *config.Config) { db.DBinit(cfg) } -func setupApi(cfg *config.Config, router *gin.Engine) { - api.InitHandleRouter(cfg, router) -} - func init() { ReadFlag() loadConfig() - setupLogger() - updateDB() - setupDB() - gin.SetMode(gin.ReleaseMode) - router = gin.Default() - router.UseH2C = false - setupApi(cfg, router) + router = touka.Default() + api.InitHandleRouter(cfg, router) +} + +func setLogger(cfg *config.Config) *reco.Logger { + lcfg := reco.Config{ + Level: reco.LevelInfo, // 最低记录级别为 DEBUG + Mode: reco.ModeText, // 输出 JSON 格式 + FilePath: cfg.Log.LogFilePath, // 日志文件路径 + EnableRotation: true, // 启用文件轮转 + MaxFileSizeMB: int64(cfg.Log.MaxLogSize), // 单个文件最大 5MB + MaxBackups: 3, // 保留 3 个旧备份 + CompressBackups: true, // 压缩旧备份 + Async: true, // 启用异步写入 (默认) + BufferSize: 4096, // 异步缓冲区大小 + } + + // 创建 Logger 实例 + logger, err := reco.New(lcfg) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create logger: %v\n", err) + os.Exit(1) + } + return logger } func main() { - err := router.Run(fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)) + logger := setLogger(cfg) + db.LoggerInit(logger) + updateDB(cfg) + setupDB(cfg) + router.SetLogger(logger) + pFS, err := fs.Sub(pagesFS, "pages") + if err != nil { + logger.Errorf("Failed to load embedded pages: %v\n", err) + return + } + router.SetUnMatchFS(http.FS(pFS)) + err = router.RunShutdown(fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)) if err != nil { - logError("Failed to start server: %v\n", err) + logger.Errorf("Failed to start server: %v\n", err) } defer logger.Close() // 确保在退出时关闭日志文件 } diff --git a/reserved/.github/SECURITY.md b/reserved/.github/SECURITY.md deleted file mode 100644 index 4873ca6..0000000 --- a/reserved/.github/SECURITY.md +++ /dev/null @@ -1,15 +0,0 @@ -# 安全政策 - -## 支持的版本 - -| Version | Supported | -| ------- | ------------------ | -| 24W*B | :white_check_mark: | -| 24W*A | :x: | -| < 24W* | :x: | - -# 報告問題 - -若發現重大漏洞,請在ISSUES提出並附帶盡可能詳細的相關信息; - -若**事態緊急**,請在提出ISSUES的同時聯繫項目管理員或組織管理員,謝謝 diff --git a/reserved/.github/dependabot.yml b/reserved/.github/dependabot.yml deleted file mode 100644 index fc86aa3..0000000 --- a/reserved/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - -version: 2 -updates: - - package-ecosystem: "gomod" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - diff --git a/reserved/.github/workflows/dev.yml b/reserved/.github/workflows/dev.yml deleted file mode 100644 index e30dc51..0000000 --- a/reserved/.github/workflows/dev.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Build Dev - -on: - workflow_dispatch: - push: - branches: - - 'main' - paths: - - 'DEV-VERSION' - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - goos: [linux] - goarch: [amd64, arm64] - env: - OUTPUT_BINARY: ip - GO_VERSION: 1.23.3 - - steps: - - uses: actions/checkout@v3 - - name: Load VERSION - run: | - if [ -f DEV-VERSION ]; then - echo "VERSION=$(cat DEV-VERSION)" >> $GITHUB_ENV - else - echo "DEV-VERSION file not found!" && exit 1 - fi - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - - name: Build - run: | - CGO_ENABLED=0 go build -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go - - name: Package - run: | - tar -czvf ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}.tar.gz ./${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} - - name: Upload to GitHub Artifacts - uses: actions/upload-artifact@v3 - with: - name: ${{ env.OUTPUT_BINARY }} - path: | - ./${{ env.OUTPUT_BINARY }}* - - name: 上传至Release - id: create_release - uses: ncipollo/release-action@v1 - with: - name: ${{ env.VERSION }} - artifacts: ./${{ env.OUTPUT_BINARY }}* - token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ env.VERSION }} - allowUpdates: true - prerelease: true - env: - export PATH: $PATH:/usr/local/go/bin - - docker: - runs-on: ubuntu-latest - needs: build - env: - IMAGE_NAME: wjqserver/ip - DOCKERFILE: docker/dockerfile/dev/Dockerfile - - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Load VERSION - run: | - if [ -f DEV-VERSION ]; then - echo "VERSION=$(cat DEV-VERSION)" >> $GITHUB_ENV - else - echo "DEV-VERSION file not found!" && exit 1 - fi - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: 构建镜像 - uses: docker/build-push-action@v5 - with: - file: ./${{ env.DOCKERFILE }} - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ env.IMAGE_NAME }}:${{ env.VERSION }} - ${{ env.IMAGE_NAME }}:dev diff --git a/reserved/.github/workflows/release.yml b/reserved/.github/workflows/release.yml deleted file mode 100644 index c7cdcfe..0000000 --- a/reserved/.github/workflows/release.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Build - -on: - workflow_dispatch: - push: - branches: - - 'main' - paths: - - 'VERSION' - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - goos: [linux] - goarch: [amd64, arm64] - env: - OUTPUT_BINARY: ip - GO_VERSION: 1.23.3 - - steps: - - uses: actions/checkout@v3 - - name: Load VERSION - run: | - if [ -f VERSION ]; then - echo "VERSION=$(cat VERSION)" >> $GITHUB_ENV - else - echo "VERSION file not found!" && exit 1 - fi - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - - name: Build - run: | - CGO_ENABLED=0 go build -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go - - name: Package - run: | - tar -czvf ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}.tar.gz ./${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} - - name: Upload to GitHub Artifacts - uses: actions/upload-artifact@v3 - with: - name: ${{ env.OUTPUT_BINARY }} - path: | - ./${{ env.OUTPUT_BINARY }}* - - name: 上传至Release - id: create_release - uses: ncipollo/release-action@v1 - with: - name: ${{ env.VERSION }} - artifacts: ./${{ env.OUTPUT_BINARY }}* - token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ env.VERSION }} - allowUpdates: true - env: - export PATH: $PATH:/usr/local/go/bin - docker: - runs-on: ubuntu-latest - needs: build # 确保这个作业在 build 作业完成后运行 - env: - IMAGE_NAME: wjqserver/ip # 定义镜像名称变量 - DOCKERFILE: docker/dockerfile/release/Dockerfile # 定义 Dockerfile 路径变量 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Load VERSION - run: | - if [ -f VERSION ]; then - echo "VERSION=$(cat VERSION)" >> $GITHUB_ENV - else - echo "VERSION file not found!" && exit 1 - fi - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: 构建镜像 - uses: docker/build-push-action@v5 - with: - file: ./${{ env.DOCKERFILE }} - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ env.IMAGE_NAME }}:${{ env.VERSION }} - ${{ env.IMAGE_NAME }}:latest diff --git a/reserved/Caddyfile b/reserved/Caddyfile deleted file mode 100644 index 21dc971..0000000 --- a/reserved/Caddyfile +++ /dev/null @@ -1,101 +0,0 @@ -{ - debug - http_port 80 - https_port 443 - order cache before rewrite - cache { - cache_name IPinfoCache - } - log { - level INFO - output file /data/caddy/log/caddy.log { - roll_size 5MB - roll_keep 10 - } - } -} - -(log) { - log { - format transform `{request>headers>X-Forwarded-For>[0]:request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` { - time_format "02/Jan/2006:15:04:05 -0700" - } - output file /data/caddy/log/{args[0]}/access.log { - roll_size 5MB - roll_keep 10 - roll_keep_for 24h - } - } -} - -(error_page) { - handle_errors { - rewrite * /{err.status_code}.html - root * /data/caddy/pages/errors - file_server - } -} - -(encode) { - encode { - zstd best - br 5 v2 - gzip 5 - minimum_length 1024 - } -} - -(cache) { - cache { - allowed_http_verbs GET - stale {args[0]} - ttl {args[1]} - } -} - -(header_realip) { - header_up X-Real-IP {remote_host} - header_up X-Real-IP {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-For {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-Proto {http.request.header.CF-Visitor} -} - -(buffer) { - flush_interval 2000s - buffer_responses - max_buffer_size 256k -} - -(rate_limit) { - route /* { - rate_limit {remote.ip} {args[0]}r/m 10000 429 - } -} - -:80 { - root * /data/www - file_server - import log ip - import error_page - import encode - header Cache-Control {no-cache} - route / { - rate_limit {remote.ip} 100r/m 10000 429 - } - route /ip* { - reverse_proxy { - to 127.0.0.1:8080 - import header_realip - } - rate_limit {remote.ip} 60r/m 10000 429 - } - route /bilibili* { - reverse_proxy { - to 127.0.0.1:8080 - import header_realip - } - rate_limit {remote.ip} 30r/m 10000 429 - } -} - -import /data/caddy/config.d/* diff --git a/reserved/DEV-VERSION b/reserved/DEV-VERSION deleted file mode 100644 index 458ce3b..0000000 --- a/reserved/DEV-VERSION +++ /dev/null @@ -1 +0,0 @@ -24w17a \ No newline at end of file diff --git a/reserved/Dockerfile b/reserved/Dockerfile deleted file mode 100644 index 7ea33a3..0000000 --- a/reserved/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM wjqserver/caddy:latest - -RUN mkdir -p /data/www -RUN mkdir -p /data/ipinfo/db -RUN mkdir -p /data/ipinfo/log - -RUN wget -O /data/www/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ip/main/pages/index.html -RUN wget -O /data/www/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ip/main/pages/favicon.ico - -RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/WJQSERVER-STUDIO/ip/main/Caddyfile -RUN VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ip/main/VERSION) && \ - wget -O /data/ipinfo/ip https://github.com/WJQSERVER-STUDIO/ip/releases/download/$VERSION/ip -RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/WJQSERVER-STUDIO/ip/main/init.sh - -RUN chmod +x /data/ipinfo/ip -RUN chmod +x /usr/local/bin/init.sh - -CMD ["/usr/local/bin/init.sh"] - diff --git a/reserved/LICENSE b/reserved/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/reserved/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/reserved/README.md b/reserved/README.md deleted file mode 100644 index 33d1918..0000000 --- a/reserved/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# ip - -![GitHub Release](https://img.shields.io/github/v/release/WJQSERVER-STUDIO/ip?display_name=tag&style=flat) -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/WJQSERVER-STUDIO/ip) -![pull](https://img.shields.io/docker/pulls/wjqserver/ip.svg) -![size](https://img.shields.io/docker/image-size/wjqserver/ip) - -用于获取IP相关信息的API程序 - -## Demo - -[演示站](https://ip.1888866.xyz) - -## 部署 - -``` -docker run -d -p 8980:80 -v ./ipinfo/db:/data/ipinfo/db -v ./ipinfo/log:/data/ipinfo/log --restart always wjqserver/ip:latest -``` - -需导入mmdb数据库(ipinfo)才可正常使用自有API diff --git a/reserved/VERSION b/reserved/VERSION deleted file mode 100644 index e6d5cb8..0000000 --- a/reserved/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.0.2 \ No newline at end of file diff --git a/reserved/docker/docker-compose/docker-compose.yml b/reserved/docker/docker-compose/docker-compose.yml deleted file mode 100644 index 3e7d7b3..0000000 --- a/reserved/docker/docker-compose/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: '3.9' -services: - ip: - image: 'wjqserver/ip:latest' - restart: always - volumes: - - './ip/log/run:/data/ip/log' - - './ip/log/caddy:/data/caddy/log' - - './ip/db:/data/ip/db' - ports: - - '7219:80' \ No newline at end of file diff --git a/reserved/docker/dockerfile/dev/Dockerfile b/reserved/docker/dockerfile/dev/Dockerfile deleted file mode 100644 index d80547d..0000000 --- a/reserved/docker/dockerfile/dev/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM wjqserver/caddy:daily-alpine AS builder - -ARG USER=WJQSERVER-STUDIO -ARG REPO=ip -ARG APPLICATION=ip -ARG TARGETOS -ARG TARGETARCH -ARG TARGETPLATFORM - -# 拉取依赖 -RUN apk add --no-cache wget curl - -# 创建目录 -RUN mkdir -p /data/www -RUN mkdir -p /data/${APPLICATION}/db -RUN mkdir -p /data/${APPLICATION}/log - -# 前端 -RUN wget -O /data/www/index.html https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/index.html -RUN wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico - -# Caddyfile -RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/Caddyfile - -# 后端 -RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/DEV-VERSION) && \ - wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH} -RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/dev/init.sh - -# 权限 -RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh - -FROM wjqserver/caddy:daily-alpine - -COPY --from=builder /data/www /data/www -COPY --from=builder /data/caddy /data/caddy -COPY --from=builder /data/${APPLICATION} /data/${APPLICATION} -COPY --from=builder /usr/local/bin/init.sh /usr/local/bin/init.sh - -# 权限 -RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh - -CMD ["/usr/local/bin/init.sh"] - diff --git a/reserved/docker/dockerfile/dev/init.sh b/reserved/docker/dockerfile/dev/init.sh deleted file mode 100644 index bb3c322..0000000 --- a/reserved/docker/dockerfile/dev/init.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -APPLICATION=ip - -# 检查并复制 Caddyfile -if [ ! -f /data/caddy/config/Caddyfile ]; then - cp /data/caddy/Caddyfile /data/caddy/config/Caddyfile -fi - -# 启动 Caddy -/data/caddy/caddy run --config /data/caddy/config/Caddyfile > /data/${APPLICATION}/log/caddy.log 2>&1 & - -# 启动 Go 应用 -/data/${APPLICATION}/${APPLICATION} > /data/${APPLICATION}/log/run.log 2>&1 & - -# 保持脚本运行 -while true; do - sleep 1 -done \ No newline at end of file diff --git a/reserved/docker/dockerfile/release/Dockerfile b/reserved/docker/dockerfile/release/Dockerfile deleted file mode 100644 index 99d2d72..0000000 --- a/reserved/docker/dockerfile/release/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM wjqserver/caddy:2.9.0-rc-alpine AS builder - -ARG USER=WJQSERVER-STUDIO -ARG REPO=ip -ARG APPLICATION=ip -ARG TARGETOS -ARG TARGETARCH -ARG TARGETPLATFORM - -# 拉取依赖 -RUN apk add --no-cache wget curl - -# 创建目录 -RUN mkdir -p /data/www -RUN mkdir -p /data/${APPLICATION}/db -RUN mkdir -p /data/${APPLICATION}/log - -# 前端 -RUN wget -O /data/www/index.html https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/index.html -RUN wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico - -# Caddyfile -RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/Caddyfile - -# 后端 -RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/VERSION) && \ - wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH} -RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/release/init.sh - -# 权限 -RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh - -FROM wjqserver/caddy:2.9.0-rc-alpine - -COPY --from=builder /data/www /data/www -COPY --from=builder /data/caddy /data/caddy -COPY --from=builder /data/${APPLICATION} /data/${APPLICATION} -COPY --from=builder /usr/local/bin/init.sh /usr/local/bin/init.sh - -# 权限 -RUN chmod +x /data/${APPLICATION}/${APPLICATION} -RUN chmod +x /usr/local/bin/init.sh - -CMD ["/usr/local/bin/init.sh"] - diff --git a/reserved/docker/dockerfile/release/init.sh b/reserved/docker/dockerfile/release/init.sh deleted file mode 100644 index bb3c322..0000000 --- a/reserved/docker/dockerfile/release/init.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -APPLICATION=ip - -# 检查并复制 Caddyfile -if [ ! -f /data/caddy/config/Caddyfile ]; then - cp /data/caddy/Caddyfile /data/caddy/config/Caddyfile -fi - -# 启动 Caddy -/data/caddy/caddy run --config /data/caddy/config/Caddyfile > /data/${APPLICATION}/log/caddy.log 2>&1 & - -# 启动 Go 应用 -/data/${APPLICATION}/${APPLICATION} > /data/${APPLICATION}/log/run.log 2>&1 & - -# 保持脚本运行 -while true; do - sleep 1 -done \ No newline at end of file diff --git a/reserved/go.mod b/reserved/go.mod deleted file mode 100644 index d5e84dc..0000000 --- a/reserved/go.mod +++ /dev/null @@ -1,32 +0,0 @@ -module ip - -go 1.23.3 - -require ( - github.com/imroc/req/v3 v3.48.0 - github.com/oschwald/maxminddb-golang v1.13.1 - gopkg.in/yaml.v3 v3.0.1 -) - -require ( - github.com/andybalholm/brotli v1.1.1 // indirect - github.com/cloudflare/circl v1.5.0 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/klauspost/compress v1.17.11 // indirect - github.com/onsi/ginkgo/v2 v2.22.0 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.48.2 // indirect - github.com/refraction-networking/utls v1.6.7 // indirect - go.uber.org/mock v0.5.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.31.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.27.0 // indirect -) diff --git a/reserved/go.sum b/reserved/go.sum deleted file mode 100644 index 5a10466..0000000 --- a/reserved/go.sum +++ /dev/null @@ -1,67 +0,0 @@ -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= -github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg= -github.com/imroc/req/v3 v3.48.0/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= -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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= -github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= -github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= -github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/reserved/init.sh b/reserved/init.sh deleted file mode 100644 index 69b3d0b..0000000 --- a/reserved/init.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -if [ ! -f /data/caddy/config/Caddyfile ]; then - cp /data/caddy/Caddyfile /data/caddy/config/Caddyfile -fi - -/data/caddy/caddy run --config /data/caddy/config/Caddyfile > /data/ipinfo/log/caddy.log 2>&1 & - -/data/ipinfo/ip > /data/ipinfo/log/ip.log 2>&1 & - -while [[ true ]]; do - sleep 1 -done - diff --git a/reserved/logger/logger.go b/reserved/logger/logger.go deleted file mode 100644 index a628b42..0000000 --- a/reserved/logger/logger.go +++ /dev/null @@ -1,173 +0,0 @@ -package logger - -import ( - "archive/tar" - "compress/gzip" - "fmt" - "io" - "log" - "net" - "net/http" - "os" - "path/filepath" - "sync" - "time" -) - -var ( - logw = Logw - logFile *os.File - logger *log.Logger - logChannel = make(chan string, 100) - quitChannel = make(chan struct{}) - logFileMutex sync.Mutex // 保护 logFile 的互斥锁 -) - -// Init 初始化日志记录器,接受日志文件路径作为参数 -func Init(logFilePath string, maxLogsize int) error { - logFileMutex.Lock() - defer logFileMutex.Unlock() - - var err error - logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return err - } - logger = log.New(logFile, "", 0) - - go logWorker() - go monitorLogSize(logFilePath, maxLogsize) - return nil -} - -// logWorker 处理日志记录 -func logWorker() { - for { - select { - case msg := <-logChannel: - timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700") - logger.Println(timestamp + " - " + msg) - case <-quitChannel: - return - } - } -} - -// Log 直接记录日志的函数 -func Log(customMessage string) { - logChannel <- customMessage -} - -// Logw 用于格式化日志记录 -func Logw(format string, args ...interface{}) { - message := fmt.Sprintf(format, args...) - Log(message) -} - -// LogRequest 记录请求日志 -func LogHTTP(r *http.Request) { - ip, _, _ := net.SplitHostPort(r.RemoteAddr) - userAgent := r.UserAgent() - dateTime := time.Now().Format("02/Jan/2006:15:04:05 -0700") - - logEntry := fmt.Sprintf("%s - - [%s] \"%s %s %s\" 200 0 \"-\" \"%s\"", - ip, dateTime, r.Method, r.RequestURI, r.Proto, userAgent) - - Log(logEntry) -} - -// Close 关闭日志文件 -func Close() { - logFileMutex.Lock() - defer logFileMutex.Unlock() - - if logFile != nil { - quitChannel <- struct{}{} - if err := logFile.Close(); err != nil { - fmt.Printf("Error closing log file: %v", err) - } - } -} - -func monitorLogSize(logFilePath string, maxLogsize int) { - var maxLogsizeBytes int64 = int64(maxLogsize) * 1024 * 1024 // 最大日志文件大小,单位为MB - for { - time.Sleep(600 * time.Second) // 每10分钟检查一次 - logFileMutex.Lock() - info, err := logFile.Stat() - logFileMutex.Unlock() - - if err == nil && info.Size() > maxLogsizeBytes { - if err := rotateLogFile(logFilePath); err != nil { - logw("Log Rotation Failed: %s", err) - } - } - } -} - -func rotateLogFile(logFilePath string) error { - logFileMutex.Lock() - defer logFileMutex.Unlock() - - if logFile != nil { - if err := logFile.Close(); err != nil { - logw("Error closing log file for rotation: %v", err) - } - } - - // 打开当前日志文件 - logFile, err := os.Open(logFilePath) - if err != nil { - return fmt.Errorf("failed to open log file: %s, error: %w", logFilePath, err) - } - defer logFile.Close() - - newLogFilePath := logFilePath + "-" + time.Now().Format("20060102-150405") + ".tar.gz" - outFile, err := os.Create(newLogFilePath) - if err != nil { - return fmt.Errorf("failed to create gz file: %s, error: %w", newLogFilePath, err) - } - defer outFile.Close() - - gzWriter, err := gzip.NewWriterLevel(outFile, gzip.BestCompression) - if err != nil { - return fmt.Errorf("failed to create gz writer: %w", err) - } - defer gzWriter.Close() - - tarWriter := tar.NewWriter(gzWriter) - defer tarWriter.Close() - - logFileStat, err := logFile.Stat() - if err != nil { - return fmt.Errorf("failed to stat log file: %s, error: %w", logFilePath, err) - } - - logFileHeader := &tar.Header{ - Name: filepath.Base(logFilePath), - Size: logFileStat.Size(), - Mode: 0644, - ModTime: logFileStat.ModTime(), - } - - if err := tarWriter.WriteHeader(logFileHeader); err != nil { - return fmt.Errorf("failed to write log file header: %s, error: %w", logFilePath, err) - } - - if _, err := io.Copy(tarWriter, logFile); err != nil { - return fmt.Errorf("failed to copy log file: %s, error: %w", logFilePath, err) - } - - if err := os.Truncate(logFilePath, 0); err != nil { - return fmt.Errorf("failed to truncate log file: %s, error: %w", logFilePath, err) - } - - // 重新打开日志文件 - logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return fmt.Errorf("failed to reopen log file: %s, error: %w", logFilePath, err) - } - logger.SetOutput(logFile) - - return nil -} diff --git a/reserved/lookup/lookup.go b/reserved/lookup/lookup.go deleted file mode 100644 index a175da2..0000000 --- a/reserved/lookup/lookup.go +++ /dev/null @@ -1,140 +0,0 @@ -package lookup - -import ( - "encoding/json" - "fmt" - "html" - "log" - "net" - "net/http" - - "github.com/oschwald/maxminddb-golang" -) - -var ( - asnDB *maxminddb.Reader - countryDB *maxminddb.Reader - ASNDB_Path = "/data/ip/db/asn.mmdb" - CountryDB_Path = "/data/ip/db/country.mmdb" -) - -// ASNRecord 保存ASN数据库的查询结果 -type ASNRecord struct { - ASN string `maxminddb:"asn"` - Domain string `maxminddb:"domain"` - Name string `maxminddb:"name"` -} - -// CountryRecord 保存国家数据库的查询结果 -type CountryRecord struct { - Continent string `maxminddb:"continent"` - ContinentName string `maxminddb:"continent_name"` - Country string `maxminddb:"country"` - CountryName string `maxminddb:"country_name"` -} - -func openDB(db **maxminddb.Reader, path string) { - var err error - *db, err = maxminddb.Open(path) - if err != nil { - log.Fatalf("Error opening database at %s: %v", path, err) - } -} - -// Init 初始化日志文件和数据库 -func Init() { - openDB(&asnDB, ASNDB_Path) - openDB(&countryDB, CountryDB_Path) -} - -// GetIPHandler 获取IP地址的处理函数 -func GetIPHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - fwdIP := getForwardedIP(r) - - if fwdIP == "" || net.ParseIP(fwdIP) == nil { - http.Error(w, "Invalid IP address", http.StatusBadRequest) - log.Printf("Invalid IP address: %s", fwdIP) - return - } - - fmt.Fprintf(w, html.EscapeString(fwdIP)) -} - -// getForwardedIP 从请求中获取转发的IP地址 -func getForwardedIP(r *http.Request) string { - if fwdIP := r.Header.Get("X-Forwarded-For"); fwdIP != "" { - return fwdIP - } - return r.Header.Get("X-Real-IP") -} - -// getIPFromRequest 从请求中获取IP地址 -func getIPFromRequest(r *http.Request) string { - ipStr := r.URL.Query().Get("ip") - if ipStr == "" { - ipStr = getForwardedIP(r) - } - if ipStr == "" { - ipStr, _, _ = net.SplitHostPort(r.RemoteAddr) - } - return ipStr -} - -// IPLookupHandler IP查询的处理函数 -func IPLookupHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - userAgent := r.Header.Get("User-Agent") - ipStr := getIPFromRequest(r) - - ip := net.ParseIP(ipStr) - if ip == nil { - http.Error(w, "Invalid IP address", http.StatusBadRequest) - return - } - - responseData, err := getIPInfo(ip, userAgent) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(responseData); err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - } -} - -func getIPInfo(ip net.IP, userAgent string) (interface{}, error) { - var asn ASNRecord - if err := asnDB.Lookup(ip, &asn); err != nil { - return nil, fmt.Errorf("ASN Lookup failed: %v", err) - } - - var country CountryRecord - if err := countryDB.Lookup(ip, &country); err != nil { - return nil, fmt.Errorf("Country Lookup failed: %v", err) - } - - return struct { - IP string `json:"ip"` - ASN string `json:"asn"` - Domain string `json:"domain"` - ISP string `json:"isp"` - ContinentCode string `json:"continent_code"` - ContinentName string `json:"continent_name"` - CountryCode string `json:"country_code"` - CountryName string `json:"country_name"` - UserAgent string `json:"user_agent"` - }{ - IP: ip.String(), - ASN: asn.ASN, - Domain: asn.Domain, - ISP: asn.Name, - ContinentCode: country.Continent, - ContinentName: country.ContinentName, - CountryCode: country.Country, - CountryName: country.CountryName, - UserAgent: userAgent, - }, nil -} diff --git a/reserved/main.go b/reserved/main.go deleted file mode 100644 index 7811d7f..0000000 --- a/reserved/main.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "ip/logger" - "ip/lookup" - "ip/proxy" -) - -var logw = logger.Logw -var LogFilePath = "/data/ip/log/access.log" -var MaxLogSize = 1024 * 1024 * 5 // 10MB - -func setupLogger() { - // 初始化日志模块 - var err error - err = logger.Init(LogFilePath, MaxLogSize) // 传递日志文件路径 - if err != nil { - log.Fatalf("Failed to initialize logger: %v", err) - } - logw("Logger initialized") -} - -func init() { - // 初始化日志记录器,传入日志文件路径 - setupLogger() - - // 初始化数据库 - lookup.Init() - logw("Database initialized") -} - -func main() { - defer logger.Close() // 确保在程序结束时关闭日志文件 - // 设置HTTP路由处理器并启动服务器 - http.HandleFunc("/", LogRequestWrapper(func(w http.ResponseWriter, r *http.Request) { - // 其他处理逻辑... - })) - http.HandleFunc("/ip-lookup", LogRequestWrapper(lookup.IPLookupHandler)) - http.HandleFunc("/ip", LogRequestWrapper(lookup.GetIPHandler)) - http.HandleFunc("/bilibili", LogRequestWrapper(proxy.BilibiliHandlerWithChromeTLS)) - - log.Println("Server running on :8080") - if err := http.ListenAndServe(":8080", nil); err != nil { - log.Fatalf("Error starting server: %v", err) - } -} - -// LogRequestWrapper 包装日志记录功能 -func LogRequestWrapper(handler func(http.ResponseWriter, *http.Request)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - logger.LogHTTP(r) - handler(w, r) - } -} diff --git a/reserved/proxy/proxy.go b/reserved/proxy/proxy.go deleted file mode 100644 index 32aa84b..0000000 --- a/reserved/proxy/proxy.go +++ /dev/null @@ -1,110 +0,0 @@ -package proxy - -import ( - "io" - "log" - "net" - "net/http" - "net/url" - - "github.com/imroc/req/v3" -) - -// BilibiliHandler 处理 /bilibili 路由的请求 -/*func BilibiliHandler(w http.ResponseWriter, r *http.Request) { - // 设置CORS头部允许跨站请求 - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36") - - // 从请求中获取ip参数 - ip := r.URL.Query().Get("ip") - if ip == "" { - http.Error(w, "Missing ip parameter", http.StatusBadRequest) - return - } - - // 定义源API的URL并添加ip参数 - apiURL := "https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr?ip=" + url.QueryEscape(ip) - - // 向源API发送请求 - resp, err := http.Get(apiURL) - if err != nil { - http.Error(w, "Failed to get response from source API", http.StatusInternalServerError) - return - } - defer resp.Body.Close() - - // 检查源API的响应状态码 - if resp.StatusCode != http.StatusOK { - http.Error(w, "Source API returned non-OK status", http.StatusInternalServerError) - return - } - - // 读取源API返回的内容 - body, err := io.ReadAll(resp.Body) - if err != nil { - http.Error(w, "Failed to read response body", http.StatusInternalServerError) - return - } - - // 设置响应头Content-Type为application/json - w.Header().Set("Content-Type", "application/json") - // 将源API的响应内容写入响应体 - w.Write(body) -}*/ - -// 使用req库处理 /bilibili 路由的请求并使用chrome的TLS指纹 -func BilibiliHandlerWithChromeTLS(w http.ResponseWriter, r *http.Request) { - // 设置CORS头部允许跨站请求 - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36") - - // 从请求中获取ip参数 - ip := r.URL.Query().Get("ip") - if ip == "" { - http.Error(w, "Missing ip parameter", http.StatusBadRequest) - return - } - - // 验证IP是否正确 - if net.ParseIP(ip) == nil { - http.Error(w, "Invalid IP address", http.StatusBadRequest) - log.Printf("Invalid IP address: %s", ip) - return - } - - // 定义源API的URL并添加ip参数 - apiURL := "https://api.live.bilibili.com/ip_service/v1/ip_service/get_ip_addr?ip=" + url.QueryEscape(ip) - - // 使用req库发送请求并使用chrome的TLS指纹 - client := req.C(). - SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"). - SetTLSFingerprintChrome() - - client.ImpersonateChrome() - //client.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36") - resp, err := client.R().Get(apiURL) - if err != nil { - http.Error(w, "Failed to get response from source API", http.StatusInternalServerError) - return - } - defer resp.Body.Close() - - // 检查源API的响应状态码 - if resp.StatusCode != http.StatusOK { - http.Error(w, "Source API returned non-OK status", http.StatusInternalServerError) - return - } - - // 读取源API返回的内容 - body, err := io.ReadAll(resp.Body) - if err != nil { - http.Error(w, "Failed to read response body", http.StatusInternalServerError) - return - } - - // 设置响应头Content-Type为application/json - w.Header().Set("Content-Type", "application/json") - // 将源API的响应内容写入响应体 - w.Write(body) -} diff --git a/reserved/reserved/config/config.go b/reserved/reserved/config/config.go deleted file mode 100644 index c614360..0000000 --- a/reserved/reserved/config/config.go +++ /dev/null @@ -1,25 +0,0 @@ -package config - -import ( - "os" - - "gopkg.in/yaml.v3" -) - -type Config struct { - Port int `yaml:"port"` -} - -// LoadConfig 从 YAML 配置文件加载配置 -func LoadConfig(filePath string) (*Config, error) { - var config Config - data, err := os.ReadFile(filePath) - if err != nil { - return nil, err - } - err = yaml.Unmarshal(data, &config) - if err != nil { - return nil, err - } - return &config, nil -} diff --git a/reserved/reserved/main/ip_1.0.0.go b/reserved/reserved/main/ip_1.0.0.go deleted file mode 100644 index e2c90b0..0000000 --- a/reserved/reserved/main/ip_1.0.0.go +++ /dev/null @@ -1,168 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net" - "net/http" - "os" - "time" - - "github.com/oschwald/maxminddb-golang" -) - -var ( - asnDB *maxminddb.Reader - countryDB *maxminddb.Reader -) - -// ASNRecord 保存ASN数据库的查询结果 -type ASNRecord struct { - ASN string `maxminddb:"asn"` - Domain string `maxminddb:"domain"` - Name string `maxminddb:"name"` -} - -// CountryRecord 保存国家数据库的查询结果 -type CountryRecord struct { - Continent string `maxminddb:"continent"` - Continent_name string `maxminddb:"continent_name"` - Country string `maxminddb:"country"` - Country_name string `maxminddb:"country_name"` -} - -func main() { - var err error - // 打开ASN数据库 - asnDB, err = maxminddb.Open("/data/ipinfo/db/asn.mmdb") - if err != nil { - log.Fatal("Error opening ASN database:", err) - } - defer asnDB.Close() - - // 打开国家数据库 - countryDB, err = maxminddb.Open("/data/ipinfo/db/country.mmdb") - if err != nil { - log.Fatal("Error opening country database:", err) - } - defer countryDB.Close() - - // 打开或创建日志文件 - logFile, err := os.OpenFile("/data/ipinfo/log/access.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - log.Fatalf("Error opening file: %v", err) - } - defer logFile.Close() - // 创建一个日志记录器 - logger := log.New(logFile, "", 0) - // 设置HTTP路由处理器并启动服务器 - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - logRequest(logger, r) - // 其他处理逻辑... - }) - http.HandleFunc("/ip-lookup", ipLookupHandler) - http.HandleFunc("/ip", getIPHandler) - fmt.Println("Server running on http://localhost:8080") - log.Fatal(http.ListenAndServe(":8080", nil)) -} -func logRequest(logger *log.Logger, r *http.Request) { - // 假设 response size 和 status code 固定为 示例值 - // 在实际应用中,您可能需要动态获取这些值 - responseSize := 0 - statusCode := 0 - // 获取请求的IP地址 - ip, _, _ := net.SplitHostPort(r.RemoteAddr) - // 获取用户代理 - userAgent := r.UserAgent() - // 获取日期信息 - dateTime := time.Now().Format("02/Jan/2006:15:04:05 -0700") - // 格式化日志信息 - logEntry := fmt.Sprintf("%s - - [%s] \"%s %s %s\" %d %d \"-\" \"%s\"", - ip, dateTime, r.Method, r.RequestURI, r.Proto, statusCode, responseSize, userAgent) - // 将日志写入文件 - logger.Println(logEntry) -} -func getIPHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - // 尝试从X-Forwarded-For头部取得IP - fwdIP := r.Header.Get("X-Forwarded-For") - if fwdIP == "" { - fwdIP = r.Header.Get("X-Real-IP") - } - // 如果两个头部都没有,则从连接中获取IP - if fwdIP == "" { - ip, _, _ := net.SplitHostPort(r.RemoteAddr) - fwdIP = ip - } - - // 直接返回IP地址,不使用JSON格式化 - fmt.Fprintf(w, fwdIP) -} -func ipLookupHandler(w http.ResponseWriter, r *http.Request) { - //允许跨站请求 - w.Header().Set("Access-Control-Allow-Origin", "*") - - // 从请求中获取User-Agent头部,即浏览器信息 - userAgent := r.Header.Get("User-Agent") - - // 尝试从查询参数获取IP - ipStr := r.URL.Query().Get("ip") - if ipStr == "" { - // 尝试从X-Forwarded-For头部取得IP - fwdIP := r.Header.Get("X-Forwarded-For") - if fwdIP == "" { - fwdIP = r.Header.Get("X-Real-IP") - } - // 如果两个头部都没有,则从连接中获取IP - if fwdIP == "" { - ip, _, _ := net.SplitHostPort(r.RemoteAddr) - fwdIP = ip - } - ipStr = fwdIP - } - ip := net.ParseIP(ipStr) - if ip == nil { - http.Error(w, "Invalid IP address", http.StatusBadRequest) - return - } - - // 查询ASN记录 - var asn ASNRecord - err := asnDB.Lookup(ip, &asn) - if err != nil { - http.Error(w, fmt.Sprintf("ASN Lookup failed: %v", err), http.StatusInternalServerError) - return - } - // 查询国家记录 - var country CountryRecord - err = countryDB.Lookup(ip, &country) - if err != nil { - http.Error(w, fmt.Sprintf("Country Lookup failed: %v", err), http.StatusInternalServerError) - return - } - // 整理响应数据 - responseData := struct { - IP string `json:"ip"` - ASN string `json:"asn"` - Domain string `json:"domain"` - ISP string `json:"isp"` - ContinentCode string `json:"continent_code"` - ContinentName string `json:"continent_name"` - CountryCode string `json:"country_code"` - CountryName string `json:"country_name"` - UserAgent string `json:"user_agent"` - }{ - IP: ipStr, - ASN: asn.ASN, - Domain: asn.Domain, - ISP: asn.Name, - ContinentCode: country.Continent, - ContinentName: country.Continent_name, - CountryCode: country.Country, - CountryName: country.Country_name, - UserAgent: userAgent, - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(responseData) -} diff --git a/reserved/reserved/mmdb/mmdb_1.0.0.go b/reserved/reserved/mmdb/mmdb_1.0.0.go deleted file mode 100644 index 1f8a330..0000000 --- a/reserved/reserved/mmdb/mmdb_1.0.0.go +++ /dev/null @@ -1,36 +0,0 @@ -package database - -import ( - "log" - - "github.com/oschwald/maxminddb-golang" -) - -type ASNRecord struct { - ASN string `maxminddb:"asn"` - Domain string `maxminddb:"domain"` - Name string `maxminddb:"name"` -} - -type CountryRecord struct { - Continent string `maxminddb:"continent"` - Continent_name string `maxminddb:"continent_name"` - Country string `maxminddb:"country"` - Country_name string `maxminddb:"country_name"` -} - -func OpenASNDB(path string) (*maxminddb.Reader, error) { - db, err := maxminddb.Open(path) - if err != nil { - log.Fatal("Error opening ASN database:", err) - } - return db, err -} - -func OpenCountryDB(path string) (*maxminddb.Reader, error) { - db, err := maxminddb.Open(path) - if err != nil { - log.Fatal("Error opening country database:", err) - } - return db, err -}