commit 59f1de2cd31e3caec6255b1b197368c6989b7295 Author: timerzz Date: Wed Apr 10 17:36:56 2024 +0800 feat 实现基本功能 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fdf2602 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ + +FROM golang:alpine as back +ARG GOPROXY=https://goproxy.cn,direct +WORKDIR /build +COPY . /build +RUN CGO_ENABLED=0 go build -trimpath -ldflags '-w -s' -o watcher ./main + +FROM oven/bun:1 as front +COPY . /build +WORKDIR /build/wwwroot +RUN bun install && bun run build + +FROM alpine:latest +ARG HTTP_PROXY=http://192.168.31.55:10809 +ARG HTTPS_PROXY=http://192.168.31.55:10809 +RUN apk add --no-cache ca-certificates tzdata +WORKDIR /work +COPY --from=back /build/watcher /work/ +COPY --from=front /build/wwwroot/dist /work/ +ENTRYPOINT ["/work/watcher"] + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2d58ed1 --- /dev/null +++ b/go.mod @@ -0,0 +1,151 @@ +module haitao_watcher + +go 1.22.2 + +require ( + github.com/bytedance/sonic v1.9.1 + github.com/glebarez/sqlite v1.11.0 + github.com/go-resty/resty/v2 v2.12.0 + github.com/gofiber/fiber/v3 v3.0.0-beta.2 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/metacubex/mihomo v1.18.3 + github.com/pkg/errors v0.9.1 + github.com/timerzz/proxypool v0.0.0-20240407102509-f13ba2d1b0ca + gopkg.in/yaml.v3 v3.0.1 + gorm.io/gorm v1.25.9 +) + +require ( + github.com/3andne/restls-client-go v0.1.6 // indirect + github.com/PuerkitoBio/goquery v1.8.1 // indirect + github.com/RyuaNerin/go-krypto v1.2.4 // indirect + github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/antchfx/htmlquery v1.3.1 // indirect + github.com/antchfx/xmlquery v1.3.15 // indirect + github.com/antchfx/xpath v1.3.0 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/cloudflare/circl v1.3.6 // indirect + github.com/coreos/go-iptables v0.7.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect + github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect + github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect + github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect + github.com/gaukas/godicttls v0.0.4 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.3.2 // indirect + github.com/gocolly/colly/v2 v2.1.0 // indirect + github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect + github.com/gofrs/uuid/v5 v5.0.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hpcloud/tail v1.0.0 // indirect + github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/native v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kennygrant/sanitize v1.2.4 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/socket v0.4.1 // indirect + github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect + github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect + github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c // indirect + github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 // indirect + github.com/metacubex/sing-shadowsocks v0.2.6 // indirect + github.com/metacubex/sing-shadowsocks2 v0.2.0 // indirect + github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f // indirect + github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 // indirect + github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/miekg/dns v1.1.58 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mroth/weightedrand/v2 v2.1.0 // indirect + github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect + github.com/onsi/ginkgo v1.10.1 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/openacid/low v0.1.21 // indirect + github.com/oschwald/maxminddb-golang v1.12.0 // indirect + github.com/pierrec/lz4/v4 v4.1.14 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/puzpuzpuz/xsync/v3 v3.1.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/robertkrimen/otto v0.2.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect + github.com/sagernet/sing v0.3.6 // indirect + github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 // indirect + github.com/sagernet/sing-shadowtls v0.1.4 // indirect + github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect + github.com/sagernet/utls v1.5.4 // indirect + github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e // indirect + github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect + github.com/samber/lo v1.39.0 // indirect + github.com/shirou/gopsutil/v3 v3.24.2 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect + github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect + github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/temoto/robotstxt v1.1.2 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.52.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zhangyunhao116/fastrand v0.3.0 // indirect + gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect + go.uber.org/mock v0.4.0 // indirect + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.18.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/fsnotify.v1 v1.4.7 // indirect + gopkg.in/sourcemap.v1 v1.0.5 // indirect + lukechampine.com/blake3 v1.2.1 // indirect + modernc.org/libc v1.22.5 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/sqlite v1.23.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5b2308c --- /dev/null +++ b/go.sum @@ -0,0 +1,504 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08= +github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +github.com/RyuaNerin/elliptic2 v1.0.0/go.mod h1:wWB8fWrJI/6EPJkyV/r1Rj0hxUgrusmqSj8JN6yNf/A= +github.com/RyuaNerin/go-krypto v1.2.4 h1:mXuNdK6M317aPV0llW6Xpjbo4moOlPF7Yxz4tb4b4Go= +github.com/RyuaNerin/go-krypto v1.2.4/go.mod h1:QqCYkoutU3yInyD9INt2PGolVRsc3W4oraQadVGXJ/8= +github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= +github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= +github.com/antchfx/htmlquery v1.3.1 h1:wm0LxjLMsZhRHfQKKZscDf2COyH4vDYA3wyH+qZ+Ylc= +github.com/antchfx/htmlquery v1.3.1/go.mod h1:PTj+f1V2zksPlwNt7uVvZPsxpKNa7mlVliCRxLX6Nx8= +github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM= +github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8Tw= +github.com/antchfx/xmlquery v1.3.15/go.mod h1:zMDv5tIGjOxY/JCNNinnle7V/EwthZ5IT8eeCGJKRWA= +github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= +github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= +github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/antchfx/xpath v1.3.0 h1:nTMlzGAK3IJ0bPpME2urTuFL76o4A96iYvoKFHRXJgc= +github.com/antchfx/xpath v1.3.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= +github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= +github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= +github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= +github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= +github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po= +github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg= +github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c/go.mod h1:ETASDWf/FmEb6Ysrtd1QhjNedUU/ZQxBCRLh60bQ/UI= +github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY= +github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= +github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA= +github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok= +github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= +github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= +github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= +github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= +github.com/gocolly/colly/v2 v2.1.0 h1:k0DuZkDoCsx51bKpRJNEmcxcp+W5N8ziuwGaSDuFoGs= +github.com/gocolly/colly/v2 v2.1.0/go.mod h1:I2MuhsLjQ+Ex+IzK3afNS8/1qP3AedHOusRPcRdC5o0= +github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao= +github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= +github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= +github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= +github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= +github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= +github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 h1:V3plQrMHRWOB5zMm3yNqvBxDQVW1+/wHBSok5uPdmVs= +github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw= +github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +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/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= +github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= +github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= +github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= +github.com/metacubex/mihomo v1.18.3 h1:pIozbxnfHHYzSsF6m9uafcGIscMyHsRj/D3WuTKi2TM= +github.com/metacubex/mihomo v1.18.3/go.mod h1:9CPB84Z1tk2c3W1be7G9tm4kAqCdAkY0SRZkwB2cqaA= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= +github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= +github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= +github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= +github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= +github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= +github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= +github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= +github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +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/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= +github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= +github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= +github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0= +github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo= +github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0= +github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I= +github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do= +github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= +github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= +github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= +github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4= +github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= +github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= +github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= +github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.3.6 h1:dsEdYLKBQlrxUfw1a92x0VdPvR1/BOxQ+HIMyaoEJsQ= +github.com/sagernet/sing v0.3.6/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 h1:5bCAkvDDzSMITiHFjolBwpdqYsvycdTu71FsMEFXQ14= +github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= +github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= +github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= +github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= +github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= +github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= +github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= +github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= +github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= +github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= +github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= +github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM= +github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk= +github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU= +github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo= +github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= +github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg= +github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= +github.com/timerzz/proxypool v0.0.0-20240407102509-f13ba2d1b0ca h1:EbyIUW/bB7vZtwgrno1V5nyx38mCE+4DrWurtVMs0fQ= +github.com/timerzz/proxypool v0.0.0-20240407102509-f13ba2d1b0ca/go.mod h1:KVeB0Qlvv/sdR7dbB7ys1EqK4qGKpSnSiIhPhkHukwQ= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +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/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= +github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= +github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= +gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= +gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= +gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= +modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= +modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main/main.go b/main/main.go new file mode 100644 index 0000000..5c021a9 --- /dev/null +++ b/main/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "context" + "github.com/gofiber/fiber/v3" + "github.com/golang/glog" + "haitao_watcher/pkg/database" + "haitao_watcher/pkg/model" + "haitao_watcher/pkg/options" + "haitao_watcher/pkg/pools" + "haitao_watcher/pkg/pusher" + "haitao_watcher/pkg/watcher" + "haitao_watcher/server" + "log/slog" + "os" + "os/signal" +) + +func main() { + slog.Info(">>> BEGIN INIT<<<") + ctx := context.Background() + _ctx, cancel := signal.NotifyContext(ctx, os.Kill, os.Interrupt) + defer cancel() + cfg, _ := options.LoadConfig() + + db, err := database.InitDatabase(&cfg.DB) + if err != nil { + glog.Fatalf("初始化数据库失败:%v", err) + } + + pool := pools.NewProxyPool(cfg.Proxy.Subscribes) + pusherCtl := pusher.NewController(_ctx, db) + ch := make(chan model.PushMsg, 30) + pusherCtl.Consume(ch) + + watcherCtl := watcher.NewController(_ctx, db, pool, ch) + + r := fiber.New() + + r.Static("/", "/work/dist") + + api := r.Group("/api/v1") + + server.NewWatcherController(watcherCtl).RegistryRouter(api) + server.NewPusherSvcController(pusherCtl).RegistryRouter(api) + + if err = r.Listen(":2280"); err != nil { + glog.Warningf("server over: %v", err) + } +} diff --git a/pkg/database/init.go b/pkg/database/init.go new file mode 100644 index 0000000..7f55760 --- /dev/null +++ b/pkg/database/init.go @@ -0,0 +1,17 @@ +package database + +import ( + "fmt" + "github.com/glebarez/sqlite" + "gorm.io/gorm" + "haitao_watcher/pkg/options" +) + +func InitDatabase(opt *options.DBOption) (*gorm.DB, error) { + if opt.Timeout == 0 { + opt.Timeout = 3000 + } + dialector := sqlite.Open(fmt.Sprintf("%s?_pragma=busy_timeout(%d)", opt.Path, opt.Timeout)) + + return gorm.Open(dialector, &gorm.Config{}) +} diff --git a/pkg/model/msg.go b/pkg/model/msg.go new file mode 100644 index 0000000..917a639 --- /dev/null +++ b/pkg/model/msg.go @@ -0,0 +1,7 @@ +package model + +type PushMsg struct { + Title string + Content string + ToPusher []uint +} diff --git a/pkg/model/product.go b/pkg/model/product.go new file mode 100644 index 0000000..bb9c326 --- /dev/null +++ b/pkg/model/product.go @@ -0,0 +1,45 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +type Product struct { + ID uint `gorm:"primaryKey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index"` + UpdateErr bool `json:"updateErr"` //更新出错了 + + Uid string `json:"uid" gorm:"index:,unique"` + Pid string `json:"pid" gorm:"index"` //产品编号 + Name string `json:"name"` + Brand string `json:"brand"` + Website WebsiteType `json:"website"` //是什么网站 + Watch bool `json:"watch"` + Orderable bool `json:"orderable"` + Link string `json:"link"` + Remark string `json:"remark,omitempty"` + AllocationResetDate time.Time `json:"allocationResetDate"` //上次补货时间 + + PusherIds []uint `json:"pusherIds" gorm:"serializer:json"` +} + +type WebsiteType uint + +func (t WebsiteType) IsZero() bool { + return t == WebsiteType(0) +} + +const ( + CoachOutlet WebsiteType = iota + 1 +) + +func (p *Product) TableName() string { + return "product_info" +} + +type Producter interface { + Product() *Product +} diff --git a/pkg/model/pusher.go b/pkg/model/pusher.go new file mode 100644 index 0000000..439ffbf --- /dev/null +++ b/pkg/model/pusher.go @@ -0,0 +1,28 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +type PusherType int + +const ( + PusherType_AnPusher = iota + 1 +) + +type Pusher[T any] struct { + ID uint `gorm:"primaryKey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index"` + + Type PusherType `json:"type"` + Name string `json:"name"` + Remark string `json:"remark"` + Option T `json:"option" gorm:"serializer:json"` +} + +func (p *Pusher[T]) TableName() string { + return "pusher" +} diff --git a/pkg/model/response.go b/pkg/model/response.go new file mode 100644 index 0000000..d7e223b --- /dev/null +++ b/pkg/model/response.go @@ -0,0 +1,6 @@ +package model + +type ListResponse[E any] struct { + Total int64 `json:"total"` + List []E `json:"list"` +} diff --git a/pkg/options/coachoutlet.go b/pkg/options/coachoutlet.go new file mode 100644 index 0000000..de9d35c --- /dev/null +++ b/pkg/options/coachoutlet.go @@ -0,0 +1,18 @@ +package options + +import ( + "fmt" + "time" +) + +type CoachOutletOption struct { + RawUrl string `json:"raw_url"` + Pid string `json:"pid"` //商品的id + Remark string `json:"remark"` + PusherIds []uint `json:"pusherIds"` + Interval time.Duration `json:"interval"` +} + +func (o *CoachOutletOption) Uid() string { + return fmt.Sprintf("coachOutlet_%s", o.Pid) +} diff --git a/pkg/options/config_test.go b/pkg/options/config_test.go new file mode 100644 index 0000000..6985b66 --- /dev/null +++ b/pkg/options/config_test.go @@ -0,0 +1,15 @@ +package options + +import ( + "gopkg.in/yaml.v3" + "os" + "testing" +) + +func TestGenConfig(t *testing.T) { + var opt = Config{ + DB: DBOption{}, + Proxy: ProxyOption{Subscribes: []string{"123", "345"}}, + } + yaml.NewEncoder(os.Stdout).Encode(&opt) +} diff --git a/pkg/options/db.go b/pkg/options/db.go new file mode 100644 index 0000000..ee4e915 --- /dev/null +++ b/pkg/options/db.go @@ -0,0 +1,6 @@ +package options + +type DBOption struct { + Path string `json:"path"` + Timeout int `json:"timeout"` +} diff --git a/pkg/options/init.go b/pkg/options/init.go new file mode 100644 index 0000000..b716fd6 --- /dev/null +++ b/pkg/options/init.go @@ -0,0 +1,21 @@ +package options + +import ( + "gopkg.in/yaml.v3" + "os" +) + +type Config struct { + DB DBOption `yaml:"db"` + Proxy ProxyOption `yaml:"proxy"` +} + +func LoadConfig() (*Config, error) { + var opt Config + f, err := os.Open("/data/cfg.yaml") + if err != nil { + return nil, err + } + defer f.Close() + return &opt, yaml.NewDecoder(f).Decode(&opt) +} diff --git a/pkg/options/proxy.go b/pkg/options/proxy.go new file mode 100644 index 0000000..8b1af31 --- /dev/null +++ b/pkg/options/proxy.go @@ -0,0 +1,5 @@ +package options + +type ProxyOption struct { + Subscribes []string `yaml:"subscribes"` +} diff --git a/pkg/options/pusher.go b/pkg/options/pusher.go new file mode 100644 index 0000000..50293c4 --- /dev/null +++ b/pkg/options/pusher.go @@ -0,0 +1,6 @@ +package options + +type AnPushOption struct { + Token string `json:"token"` + Channel string `json:"channel"` +} diff --git a/pkg/pools/proxy.go b/pkg/pools/proxy.go new file mode 100644 index 0000000..57e52f5 --- /dev/null +++ b/pkg/pools/proxy.go @@ -0,0 +1,72 @@ +package pools + +import ( + "context" + "fmt" + "github.com/golang/glog" + "github.com/timerzz/proxypool/pkg/getter" + "github.com/timerzz/proxypool/pkg/proxy" + "github.com/timerzz/proxypool/pkg/tool" + "log/slog" + "math/rand" + "sync" + "time" +) + +type ProxyPool struct { + m sync.Mutex + proxys proxy.ProxyList + subscribes []string //订阅url +} + +func NewProxyPool(subscribes []string) *ProxyPool { + var p = &ProxyPool{} + p.subscribes = subscribes + p.Update() + return p +} + +// Update 更新代理池 +func (p *ProxyPool) Update() { + var list = make(proxy.ProxyList, 0, len(p.proxys)) + for _, url := range p.subscribes { + subscribeGetter, err := getter.NewSubscribe(tool.Options{"url": url}) + if err != nil { + slog.Warn(fmt.Sprintf("创建Subscribe Getter失败:%v", err)) + continue + } + list = list.UniqAppendProxyList(subscribeGetter.Get()) + } + glog.Infof("代理源共 %d 个: %v", len(p.subscribes), p.subscribes) + glog.Infof("获取代理共 %d 个", len(list)) + p.m.Lock() + p.proxys = list + p.m.Unlock() +} + +// CronUpdate 定时更新 +func (p *ProxyPool) CronUpdate(ctx context.Context, interval time.Duration) { + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + p.Update() + } + } +} + +// RandomIterator 获取随机代理的迭代器 +func (p *ProxyPool) RandomIterator() func() proxy.Proxy { + return func() (proxy proxy.Proxy) { + if len(p.proxys) == 0 { + return nil + } + p.m.Lock() + defer p.m.Unlock() + curIndex := rand.Intn(len(p.proxys)) + return p.proxys[curIndex] + } +} diff --git a/pkg/pools/resty.go b/pkg/pools/resty.go new file mode 100644 index 0000000..65f3d83 --- /dev/null +++ b/pkg/pools/resty.go @@ -0,0 +1,67 @@ +package pools + +import ( + "context" + "github.com/go-resty/resty/v2" + "net" + "net/http" + "sync" + "time" +) + +var rp *restyPool + +func init() { + rp = NewRestyPool() +} + +func GetRestyClient(conn net.Conn) (cli *resty.Client) { + return rp.Get(conn) +} + +func PutRestyClient(cli *resty.Client) { + rp.Put(cli) +} + +type restyPool struct { + resty sync.Pool + transport sync.Pool +} + +const ( + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0" +) + +func NewRestyPool() *restyPool { + return &restyPool{ + resty: sync.Pool{ + New: func() any { + return resty.New().SetHeader("User-Agent", UserAgent) + }, + }, + transport: sync.Pool{ + New: func() any { + return &http.Transport{ + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 5 * time.Second, + } + }, + }, + } +} + +func (r *restyPool) Get(conn net.Conn) (cli *resty.Client) { + cli = r.resty.Get().(*resty.Client) + transport := r.transport.Get().(*http.Transport) + transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + return conn, nil + } + return cli.SetTransport(transport) +} + +func (r *restyPool) Put(cli *resty.Client) { + transport, _ := cli.Transport() + r.transport.Put(transport) + r.resty.Put(cli) +} diff --git a/pkg/pusher/anPush.go b/pkg/pusher/anPush.go new file mode 100644 index 0000000..7112453 --- /dev/null +++ b/pkg/pusher/anPush.go @@ -0,0 +1,34 @@ +package pusher + +import ( + "fmt" + "github.com/go-resty/resty/v2" + "haitao_watcher/pkg/options" + "strings" +) + +type AnPush struct { + opt *options.AnPushOption + client *resty.Client +} + +func NewAnPush(opt *options.AnPushOption) *AnPush { + return &AnPush{ + opt: opt, + client: resty.New().SetHeader("Content-Type", "application/x-www-form-urlencoded"), + } +} + +type msgResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + Msgid uint64 `json:"msgid"` +} + +func (a *AnPush) Push(title, content string) error { + r := a.client.R().SetBody(strings.NewReader(fmt.Sprintf("title=%s&content=%s&channel=%s", title, content, a.opt.Channel))) + var result msgResponse + r.SetResult(&result) + _, err := r.Post(fmt.Sprintf("https://api.anpush.com/push/%s", a.opt.Token)) + return err +} diff --git a/pkg/pusher/anPush_test.go b/pkg/pusher/anPush_test.go new file mode 100644 index 0000000..82779dc --- /dev/null +++ b/pkg/pusher/anPush_test.go @@ -0,0 +1,18 @@ +package pusher + +import ( + "haitao_watcher/pkg/options" + "testing" +) + +func TestAnPush_Push(t *testing.T) { + var opt = &options.AnPushOption{ + Token: "LHS13BO3FGBLGBP9MFBM53R8WV32P1", + Channel: "06683", + } + pusher := NewAnPush(opt) + err := pusher.Push("测试", "这是一个测试") + if err != nil { + t.Fatal(err) + } +} diff --git a/pkg/pusher/controller.go b/pkg/pusher/controller.go new file mode 100644 index 0000000..e7c03c1 --- /dev/null +++ b/pkg/pusher/controller.go @@ -0,0 +1,124 @@ +package pusher + +import ( + "context" + "fmt" + "github.com/golang/glog" + "gorm.io/gorm" + "gorm.io/gorm/schema" + "haitao_watcher/pkg/model" + "haitao_watcher/pkg/options" +) + +type Pusher interface { + Push(title, content string) error +} + +type Controller struct { + m map[uint]Pusher + db *gorm.DB + ctx context.Context +} + +func NewController(ctx context.Context, db *gorm.DB) *Controller { + ctl := &Controller{ + m: make(map[uint]Pusher), + db: db, + ctx: ctx, + } + ctl.migrateTables() + go func() { + if err := ctl.initPushers(); err != nil { + glog.Errorf("pusher init :%v", err) + } + }() + return ctl +} + +func (c *Controller) Consume(ch <-chan model.PushMsg) { + go func() { + for { + select { + case <-c.ctx.Done(): + return + case msg := <-ch: + for _, pusherId := range msg.ToPusher { + if pusher, ok := c.m[pusherId]; ok { + if err := pusher.Push(msg.Title, msg.Content); err != nil { + glog.Errorf("pusher %d err: %v", pusherId, err) + } + } + } + } + } + }() +} + +func (c *Controller) migrateTables() { + tables := []schema.Tabler{ + &model.Pusher[options.AnPushOption]{}, + } + for _, table := range tables { + if err := c.db.AutoMigrate(table); err != nil { + glog.Fatalf("failed to migrate table %s: %v", table.TableName(), err) + } + } +} + +func (c *Controller) initPushers() error { + var list []model.Pusher[*options.AnPushOption] + if err := c.db.Find(&list).Error; err != nil { + return err + } + for _, p := range list { + c.m[p.ID] = NewAnPush(p.Option) + } + return nil +} + +func (c *Controller) AddPusher(opt *model.Pusher[options.AnPushOption]) error { + return c.db.Transaction(func(tx *gorm.DB) error { + if err := tx.Create(opt).Error; err != nil { + return err + } + fmt.Println("id", opt.ID) + return nil + }) +} + +type ListPusherInfoRequest struct { + Keyword string `query:"keyword,omitempty"` + Page int `query:"page"` + Size int `query:"size"` + All bool `query:"all"` +} + +func (c *Controller) List(req ListPusherInfoRequest) (resp model.ListResponse[model.Pusher[*options.AnPushOption]], err error) { + tx := c.db + if req.Keyword != "" { + tx = tx.Where("name LIKE ? or remark LIKE ?", fmt.Sprintf("%%%s%%", req.Keyword), fmt.Sprintf("%%%s%%", req.Keyword)) + } + + if err = tx.Model(&model.Pusher[*options.AnPushOption]{}).Find(&resp.List).Error; err != nil { + return resp, fmt.Errorf("查询总数失败:%v", err) + } + resp.Total = int64(len(resp.List)) + if req.All || resp.Total == 0 { + return + } + + // 查询列表 + if req.Page < 1 { + req.Page = 1 + } + if req.Size < 1 { + req.Size = 10 + } + offset := (req.Page - 1) * req.Size + + if err = tx.Order("created_at desc").Limit(req.Size).Offset(offset). + Find(&resp.List).Error; err != nil { + return resp, fmt.Errorf("查询列表失败:%v", err) + } + return +} diff --git a/pkg/utils/proxy.go b/pkg/utils/proxy.go new file mode 100644 index 0000000..3d1e17b --- /dev/null +++ b/pkg/utils/proxy.go @@ -0,0 +1,57 @@ +package utils + +import ( + "context" + "fmt" + "github.com/bytedance/sonic" + "github.com/metacubex/mihomo/adapter" + "github.com/metacubex/mihomo/constant" + "github.com/timerzz/proxypool/pkg/proxy" + "net" + "net/url" + "strconv" +) + +func ConnFromProxy(ctx context.Context, p proxy.Proxy, addr constant.Metadata) (conn net.Conn, err error) { + pmap := make(map[string]interface{}) + err = sonic.UnmarshalString(p.String(), &pmap) + if err != nil { + return + } + pmap["port"] = int(pmap["port"].(float64)) + if p.TypeName() == "vmess" { + pmap["alterId"] = int(pmap["alterId"].(float64)) + } + clashProxy, err := adapter.ParseProxy(pmap) + if err != nil { + return nil, err + } + return clashProxy.DialContext(ctx, &addr) // 建立到proxy server的connection,对Proxy的类别做了自适应相当于泛型 +} + +// UrlToMetadata DO NOT EDIT. Copied from clash because it's an unexported function +func UrlToMetadata(rawURL string) (addr constant.Metadata, err error) { + u, err := url.Parse(rawURL) + if err != nil { + return + } + + port := u.Port() + if port == "" { + switch u.Scheme { + case "https": + port = "443" + case "http": + port = "80" + default: + err = fmt.Errorf("%s scheme not Support", rawURL) + return + } + } + pi, _ := strconv.Atoi(port) + addr = constant.Metadata{ + Host: u.Hostname(), + DstPort: uint16(pi), + } + return +} diff --git a/pkg/watcher/base.go b/pkg/watcher/base.go new file mode 100644 index 0000000..d08eadf --- /dev/null +++ b/pkg/watcher/base.go @@ -0,0 +1,16 @@ +package watcher + +import ( + "haitao_watcher/pkg/model" + "haitao_watcher/pkg/options" +) + +type Watcher interface { + Watch() + Cancel() + SetOnOrderableMsgChannel(ch chan<- model.PushMsg) Watcher + Option() options.CoachOutletOption + SetOption(options.CoachOutletOption) + Uid() string + Restart() +} diff --git a/pkg/watcher/coach.go b/pkg/watcher/coach.go new file mode 100644 index 0000000..75e9ba2 --- /dev/null +++ b/pkg/watcher/coach.go @@ -0,0 +1,261 @@ +package watcher + +import ( + "context" + "fmt" + "github.com/go-resty/resty/v2" + "github.com/pkg/errors" + "github.com/timerzz/proxypool/pkg/proxy" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "haitao_watcher/pkg/model" + "haitao_watcher/pkg/options" + pool "haitao_watcher/pkg/pools" + "haitao_watcher/pkg/utils" + "log/slog" + "math/rand/v2" + "net/url" + "time" +) + +type CoachOutlet struct { + pool *pool.ProxyPool + cfg *options.CoachOutletOption + detail *model.Product + iter func() (proxy proxy.Proxy) + db *gorm.DB + fCtx context.Context + ctx context.Context + cancel context.CancelFunc + onOrderable chan<- model.PushMsg +} + +func NewCoachWatcher(ctx context.Context, p *pool.ProxyPool, cfg *options.CoachOutletOption, db *gorm.DB) *CoachOutlet { + return &CoachOutlet{ + fCtx: ctx, + pool: p, + cfg: cfg, + iter: p.RandomIterator(), + db: db, + } +} +func (c *CoachOutlet) Option() options.CoachOutletOption { + return *c.cfg +} + +func (c *CoachOutlet) SetOption(cfg options.CoachOutletOption) { + c.cfg = &cfg +} + +func (c *CoachOutlet) SetOnOrderableMsgChannel(ch chan<- model.PushMsg) Watcher { + c.onOrderable = ch + return c +} +func (c *CoachOutlet) Cancel() { + c.cancel() +} + +func (c *CoachOutlet) Uid() string { + return fmt.Sprintf("coachOutlet_%s", c.cfg.Pid) +} + +func (c *CoachOutlet) getDetail() { + if c.detail == nil { + subCtx, cancel := context.WithTimeout(c.ctx, time.Minute*5) + defer cancel() + if err := c.requestProductDetail(subCtx); err != nil { + slog.Error(fmt.Sprintf("获取coach %s 详情失败:%v", c.cfg.Pid, err)) + } + if c.detail != nil && c.detail.Orderable { + if c.onOrderable != nil { + c.onOrderable <- model.PushMsg{ + Title: "coachoutlet 商品补货", + Content: fmt.Sprintf("商品 %s 可以购买,链接: %s", c.detail.Name, c.detail.Link), + ToPusher: c.detail.PusherIds, + } + } + c.cancel() + } + } +} + +func (c *CoachOutlet) Restart() { + c.Cancel() + go c.Watch() +} +func (c *CoachOutlet) Watch() { + c.ctx, c.cancel = context.WithCancel(c.fCtx) + c.getDetail() + ticker := time.NewTicker(c.cfg.Interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + c.getDetail() + if c.detail == nil { + continue + } + // 加一些干扰 + salt := rand.IntN(70) + time.Sleep(time.Second * time.Duration(salt)) + // 请求 + subCtx, cancel := context.WithTimeout(c.ctx, time.Minute*5) + inventory, err := c.requestProductInventory(subCtx) + cancel() + if err != nil { + slog.Warn(fmt.Sprintf("获取coach %s 库存失败:%v", c.cfg.Pid, err)) + } + c.detail.UpdateErr = err != nil + //如果获取库存没出错,那么更新一下状态 + if err == nil { + c.detail.Orderable = inventory.Orderable + if !inventory.AllocationResetDate.IsZero() { + c.detail.AllocationResetDate = inventory.AllocationResetDate + } + if c.detail.Orderable { + c.detail.Watch = false + c.detail.AllocationResetDate = time.Now() + } + } + + if err = c.db.Save(c.detail).Error; err != nil { + slog.Error(fmt.Sprintf("更新数据库失败:%v", err)) + } + // 如果可以预定了,那么退出 + if c.detail.Orderable { + if c.onOrderable != nil { + c.onOrderable <- model.PushMsg{ + Title: "coachoutlet 商品补货", + Content: fmt.Sprintf("商品 %s 可以购买,链接: %s", c.detail.Name, c.detail.Link), + ToPusher: c.detail.PusherIds, + } + } + return + } + case <-c.ctx.Done(): + return + } + } +} + +type ProductData struct { + Id string `json:"id"` + Name string `json:"name"` + Brand string `json:"brand"` + Inventory Inventory `json:"inventory"` + Url string `json:"url"` + MasterId string `json:"masterId"` + Prices struct { + CurrentPrice float64 `json:"currentPrice"` + } `json:"prices"` + Remark string `json:"-"` +} + +func (p *ProductData) Product() *model.Product { + return &model.Product{ + Uid: fmt.Sprintf("coachOutlet_%s", p.Id), + Pid: p.Id, + Name: p.Name, + Brand: p.Brand, + Website: model.CoachOutlet, + Watch: true, + Orderable: p.Inventory.Orderable, + Link: p.Url, + Remark: p.Remark, + } +} + +type Inventory struct { + Id string `json:"id"` + Ats int `json:"ats"` + PreOrderable bool `json:"preorderable"` + BackOrderable bool `json:"backorderable"` + Orderable bool `json:"orderable"` + AllocationResetDate time.Time `json:"allocationResetDate,omitempty"` + Perpetual bool `json:"perpetual"` + StockLevel int `json:"stockLevel"` +} + +type ProductDataResponse struct { + ProductData []*ProductData `json:"productsData"` +} + +// 获取 +func (c *CoachOutlet) requestProductDetail(ctx context.Context) error { + urlPath := fmt.Sprintf("https://www.coachoutlet.com/api/get-products?ids=%s&includeInventory=false", url.QueryEscape(c.cfg.Pid)) + var resp ProductDataResponse + err := tryRequest(ctx, urlPath, &resp, c.iter) + if err != nil { + return err + } + if len(resp.ProductData) == 0 { + return fmt.Errorf("coachoutlet %s 详情信息长度为0", c.cfg.Pid) + } + c.detail = resp.ProductData[0].Product() + c.detail.Remark = c.cfg.Remark + c.detail.Link = fmt.Sprintf("https://www.coachoutlet.com%s", c.detail.Link) + if c.detail.Orderable { + c.detail.Watch = false + if c.detail.AllocationResetDate.IsZero() { + c.detail.AllocationResetDate = time.Now() + } + } + c.detail.PusherIds = c.cfg.PusherIds + // 更新数据库 + return c.db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "uid"}}, + UpdateAll: true, + }).Create(c.detail).Error +} + +type InventoryResponse struct { + Inventory struct { + Status string `json:"status"` + InventoryListID string `json:"inventoryListID"` + InventoryInfo Inventory `json:"inventoryInfo"` + } `json:"inventory"` +} + +func (c *CoachOutlet) requestProductInventory(ctx context.Context) (Inventory, error) { + urlPath := fmt.Sprintf("https://www.coachoutlet.com/api/inventory?vgId=%s&includeVariantData=false", url.QueryEscape(c.cfg.Pid)) + var resp InventoryResponse + return resp.Inventory.InventoryInfo, tryRequest(ctx, urlPath, &resp, c.iter) +} + +func tryRequest(ctx context.Context, urlPath string, respData any, proxyGetter func() proxy.Proxy) error { + for p := proxyGetter(); p != nil; p = proxyGetter() { + select { + case <-ctx.Done(): + return nil + default: + + } + _, err := callByProxy(ctx, p, urlPath, respData) + if err != nil { + slog.Debug(err.Error()) + continue + } + return nil + } + return errors.New("can not get ") +} + +func callByProxy(ctx context.Context, p proxy.Proxy, urlPath string, result any) (*resty.Response, error) { + addr, err := utils.UrlToMetadata(urlPath) + if err != nil { + return nil, err + } + subCtx, cancel := context.WithTimeout(ctx, time.Second*5) + defer cancel() + conn, err := utils.ConnFromProxy(subCtx, p, addr) + if err != nil { + return nil, errors.Wrap(err, "创建conn失败") + } + defer conn.Close() + + var cli = pool.GetRestyClient(conn) + defer pool.PutRestyClient(cli) + + return cli.R().SetResult(result).Get(urlPath) +} diff --git a/pkg/watcher/controller.go b/pkg/watcher/controller.go new file mode 100644 index 0000000..055c787 --- /dev/null +++ b/pkg/watcher/controller.go @@ -0,0 +1,192 @@ +package watcher + +import ( + "context" + "fmt" + "github.com/golang/glog" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + "haitao_watcher/pkg/model" + "haitao_watcher/pkg/options" + pool "haitao_watcher/pkg/pools" + "sync" + "time" +) + +type Controller struct { + lock sync.RWMutex + m map[string]Watcher + db *gorm.DB + pool *pool.ProxyPool + ctx context.Context + ch chan<- model.PushMsg +} + +func NewController(ctx context.Context, db *gorm.DB, pool *pool.ProxyPool, ch chan<- model.PushMsg) *Controller { + ctl := &Controller{ + m: make(map[string]Watcher), + db: db, + pool: pool, + ctx: ctx, + ch: ch, + } + ctl.migrateTables() + go func() { + if err := ctl.initWatchers(); err != nil { + glog.Errorf("watcher init :%v", err) + } + }() + return ctl +} + +func (c *Controller) migrateTables() { + tables := []schema.Tabler{ + &model.Product{}, + } + for _, table := range tables { + if err := c.db.AutoMigrate(table); err != nil { + glog.Fatalf("failed to migrate table %s: %v", table.TableName(), err) + } + } +} + +func (c *Controller) initWatchers() error { + var list []model.Product + if err := c.db.Find(&list, "orderable = ?", false).Error; err != nil { + return err + } + for _, p := range list { + if err := c.RunWatcher(&options.CoachOutletOption{ + Pid: p.Pid, + Remark: p.Remark, + Interval: time.Minute * 5, + PusherIds: p.PusherIds, + }); err != nil { + glog.Errorf("run watcher err: %v", err) + } + } + return nil +} + +func (c *Controller) RunWatcher(opt *options.CoachOutletOption) error { + uid := opt.Uid() + err := c.db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "uid"}}, + DoUpdates: clause.AssignmentColumns([]string{"watch", "remark", "orderable", "pusher_ids"}), + }).Create(&model.Product{Uid: uid, Watch: true, Remark: opt.Remark, Orderable: false, PusherIds: opt.PusherIds}).Error + + if err != nil { + return err + } + + if water, ok := c.m[uid]; ok { + water.Restart() + return nil + } + + watcher := NewCoachWatcher(c.ctx, c.pool, opt, c.db).SetOnOrderableMsgChannel(c.ch) + go watcher.Watch() + + c.lock.Lock() + c.m[watcher.Uid()] = watcher + c.lock.Unlock() + return nil +} + +type ListWatcherInfoRequest struct { + Website model.WebsiteType `query:"website,omitempty"` //是什么网站 + Watch *bool `query:"watch,omitempty"` + Orderable *bool `query:"orderable,omitempty"` + Keyword string `query:"keyword,omitempty"` + Page int `query:"page"` + Size int `query:"size"` +} + +func (c *Controller) List(req ListWatcherInfoRequest) (resp model.ListResponse[model.Product], err error) { + tx := c.db + if req.Keyword != "" { + tx = tx.Where("name LIKE ? or remark LIKE ?", fmt.Sprintf("%%%s%%", req.Keyword), fmt.Sprintf("%%%s%%", req.Keyword)) + } + if !req.Website.IsZero() { + tx = tx.Where("website = ?", req.Website) + } + if req.Watch != nil { + tx = tx.Where("watch = ?", req.Watch) + } + if req.Orderable != nil { + tx = tx.Where("orderable = ?", req.Orderable) + } + if err = tx.Model(&model.Product{}).Count(&resp.Total).Error; err != nil { + return resp, fmt.Errorf("查询总数失败:%v", err) + } + if resp.Total == 0 { + resp.List = make([]model.Product, 0) + return + } + // 查询列表 + if req.Page < 1 { + req.Page = 1 + } + if req.Size < 1 { + req.Size = 10 + } + offset := (req.Page - 1) * req.Size + + if err = tx.Order("created_at desc").Limit(req.Size).Offset(offset). + Find(&resp.List).Error; err != nil { + return resp, fmt.Errorf("查询列表失败:%v", err) + } + return +} + +func (c *Controller) Delete(uid string) error { + c.lock.Lock() + if watcher, ok := c.m[uid]; ok { + watcher.Cancel() + delete(c.m, uid) + } + c.lock.Unlock() + return c.db.Delete(&model.Product{Uid: uid}, "uid = ?", uid).Error +} + +func (c *Controller) Stop(uid string) error { + c.lock.RLock() + if watcher, ok := c.m[uid]; ok { + watcher.Cancel() + } + c.lock.RUnlock() + return c.db.Model(&model.Product{}).Where("uid = ?", uid).UpdateColumn("watch", false).Error +} + +func (c *Controller) Start(uid string) error { + c.lock.RLock() + if watcher, ok := c.m[uid]; ok { + watcher.Restart() + c.lock.RUnlock() + return nil + } + c.lock.RUnlock() + + var product model.Product + if err := c.db.Find(&product, "uid = ?", uid).Error; err != nil { + return err + } + return c.RunWatcher(&options.CoachOutletOption{ + Pid: product.Pid, + Remark: product.Remark, + Interval: time.Minute * 5, + PusherIds: product.PusherIds, + }) +} + +func (c *Controller) SetPushers(uid string, pushers []uint) (err error) { + c.lock.RLock() + if watcher, ok := c.m[uid]; ok { + cfg := watcher.Option() + cfg.PusherIds = pushers + watcher.SetOption(cfg) + } + c.lock.RUnlock() + return c.db.Model(&model.Product{}).Where("uid = ?", uid).UpdateColumn("pusher_ids", pushers).Error +} diff --git a/server/pusher_svc.go b/server/pusher_svc.go new file mode 100644 index 0000000..95a4e19 --- /dev/null +++ b/server/pusher_svc.go @@ -0,0 +1,43 @@ +package server + +import ( + "github.com/gofiber/fiber/v3" + "haitao_watcher/pkg/model" + "haitao_watcher/pkg/options" + "haitao_watcher/pkg/pusher" +) + +type PusherSvc struct { + ctl *pusher.Controller +} + +func NewPusherSvcController(ctl *pusher.Controller) *PusherSvc { + return &PusherSvc{ + ctl: ctl, + } +} + +func (s *PusherSvc) RegistryRouter(r fiber.Router) { + r.Get("pushers", s.ListPusherInfo) + r.Post("pushers", s.CreatePusher) +} + +func (s *PusherSvc) ListPusherInfo(ctx fiber.Ctx) error { + var req pusher.ListPusherInfoRequest + if err := ctx.Bind().Query(&req); err != nil { + return err + } + resp, err := s.ctl.List(req) + if err != nil { + return err + } + return ctx.JSON(resp) +} + +func (s *PusherSvc) CreatePusher(ctx fiber.Ctx) error { + var opt model.Pusher[options.AnPushOption] + if err := ctx.Bind().JSON(&opt); err != nil { + return err + } + return s.ctl.AddPusher(&opt) +} diff --git a/server/watcher_svc.go b/server/watcher_svc.go new file mode 100644 index 0000000..01c34c0 --- /dev/null +++ b/server/watcher_svc.go @@ -0,0 +1,98 @@ +package server + +import ( + "github.com/gofiber/fiber/v3" + "github.com/pkg/errors" + "haitao_watcher/pkg/options" + "haitao_watcher/pkg/watcher" + "net/url" + "time" +) + +type WatcherSvc struct { + ctl *watcher.Controller +} + +func NewWatcherController(ctl *watcher.Controller) *WatcherSvc { + return &WatcherSvc{ + ctl: ctl, + } +} + +func (s *WatcherSvc) RegistryRouter(r fiber.Router) { + r.Get("watchers", s.ListWatcherInfo) + r.Post("watchers", s.CreateWatcher) + r.Delete("watchers/:uid", s.DeleteWatcher) + r.Delete("watchers/:uid/status", s.StopWatcher) + r.Post("watchers/:uid/status", s.StartWatcher) +} + +func (s *WatcherSvc) ListWatcherInfo(ctx fiber.Ctx) error { + var req watcher.ListWatcherInfoRequest + if err := ctx.Bind().Query(&req); err != nil { + return err + } + resp, err := s.ctl.List(req) + if err != nil { + return err + } + return ctx.JSON(resp) +} + +func (s *WatcherSvc) CreateWatcher(ctx fiber.Ctx) (err error) { + var opt options.CoachOutletOption + if err = ctx.Bind().JSON(&opt); err != nil { + return err + } + if opt.Interval == 0 { + opt.Interval = 5 * time.Minute + } + if opt.Pid, err = url.QueryUnescape(opt.Pid); err != nil { + return + } + return s.ctl.RunWatcher(&opt) +} + +func (s *WatcherSvc) StopWatcher(ctx fiber.Ctx) (err error) { + uid, err := url.QueryUnescape(ctx.Params("uid")) + if err != nil { + return err + } + if uid == "" { + return errors.New("uid is empty") + } + return s.ctl.Stop(uid) +} + +func (s *WatcherSvc) StartWatcher(ctx fiber.Ctx) (err error) { + uid, err := url.QueryUnescape(ctx.Params("uid")) + if err != nil { + return err + } + if uid == "" { + return errors.New("uid is empty") + } + return s.ctl.Start(uid) +} +func (s *WatcherSvc) DeleteWatcher(ctx fiber.Ctx) error { + uid, err := url.QueryUnescape(ctx.Params("uid")) + if err != nil { + return err + } + if uid == "" { + return errors.New("uid is empty") + } + return s.ctl.Delete(uid) +} + +func (s *WatcherSvc) SetWatcherPushers(ctx fiber.Ctx) (err error) { + var pusherIds []uint + if err = ctx.Bind().JSON(&pusherIds); err != nil { + return + } + uid := ctx.Params("uid") + if uid == "" { + return errors.New("uid is empty") + } + return s.ctl.SetPushers(uid, pusherIds) +} diff --git a/wwwroot/.gitignore b/wwwroot/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/wwwroot/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/wwwroot/.vscode/extensions.json b/wwwroot/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/wwwroot/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/wwwroot/README.md b/wwwroot/README.md new file mode 100644 index 0000000..e0acb37 --- /dev/null +++ b/wwwroot/README.md @@ -0,0 +1,7 @@ +# Vue 3 + Vite + +This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` + + diff --git a/wwwroot/package.json b/wwwroot/package.json new file mode 100644 index 0000000..14fbd17 --- /dev/null +++ b/wwwroot/package.json @@ -0,0 +1,26 @@ +{ + "name": "wwwroot", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "ant-design-vue": "4.x", + "mande": "^2.0.8", + "moment": "^2.30.1", + "vue": "^3.4.21", + "vue-router": "4" + }, + "devDependencies": { + "@types/node": "^20.12.5", + "@vitejs/plugin-vue": "^5.0.4", + "unocss": "^0.59.0", + "unplugin-vue-components": "^0.26.0", + "vite": "^5.2.0" + } +} diff --git a/wwwroot/src/App.vue b/wwwroot/src/App.vue new file mode 100644 index 0000000..604710e --- /dev/null +++ b/wwwroot/src/App.vue @@ -0,0 +1,15 @@ + + + + + + diff --git a/wwwroot/src/api/pusher.js b/wwwroot/src/api/pusher.js new file mode 100644 index 0000000..025d50d --- /dev/null +++ b/wwwroot/src/api/pusher.js @@ -0,0 +1,11 @@ +import {mande} from "mande"; + +const pushers = mande('/api/v1/pushers') + +export const ListPushers = (query) => { + return pushers.get({query:query}) +} + +export const AddPusher = (opt)=>{ + return pushers.post(opt) +} \ No newline at end of file diff --git a/wwwroot/src/api/watcher.js b/wwwroot/src/api/watcher.js new file mode 100644 index 0000000..d665cae --- /dev/null +++ b/wwwroot/src/api/watcher.js @@ -0,0 +1,22 @@ +import {mande} from "mande"; + +const watchers = mande('/api/v1/watchers') + +export const ListWatchers = (query) => { + return watchers.get({query:query}) +} + +export const CreateWatcher = (opt)=>{ + return watchers.post(opt) +} + +export const DeleteWatcher = (uid)=>{ + return watchers.delete(`/${encodeURIComponent(uid)}`) +} + +export const StopWatcher = (uid)=>{ + return watchers.delete(`/${encodeURIComponent(uid)}/status`) +} +export const StartWatcher = (uid)=>{ + return watchers.post(`/${encodeURIComponent(uid)}/status`) +} \ No newline at end of file diff --git a/wwwroot/src/assets/logo.png b/wwwroot/src/assets/logo.png new file mode 100644 index 0000000..3a02aac Binary files /dev/null and b/wwwroot/src/assets/logo.png differ diff --git a/wwwroot/src/constants/pusher.js b/wwwroot/src/constants/pusher.js new file mode 100644 index 0000000..24af822 --- /dev/null +++ b/wwwroot/src/constants/pusher.js @@ -0,0 +1,10 @@ +export const PUSHER = { + ANPUSHER: 1, +} + +export const WEBSITE_OPTIONS = [ + { + label: 'anPush', + value: PUSHER.ANPUSHER + } +] \ No newline at end of file diff --git a/wwwroot/src/constants/website.js b/wwwroot/src/constants/website.js new file mode 100644 index 0000000..d91712b --- /dev/null +++ b/wwwroot/src/constants/website.js @@ -0,0 +1,15 @@ +export const WEBSITES = { + UNKNOWN: 0, + COACHOUTLET: 1, +} + +export const WEBSITE_OPTIONS = [ + { + label: '未知', + value: WEBSITES.UNKNOWN + }, + { + label: 'coachoutlet', + value: WEBSITES.COACHOUTLET + } +] \ No newline at end of file diff --git a/wwwroot/src/css/base.css b/wwwroot/src/css/base.css new file mode 100644 index 0000000..d155e04 --- /dev/null +++ b/wwwroot/src/css/base.css @@ -0,0 +1,6 @@ +html,body,#app { + height: 100%; + width: 100%; + padding: 0; + margin: 0; +} \ No newline at end of file diff --git a/wwwroot/src/main.js b/wwwroot/src/main.js new file mode 100644 index 0000000..e83a210 --- /dev/null +++ b/wwwroot/src/main.js @@ -0,0 +1,9 @@ +import { createApp } from 'vue' +import './css/base.css' +import App from './App.vue' +import 'virtual:uno.css' +import router from "@/routers/index.js"; + +const app = createApp(App) +app.use(router) +app.mount('#app') diff --git a/wwwroot/src/routers/index.js b/wwwroot/src/routers/index.js new file mode 100644 index 0000000..3e3bcf9 --- /dev/null +++ b/wwwroot/src/routers/index.js @@ -0,0 +1,25 @@ +import {createRouter, createWebHashHistory} from "vue-router"; + +const routes = [ + { + path: '/', + redirect: '/watcher' + }, + { + path: '/watcher', + name: 'watcher', + component: ()=>import('@/views/Watcher/index.vue') + }, + { + path: '/pusher', + name: 'pusher', + component: ()=>import('@/views/Pusher/index.vue') + } +] + +const router = createRouter({ + history: createWebHashHistory(), + routes +}) + +export default router \ No newline at end of file diff --git a/wwwroot/src/views/Pusher/index.vue b/wwwroot/src/views/Pusher/index.vue new file mode 100644 index 0000000..732555b --- /dev/null +++ b/wwwroot/src/views/Pusher/index.vue @@ -0,0 +1,178 @@ + + + + + \ No newline at end of file diff --git a/wwwroot/src/views/Watcher/index.vue b/wwwroot/src/views/Watcher/index.vue new file mode 100644 index 0000000..53462a2 --- /dev/null +++ b/wwwroot/src/views/Watcher/index.vue @@ -0,0 +1,253 @@ + + + + + + \ No newline at end of file diff --git a/wwwroot/src/views/layout/Aside.vue b/wwwroot/src/views/layout/Aside.vue new file mode 100644 index 0000000..4100d45 --- /dev/null +++ b/wwwroot/src/views/layout/Aside.vue @@ -0,0 +1,42 @@ + + + + \ No newline at end of file diff --git a/wwwroot/src/views/layout/Header.vue b/wwwroot/src/views/layout/Header.vue new file mode 100644 index 0000000..703203c --- /dev/null +++ b/wwwroot/src/views/layout/Header.vue @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/wwwroot/src/views/layout/Main.vue b/wwwroot/src/views/layout/Main.vue new file mode 100644 index 0000000..657f1ee --- /dev/null +++ b/wwwroot/src/views/layout/Main.vue @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/wwwroot/src/views/layout/index.vue b/wwwroot/src/views/layout/index.vue new file mode 100644 index 0000000..d92d49c --- /dev/null +++ b/wwwroot/src/views/layout/index.vue @@ -0,0 +1,17 @@ + + + \ No newline at end of file diff --git a/wwwroot/vite.config.js b/wwwroot/vite.config.js new file mode 100644 index 0000000..bc42ab8 --- /dev/null +++ b/wwwroot/vite.config.js @@ -0,0 +1,38 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import UnoCSS from 'unocss/vite' +import path from 'path' +import Components from 'unplugin-vue-components/vite'; +import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; + + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + UnoCSS(), + Components({ + resolvers: [ + AntDesignVueResolver({ + importStyle: false, // css in js + }), + ], + }), + ], + server:{ + open: true, + proxy: { + '/api/v1': { + target: 'http://172.25.168.160:2280/', + changeOrigin: true, + secure: false, + ws: true, + }, + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, "./src") + } + } +})