feat 实现基本功能
This commit is contained in:
commit
59f1de2cd3
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@ -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"]
|
||||
|
151
go.mod
Normal file
151
go.mod
Normal file
@ -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
|
||||
)
|
504
go.sum
Normal file
504
go.sum
Normal file
@ -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=
|
50
main/main.go
Normal file
50
main/main.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
17
pkg/database/init.go
Normal file
17
pkg/database/init.go
Normal file
@ -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{})
|
||||
}
|
7
pkg/model/msg.go
Normal file
7
pkg/model/msg.go
Normal file
@ -0,0 +1,7 @@
|
||||
package model
|
||||
|
||||
type PushMsg struct {
|
||||
Title string
|
||||
Content string
|
||||
ToPusher []uint
|
||||
}
|
45
pkg/model/product.go
Normal file
45
pkg/model/product.go
Normal file
@ -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
|
||||
}
|
28
pkg/model/pusher.go
Normal file
28
pkg/model/pusher.go
Normal file
@ -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"
|
||||
}
|
6
pkg/model/response.go
Normal file
6
pkg/model/response.go
Normal file
@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
type ListResponse[E any] struct {
|
||||
Total int64 `json:"total"`
|
||||
List []E `json:"list"`
|
||||
}
|
18
pkg/options/coachoutlet.go
Normal file
18
pkg/options/coachoutlet.go
Normal file
@ -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)
|
||||
}
|
15
pkg/options/config_test.go
Normal file
15
pkg/options/config_test.go
Normal file
@ -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)
|
||||
}
|
6
pkg/options/db.go
Normal file
6
pkg/options/db.go
Normal file
@ -0,0 +1,6 @@
|
||||
package options
|
||||
|
||||
type DBOption struct {
|
||||
Path string `json:"path"`
|
||||
Timeout int `json:"timeout"`
|
||||
}
|
21
pkg/options/init.go
Normal file
21
pkg/options/init.go
Normal file
@ -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)
|
||||
}
|
5
pkg/options/proxy.go
Normal file
5
pkg/options/proxy.go
Normal file
@ -0,0 +1,5 @@
|
||||
package options
|
||||
|
||||
type ProxyOption struct {
|
||||
Subscribes []string `yaml:"subscribes"`
|
||||
}
|
6
pkg/options/pusher.go
Normal file
6
pkg/options/pusher.go
Normal file
@ -0,0 +1,6 @@
|
||||
package options
|
||||
|
||||
type AnPushOption struct {
|
||||
Token string `json:"token"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
72
pkg/pools/proxy.go
Normal file
72
pkg/pools/proxy.go
Normal file
@ -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]
|
||||
}
|
||||
}
|
67
pkg/pools/resty.go
Normal file
67
pkg/pools/resty.go
Normal file
@ -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)
|
||||
}
|
34
pkg/pusher/anPush.go
Normal file
34
pkg/pusher/anPush.go
Normal file
@ -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
|
||||
}
|
18
pkg/pusher/anPush_test.go
Normal file
18
pkg/pusher/anPush_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
124
pkg/pusher/controller.go
Normal file
124
pkg/pusher/controller.go
Normal file
@ -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
|
||||
}
|
57
pkg/utils/proxy.go
Normal file
57
pkg/utils/proxy.go
Normal file
@ -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
|
||||
}
|
16
pkg/watcher/base.go
Normal file
16
pkg/watcher/base.go
Normal file
@ -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()
|
||||
}
|
261
pkg/watcher/coach.go
Normal file
261
pkg/watcher/coach.go
Normal file
@ -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)
|
||||
}
|
192
pkg/watcher/controller.go
Normal file
192
pkg/watcher/controller.go
Normal file
@ -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
|
||||
}
|
43
server/pusher_svc.go
Normal file
43
server/pusher_svc.go
Normal file
@ -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)
|
||||
}
|
98
server/watcher_svc.go
Normal file
98
server/watcher_svc.go
Normal file
@ -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)
|
||||
}
|
24
wwwroot/.gitignore
vendored
Normal file
24
wwwroot/.gitignore
vendored
Normal file
@ -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?
|
3
wwwroot/.vscode/extensions.json
vendored
Normal file
3
wwwroot/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
7
wwwroot/README.md
Normal file
7
wwwroot/README.md
Normal file
@ -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 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously Volar) and disable Vetur
|
BIN
wwwroot/bun.lockb
Normal file
BIN
wwwroot/bun.lockb
Normal file
Binary file not shown.
13
wwwroot/index.html
Normal file
13
wwwroot/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" />-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>可达鸭海淘</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
26
wwwroot/package.json
Normal file
26
wwwroot/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
15
wwwroot/src/App.vue
Normal file
15
wwwroot/src/App.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<a-config-provider :locale="zhCN">
|
||||
<layout></layout>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import Layout from '@/views/layout/index.vue'
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
11
wwwroot/src/api/pusher.js
Normal file
11
wwwroot/src/api/pusher.js
Normal file
@ -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)
|
||||
}
|
22
wwwroot/src/api/watcher.js
Normal file
22
wwwroot/src/api/watcher.js
Normal file
@ -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`)
|
||||
}
|
BIN
wwwroot/src/assets/logo.png
Normal file
BIN
wwwroot/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 271 KiB |
10
wwwroot/src/constants/pusher.js
Normal file
10
wwwroot/src/constants/pusher.js
Normal file
@ -0,0 +1,10 @@
|
||||
export const PUSHER = {
|
||||
ANPUSHER: 1,
|
||||
}
|
||||
|
||||
export const WEBSITE_OPTIONS = [
|
||||
{
|
||||
label: 'anPush',
|
||||
value: PUSHER.ANPUSHER
|
||||
}
|
||||
]
|
15
wwwroot/src/constants/website.js
Normal file
15
wwwroot/src/constants/website.js
Normal file
@ -0,0 +1,15 @@
|
||||
export const WEBSITES = {
|
||||
UNKNOWN: 0,
|
||||
COACHOUTLET: 1,
|
||||
}
|
||||
|
||||
export const WEBSITE_OPTIONS = [
|
||||
{
|
||||
label: '未知',
|
||||
value: WEBSITES.UNKNOWN
|
||||
},
|
||||
{
|
||||
label: 'coachoutlet',
|
||||
value: WEBSITES.COACHOUTLET
|
||||
}
|
||||
]
|
6
wwwroot/src/css/base.css
Normal file
6
wwwroot/src/css/base.css
Normal file
@ -0,0 +1,6 @@
|
||||
html,body,#app {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
9
wwwroot/src/main.js
Normal file
9
wwwroot/src/main.js
Normal file
@ -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')
|
25
wwwroot/src/routers/index.js
Normal file
25
wwwroot/src/routers/index.js
Normal file
@ -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
|
178
wwwroot/src/views/Pusher/index.vue
Normal file
178
wwwroot/src/views/Pusher/index.vue
Normal file
@ -0,0 +1,178 @@
|
||||
|
||||
<template>
|
||||
<div class="h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<a-button type="primary" @click="addModal.visible=true" :disabled="loading">添加</a-button>
|
||||
<div class="flex space-x-4">
|
||||
<a-input placeholder="请输入关键词" v-model:value="query.keyword"></a-input>
|
||||
<a-button type="primary" :disabled="loading" @click="list">搜索</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full border-0 border-t-1 border-solid border-gray-300 pt-4">
|
||||
<a-spin :spinning="loading" :indicator="indicator">
|
||||
<a-table :dataSource="data.list" :columns="columns" :pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'type'">
|
||||
<span>{{WEBSITE_OPTIONS.find(w => w.value === record.type).label}}</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'updatedAt'">
|
||||
<span>{{moment(record.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'option'">
|
||||
<span>{{record.option}}</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-pagination :disabled="loading" class="text-right" v-model:current="query.page" :total="data.total" show-less-items />
|
||||
</div>
|
||||
<a-modal v-model:open="addModal.visible" title="添加推送通知" @ok="handleOk" >
|
||||
<a-spin :spinning="addModal.loading" :indicator="indicator">
|
||||
<a-form
|
||||
:model="addModal.data"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="closeAddModal"
|
||||
@finishFailed="closeAddModal"
|
||||
>
|
||||
<a-form-item label="名称" name="name" :rules="[{ required: true, message: '请填写推送名称' }]">
|
||||
<a-input v-model:value="addModal.data.name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="token" name="option.token" :rules="[{ required: false, message: '请填写token' }]">
|
||||
<a-input v-model:value="addModal.data.option.token" />
|
||||
</a-form-item>
|
||||
<a-form-item label="channel" name="option.channel" :rules="[{ required: false, message: '请填写channel' }]">
|
||||
<a-input v-model:value="addModal.data.option.channel" />
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" name="remark">
|
||||
<a-textarea v-model:value="addModal.data.remark" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import {h, onMounted, reactive, ref} from "vue";
|
||||
import {WEBSITE_OPTIONS} from "@/constants/website.js";
|
||||
import moment from "moment/moment.js";
|
||||
import {LoadingOutlined} from "@ant-design/icons-vue";
|
||||
import {AddPusher, ListPushers} from "@/api/pusher.js";
|
||||
import {PUSHER} from "@/constants/pusher.js";
|
||||
import {message} from "ant-design-vue";
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted(()=>{
|
||||
list()
|
||||
})
|
||||
|
||||
|
||||
const query = reactive({
|
||||
// Website database.WebsiteType `query:"website,omitempty"` //是什么网站
|
||||
// Watch *bool `query:"watch,omitempty"`
|
||||
// Orderable *bool `query:"orderable,omitempty"`
|
||||
// Keyword string `query:"keyword,omitempty"`
|
||||
keyword: '',
|
||||
page: 1,
|
||||
size:10
|
||||
})
|
||||
|
||||
|
||||
const data = ref({
|
||||
total: 0,
|
||||
list:[]
|
||||
})
|
||||
|
||||
const list = ()=>{
|
||||
loading.value = true
|
||||
ListPushers(query).then(res=>{
|
||||
data.value = res
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const addModal = reactive({
|
||||
visible: false,
|
||||
data: {
|
||||
type:PUSHER.ANPUSHER,
|
||||
name:'',
|
||||
remark:'',
|
||||
option:{
|
||||
token:'',
|
||||
channel: ''
|
||||
}
|
||||
},
|
||||
loading: false
|
||||
})
|
||||
|
||||
const closeAddModal = ()=>{
|
||||
addModal.visible = false
|
||||
addModal.data = {
|
||||
type:PUSHER.ANPUSHER,
|
||||
name:'',
|
||||
remark:'',
|
||||
option:{
|
||||
token:'',
|
||||
channel: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = ()=>{
|
||||
AddPusher(addModal.data).then(res=>{
|
||||
message.success("添加成功")
|
||||
}).catch(err => {
|
||||
message.error("添加失败")
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
list(false)
|
||||
closeAddModal()
|
||||
})
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
},
|
||||
{
|
||||
title: '配置',
|
||||
dataIndex: 'option',
|
||||
key: 'option',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updatedAt',
|
||||
key: 'updatedAt',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
},
|
||||
]
|
||||
|
||||
const indicator = h(LoadingOutlined, {
|
||||
style: {
|
||||
fontSize: '32px',
|
||||
},
|
||||
spin: true,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
253
wwwroot/src/views/Watcher/index.vue
Normal file
253
wwwroot/src/views/Watcher/index.vue
Normal file
@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<div class="h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<a-button type="primary" @click="addModal.visible=true" :disabled="loading">添加</a-button>
|
||||
<div class="flex space-x-4">
|
||||
<a-input placeholder="请输入关键词" v-model:value="query.keyword"></a-input>
|
||||
<a-button type="primary" :disabled="loading" @click="list">搜索</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full border-0 border-t-1 border-solid border-gray-300 pt-4">
|
||||
<a-spin :spinning="loading" :indicator="indicator">
|
||||
<a-table :dataSource="data.list" :columns="columns" :pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'website'">
|
||||
<span>{{WEBSITE_OPTIONS.find(w => w.value === record.website).label}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'watch'">
|
||||
<a-switch :checked="record.watch" @click="watcherStatusChange(!record.watch,record.uid)"/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'name'">
|
||||
<a v-if="record.name !== '' " :href="record.link" target="_blank">{{record.name}}</a>
|
||||
<span v-else>正在抓取信息</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'updatedAt'">
|
||||
<span>{{moment(record.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'pusherIds'">
|
||||
<template v-if="record.pusherIds" v-for="id in record.pusherIds">
|
||||
<a-tag :bordered="false" color="processing">{{pusher.list.find(p =>p.id === id)?.name}}</a-tag>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'opt'">
|
||||
<a-button type="link" danger @click="onDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-pagination :disabled="loading" class="text-right" v-model:current="query.page" :total="data.total" show-less-items />
|
||||
</div>
|
||||
<a-modal v-model:open="addModal.visible" title="添加监听任务" @ok="handleOk" >
|
||||
<a-spin :spinning="addModal.loading" :indicator="indicator">
|
||||
<a-form
|
||||
:model="addModal.data"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="closeAddModal"
|
||||
@finishFailed="closeAddModal"
|
||||
>
|
||||
<a-form-item label="ID" name="pid" :rules="[{ required: true, message: '请填写商品id' }]">
|
||||
<a-input v-model:value="addModal.data.pid" />
|
||||
</a-form-item>
|
||||
<a-form-item label="推送" name="pusherIds" :rules="[{ required: true, message: '请选择通知推送' }]">
|
||||
<a-select
|
||||
v-model:value="addModal.data.pusherIds"
|
||||
mode="multiple"
|
||||
@dropdown-visible-change="getPushers"
|
||||
placeholder="请选择通知推送" :fieldNames="{label:'name',value:'id'}"
|
||||
:options="pusher.list"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" name="remark">
|
||||
<a-textarea v-model:value="addModal.data.remark" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup>
|
||||
import moment from "moment";
|
||||
import {h, onMounted, reactive, ref} from "vue";
|
||||
import {ListWatchers, CreateWatcher, DeleteWatcher, StartWatcher, StopWatcher} from "@/api/watcher.js";
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import {message, Modal} from 'ant-design-vue';
|
||||
import {WEBSITE_OPTIONS} from "@/constants/website.js";
|
||||
import {onBeforeRouteLeave} from "vue-router";
|
||||
import {ListPushers} from "@/api/pusher.js";
|
||||
|
||||
let ticker = null
|
||||
onMounted(()=>{
|
||||
list(false)
|
||||
getPushers()
|
||||
ticker = setInterval(list, 2000, true)
|
||||
})
|
||||
|
||||
onBeforeRouteLeave(()=>{
|
||||
if(ticker){
|
||||
clearInterval(ticker)
|
||||
}
|
||||
})
|
||||
|
||||
const query = reactive({
|
||||
// Website database.WebsiteType `query:"website,omitempty"` //是什么网站
|
||||
// Watch *bool `query:"watch,omitempty"`
|
||||
// Orderable *bool `query:"orderable,omitempty"`
|
||||
// Keyword string `query:"keyword,omitempty"`
|
||||
page: 1,
|
||||
size:10
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const data = ref({
|
||||
total: 0,
|
||||
list:[]
|
||||
})
|
||||
|
||||
const list = (silent)=>{
|
||||
if(!silent){
|
||||
loading.value = true
|
||||
}
|
||||
ListWatchers(query).then(res=>{
|
||||
data.value = res
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
if(!silent){
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const addModal = reactive({
|
||||
visible: false,
|
||||
data: {
|
||||
pid:'',
|
||||
remark:'',
|
||||
pusherIds:[]
|
||||
},
|
||||
loading: false
|
||||
})
|
||||
|
||||
const closeAddModal = ()=>{
|
||||
addModal.visible = false
|
||||
addModal.data = {
|
||||
pid:'',
|
||||
remark:'',
|
||||
pusherIds: []
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = ()=>{
|
||||
CreateWatcher(addModal.data).then(res=>{
|
||||
message.success("添加成功")
|
||||
}).catch(err => {
|
||||
message.error("添加失败")
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
list(false)
|
||||
closeAddModal()
|
||||
})
|
||||
}
|
||||
|
||||
const pusher = reactive({
|
||||
list: [],
|
||||
query: {
|
||||
keyword:'',
|
||||
all: true
|
||||
}
|
||||
})
|
||||
|
||||
const getPushers = ()=>{
|
||||
ListPushers(pusher.query).then(res=>{
|
||||
pusher.list = res.list || []
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
const onDelete = ({name, uid})=>{
|
||||
Modal.confirm({
|
||||
title: '确认',
|
||||
content: `确定删除 ${name} 监听?`,
|
||||
centered: true,
|
||||
onOk() {
|
||||
DeleteWatcher(uid).then(res=>{
|
||||
message.success("删除成功")
|
||||
list(false)
|
||||
}).catch(err=>{
|
||||
message.error("删除失败")
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const watcherStatusChange=(changed, uid)=>{
|
||||
const api = changed ? StartWatcher:StopWatcher
|
||||
loading.value = true
|
||||
api(uid).then(res=>{
|
||||
message.success(`${changed?'开启':'关闭'}成功`)
|
||||
}).catch(err=>{
|
||||
message.error(`${changed?'开启':'关闭'}失败`)
|
||||
}).finally(list)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '品牌',
|
||||
dataIndex: 'brand',
|
||||
key: 'brand',
|
||||
},
|
||||
{
|
||||
title: '网站',
|
||||
dataIndex: 'website',
|
||||
key: 'website',
|
||||
},
|
||||
{
|
||||
title: '正在蹲货',
|
||||
dataIndex: 'watch',
|
||||
key: 'watch',
|
||||
},
|
||||
{
|
||||
title: '上次抓取时间',
|
||||
dataIndex: 'updatedAt',
|
||||
key: 'updatedAt',
|
||||
},
|
||||
{
|
||||
title: '推送',
|
||||
key: 'pusherIds',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'opt',
|
||||
},
|
||||
]
|
||||
|
||||
const indicator = h(LoadingOutlined, {
|
||||
style: {
|
||||
fontSize: '32px',
|
||||
},
|
||||
spin: true,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
42
wwwroot/src/views/layout/Aside.vue
Normal file
42
wwwroot/src/views/layout/Aside.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<a-menu
|
||||
@click="onclick"
|
||||
id="aside"
|
||||
style="width: 256px"
|
||||
mode="inline"
|
||||
:items="items"
|
||||
></a-menu>
|
||||
</template>
|
||||
<script setup>
|
||||
import {AccountBookOutlined, BellOutlined} from "@ant-design/icons-vue";
|
||||
import {h} from "vue";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'watcher',
|
||||
icon: () => h(AccountBookOutlined),
|
||||
label: '蹲货',
|
||||
title: '蹲货',
|
||||
},
|
||||
{
|
||||
key: 'pusher',
|
||||
icon: () => h(BellOutlined),
|
||||
label: '推送',
|
||||
title: '推送',
|
||||
},
|
||||
]
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const onclick = ({key}) => {
|
||||
router.push({
|
||||
name: key
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
14
wwwroot/src/views/layout/Header.vue
Normal file
14
wwwroot/src/views/layout/Header.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="h-[100px] shadow-lg flex items-center px-12 space-x-4 z-10">
|
||||
<img class="h-[60px]" src="@/assets/logo.png" alt="">
|
||||
<div class="text-[24px] font-bold">
|
||||
可达鸭海淘蹲货
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
14
wwwroot/src/views/layout/Main.vue
Normal file
14
wwwroot/src/views/layout/Main.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="h-full w-full bg-[#f0f3f7] flex flex-col">
|
||||
<router-view ></router-view>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
17
wwwroot/src/views/layout/index.vue
Normal file
17
wwwroot/src/views/layout/index.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="h-full w-full flex flex-col">
|
||||
<Header></Header>
|
||||
<div class="h-full w-full flex">
|
||||
<Aside></Aside>
|
||||
<Main></Main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import Header from "@/views/layout/Header.vue";
|
||||
import Aside from "@/views/layout/Aside.vue";
|
||||
import Main from "@/views/layout/Main.vue";
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
38
wwwroot/vite.config.js
Normal file
38
wwwroot/vite.config.js
Normal file
@ -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")
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user