mirror of
https://github.com/yusing/godoxy.git
synced 2026-02-14 14:37:41 +01:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc19a54976 | ||
|
|
12d999809f | ||
|
|
6771293336 | ||
|
|
d240c9dfee | ||
|
|
c7eda38933 | ||
|
|
09caa888ad | ||
|
|
e41a487371 | ||
|
|
7c08a8da2e | ||
|
|
82df824490 | ||
|
|
2f341001c1 | ||
|
|
25ee8041da | ||
|
|
8687a57b6c | ||
|
|
3f4ed31e46 | ||
|
|
9930f3fa2e | ||
|
|
2157545e17 | ||
|
|
f721395ff0 | ||
|
|
0dc7c59af1 | ||
|
|
e3fe126a5c | ||
|
|
aa2575696d | ||
|
|
c1f9c2c957 | ||
|
|
c098fef615 | ||
|
|
9cdc985fb0 | ||
|
|
2034738422 | ||
|
|
55a42b81de | ||
|
|
48627753d6 | ||
|
|
09b514393d | ||
|
|
3b2ae5dbd6 | ||
|
|
fac3d67a51 | ||
|
|
cb642d7b32 | ||
|
|
9285977495 | ||
|
|
e00cd8a35b | ||
|
|
8ac459c038 | ||
|
|
1bcaf0dab5 | ||
|
|
a291a49a0e | ||
|
|
28fdf3d2f4 | ||
|
|
84b17baf46 | ||
|
|
06ddb178f8 | ||
|
|
61fa7d2665 | ||
|
|
615521ee1c | ||
|
|
bbe308e821 | ||
|
|
c156173757 | ||
|
|
b1aae1cacf | ||
|
|
f46552b477 | ||
|
|
efe1350ffd | ||
|
|
219eedf3c5 |
@@ -1,5 +1,5 @@
|
||||
# Stage 1: deps
|
||||
FROM golang:1.25.4-alpine AS deps
|
||||
FROM golang:1.25.5-alpine AS deps
|
||||
HEALTHCHECK NONE
|
||||
|
||||
# package version does not matter
|
||||
@@ -19,7 +19,9 @@ COPY go.mod go.sum ./
|
||||
# remove godoxy stuff from go.mod first
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/root/go/pkg/mod \
|
||||
sed -i '/^module github\.com\/yusing\/godoxy/!{/github\.com\/yusing\/godoxy/d}' go.mod && go mod download -x
|
||||
sed -i '/^module github\.com\/yusing\/godoxy/!{/github\.com\/yusing\/godoxy/d}' go.mod && \
|
||||
sed -i '/^module github\.com\/yusing\/goutils/!{/github\.com\/yusing\/goutils/d}' go.mod && \
|
||||
go mod download -x
|
||||
|
||||
# Stage 2: builder
|
||||
FROM deps AS builder
|
||||
|
||||
11
Jenkinsfile
vendored
Normal file
11
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
node {
|
||||
stage('SCM') {
|
||||
checkout scm
|
||||
}
|
||||
stage('SonarQube Analysis') {
|
||||
def scannerHome = tool 'SonarScanner';
|
||||
withSonarQubeEnv() {
|
||||
sh "${scannerHome}/bin/sonar-scanner"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Makefile
3
Makefile
@@ -80,6 +80,7 @@ test:
|
||||
docker-build-test:
|
||||
docker build -t godoxy .
|
||||
docker build --build-arg=MAKE_ARGS=agent=1 -t godoxy-agent .
|
||||
docker build --build-arg=MAKE_ARGS=socket-proxy=1 -t godoxy-socket-proxy .
|
||||
|
||||
go_ver := $(shell go version | cut -d' ' -f3 | cut -d'o' -f2)
|
||||
files := $(shell find . -name go.mod -type f -or -name Dockerfile -type f)
|
||||
@@ -110,7 +111,7 @@ mod-tidy:
|
||||
|
||||
build:
|
||||
mkdir -p $(shell dirname ${BIN_PATH})
|
||||
cd ${PWD} && go build ${BUILD_FLAGS} -o ${BIN_PATH} ./cmd
|
||||
go build -C ${PWD} ${BUILD_FLAGS} -o ${BIN_PATH} ./cmd
|
||||
${POST_BUILD}
|
||||
|
||||
run:
|
||||
|
||||
83
agent/go.mod
83
agent/go.mod
@@ -1,14 +1,16 @@
|
||||
module github.com/yusing/godoxy/agent
|
||||
|
||||
go 1.25.4
|
||||
go 1.25.5
|
||||
|
||||
replace github.com/yusing/godoxy => ..
|
||||
|
||||
replace github.com/yusing/godoxy/socketproxy => ../socket-proxy
|
||||
|
||||
replace github.com/shirou/gopsutil/v4 => ../internal/gopsutil
|
||||
|
||||
replace github.com/yusing/goutils => ../goutils
|
||||
replace (
|
||||
github.com/shirou/gopsutil/v4 => ../internal/gopsutil
|
||||
github.com/yusing/godoxy => ../
|
||||
github.com/yusing/godoxy/socketproxy => ../socket-proxy
|
||||
github.com/yusing/goutils => ../goutils
|
||||
github.com/yusing/goutils/http/reverseproxy => ../goutils/http/reverseproxy
|
||||
github.com/yusing/goutils/http/websocket => ../goutils/http/websocket
|
||||
github.com/yusing/goutils/server => ../goutils/server
|
||||
)
|
||||
|
||||
exclude github.com/containerd/nerdctl/mod/tigron v0.0.0
|
||||
|
||||
@@ -20,17 +22,19 @@ require (
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/valyala/fasthttp v1.68.0
|
||||
github.com/yusing/godoxy v0.20.2
|
||||
github.com/yusing/godoxy v0.20.10
|
||||
github.com/yusing/godoxy/socketproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils v0.7.0
|
||||
github.com/yusing/goutils/http/reverseproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils/server v0.0.0-20250927113415-dd977d2edaeb
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
@@ -38,78 +42,85 @@ require (
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/diskfs/go-diskfs v1.7.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/cli v28.5.2+incompatible // indirect
|
||||
github.com/docker/docker v28.5.2+incompatible // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/docker/cli v29.1.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-acme/lego/v4 v4.29.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/goccy/go-yaml v1.19.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gotify/server/v2 v2.7.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/luthermonson/go-proxmox v0.2.3 // indirect
|
||||
github.com/magefile/mage v1.15.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.68 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/moby/api v1.52.0 // indirect
|
||||
github.com/moby/moby/client v0.2.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.55.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.57.1 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/samber/slog-common v0.19.0 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.8.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.10 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vincent-petithory/dataurl v1.0.0 // indirect
|
||||
github.com/yusing/ds v0.3.1 // indirect
|
||||
github.com/yusing/gointernals v0.1.16 // indirect
|
||||
github.com/yusing/goutils/http/websocket v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/arch v0.22.0 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
)
|
||||
|
||||
139
agent/go.sum
139
agent/go.sum
@@ -1,9 +1,9 @@
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
|
||||
github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
@@ -24,10 +24,8 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -39,16 +37,16 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/docker/cli v28.5.2+incompatible h1:XmG99IHcBmIAoC1PPg9eLBZPlTrNUAijsHLm8PjhBlg=
|
||||
github.com/docker/cli v28.5.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/cli v29.1.2+incompatible h1:s4QI7drXpIo78OM+CwuthPsO5kCf8cpNsck5PsLVTH8=
|
||||
github.com/docker/cli v29.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
@@ -59,8 +57,8 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-acme/lego/v4 v4.28.0 h1:URKsCcybo7SjqqZckeBcDN9Vl29/bKS///75tcNkMHQ=
|
||||
github.com/go-acme/lego/v4 v4.28.0/go.mod h1:bzjilr03IgbaOwlH396hq5W56Bi0/uoRwW/JM8hP7m4=
|
||||
github.com/go-acme/lego/v4 v4.29.0 h1:vKMEtvoKb0gOO9rWO9zMBwE4CgI5A5CWDsK4QEeBqzo=
|
||||
github.com/go-acme/lego/v4 v4.29.0/go.mod h1:rnYyDj1NdDd9y1dHkVuUS97j7bfe9I61+oY9odKaHM8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -79,12 +77,14 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
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/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
|
||||
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
@@ -100,14 +100,16 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotify/server/v2 v2.7.3 h1:nro/ZnxdlZFvxFcw9LREGA8zdk6CK744azwhuhX/A4g=
|
||||
github.com/gotify/server/v2 v2.7.3/go.mod h1:VAtE1RIc/2j886PYs9WPQbMjqbFsoyQ0G8IdFtnAxU0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU=
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -135,19 +137,15 @@ github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
@@ -156,10 +154,13 @@ github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -167,10 +168,10 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
||||
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
@@ -180,8 +181,8 @@ github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89G9YI=
|
||||
github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M=
|
||||
github.com/samber/slog-zerolog/v2 v2.8.0 h1:K3+PJieRyi2rX/eaJZ95EdmpY/pzdeDd3jRnIQZG6kU=
|
||||
github.com/samber/slog-zerolog/v2 v2.8.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 h1:6LkOabJmZdNLaUWkTC3IVVA+dq7b/V0FM6lz6/7+THI=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
@@ -197,14 +198,16 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
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.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
@@ -226,10 +229,6 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
@@ -238,29 +237,27 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@@ -270,10 +267,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
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=
|
||||
@@ -281,15 +278,16 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20210331175145-43e1dd70ce54/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-20210616094352-59db8d763f22/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-20220615213510-4f61da869c0c/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=
|
||||
@@ -301,8 +299,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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=
|
||||
@@ -321,8 +319,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -331,16 +329,9 @@ 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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 h1:8XJ4pajGwOlasW+L13MnEGA8W4115jJySQtVfS2/IBU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -351,3 +342,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -12,8 +13,6 @@ import (
|
||||
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
var defaultHealthConfig = types.DefaultHealthConfig()
|
||||
|
||||
func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
scheme := query.Get("scheme")
|
||||
@@ -49,7 +48,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
Path: path,
|
||||
}, defaultHealthConfig).CheckHealth()
|
||||
}, healthCheckConfigFromRequest(r)).CheckHealth()
|
||||
case "tcp", "udp":
|
||||
host := query.Get("host")
|
||||
if host == "" {
|
||||
@@ -68,7 +67,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
result, err = monitor.NewRawHealthMonitor(&url.URL{
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
}, defaultHealthConfig).CheckHealth()
|
||||
}, healthCheckConfigFromRequest(r)).CheckHealth()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -80,3 +79,13 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
sonic.ConfigDefault.NewEncoder(w).Encode(result)
|
||||
}
|
||||
|
||||
func healthCheckConfigFromRequest(r *http.Request) types.HealthCheckConfig {
|
||||
// we only need timeout and base context because it's one shot request
|
||||
return types.HealthCheckConfig{
|
||||
Timeout: types.HealthCheckTimeoutDefault,
|
||||
BaseContext: func() context.Context {
|
||||
return r.Context()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/godoxy/internal/metrics/uptime"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/server"
|
||||
"github.com/yusing/goutils/task"
|
||||
@@ -58,9 +59,12 @@ func main() {
|
||||
}
|
||||
|
||||
config.StartProxyServers()
|
||||
|
||||
if err := auth.Initialize(); err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to initialize authentication")
|
||||
}
|
||||
rules.InitAuthHandler(auth.AuthOrProceed)
|
||||
|
||||
// API Handler needs to start after auth is initialized.
|
||||
server.StartServer(task.RootTask("api_server", false), server.Options{
|
||||
Name: "api",
|
||||
|
||||
@@ -88,6 +88,12 @@ entrypoint:
|
||||
# - name: default
|
||||
# do: proxy http://other-proxy:8080
|
||||
|
||||
defaults:
|
||||
healthcheck:
|
||||
interval: 5s
|
||||
timeout: 15s
|
||||
retries: 3
|
||||
|
||||
providers:
|
||||
# include files are standalone yaml files under `config/` directory
|
||||
#
|
||||
|
||||
128
go.mod
128
go.mod
@@ -1,24 +1,24 @@
|
||||
module github.com/yusing/godoxy
|
||||
|
||||
go 1.25.4
|
||||
go 1.25.5
|
||||
|
||||
replace github.com/yusing/godoxy/agent => ./agent
|
||||
|
||||
replace github.com/yusing/godoxy/internal/dnsproviders => ./internal/dnsproviders
|
||||
|
||||
replace github.com/coreos/go-oidc/v3 => ./internal/go-oidc
|
||||
|
||||
replace github.com/shirou/gopsutil/v4 => ./internal/gopsutil
|
||||
|
||||
replace github.com/yusing/goutils => ./goutils
|
||||
replace (
|
||||
github.com/coreos/go-oidc/v3 => ./internal/go-oidc
|
||||
github.com/shirou/gopsutil/v4 => ./internal/gopsutil
|
||||
github.com/yusing/godoxy/agent => ./agent
|
||||
github.com/yusing/godoxy/internal/dnsproviders => ./internal/dnsproviders
|
||||
github.com/yusing/goutils => ./goutils
|
||||
github.com/yusing/goutils/http/reverseproxy => ./goutils/http/reverseproxy
|
||||
github.com/yusing/goutils/http/websocket => ./goutils/http/websocket
|
||||
github.com/yusing/goutils/server => ./goutils/server
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // parsing HTML for extract fav icon
|
||||
github.com/coreos/go-oidc/v3 v3.16.0 // oidc authentication
|
||||
github.com/docker/docker v28.5.2+incompatible // docker daemon
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // parsing HTML for extract fav icon
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 // oidc authentication
|
||||
github.com/fsnotify/fsnotify v1.9.0 // file watcher
|
||||
github.com/gin-gonic/gin v1.11.0 // api server
|
||||
github.com/go-acme/lego/v4 v4.28.0 // acme client
|
||||
github.com/go-acme/lego/v4 v4.29.0 // acme client
|
||||
github.com/go-playground/validator/v10 v10.28.0 // validator
|
||||
github.com/gobwas/glob v0.2.3 // glob matcher for route rules
|
||||
github.com/gorilla/websocket v1.5.3 // websocket for API and agent
|
||||
@@ -28,35 +28,44 @@ require (
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0 // lock free map for concurrent operations
|
||||
github.com/rs/zerolog v1.34.0 // logging
|
||||
github.com/vincent-petithory/dataurl v1.0.0 // data url for fav icon
|
||||
golang.org/x/crypto v0.43.0 // encrypting password with bcrypt
|
||||
golang.org/x/net v0.46.0 // HTTP header utilities
|
||||
golang.org/x/oauth2 v0.32.0 // oauth2 authentication
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/crypto v0.45.0 // encrypting password with bcrypt
|
||||
golang.org/x/net v0.47.0 // HTTP header utilities
|
||||
golang.org/x/oauth2 v0.33.0 // oauth2 authentication
|
||||
golang.org/x/sync v0.18.0
|
||||
golang.org/x/time v0.14.0 // time utilities
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/docker/cli v28.5.2+incompatible
|
||||
github.com/goccy/go-yaml v1.18.0 // yaml parsing for different config files
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/luthermonson/go-proxmox v0.2.3
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/quic-go/quic-go v0.55.0 // http3 support
|
||||
github.com/samber/slog-zerolog/v2 v2.8.0 // indirect
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/yusing/ds v0.3.1
|
||||
github.com/yusing/godoxy/agent v0.0.0-20251101040722-306cb7a20ef4
|
||||
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20251101040722-306cb7a20ef4
|
||||
github.com/bytedance/gopkg v0.1.3 // xxhash64 for fast hash
|
||||
github.com/bytedance/sonic v1.14.2 // fast json parsing
|
||||
github.com/docker/cli v29.1.2+incompatible // needs docker/cli/cli/connhelper connection helper for docker client
|
||||
github.com/goccy/go-yaml v1.19.0 // yaml parsing for different config files
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // jwt authentication
|
||||
github.com/luthermonson/go-proxmox v0.2.3 // proxmox API client
|
||||
github.com/moby/moby/api v1.52.0 // docker API
|
||||
github.com/moby/moby/client v0.2.1 // docker client
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // maxminddb for geoip database
|
||||
github.com/quic-go/quic-go v0.57.1 // http3 support
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 // system information
|
||||
github.com/spf13/afero v1.15.0 // afero for file system operations
|
||||
github.com/stretchr/testify v1.11.1 // testing framework
|
||||
github.com/valyala/fasthttp v1.68.0 // fast http for health check
|
||||
github.com/yusing/ds v0.3.1 // data structures and algorithms
|
||||
github.com/yusing/godoxy/agent v0.0.0-20251204100826-e3fe126a5c12
|
||||
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20251204100826-e3fe126a5c12
|
||||
github.com/yusing/gointernals v0.1.16
|
||||
github.com/yusing/goutils v0.7.0
|
||||
github.com/yusing/goutils/http/reverseproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils/http/websocket v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils/server v0.0.0-20250927113415-dd977d2edaeb
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||
@@ -106,11 +115,11 @@ require (
|
||||
github.com/ovh/go-ovh v1.9.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/samber/slog-common v0.19.0 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||
github.com/sony/gobreaker v1.0.0 // indirect
|
||||
@@ -122,27 +131,19 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/ratelimit v0.3.1 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/api v0.255.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
|
||||
google.golang.org/grpc v1.76.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
google.golang.org/api v0.257.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/gopkg v0.1.3
|
||||
github.com/bytedance/sonic v1.14.2
|
||||
github.com/shirou/gopsutil/v4 v4.25.10
|
||||
github.com/valyala/fasthttp v1.68.0
|
||||
github.com/yusing/gointernals v0.1.16
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
@@ -151,36 +152,31 @@ require (
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/go-resty/resty/v2 v2.17.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/linode/linodego v1.60.0 // indirect
|
||||
github.com/linode/linodego v1.61.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.104.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.104.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.14 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vultr/govultr/v3 v3.24.0 // indirect
|
||||
github.com/vultr/govultr/v3 v3.25.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||
golang.org/x/arch v0.22.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||
)
|
||||
|
||||
161
go.sum
161
go.sum
@@ -5,10 +5,10 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3R
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
@@ -23,16 +23,14 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourceg
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 h1:h/33OxYLqBk0BYmEbSUy7MlvgQR/m1w1/7OJFKoPL1I=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0/go.mod h1:rvh3imDA6EaQi+oM/GQHkQAOHbXPKJ7EWJvfjuw141Q=
|
||||
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
|
||||
@@ -62,8 +60,6 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -75,10 +71,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/docker/cli v28.5.2+incompatible h1:XmG99IHcBmIAoC1PPg9eLBZPlTrNUAijsHLm8PjhBlg=
|
||||
github.com/docker/cli v28.5.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/cli v29.1.2+incompatible h1:s4QI7drXpIo78OM+CwuthPsO5kCf8cpNsck5PsLVTH8=
|
||||
github.com/docker/cli v29.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -99,8 +93,8 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-acme/lego/v4 v4.28.0 h1:URKsCcybo7SjqqZckeBcDN9Vl29/bKS///75tcNkMHQ=
|
||||
github.com/go-acme/lego/v4 v4.28.0/go.mod h1:bzjilr03IgbaOwlH396hq5W56Bi0/uoRwW/JM8hP7m4=
|
||||
github.com/go-acme/lego/v4 v4.29.0 h1:vKMEtvoKb0gOO9rWO9zMBwE4CgI5A5CWDsK4QEeBqzo=
|
||||
github.com/go-acme/lego/v4 v4.29.0/go.mod h1:rnYyDj1NdDd9y1dHkVuUS97j7bfe9I61+oY9odKaHM8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -121,16 +115,16 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
|
||||
github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
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/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
|
||||
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
|
||||
@@ -157,8 +151,6 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotify/server/v2 v2.7.3 h1:nro/ZnxdlZFvxFcw9LREGA8zdk6CK744azwhuhX/A4g=
|
||||
github.com/gotify/server/v2 v2.7.3/go.mod h1:VAtE1RIc/2j886PYs9WPQbMjqbFsoyQ0G8IdFtnAxU0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
@@ -177,8 +169,8 @@ github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -189,8 +181,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/linode/linodego v1.60.0 h1:SgsebJFRCi+lSmYy+C40wmKZeJllGGm+W12Qw4+yVdI=
|
||||
github.com/linode/linodego v1.60.0/go.mod h1:1+Bt0oTz5rBnDOJbGhccxn7LYVytXTIIfAy7QYmijDs=
|
||||
github.com/linode/linodego v1.61.0 h1:9g20NWl+/SbhDFj6X5EOZXtM2hBm1Mx8I9h8+F3l1LM=
|
||||
github.com/linode/linodego v1.61.0/go.mod h1:64o30geLNwR0NeYh5HM/WrVCBXcSqkKnRK3x9xoRuJI=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||
@@ -214,25 +206,21 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0=
|
||||
github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.104.0 h1:Q+nfcttPQcZHNlSXiHptnFLf1UeDGk2NEHDYI/Cr8Ts=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.104.0/go.mod h1:SfDIKzNQ5AGNMMOA3LGqSPnn63F6Gc4E4bsKArqymvg=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.104.0 h1:r1PHNdkYK2+cQlDAFvirra1u7VJ04Kjol4aTbiaWG0c=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.104.0/go.mod h1:0dBUJS+h0TkCdytcRzIBT1B5q84k9O92Hcs5YwIVtAY=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0 h1:bppmFqrJ87U4gWilemAW9oa4Qepf2JBTK/mPgaZLP2A=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0/go.mod h1:SfDIKzNQ5AGNMMOA3LGqSPnn63F6Gc4E4bsKArqymvg=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0 h1:IHPZs4Mo/lxyo+gYB+baheb2kGmHtNGQk2DKPDHqPjA=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0/go.mod h1:yELd0uJLiIyv9sGIh5ZRCHEB1B2QFNURWkQIMqb3ZwE=
|
||||
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
|
||||
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
@@ -251,7 +239,6 @@ github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaAS
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
@@ -262,10 +249,10 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
||||
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
@@ -275,8 +262,8 @@ github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89G9YI=
|
||||
github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M=
|
||||
github.com/samber/slog-zerolog/v2 v2.8.0 h1:K3+PJieRyi2rX/eaJZ95EdmpY/pzdeDd3jRnIQZG6kU=
|
||||
github.com/samber/slog-zerolog/v2 v2.8.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 h1:6LkOabJmZdNLaUWkTC3IVVA+dq7b/V0FM6lz6/7+THI=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 h1:8xfn1RzeI9yoCUuEwDy08F+No6PcKZGEDOQ6hrRyLts=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35/go.mod h1:47B1d/YXmSAxlJxUJxClzHR6b3T4M1WyCvwENPQNBWc=
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
|
||||
@@ -300,24 +287,24 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg=
|
||||
github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
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.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||
github.com/vultr/govultr/v3 v3.24.0 h1:fTTTj0VBve+Miy+wGhlb90M2NMDfpGFi6Frlj3HVy6M=
|
||||
github.com/vultr/govultr/v3 v3.24.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
|
||||
github.com/vultr/govultr/v3 v3.25.0 h1:rS8/Vdy8HlHArwmD4MtLY+hbbpYAbcnZueZrE6b0oUg=
|
||||
github.com/vultr/govultr/v3 v3.25.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
@@ -337,10 +324,6 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
@@ -349,31 +332,29 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
|
||||
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
|
||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@@ -383,10 +364,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
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=
|
||||
@@ -394,8 +375,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -415,8 +396,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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=
|
||||
@@ -435,8 +416,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -445,22 +426,22 @@ 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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.255.0 h1:OaF+IbRwOottVCYV2wZan7KUq7UeNUQn1BcPc4K7lE4=
|
||||
google.golang.org/api v0.255.0/go.mod h1:d1/EtvCLdtiWEV4rAEHDHGh2bCnqsWhw+M8y2ECN4a8=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090/go.mod h1:zwJI9HzbJJlw2KXy0wX+lmT2JuZoaKK9JC4ppqmxxjk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA=
|
||||
google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4=
|
||||
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba h1:Ze6qXW0j37YCqZdCD2LkzVSxgEWez0cO4NUyd44DiDY=
|
||||
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:4FLPzLA8eGAktPOTemJGDgDYRpLYwrNu4u2JtWINhnI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -476,3 +457,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
|
||||
2
goutils
2
goutils
Submodule goutils updated: d2c3b1966a...3fd00b70fa
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/maxmind"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
@@ -82,7 +81,7 @@ var ActiveConfig atomic.Pointer[Config]
|
||||
const cacheTTL = 1 * time.Minute
|
||||
|
||||
func (c *checkCache) Expired() bool {
|
||||
return c.created.Add(cacheTTL).Before(utils.TimeNow())
|
||||
return c.created.Add(cacheTTL).Before(time.Now())
|
||||
}
|
||||
|
||||
// TODO: add stats
|
||||
@@ -180,7 +179,7 @@ func (c *Config) cacheRecord(info *maxmind.IPInfo, allow bool) {
|
||||
c.ipCache.Store(info.Str, &checkCache{
|
||||
IPInfo: info,
|
||||
allow: allow,
|
||||
created: utils.TimeNow(),
|
||||
created: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
@@ -34,30 +35,30 @@ func GetContainer(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
client, err := docker.NewClient(dockerHost)
|
||||
dockerClient, err := docker.NewClient(dockerHost)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to create docker client"))
|
||||
return
|
||||
}
|
||||
|
||||
defer client.Close()
|
||||
defer dockerClient.Close()
|
||||
|
||||
cont, err := client.ContainerInspect(c.Request.Context(), id)
|
||||
cont, err := dockerClient.ContainerInspect(c.Request.Context(), id, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to inspect container"))
|
||||
return
|
||||
}
|
||||
|
||||
var state ContainerState
|
||||
if cont.State != nil {
|
||||
state = cont.State.Status
|
||||
if cont.Container.State != nil {
|
||||
state = cont.Container.State.Status
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &Container{
|
||||
Server: dockerHost,
|
||||
Name: cont.Name,
|
||||
ID: cont.ID,
|
||||
Image: cont.Image,
|
||||
Name: cont.Container.Name,
|
||||
ID: cont.Container.ID,
|
||||
Image: cont.Container.Image,
|
||||
State: state,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
@@ -39,12 +40,12 @@ func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Containe
|
||||
errs := gperr.NewBuilder("failed to get containers")
|
||||
containers := make([]Container, 0)
|
||||
for server, dockerClient := range dockerClients {
|
||||
conts, err := dockerClient.ContainerList(ctx, container.ListOptions{All: true})
|
||||
conts, err := dockerClient.ContainerList(ctx, client.ContainerListOptions{All: true})
|
||||
if err != nil {
|
||||
errs.Add(err)
|
||||
continue
|
||||
}
|
||||
for _, cont := range conts {
|
||||
for _, cont := range conts.Items {
|
||||
containers = append(containers, Container{
|
||||
Server: server,
|
||||
Name: cont.Names[0],
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
dockerSystem "github.com/docker/docker/api/types/system"
|
||||
"github.com/gin-gonic/gin"
|
||||
dockerSystem "github.com/moby/moby/api/types/system"
|
||||
"github.com/moby/moby/client"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
|
||||
@@ -64,13 +65,13 @@ func GetDockerInfo(ctx context.Context, dockerClients DockerClients) ([]dockerIn
|
||||
|
||||
i := 0
|
||||
for name, dockerClient := range dockerClients {
|
||||
info, err := dockerClient.Info(ctx)
|
||||
info, err := dockerClient.Info(ctx, client.InfoOptions{})
|
||||
if err != nil {
|
||||
errs.Add(err)
|
||||
continue
|
||||
}
|
||||
info.Name = name
|
||||
dockerInfos[i] = toDockerInfo(info)
|
||||
info.Info.Name = name
|
||||
dockerInfos[i] = toDockerInfo(info.Info)
|
||||
i++
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/api/pkg/stdcopy"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
@@ -70,7 +70,7 @@ func Logs(c *gin.Context) {
|
||||
}
|
||||
defer dockerClient.Close()
|
||||
|
||||
opts := container.LogsOptions{
|
||||
opts := client.ContainerLogsOptions{
|
||||
ShowStdout: queryParams.Stdout,
|
||||
ShowStderr: queryParams.Stderr,
|
||||
Since: queryParams.Since,
|
||||
|
||||
@@ -4,17 +4,23 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type RestartRequest struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
client.ContainerRestartOptions
|
||||
}
|
||||
|
||||
// @x-id "restart"
|
||||
// @BasePath /api/v1
|
||||
// @Summary Restart container
|
||||
// @Description Restart container by container id
|
||||
// @Tags docker
|
||||
// @Produce json
|
||||
// @Param request body StopRequest true "Request"
|
||||
// @Param request body RestartRequest true "Request"
|
||||
// @Success 200 {object} apitypes.SuccessResponse
|
||||
// @Failure 400 {object} apitypes.ErrorResponse "Invalid request"
|
||||
// @Failure 403 {object} apitypes.ErrorResponse
|
||||
@@ -22,7 +28,7 @@ import (
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /docker/restart [post]
|
||||
func Restart(c *gin.Context) {
|
||||
var req StopRequest
|
||||
var req RestartRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
@@ -42,7 +48,7 @@ func Restart(c *gin.Context) {
|
||||
|
||||
defer client.Close()
|
||||
|
||||
err = client.ContainerRestart(c.Request.Context(), req.ID, req.StopOptions)
|
||||
_, err = client.ContainerRestart(c.Request.Context(), req.ID, req.ContainerRestartOptions)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to restart container"))
|
||||
return
|
||||
|
||||
@@ -3,15 +3,15 @@ package dockerapi
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type StartRequest struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
container.StartOptions
|
||||
client.ContainerStartOptions
|
||||
}
|
||||
|
||||
// @x-id "start"
|
||||
@@ -48,7 +48,7 @@ func Start(c *gin.Context) {
|
||||
|
||||
defer client.Close()
|
||||
|
||||
err = client.ContainerStart(c.Request.Context(), req.ID, req.StartOptions)
|
||||
_, err = client.ContainerStart(c.Request.Context(), req.ID, req.ContainerStartOptions)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to start container"))
|
||||
return
|
||||
|
||||
@@ -3,15 +3,15 @@ package dockerapi
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type StopRequest struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
container.StopOptions
|
||||
client.ContainerStopOptions
|
||||
}
|
||||
|
||||
// @x-id "stop"
|
||||
@@ -48,7 +48,7 @@ func Stop(c *gin.Context) {
|
||||
|
||||
defer client.Close()
|
||||
|
||||
err = client.ContainerStop(c.Request.Context(), req.ID, req.StopOptions)
|
||||
_, err = client.ContainerStop(c.Request.Context(), req.ID, req.ContainerStopOptions)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to stop container"))
|
||||
return
|
||||
|
||||
@@ -618,7 +618,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dockerapi.StopRequest"
|
||||
"$ref": "#/definitions/dockerapi.RestartRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -3515,6 +3515,16 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"sticky": {
|
||||
"type": "boolean",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"sticky_max_age": {
|
||||
"$ref": "#/definitions/time.Duration",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"weight": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
@@ -4198,9 +4208,13 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"healthcheck": {
|
||||
"$ref": "#/definitions/HealthCheckConfig",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
"description": "null on load-balancer routes",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HealthCheckConfig"
|
||||
}
|
||||
],
|
||||
"x-nullable": true
|
||||
},
|
||||
"homepage": {
|
||||
"$ref": "#/definitions/HomepageItemConfig",
|
||||
@@ -4916,12 +4930,16 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"container.Port": {
|
||||
"container.PortSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"IP": {
|
||||
"description": "Host IP address that the container's port is mapped to",
|
||||
"type": "string",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/netip.Addr"
|
||||
}
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -4938,7 +4956,7 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"Type": {
|
||||
"description": "type\nRequired: true",
|
||||
"description": "type\nRequired: true\nEnum: [\"tcp\",\"udp\",\"sctp\"]",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -5033,6 +5051,29 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"dockerapi.RestartRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"signal": {
|
||||
"description": "Signal (optional) is the signal to send to the container to (gracefully)\nstop it before forcibly terminating the container with SIGKILL after the\ntimeout expires. If no value is set, the default (SIGTERM) is used.",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "Timeout (optional) is the timeout (in seconds) to wait for the container\nto stop gracefully before forcibly terminating it with SIGKILL.\n\n- Use nil to use the default timeout (10 seconds).\n- Use '-1' to wait indefinitely.\n- Use '0' to not wait for the container to exit gracefully, and\n immediately proceeds to forcibly terminating the container.\n- Other positive values are used as timeout (in seconds).",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"dockerapi.StartRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -5066,7 +5107,7 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"signal": {
|
||||
"description": "Signal (optional) is the signal to send to the container to (gracefully)\nstop it before forcibly terminating the container with SIGKILL after the\ntimeout expires. If not value is set, the default (SIGTERM) is used.",
|
||||
"description": "Signal (optional) is the signal to send to the container to (gracefully)\nstop it before forcibly terminating the container with SIGKILL after the\ntimeout expires. If no value is set, the default (SIGTERM) is used.",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
@@ -5219,6 +5260,20 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"netip.Addr": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "ipv4"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "ipv6"
|
||||
}
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"route.Route": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5273,9 +5328,13 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"healthcheck": {
|
||||
"$ref": "#/definitions/HealthCheckConfig",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
"description": "null on load-balancer routes",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HealthCheckConfig"
|
||||
}
|
||||
],
|
||||
"x-nullable": true
|
||||
},
|
||||
"homepage": {
|
||||
"$ref": "#/definitions/HomepageItemConfig",
|
||||
@@ -5530,7 +5589,7 @@
|
||||
"types.PortMapping": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/container.Port"
|
||||
"$ref": "#/definitions/container.PortSummary"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
|
||||
@@ -555,6 +555,10 @@ definitions:
|
||||
options:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
sticky:
|
||||
type: boolean
|
||||
sticky_max_age:
|
||||
$ref: '#/definitions/time.Duration'
|
||||
weight:
|
||||
type: integer
|
||||
type: object
|
||||
@@ -881,7 +885,10 @@ definitions:
|
||||
- $ref: '#/definitions/HealthJSON'
|
||||
description: for swagger
|
||||
healthcheck:
|
||||
$ref: '#/definitions/HealthCheckConfig'
|
||||
allOf:
|
||||
- $ref: '#/definitions/HealthCheckConfig'
|
||||
description: null on load-balancer routes
|
||||
x-nullable: true
|
||||
homepage:
|
||||
$ref: '#/definitions/HomepageItemConfig'
|
||||
host:
|
||||
@@ -1248,11 +1255,12 @@ definitions:
|
||||
- StateRemoving
|
||||
- StateExited
|
||||
- StateDead
|
||||
container.Port:
|
||||
container.PortSummary:
|
||||
properties:
|
||||
IP:
|
||||
allOf:
|
||||
- $ref: '#/definitions/netip.Addr'
|
||||
description: Host IP address that the container's port is mapped to
|
||||
type: string
|
||||
PrivatePort:
|
||||
description: |-
|
||||
Port on the container
|
||||
@@ -1265,6 +1273,7 @@ definitions:
|
||||
description: |-
|
||||
type
|
||||
Required: true
|
||||
Enum: ["tcp","udp","sctp"]
|
||||
type: string
|
||||
type: object
|
||||
disk.IOCountersStat:
|
||||
@@ -1316,6 +1325,30 @@ definitions:
|
||||
used_percent:
|
||||
type: number
|
||||
type: object
|
||||
dockerapi.RestartRequest:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
signal:
|
||||
description: |-
|
||||
Signal (optional) is the signal to send to the container to (gracefully)
|
||||
stop it before forcibly terminating the container with SIGKILL after the
|
||||
timeout expires. If no value is set, the default (SIGTERM) is used.
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
Timeout (optional) is the timeout (in seconds) to wait for the container
|
||||
to stop gracefully before forcibly terminating it with SIGKILL.
|
||||
|
||||
- Use nil to use the default timeout (10 seconds).
|
||||
- Use '-1' to wait indefinitely.
|
||||
- Use '0' to not wait for the container to exit gracefully, and
|
||||
immediately proceeds to forcibly terminating the container.
|
||||
- Other positive values are used as timeout (in seconds).
|
||||
type: integer
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
dockerapi.StartRequest:
|
||||
properties:
|
||||
checkpointDir:
|
||||
@@ -1335,7 +1368,7 @@ definitions:
|
||||
description: |-
|
||||
Signal (optional) is the signal to send to the container to (gracefully)
|
||||
stop it before forcibly terminating the container with SIGKILL after the
|
||||
timeout expires. If not value is set, the default (SIGTERM) is used.
|
||||
timeout expires. If no value is set, the default (SIGTERM) is used.
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
@@ -1429,6 +1462,8 @@ definitions:
|
||||
description: godoxy
|
||||
type: number
|
||||
type: object
|
||||
netip.Addr:
|
||||
type: object
|
||||
route.Route:
|
||||
properties:
|
||||
access_log:
|
||||
@@ -1457,7 +1492,10 @@ definitions:
|
||||
- $ref: '#/definitions/HealthJSON'
|
||||
description: for swagger
|
||||
healthcheck:
|
||||
$ref: '#/definitions/HealthCheckConfig'
|
||||
allOf:
|
||||
- $ref: '#/definitions/HealthCheckConfig'
|
||||
description: null on load-balancer routes
|
||||
x-nullable: true
|
||||
homepage:
|
||||
$ref: '#/definitions/HomepageItemConfig'
|
||||
host:
|
||||
@@ -1592,7 +1630,7 @@ definitions:
|
||||
type: object
|
||||
types.PortMapping:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/container.Port'
|
||||
$ref: '#/definitions/container.PortSummary'
|
||||
type: object
|
||||
widgets.Config:
|
||||
properties:
|
||||
@@ -2008,7 +2046,7 @@ paths:
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dockerapi.StopRequest'
|
||||
$ref: '#/definitions/dockerapi.RestartRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
"github.com/yusing/goutils/fs"
|
||||
)
|
||||
|
||||
type ListFilesResponse struct {
|
||||
@@ -35,7 +35,7 @@ func List(c *gin.Context) {
|
||||
}
|
||||
|
||||
// config/
|
||||
files, err := utils.ListFiles(common.ConfigBasePath, 0, true)
|
||||
files, err := fs.ListFiles(common.ConfigBasePath, 0, true)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to list files"))
|
||||
return
|
||||
@@ -48,7 +48,7 @@ func List(c *gin.Context) {
|
||||
}
|
||||
|
||||
// config/middlewares/
|
||||
mids, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||
mids, err := fs.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to list files"))
|
||||
return
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
type RawRule struct {
|
||||
@@ -348,7 +349,7 @@ func checkMatchedRules(rulesList rules.Rules, w http.ResponseWriter, r *http.Req
|
||||
var matched []string
|
||||
|
||||
// Create a ResponseModifier to properly check rules
|
||||
rm := rules.NewResponseModifier(w)
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
|
||||
for _, rule := range rulesList {
|
||||
// Check if rule matches
|
||||
|
||||
@@ -31,6 +31,8 @@ type (
|
||||
endSessionURL *url.URL
|
||||
allowedUsers []string
|
||||
allowedGroups []string
|
||||
|
||||
onUnknownPathHandler http.HandlerFunc
|
||||
}
|
||||
|
||||
IDTokenClaims struct {
|
||||
@@ -64,8 +66,9 @@ func (auth *OIDCProvider) getAppScopedCookieName(baseName string) string {
|
||||
|
||||
const (
|
||||
OIDCAuthInitPath = "/"
|
||||
OIDCPostAuthPath = "/auth/callback"
|
||||
OIDCLogoutPath = "/auth/logout"
|
||||
OIDCAuthBasePath = "/auth"
|
||||
OIDCPostAuthPath = OIDCAuthBasePath + "/callback"
|
||||
OIDCLogoutPath = OIDCAuthBasePath + "/logout"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -177,6 +180,10 @@ func (auth *OIDCProvider) SetScopes(scopes []string) {
|
||||
auth.oauthConfig.Scopes = scopes
|
||||
}
|
||||
|
||||
func (auth *OIDCProvider) SetOnUnknownPathHandler(handler http.HandlerFunc) {
|
||||
auth.onUnknownPathHandler = handler
|
||||
}
|
||||
|
||||
// optRedirectPostAuth returns an oauth2 option that sets the "redirect_uri"
|
||||
// parameter of the authorization URL to the post auth path of the current
|
||||
// request host.
|
||||
@@ -213,6 +220,10 @@ func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
|
||||
case OIDCLogoutPath:
|
||||
auth.LogoutHandler(w, r)
|
||||
default:
|
||||
if auth.onUnknownPathHandler != nil {
|
||||
auth.onUnknownPathHandler(w, r)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, OIDCAuthInitPath, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,18 @@ import (
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
Domains []string `json:"domains,omitempty"`
|
||||
CertPath string `json:"cert_path,omitempty"`
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
ACMEKeyPath string `json:"acme_key_path,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Options map[string]any `json:"options,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Domains []string `json:"domains,omitempty"`
|
||||
CertPath string `json:"cert_path,omitempty"`
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
ACMEKeyPath string `json:"acme_key_path,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Options map[string]strutils.Redacted `json:"options,omitempty"`
|
||||
|
||||
Resolvers []string `json:"resolvers,omitempty"`
|
||||
|
||||
@@ -96,7 +96,7 @@ func (cfg *Config) Validate() gperr.Error {
|
||||
if cfg.Provider != ProviderCustom {
|
||||
b.Add(ErrUnknownProvider.
|
||||
Subject(cfg.Provider).
|
||||
With(gperr.DoYouMean(utils.NearestField(cfg.Provider, Providers))))
|
||||
With(gperr.DoYouMeanField(cfg.Provider, Providers)))
|
||||
}
|
||||
} else {
|
||||
provider, err := providerConstructor(cfg.Options)
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
type Generator func(map[string]any) (challenge.Provider, gperr.Error)
|
||||
type Generator func(map[string]strutils.Redacted) (challenge.Provider, gperr.Error)
|
||||
|
||||
var Providers = make(map[string]Generator)
|
||||
|
||||
@@ -14,10 +15,10 @@ func DNSProvider[CT any, PT challenge.Provider](
|
||||
defaultCfg func() *CT,
|
||||
newProvider func(*CT) (PT, error),
|
||||
) Generator {
|
||||
return func(opt map[string]any) (challenge.Provider, gperr.Error) {
|
||||
return func(opt map[string]strutils.Redacted) (challenge.Provider, gperr.Error) {
|
||||
cfg := defaultCfg()
|
||||
if len(opt) > 0 {
|
||||
err := serialization.MapUnmarshalValidate(opt, &cfg)
|
||||
err := serialization.MapUnmarshalValidate(serialization.ToSerializedObject(opt), &cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// file, folder structure
|
||||
|
||||
const (
|
||||
@@ -38,10 +34,6 @@ var RequiredDirectories = []string{
|
||||
const DockerHostFromEnv = "$DOCKER_HOST"
|
||||
|
||||
const (
|
||||
HealthCheckIntervalDefault = 5 * time.Second
|
||||
HealthCheckTimeoutDefault = 5 * time.Second
|
||||
HealthCheckDownNotifyDelayDefault = 15 * time.Second
|
||||
|
||||
WakeTimeoutDefault = "3m"
|
||||
StopTimeoutDefault = "3m"
|
||||
StopMethodDefault = "stop"
|
||||
|
||||
@@ -409,5 +409,6 @@ func (state *state) printRoutesByProvider(lenLongestName int) {
|
||||
|
||||
func (state *state) printState() {
|
||||
state.tmpLog.Info().Msg("active config:")
|
||||
yaml.NewEncoder(state.tmpLog).Encode(state.Config)
|
||||
yamlRepr, _ := yaml.Marshal(state.Config)
|
||||
state.tmpLog.Info().Msgf("%s", yamlRepr) // prevent copying when casting to string
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/proxmox"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
@@ -25,8 +26,12 @@ type (
|
||||
Providers Providers `json:"providers"`
|
||||
MatchDomains []string `json:"match_domains" validate:"domain_name"`
|
||||
Homepage homepage.Config `json:"homepage"`
|
||||
Defaults Defaults `json:"defaults"`
|
||||
TimeoutShutdown int `json:"timeout_shutdown" validate:"gte=0"`
|
||||
}
|
||||
Defaults struct {
|
||||
HealthCheck types.HealthCheckConfig `json:"healthcheck"`
|
||||
}
|
||||
Providers struct {
|
||||
Files []string `json:"include" yaml:"include,omitempty" validate:"dive,filepath"`
|
||||
Docker map[string]string `json:"docker" yaml:"docker,omitempty" validate:"non_empty_docker_keys,dive,unix_addr|url"`
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
module github.com/yusing/godoxy/internal/dnsproviders
|
||||
|
||||
go 1.25.4
|
||||
go 1.25.5
|
||||
|
||||
replace github.com/yusing/godoxy => ../..
|
||||
|
||||
require (
|
||||
github.com/go-acme/lego/v4 v4.28.0
|
||||
github.com/yusing/godoxy v0.20.6
|
||||
github.com/go-acme/lego/v4 v4.29.0
|
||||
github.com/yusing/godoxy v0.20.11
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||
@@ -37,8 +37,8 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.17.0 // indirect
|
||||
github.com/goccy/go-yaml v1.19.0 // indirect
|
||||
github.com/gofrs/flock v0.13.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
@@ -52,15 +52,15 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/linode/linodego v1.60.0 // indirect
|
||||
github.com/linode/linodego v1.61.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/maxatome/go-testdeep v1.14.0 // indirect
|
||||
github.com/miekg/dns v1.1.68 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/nrdcg/goacmedns v0.2.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.104.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.104.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||
github.com/ovh/go-ovh v1.9.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
@@ -72,7 +72,7 @@ require (
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/vultr/govultr/v3 v3.24.0 // indirect
|
||||
github.com/vultr/govultr/v3 v3.25.0 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yusing/gointernals v0.1.16 // indirect
|
||||
github.com/yusing/goutils v0.7.0 // indirect
|
||||
@@ -81,20 +81,19 @@ require (
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.1 // indirect
|
||||
golang.org/x/arch v0.22.0 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/oauth2 v0.32.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/api v0.255.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
|
||||
google.golang.org/grpc v1.76.0 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.33.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
google.golang.org/api v0.257.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
@@ -5,10 +5,10 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3R
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
@@ -55,8 +55,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/go-acme/lego/v4 v4.28.0 h1:URKsCcybo7SjqqZckeBcDN9Vl29/bKS///75tcNkMHQ=
|
||||
github.com/go-acme/lego/v4 v4.28.0/go.mod h1:bzjilr03IgbaOwlH396hq5W56Bi0/uoRwW/JM8hP7m4=
|
||||
github.com/go-acme/lego/v4 v4.29.0 h1:vKMEtvoKb0gOO9rWO9zMBwE4CgI5A5CWDsK4QEeBqzo=
|
||||
github.com/go-acme/lego/v4 v4.29.0/go.mod h1:rnYyDj1NdDd9y1dHkVuUS97j7bfe9I61+oY9odKaHM8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -74,10 +74,10 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
|
||||
github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
|
||||
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
|
||||
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
|
||||
@@ -120,8 +120,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/linode/linodego v1.60.0 h1:SgsebJFRCi+lSmYy+C40wmKZeJllGGm+W12Qw4+yVdI=
|
||||
github.com/linode/linodego v1.60.0/go.mod h1:1+Bt0oTz5rBnDOJbGhccxn7LYVytXTIIfAy7QYmijDs=
|
||||
github.com/linode/linodego v1.61.0 h1:9g20NWl+/SbhDFj6X5EOZXtM2hBm1Mx8I9h8+F3l1LM=
|
||||
github.com/linode/linodego v1.61.0/go.mod h1:64o30geLNwR0NeYh5HM/WrVCBXcSqkKnRK3x9xoRuJI=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
@@ -137,10 +137,10 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0=
|
||||
github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.104.0 h1:Q+nfcttPQcZHNlSXiHptnFLf1UeDGk2NEHDYI/Cr8Ts=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.104.0/go.mod h1:SfDIKzNQ5AGNMMOA3LGqSPnn63F6Gc4E4bsKArqymvg=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.104.0 h1:r1PHNdkYK2+cQlDAFvirra1u7VJ04Kjol4aTbiaWG0c=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.104.0/go.mod h1:0dBUJS+h0TkCdytcRzIBT1B5q84k9O92Hcs5YwIVtAY=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0 h1:bppmFqrJ87U4gWilemAW9oa4Qepf2JBTK/mPgaZLP2A=
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0/go.mod h1:SfDIKzNQ5AGNMMOA3LGqSPnn63F6Gc4E4bsKArqymvg=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0 h1:IHPZs4Mo/lxyo+gYB+baheb2kGmHtNGQk2DKPDHqPjA=
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0/go.mod h1:yELd0uJLiIyv9sGIh5ZRCHEB1B2QFNURWkQIMqb3ZwE=
|
||||
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
|
||||
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
|
||||
github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=
|
||||
@@ -178,8 +178,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
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/vultr/govultr/v3 v3.24.0 h1:fTTTj0VBve+Miy+wGhlb90M2NMDfpGFi6Frlj3HVy6M=
|
||||
github.com/vultr/govultr/v3 v3.24.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
|
||||
github.com/vultr/govultr/v3 v3.25.0 h1:rS8/Vdy8HlHArwmD4MtLY+hbbpYAbcnZueZrE6b0oUg=
|
||||
github.com/vultr/govultr/v3 v3.25.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yusing/gointernals v0.1.16 h1:GrhZZdxzA+jojLEqankctJrOuAYDb7kY1C93S1pVR34=
|
||||
@@ -206,43 +206,43 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
|
||||
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
|
||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.255.0 h1:OaF+IbRwOottVCYV2wZan7KUq7UeNUQn1BcPc4K7lE4=
|
||||
google.golang.org/api v0.255.0/go.mod h1:d1/EtvCLdtiWEV4rAEHDHGh2bCnqsWhw+M8y2ECN4a8=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090/go.mod h1:zwJI9HzbJJlw2KXy0wX+lmT2JuZoaKK9JC4ppqmxxjk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA=
|
||||
google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4=
|
||||
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba h1:Ze6qXW0j37YCqZdCD2LkzVSxgEWez0cO4NUyd44DiDY=
|
||||
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:4FLPzLA8eGAktPOTemJGDgDYRpLYwrNu4u2JtWINhnI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
@@ -110,6 +110,8 @@ func Clients() map[string]*SharedClient {
|
||||
return clients
|
||||
}
|
||||
|
||||
var versionArg = client.WithAPIVersionNegotiation()
|
||||
|
||||
// NewClient creates a new Docker client connection to the specified host.
|
||||
//
|
||||
// Returns existing client if available.
|
||||
@@ -152,7 +154,7 @@ func NewClient(host string, unique ...bool) (*SharedClient, error) {
|
||||
opt = []client.Opt{
|
||||
client.WithHost(agent.DockerHost),
|
||||
client.WithHTTPClient(cfg.NewHTTPClient()),
|
||||
client.WithAPIVersionNegotiation(),
|
||||
versionArg,
|
||||
}
|
||||
addr = "tcp://" + cfg.Addr
|
||||
dial = cfg.DialContext
|
||||
@@ -163,7 +165,7 @@ func NewClient(host string, unique ...bool) (*SharedClient, error) {
|
||||
case common.DockerHostFromEnv:
|
||||
opt = []client.Opt{
|
||||
client.WithHostFromEnv(),
|
||||
client.WithAPIVersionNegotiation(),
|
||||
versionArg,
|
||||
}
|
||||
default:
|
||||
helper, err := connhelper.GetConnectionHelper(host)
|
||||
@@ -179,19 +181,19 @@ func NewClient(host string, unique ...bool) (*SharedClient, error) {
|
||||
opt = []client.Opt{
|
||||
client.WithHTTPClient(httpClient),
|
||||
client.WithHost(helper.Host),
|
||||
client.WithAPIVersionNegotiation(),
|
||||
versionArg,
|
||||
client.WithDialContext(helper.Dialer),
|
||||
}
|
||||
} else {
|
||||
opt = []client.Opt{
|
||||
client.WithHost(host),
|
||||
client.WithAPIVersionNegotiation(),
|
||||
versionArg,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client, err := client.NewClientWithOpts(opt...)
|
||||
client, err := client.New(opt...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
@@ -92,24 +92,24 @@ func IsBlacklisted(c *types.Container) bool {
|
||||
}
|
||||
|
||||
func UpdatePorts(c *types.Container) error {
|
||||
client, err := NewClient(c.DockerHost)
|
||||
dockerClient, err := NewClient(c.DockerHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
defer dockerClient.Close()
|
||||
|
||||
inspect, err := client.ContainerInspect(context.Background(), c.ContainerID)
|
||||
inspect, err := dockerClient.ContainerInspect(context.Background(), c.ContainerID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for port := range inspect.Config.ExposedPorts {
|
||||
proto, portStr := nat.SplitProtoPort(string(port))
|
||||
for port := range inspect.Container.Config.ExposedPorts {
|
||||
proto, portStr := nat.SplitProtoPort(port.String())
|
||||
portInt, _ := nat.ParsePort(portStr)
|
||||
if portInt == 0 {
|
||||
continue
|
||||
}
|
||||
c.PublicPortMapping[portInt] = container.Port{
|
||||
c.PublicPortMapping[portInt] = container.PortSummary{
|
||||
PublicPort: uint16(portInt), //nolint:gosec
|
||||
PrivatePort: uint16(portInt), //nolint:gosec
|
||||
Type: proto,
|
||||
@@ -207,8 +207,8 @@ func setPrivateHostname(c *types.Container, helper containerHelper) {
|
||||
}
|
||||
if c.Network != "" {
|
||||
v, ok := helper.NetworkSettings.Networks[c.Network]
|
||||
if ok {
|
||||
c.PrivateHostname = v.IPAddress
|
||||
if ok && v.IPAddress.IsValid() {
|
||||
c.PrivateHostname = v.IPAddress.String()
|
||||
return
|
||||
}
|
||||
// try {project_name}_{network_name}
|
||||
@@ -216,22 +216,22 @@ func setPrivateHostname(c *types.Container, helper containerHelper) {
|
||||
oldNetwork, newNetwork := c.Network, fmt.Sprintf("%s_%s", proj, c.Network)
|
||||
if newNetwork != oldNetwork {
|
||||
v, ok = helper.NetworkSettings.Networks[newNetwork]
|
||||
if ok {
|
||||
if ok && v.IPAddress.IsValid() {
|
||||
c.Network = newNetwork // update network to the new one
|
||||
c.PrivateHostname = v.IPAddress
|
||||
c.PrivateHostname = v.IPAddress.String()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
nearest := gperr.DoYouMean(utils.NearestField(c.Network, helper.NetworkSettings.Networks))
|
||||
nearest := gperr.DoYouMeanField(c.Network, helper.NetworkSettings.Networks)
|
||||
addError(c, fmt.Errorf("network %q not found, %w", c.Network, nearest))
|
||||
return
|
||||
}
|
||||
// fallback to first network if no network is specified
|
||||
for k, v := range helper.NetworkSettings.Networks {
|
||||
if v.IPAddress != "" {
|
||||
if v.IPAddress.IsValid() {
|
||||
c.Network = k // update network to the first network
|
||||
c.PrivateHostname = v.IPAddress
|
||||
c.PrivateHostname = v.IPAddress.String()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package docker
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/yusing/ds/ordered"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
|
||||
@@ -3,7 +3,7 @@ package docker
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
@@ -12,6 +13,16 @@ import (
|
||||
|
||||
var ErrInvalidLabel = gperr.New("invalid label")
|
||||
|
||||
const nsProxyDot = NSProxy + "."
|
||||
|
||||
var refPrefixes = func() []string {
|
||||
prefixes := make([]string, 100)
|
||||
for i := range prefixes {
|
||||
prefixes[i] = nsProxyDot + "#" + strconv.Itoa(i+1) + "."
|
||||
}
|
||||
return prefixes
|
||||
}()
|
||||
|
||||
func ParseLabels(labels map[string]string, aliases ...string) (types.LabelMap, gperr.Error) {
|
||||
nestedMap := make(types.LabelMap)
|
||||
errs := gperr.NewBuilder("labels error")
|
||||
@@ -57,57 +68,83 @@ func ParseLabels(labels map[string]string, aliases ...string) (types.LabelMap, g
|
||||
}
|
||||
|
||||
func ExpandWildcard(labels map[string]string, aliases ...string) {
|
||||
// collect all explicit aliases first
|
||||
aliasSet := make(map[string]int, len(labels))
|
||||
// wildcardLabels holds mapping suffix -> value derived from wildcard label definitions
|
||||
wildcardLabels := make(map[string]string)
|
||||
|
||||
aliasSet := make(map[string]int, len(aliases))
|
||||
for i, alias := range aliases {
|
||||
aliasSet[alias] = i
|
||||
}
|
||||
|
||||
// iterate over a copy of the keys to safely mutate the map while ranging
|
||||
wildcardLabels := make(map[string]string)
|
||||
|
||||
// First pass: collect wildcards and discover aliases
|
||||
for lbl, value := range labels {
|
||||
parts := strings.SplitN(lbl, ".", 3)
|
||||
if len(parts) < 2 || parts[0] != NSProxy {
|
||||
if !strings.HasPrefix(lbl, nsProxyDot) {
|
||||
continue
|
||||
}
|
||||
alias := parts[1]
|
||||
if alias == WildcardAlias { // "*"
|
||||
// remove wildcard label from original map – it should not remain afterwards
|
||||
// lbl is "proxy.X..." where X is alias or wildcard
|
||||
rest := lbl[len(nsProxyDot):] // "X..." or "X.suffix"
|
||||
dotIdx := strings.IndexByte(rest, '.')
|
||||
var alias, suffix string
|
||||
if dotIdx == -1 {
|
||||
alias = rest
|
||||
} else {
|
||||
alias = rest[:dotIdx]
|
||||
suffix = rest[dotIdx+1:]
|
||||
}
|
||||
|
||||
if alias == WildcardAlias {
|
||||
delete(labels, lbl)
|
||||
|
||||
// value looks like YAML (multiline)
|
||||
if strings.Count(value, "\n") > 1 {
|
||||
if suffix == "" || strings.Count(value, "\n") > 1 {
|
||||
expandYamlWildcard(value, wildcardLabels)
|
||||
continue
|
||||
} else {
|
||||
wildcardLabels[suffix] = value
|
||||
}
|
||||
|
||||
// normal wildcard label with suffix – store directly
|
||||
wildcardLabels[parts[2]] = value
|
||||
continue
|
||||
}
|
||||
// explicit alias label – remember the alias (but not reference aliases like #1, #2)
|
||||
if _, ok := aliasSet[alias]; !ok && !strings.HasPrefix(alias, "#") {
|
||||
|
||||
if suffix == "" || alias[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, known := aliasSet[alias]; !known {
|
||||
aliasSet[alias] = len(aliasSet)
|
||||
}
|
||||
}
|
||||
|
||||
if len(aliasSet) == 0 || len(wildcardLabels) == 0 {
|
||||
return // nothing to expand
|
||||
return
|
||||
}
|
||||
|
||||
// expand collected wildcard labels for every alias
|
||||
for suffix, v := range wildcardLabels {
|
||||
for alias, i := range aliasSet {
|
||||
// use numeric index instead of the alias name
|
||||
alias = fmt.Sprintf("#%d", i+1)
|
||||
// Second pass: convert explicit labels to #N format
|
||||
for lbl, value := range labels {
|
||||
if !strings.HasPrefix(lbl, nsProxyDot) {
|
||||
continue
|
||||
}
|
||||
rest := lbl[len(nsProxyDot):]
|
||||
dotIdx := strings.IndexByte(rest, '.')
|
||||
if dotIdx == -1 {
|
||||
continue
|
||||
}
|
||||
alias := rest[:dotIdx]
|
||||
if alias[0] == '#' {
|
||||
continue
|
||||
}
|
||||
suffix := rest[dotIdx+1:]
|
||||
|
||||
key := fmt.Sprintf("%s.%s.%s", NSProxy, alias, suffix)
|
||||
if suffix == "" { // this should not happen (root wildcard handled earlier) but keep safe
|
||||
key = fmt.Sprintf("%s.%s", NSProxy, alias)
|
||||
}
|
||||
labels[key] = v
|
||||
idx, known := aliasSet[alias]
|
||||
if !known {
|
||||
continue
|
||||
}
|
||||
|
||||
delete(labels, lbl)
|
||||
if _, overridden := wildcardLabels[suffix]; !overridden {
|
||||
labels[refPrefixes[idx]+suffix] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Expand wildcards for all aliases
|
||||
for suffix, value := range wildcardLabels {
|
||||
for _, idx := range aliasSet {
|
||||
labels[refPrefixes[idx]+suffix] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,12 +176,46 @@ func flattenMap(prefix string, src map[string]any, dest map[string]string) {
|
||||
case map[string]any:
|
||||
flattenMap(key, vv, dest)
|
||||
case map[any]any:
|
||||
// convert to map[string]any by stringifying keys
|
||||
tmp := make(map[string]any, len(vv))
|
||||
for kk, vvv := range vv {
|
||||
tmp[fmt.Sprintf("%v", kk)] = vvv
|
||||
}
|
||||
flattenMap(key, tmp, dest)
|
||||
flattenMapAny(key, vv, dest)
|
||||
case string:
|
||||
dest[key] = vv
|
||||
case int:
|
||||
dest[key] = strconv.Itoa(vv)
|
||||
case bool:
|
||||
dest[key] = strconv.FormatBool(vv)
|
||||
case float64:
|
||||
dest[key] = strconv.FormatFloat(vv, 'f', -1, 64)
|
||||
default:
|
||||
dest[key] = fmt.Sprint(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func flattenMapAny(prefix string, src map[any]any, dest map[string]string) {
|
||||
for k, v := range src {
|
||||
var key string
|
||||
switch kk := k.(type) {
|
||||
case string:
|
||||
key = kk
|
||||
default:
|
||||
key = fmt.Sprint(k)
|
||||
}
|
||||
if prefix != "" {
|
||||
key = prefix + "." + key
|
||||
}
|
||||
switch vv := v.(type) {
|
||||
case map[string]any:
|
||||
flattenMap(key, vv, dest)
|
||||
case map[any]any:
|
||||
flattenMapAny(key, vv, dest)
|
||||
case string:
|
||||
dest[key] = vv
|
||||
case int:
|
||||
dest[key] = strconv.Itoa(vv)
|
||||
case bool:
|
||||
dest[key] = strconv.FormatBool(vv)
|
||||
case float64:
|
||||
dest[key] = strconv.FormatFloat(vv, 'f', -1, 64)
|
||||
default:
|
||||
dest[key] = fmt.Sprint(v)
|
||||
}
|
||||
|
||||
@@ -8,87 +8,248 @@ import (
|
||||
)
|
||||
|
||||
func TestExpandWildcard(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.b.port": "4444",
|
||||
"proxy.b.scheme": "http",
|
||||
"proxy.*.port": "5555",
|
||||
"proxy.*.healthcheck.disable": "true",
|
||||
}
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.b.port": "4444",
|
||||
"proxy.b.scheme": "http",
|
||||
"proxy.*.port": "5555",
|
||||
"proxy.*.healthcheck.disable": "true",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.healthcheck.disable": "true",
|
||||
"proxy.#2.port": "5555",
|
||||
"proxy.#2.scheme": "http",
|
||||
"proxy.#2.healthcheck.disable": "true",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
docker.ExpandWildcard(labels)
|
||||
t.Run("no wildcards", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.b.port": "4444",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.b.port": "4444",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.a.port": "5555",
|
||||
"proxy.a.healthcheck.disable": "true",
|
||||
"proxy.b.scheme": "http",
|
||||
"proxy.b.port": "5555",
|
||||
"proxy.b.healthcheck.disable": "true",
|
||||
}, labels)
|
||||
}
|
||||
t.Run("no aliases", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.*.port": "5555",
|
||||
}
|
||||
docker.ExpandWildcard(labels)
|
||||
require.Equal(t, map[string]string{}, labels)
|
||||
})
|
||||
|
||||
func TestExpandWildcardWithFQDNAliases(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.c.host": "localhost",
|
||||
"proxy.*.port": "5555",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a.example.com", "b.example.com")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#2.port": "5555",
|
||||
"proxy.c.host": "localhost",
|
||||
"proxy.c.port": "5555",
|
||||
}, labels)
|
||||
t.Run("empty labels", func(t *testing.T) {
|
||||
labels := map[string]string{}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{}, labels)
|
||||
})
|
||||
|
||||
t.Run("only wildcards no explicit labels", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.*.port": "5555",
|
||||
"proxy.*.scheme": "https",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.scheme": "https",
|
||||
"proxy.#2.port": "5555",
|
||||
"proxy.#2.scheme": "https",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("non-proxy labels unchanged", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"other.label": "value",
|
||||
"proxy.*.port": "5555",
|
||||
"proxy.a.scheme": "http",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a")
|
||||
require.Equal(t, map[string]string{
|
||||
"other.label": "value",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.scheme": "http",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("single alias multiple labels", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.a.port": "8080",
|
||||
"proxy.a.scheme": "https",
|
||||
"proxy.*.port": "5555",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.scheme": "https",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("wildcard partial override", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.a.port": "8080",
|
||||
"proxy.a.healthcheck.path": "/health",
|
||||
"proxy.*.port": "5555",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.healthcheck.path": "/health",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("nested suffix distinction", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.a.healthcheck.path": "/health",
|
||||
"proxy.a.healthcheck.interval": "10s",
|
||||
"proxy.*.healthcheck.disable": "true",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.healthcheck.path": "/health",
|
||||
"proxy.#1.healthcheck.interval": "10s",
|
||||
"proxy.#1.healthcheck.disable": "true",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("discovered alias from explicit label", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.c.host": "localhost",
|
||||
"proxy.*.port": "5555",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#2.port": "5555",
|
||||
"proxy.#3.host": "localhost",
|
||||
"proxy.#3.port": "5555",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("ref alias not converted", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#2.port": "8080",
|
||||
"proxy.*.scheme": "https",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.scheme": "https",
|
||||
"proxy.#2.port": "8080",
|
||||
"proxy.#2.scheme": "https",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("mixed ref and named aliases", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.#1.host": "host1",
|
||||
"proxy.a.host": "host2",
|
||||
"proxy.*.port": "5555",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "host2",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#2.port": "5555",
|
||||
}, labels)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpandWildcardYAML(t *testing.T) {
|
||||
yaml := `
|
||||
t.Run("basic yaml wildcard", func(t *testing.T) {
|
||||
yaml := `
|
||||
host: localhost
|
||||
port: 5555
|
||||
healthcheck:
|
||||
disable: true`
|
||||
labels := map[string]string{
|
||||
"proxy.*": yaml[1:],
|
||||
"proxy.a.port": "4444",
|
||||
"proxy.a.healthcheck.disable": "false",
|
||||
"proxy.a.healthcheck.path": "/health",
|
||||
"proxy.b.port": "6666",
|
||||
}
|
||||
docker.ExpandWildcard(labels)
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.a.host": "localhost", // set by wildcard
|
||||
"proxy.a.port": "5555", // overridden by wildcard
|
||||
"proxy.a.healthcheck.disable": "true", // overridden by wildcard
|
||||
"proxy.a.healthcheck.path": "/health", // own label
|
||||
"proxy.b.host": "localhost", // set by wildcard
|
||||
"proxy.b.port": "5555", // overridden by wildcard
|
||||
"proxy.b.healthcheck.disable": "true", // overridden by wildcard
|
||||
}, labels)
|
||||
}
|
||||
disable: true`[1:]
|
||||
labels := map[string]string{
|
||||
"proxy.*": yaml,
|
||||
"proxy.a.port": "4444",
|
||||
"proxy.a.healthcheck.disable": "false",
|
||||
"proxy.a.healthcheck.path": "/health",
|
||||
"proxy.b.port": "6666",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.healthcheck.disable": "true",
|
||||
"proxy.#1.healthcheck.path": "/health",
|
||||
"proxy.#2.host": "localhost",
|
||||
"proxy.#2.port": "5555",
|
||||
"proxy.#2.healthcheck.disable": "true",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
func TestWildcardWithRefAliases(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.*.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a.example.com", "b.example.com")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "5555",
|
||||
"proxy.#1.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
"proxy.#2.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
}, labels)
|
||||
t.Run("yaml with nested maps", func(t *testing.T) {
|
||||
yaml := `
|
||||
middlewares:
|
||||
request:
|
||||
hide_headers: X-Secret
|
||||
add_headers:
|
||||
X-Custom: value`[1:]
|
||||
labels := map[string]string{
|
||||
"proxy.*": yaml,
|
||||
"proxy.a.middlewares.request.set_headers": "X-Override: yes",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.middlewares.request.hide_headers": "X-Secret",
|
||||
"proxy.#1.middlewares.request.add_headers.X-Custom": "value",
|
||||
"proxy.#1.middlewares.request.set_headers": "X-Override: yes",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("yaml only no explicit labels", func(t *testing.T) {
|
||||
yaml := `
|
||||
host: localhost
|
||||
port: 8080`[1:]
|
||||
labels := map[string]string{
|
||||
"proxy.*": yaml,
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a", "b")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.#1.host": "localhost",
|
||||
"proxy.#1.port": "8080",
|
||||
"proxy.#2.host": "localhost",
|
||||
"proxy.#2.port": "8080",
|
||||
}, labels)
|
||||
})
|
||||
|
||||
t.Run("invalid yaml ignored", func(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"proxy.*": "invalid: yaml: content:\n\t\tbad",
|
||||
"proxy.a.port": "8080",
|
||||
}
|
||||
docker.ExpandWildcard(labels, "a")
|
||||
require.Equal(t, map[string]string{
|
||||
"proxy.a.port": "8080",
|
||||
}, labels)
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkParseLabels(b *testing.B) {
|
||||
m := map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.b.port": "4444",
|
||||
"proxy.*.scheme": "http",
|
||||
"proxy.*.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
}
|
||||
for b.Loop() {
|
||||
_, _ = docker.ParseLabels(map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.b.port": "4444",
|
||||
"proxy.*.scheme": "http",
|
||||
"proxy.*.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
})
|
||||
_, _ = docker.ParseLabels(m, "a", "b")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package docker
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
var listOptions = container.ListOptions{
|
||||
var listOptions = client.ContainerListOptions{
|
||||
// created|restarting|running|removing|paused|exited|dead
|
||||
// Filters: filters.NewArgs(
|
||||
// filters.Arg("status", "created"),
|
||||
@@ -30,7 +30,7 @@ func ListContainers(ctx context.Context, clientHost string) ([]container.Summary
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return containers, nil
|
||||
return containers.Items, nil
|
||||
}
|
||||
|
||||
func IsErrConnectionFailed(err error) bool {
|
||||
|
||||
@@ -82,7 +82,7 @@ func BenchmarkEntrypointReal(b *testing.B) {
|
||||
Scheme: routeTypes.SchemeHTTP,
|
||||
Host: host,
|
||||
Port: route.Port{Proxy: portInt},
|
||||
HealthCheck: &types.HealthCheckConfig{Disable: true},
|
||||
HealthCheck: types.HealthCheckConfig{Disable: true},
|
||||
}
|
||||
|
||||
err = r.Validate()
|
||||
@@ -125,7 +125,7 @@ func BenchmarkEntrypoint(b *testing.B) {
|
||||
Port: route.Port{
|
||||
Proxy: 8080,
|
||||
},
|
||||
HealthCheck: &types.HealthCheckConfig{
|
||||
HealthCheck: types.HealthCheckConfig{
|
||||
Disable: true,
|
||||
},
|
||||
}
|
||||
|
||||
Submodule internal/go-oidc updated: d599436494...0118916d67
Submodule internal/gopsutil updated: 5f60518fa5...cb4cb59837
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
"github.com/yusing/godoxy/internal/utils/pool"
|
||||
"github.com/yusing/goutils/pool"
|
||||
)
|
||||
|
||||
type route interface {
|
||||
|
||||
@@ -3,7 +3,8 @@ package provider
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
@@ -17,7 +18,7 @@ type DockerProvider struct {
|
||||
containerID string
|
||||
}
|
||||
|
||||
var startOptions = container.StartOptions{}
|
||||
var startOptions = client.ContainerStartOptions{}
|
||||
|
||||
func NewDockerProvider(dockerHost, containerID string) (idlewatcher.Provider, error) {
|
||||
client, err := docker.NewClient(dockerHost)
|
||||
@@ -32,34 +33,41 @@ func NewDockerProvider(dockerHost, containerID string) (idlewatcher.Provider, er
|
||||
}
|
||||
|
||||
func (p *DockerProvider) ContainerPause(ctx context.Context) error {
|
||||
return p.client.ContainerPause(ctx, p.containerID)
|
||||
_, err := p.client.ContainerPause(ctx, p.containerID, client.ContainerPauseOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *DockerProvider) ContainerUnpause(ctx context.Context) error {
|
||||
return p.client.ContainerUnpause(ctx, p.containerID)
|
||||
_, err := p.client.ContainerUnpause(ctx, p.containerID, client.ContainerUnpauseOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *DockerProvider) ContainerStart(ctx context.Context) error {
|
||||
return p.client.ContainerStart(ctx, p.containerID, startOptions)
|
||||
_, err := p.client.ContainerStart(ctx, p.containerID, startOptions)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *DockerProvider) ContainerStop(ctx context.Context, signal types.ContainerSignal, timeout int) error {
|
||||
return p.client.ContainerStop(ctx, p.containerID, container.StopOptions{
|
||||
_, err := p.client.ContainerStop(ctx, p.containerID, client.ContainerStopOptions{
|
||||
Signal: string(signal),
|
||||
Timeout: &timeout,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *DockerProvider) ContainerKill(ctx context.Context, signal types.ContainerSignal) error {
|
||||
return p.client.ContainerKill(ctx, p.containerID, string(signal))
|
||||
_, err := p.client.ContainerKill(ctx, p.containerID, client.ContainerKillOptions{
|
||||
Signal: string(signal),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *DockerProvider) ContainerStatus(ctx context.Context) (idlewatcher.ContainerStatus, error) {
|
||||
status, err := p.client.ContainerInspect(ctx, p.containerID)
|
||||
status, err := p.client.ContainerInspect(ctx, p.containerID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return idlewatcher.ContainerStatusError, err
|
||||
}
|
||||
switch status.State.Status {
|
||||
switch status.Container.State.Status {
|
||||
case container.StateRunning:
|
||||
return idlewatcher.ContainerStatusRunning, nil
|
||||
case container.StateExited, container.StateDead, container.StateRestarting:
|
||||
@@ -67,12 +75,12 @@ func (p *DockerProvider) ContainerStatus(ctx context.Context) (idlewatcher.Conta
|
||||
case container.StatePaused:
|
||||
return idlewatcher.ContainerStatusPaused, nil
|
||||
}
|
||||
return idlewatcher.ContainerStatusError, idlewatcher.ErrUnexpectedContainerStatus.Subject(status.State.Status)
|
||||
return idlewatcher.ContainerStatusError, idlewatcher.ErrUnexpectedContainerStatus.Subject(string(status.Container.State.Status))
|
||||
}
|
||||
|
||||
func (p *DockerProvider) Watch(ctx context.Context) (eventCh <-chan watcher.Event, errCh <-chan gperr.Error) {
|
||||
return p.watcher.EventsWithOptions(ctx, watcher.DockerListOptions{
|
||||
Filters: watcher.NewDockerFilter(
|
||||
Filters: watcher.NewDockerFilters(
|
||||
watcher.DockerFilterContainer,
|
||||
watcher.DockerFilterContainerNameID(p.containerID),
|
||||
watcher.DockerFilterStart,
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/goutils/mockable"
|
||||
"github.com/yusing/goutils/task"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
@@ -57,7 +57,7 @@ func fmtLog(cfg *RequestLoggerConfig) (ts string, line string) {
|
||||
|
||||
t := time.Now()
|
||||
logger := NewMockAccessLogger(testTask, cfg)
|
||||
utils.MockTimeNow(t)
|
||||
mockable.MockTimeNow(t)
|
||||
buf = logger.(RequestFormatter).AppendRequestLog(buf, req, resp)
|
||||
return t.Format(LogTimeFormat), string(buf)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/goutils/mockable"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -67,7 +67,7 @@ func (f *CommonFormatter) AppendRequestLog(line []byte, req *http.Request, res *
|
||||
line = append(line, clientIP(req)...)
|
||||
line = append(line, " - - ["...)
|
||||
|
||||
line = utils.TimeNow().AppendFormat(line, LogTimeFormat)
|
||||
line = mockable.TimeNow().AppendFormat(line, LogTimeFormat)
|
||||
line = append(line, `] "`...)
|
||||
|
||||
line = append(line, req.Method...)
|
||||
@@ -103,7 +103,7 @@ func (f *JSONFormatter) AppendRequestLog(line []byte, req *http.Request, res *ht
|
||||
writer := bytes.NewBuffer(line)
|
||||
logger := zerolog.New(writer)
|
||||
event := logger.Info().
|
||||
Str("time", utils.TimeNow().Format(LogTimeFormat)).
|
||||
Str("time", mockable.TimeNow().Format(LogTimeFormat)).
|
||||
Str("ip", clientIP(req)).
|
||||
Str("method", req.Method).
|
||||
Str("scheme", scheme(req)).
|
||||
@@ -136,7 +136,7 @@ func (f ACLLogFormatter) AppendACLLog(line []byte, info *maxmind.IPInfo, blocked
|
||||
writer := bytes.NewBuffer(line)
|
||||
logger := zerolog.New(writer)
|
||||
event := logger.Info().
|
||||
Str("time", utils.TimeNow().Format(LogTimeFormat)).
|
||||
Str("time", mockable.TimeNow().Format(LogTimeFormat)).
|
||||
Str("ip", info.Str)
|
||||
if blocked {
|
||||
event.Str("action", "block")
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/mockable"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
@@ -81,14 +81,14 @@ func rotateLogFile(file supportRotate, config *Retention, result *RotateResult)
|
||||
|
||||
func rotateLogFileByPolicy(file supportRotate, config *Retention, result *RotateResult) (rotated bool, err error) {
|
||||
var shouldStop func() bool
|
||||
t := utils.TimeNow()
|
||||
t := mockable.TimeNow()
|
||||
|
||||
switch {
|
||||
case config.Last > 0:
|
||||
shouldStop = func() bool { return result.NumLinesKeep-result.NumLinesInvalid == int(config.Last) }
|
||||
// not needed to parse time for last N lines
|
||||
case config.Days > 0:
|
||||
cutoff := utils.TimeNow().AddDate(0, 0, -int(config.Days)+1)
|
||||
cutoff := mockable.TimeNow().AddDate(0, 0, -int(config.Days)+1)
|
||||
shouldStop = func() bool { return t.Before(cutoff) }
|
||||
default:
|
||||
return false, nil // should not happen
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/goutils/mockable"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
@@ -56,7 +56,7 @@ func TestRotateKeepLast(t *testing.T) {
|
||||
for _, format := range ReqLoggerFormats {
|
||||
t.Run(string(format)+" keep last", func(t *testing.T) {
|
||||
file := NewMockFile(true)
|
||||
utils.MockTimeNow(testTime)
|
||||
mockable.MockTimeNow(testTime)
|
||||
logger := NewAccessLoggerWithIO(task.RootTask("test", false), file, &RequestLoggerConfig{
|
||||
Format: format,
|
||||
})
|
||||
@@ -93,7 +93,7 @@ func TestRotateKeepLast(t *testing.T) {
|
||||
expect.Nil(t, logger.Config().Retention)
|
||||
nLines := 10
|
||||
for i := range nLines {
|
||||
utils.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
mockable.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
logger.Log(req, resp)
|
||||
}
|
||||
logger.Flush()
|
||||
@@ -105,7 +105,7 @@ func TestRotateKeepLast(t *testing.T) {
|
||||
expect.Equal(t, retention.KeepSize, 0)
|
||||
logger.Config().Retention = retention
|
||||
|
||||
utils.MockTimeNow(testTime)
|
||||
mockable.MockTimeNow(testTime)
|
||||
var result RotateResult
|
||||
rotated, err := logger.(AccessLogRotater).Rotate(&result)
|
||||
expect.NoError(t, err)
|
||||
@@ -139,7 +139,7 @@ func TestRotateKeepFileSize(t *testing.T) {
|
||||
expect.Nil(t, logger.Config().Retention)
|
||||
nLines := 10
|
||||
for i := range nLines {
|
||||
utils.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
mockable.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
logger.Log(req, resp)
|
||||
}
|
||||
logger.Flush()
|
||||
@@ -151,7 +151,7 @@ func TestRotateKeepFileSize(t *testing.T) {
|
||||
expect.Equal(t, retention.Last, 0)
|
||||
logger.Config().Retention = retention
|
||||
|
||||
utils.MockTimeNow(testTime)
|
||||
mockable.MockTimeNow(testTime)
|
||||
var result RotateResult
|
||||
rotated, err := logger.(AccessLogRotater).Rotate(&result)
|
||||
expect.NoError(t, err)
|
||||
@@ -171,7 +171,7 @@ func TestRotateKeepFileSize(t *testing.T) {
|
||||
expect.Nil(t, logger.Config().Retention)
|
||||
nLines := 100
|
||||
for i := range nLines {
|
||||
utils.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
mockable.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
logger.Log(req, resp)
|
||||
}
|
||||
logger.Flush()
|
||||
@@ -183,7 +183,7 @@ func TestRotateKeepFileSize(t *testing.T) {
|
||||
expect.Equal(t, retention.Last, 0)
|
||||
logger.Config().Retention = retention
|
||||
|
||||
utils.MockTimeNow(testTime)
|
||||
mockable.MockTimeNow(testTime)
|
||||
var result RotateResult
|
||||
rotated, err := logger.(AccessLogRotater).Rotate(&result)
|
||||
expect.NoError(t, err)
|
||||
@@ -205,7 +205,7 @@ func TestRotateSkipInvalidTime(t *testing.T) {
|
||||
expect.Nil(t, logger.Config().Retention)
|
||||
nLines := 10
|
||||
for i := range nLines {
|
||||
utils.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
mockable.MockTimeNow(testTime.AddDate(0, 0, -nLines+i+1))
|
||||
logger.Log(req, resp)
|
||||
logger.Flush()
|
||||
|
||||
@@ -248,7 +248,7 @@ func BenchmarkRotate(b *testing.B) {
|
||||
Format: FormatJSON,
|
||||
})
|
||||
for i := range 100 {
|
||||
utils.MockTimeNow(testTime.AddDate(0, 0, -100+i+1))
|
||||
mockable.MockTimeNow(testTime.AddDate(0, 0, -100+i+1))
|
||||
logger.Log(req, resp)
|
||||
}
|
||||
logger.Flush()
|
||||
@@ -282,7 +282,7 @@ func BenchmarkRotateWithInvalidTime(b *testing.B) {
|
||||
Format: FormatJSON,
|
||||
})
|
||||
for i := range 10000 {
|
||||
utils.MockTimeNow(testTime.AddDate(0, 0, -10000+i+1))
|
||||
mockable.MockTimeNow(testTime.AddDate(0, 0, -10000+i+1))
|
||||
logger.Log(req, resp)
|
||||
if i%10 == 0 {
|
||||
_, _ = file.Write([]byte("invalid time\n"))
|
||||
|
||||
@@ -178,7 +178,7 @@ func (cfg *MaxMind) doReq(method string) (*http.Response, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.SetBasicAuth(cfg.AccountID, cfg.LicenseKey)
|
||||
req.SetBasicAuth(cfg.AccountID, cfg.LicenseKey.String())
|
||||
resp, err := doReq(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -4,14 +4,15 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
type (
|
||||
DatabaseType string
|
||||
Config struct {
|
||||
AccountID string `json:"account_id" validate:"required"`
|
||||
LicenseKey string `json:"license_key" validate:"required"`
|
||||
Database DatabaseType `json:"database" validate:"omitempty,oneof=geolite geoip2"`
|
||||
AccountID string `json:"account_id" validate:"required"`
|
||||
LicenseKey strutils.Redacted `json:"license_key" validate:"required"`
|
||||
Database DatabaseType `json:"database" validate:"omitempty,oneof=geolite geoip2"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/utils/pool"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/pool"
|
||||
"github.com/yusing/goutils/task"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,12 @@ package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
type Bypass []rules.RuleOn
|
||||
@@ -11,6 +15,7 @@ type Bypass []rules.RuleOn
|
||||
func (b Bypass) ShouldBypass(w http.ResponseWriter, r *http.Request) bool {
|
||||
for _, rule := range b {
|
||||
if rule.Check(w, r) {
|
||||
log.Debug().Str("rule_matched", rule.String()).Str("url", r.Host+r.URL.Path).Msg("bypassing request")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -18,22 +23,38 @@ func (b Bypass) ShouldBypass(w http.ResponseWriter, r *http.Request) bool {
|
||||
}
|
||||
|
||||
type checkBypass struct {
|
||||
name string
|
||||
|
||||
bypass Bypass
|
||||
modReq RequestModifier
|
||||
modRes ResponseModifier
|
||||
|
||||
// when request path matches any of these prefixes, bypass is not applied
|
||||
enforcedPathPrefixes []string
|
||||
}
|
||||
|
||||
func (c *checkBypass) isEnforced(r *http.Request) bool {
|
||||
for _, prefix := range c.enforcedPathPrefixes {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *checkBypass) before(w http.ResponseWriter, r *http.Request) (proceedNext bool) {
|
||||
if c.modReq == nil || c.bypass.ShouldBypass(w, r) {
|
||||
if c.modReq == nil || (!c.isEnforced(r) && c.bypass.ShouldBypass(w, r)) {
|
||||
return true
|
||||
}
|
||||
log.Debug().Str("middleware", c.name).Str("url", r.Host+r.URL.Path).Msg("modifying request")
|
||||
return c.modReq.before(w, r)
|
||||
}
|
||||
|
||||
func (c *checkBypass) modifyResponse(resp *http.Response) error {
|
||||
if c.modRes == nil || c.bypass.ShouldBypass(rules.ResponseAsRW(resp), resp.Request) {
|
||||
if c.modRes == nil || (!c.isEnforced(resp.Request) && c.bypass.ShouldBypass(httputils.ResponseAsRW(resp), resp.Request)) {
|
||||
return nil
|
||||
}
|
||||
log.Debug().Str("middleware", c.name).Str("url", resp.Request.Host+resp.Request.URL.Path).Msg("modifying response")
|
||||
return c.modRes.modifyResponse(resp)
|
||||
}
|
||||
|
||||
@@ -42,10 +63,23 @@ func (m *Middleware) withCheckBypass() any {
|
||||
modReq, _ := m.impl.(RequestModifier)
|
||||
modRes, _ := m.impl.(ResponseModifier)
|
||||
return &checkBypass{
|
||||
bypass: m.Bypass,
|
||||
modReq: modReq,
|
||||
modRes: modRes,
|
||||
name: m.Name(),
|
||||
bypass: m.Bypass,
|
||||
enforcedPathPrefixes: getEnforcedPathPrefixes(modReq, modRes),
|
||||
modReq: modReq,
|
||||
modRes: modRes,
|
||||
}
|
||||
}
|
||||
return m.impl
|
||||
}
|
||||
|
||||
func getEnforcedPathPrefixes(modReq RequestModifier, modRes ResponseModifier) []string {
|
||||
if modReq == nil && modRes == nil {
|
||||
return nil
|
||||
}
|
||||
switch modReq.(type) {
|
||||
case *oidcMiddleware:
|
||||
return []string{auth.OIDCAuthBasePath}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ func TestBypassResponse(t *testing.T) {
|
||||
StatusCode: test.statusCode,
|
||||
Body: io.NopCloser(strings.NewReader("test")),
|
||||
Header: make(http.Header),
|
||||
Request: httptest.NewRequest("GET", "http://example.com", nil),
|
||||
}
|
||||
mErr := mr.ModifyResponse(resp)
|
||||
expect.NoError(t, mErr)
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/yusing/godoxy/internal/jsonstore"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
)
|
||||
|
||||
type CaptchaSession struct {
|
||||
@@ -22,7 +21,7 @@ var CaptchaSessions = jsonstore.Store[*CaptchaSession]("captcha_sessions")
|
||||
func newCaptchaSession(p Provider) *CaptchaSession {
|
||||
buf := make([]byte, 32)
|
||||
_, _ = rand.Read(buf)
|
||||
now := utils.TimeNow()
|
||||
now := time.Now()
|
||||
return &CaptchaSession{
|
||||
ID: hex.EncodeToString(buf),
|
||||
Expiry: now.Add(p.SessionExpiry()),
|
||||
@@ -30,5 +29,5 @@ func newCaptchaSession(p Provider) *CaptchaSession {
|
||||
}
|
||||
|
||||
func (s *CaptchaSession) expired() bool {
|
||||
return utils.TimeNow().After(s.Expiry)
|
||||
return time.Now().After(s.Expiry)
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/godoxy/internal/watcher"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/fs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ func GetErrorPageByStatus(statusCode int) (content []byte, ok bool) {
|
||||
}
|
||||
|
||||
func loadContent() {
|
||||
files, err := utils.ListFiles(errPagesBasePath, 0)
|
||||
files, err := fs.ListFiles(errPagesBasePath, 0)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to list error page resources")
|
||||
return
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/rs/zerolog"
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/reverseproxy"
|
||||
)
|
||||
|
||||
@@ -76,7 +76,7 @@ func NewMiddleware[ImplType any]() *Middleware {
|
||||
panic("MiddlewareFinalizer and MiddlewareFinalizerWithError are mutually exclusive")
|
||||
}
|
||||
return &Middleware{
|
||||
name: strings.ToLower(reflect.TypeFor[ImplType]().Name()),
|
||||
name: reflect.TypeFor[ImplType]().Name(),
|
||||
construct: func() any { return new(ImplType) },
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func (m *Middleware) finalize() error {
|
||||
func (m *Middleware) New(optsRaw OptionsRaw) (*Middleware, gperr.Error) {
|
||||
if m.construct == nil { // likely a middleware from compose
|
||||
if len(optsRaw) != 0 {
|
||||
return nil, gperr.New("additional options not allowed for middleware ").Subject(m.name)
|
||||
return nil, gperr.New("additional options not allowed for middleware").Subject(m.name)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -185,17 +185,59 @@ func (m *Middleware) ModifyResponse(resp *http.Response) error {
|
||||
}
|
||||
|
||||
func (m *Middleware) ServeHTTP(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) {
|
||||
if exec, ok := m.impl.(ResponseModifier); ok {
|
||||
w = httputils.NewModifyResponseWriter(w, r, func(resp *http.Response) error {
|
||||
return exec.modifyResponse(resp)
|
||||
})
|
||||
}
|
||||
if exec, ok := m.impl.(RequestModifier); ok {
|
||||
if proceed := exec.before(w, r); !proceed {
|
||||
return
|
||||
}
|
||||
}
|
||||
next(w, r)
|
||||
|
||||
if httpheaders.IsWebsocket(r.Header) || r.Header.Get("Accept") == "text/event-stream" {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if exec, ok := m.impl.(ResponseModifier); ok {
|
||||
lrm := httputils.NewLazyResponseModifier(w, needsBuffering)
|
||||
defer lrm.FlushRelease()
|
||||
next(lrm, r)
|
||||
|
||||
// Skip modification if response wasn't buffered (non-HTML content)
|
||||
if !lrm.IsBuffered() {
|
||||
return
|
||||
}
|
||||
|
||||
rm := lrm.ResponseModifier()
|
||||
currentBody := rm.BodyReader()
|
||||
currentResp := &http.Response{
|
||||
StatusCode: rm.StatusCode(),
|
||||
Header: rm.Header(),
|
||||
ContentLength: int64(rm.ContentLength()),
|
||||
Body: currentBody,
|
||||
Request: r,
|
||||
}
|
||||
if err := exec.modifyResponse(currentResp); err != nil {
|
||||
log.Err(err).Str("middleware", m.Name()).Str("url", fullURL(r)).Msg("failed to modify response")
|
||||
}
|
||||
|
||||
// override the response status code
|
||||
rm.WriteHeader(currentResp.StatusCode)
|
||||
|
||||
// overriding the response header
|
||||
maps.Copy(rm.Header(), currentResp.Header)
|
||||
|
||||
// override the content length and body if changed
|
||||
if currentResp.Body != currentBody {
|
||||
rm.SetBody(currentResp.Body)
|
||||
}
|
||||
} else {
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// needsBuffering determines if a response should be buffered for modification.
|
||||
// Only HTML responses need buffering; streaming content (video, audio, etc.) should pass through.
|
||||
func needsBuffering(header http.Header) bool {
|
||||
return httputils.GetContentType(header).IsHTML()
|
||||
}
|
||||
|
||||
func (m *Middleware) LogWarn(req *http.Request) *zerolog.Event {
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
fsutils "github.com/yusing/goutils/fs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
@@ -52,7 +52,7 @@ func Get(name string) (*Middleware, Error) {
|
||||
if !ok {
|
||||
return nil, ErrUnknownMiddleware.
|
||||
Subject(name).
|
||||
With(gperr.DoYouMean(utils.NearestField(name, allMiddlewares)))
|
||||
With(gperr.DoYouMeanField(name, allMiddlewares))
|
||||
}
|
||||
return middleware, nil
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func All() map[string]*Middleware {
|
||||
|
||||
func LoadComposeFiles() {
|
||||
errs := gperr.NewBuilder("middleware compile errors")
|
||||
middlewareDefs, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0)
|
||||
middlewareDefs, err := fsutils.ListFiles(common.MiddlewareComposeBasePath, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return
|
||||
|
||||
@@ -40,6 +40,10 @@ func (eofReader) Close() error { return nil }
|
||||
|
||||
// modifyResponse implements ResponseModifier.
|
||||
func (m *modifyHTML) modifyResponse(resp *http.Response) error {
|
||||
// Skip HEAD requests - no body to modify
|
||||
if resp.Request.Method == http.MethodHead {
|
||||
return nil
|
||||
}
|
||||
// including text/html and application/xhtml+xml
|
||||
if !httputils.GetContentType(resp.Header).IsHTML() {
|
||||
return nil
|
||||
|
||||
@@ -74,6 +74,11 @@ func (amw *oidcMiddleware) initSlow() error {
|
||||
}
|
||||
// If no custom credentials, authProvider remains the global one
|
||||
|
||||
// Always trigger login on unknown paths.
|
||||
// This prevents falling back to the default login page, which applies bypass rules.
|
||||
// Without this, redirecting to the global login page could circumvent the intended route restrictions.
|
||||
authProvider.SetOnUnknownPathHandler(authProvider.LoginHandler)
|
||||
|
||||
// Apply per-route user/group restrictions (these always override global)
|
||||
if len(amw.AllowedUsers) > 0 {
|
||||
authProvider.SetAllowedUsers(amw.AllowedUsers)
|
||||
|
||||
@@ -4,11 +4,9 @@ import (
|
||||
urlPkg "net/url"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
)
|
||||
|
||||
type URL struct {
|
||||
_ utils.NoCopy
|
||||
urlPkg.URL
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@ import (
|
||||
"github.com/luthermonson/go-proxmox"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
URL string `json:"url" validate:"required,url"`
|
||||
|
||||
TokenID string `json:"token_id" validate:"required"`
|
||||
Secret string `json:"secret" validate:"required"`
|
||||
TokenID string `json:"token_id" validate:"required"`
|
||||
Secret strutils.Redacted `json:"secret" validate:"required"`
|
||||
|
||||
NoTLSVerify bool `json:"no_tls_verify" yaml:"no_tls_verify,omitempty"`
|
||||
|
||||
@@ -48,7 +49,7 @@ func (c *Config) Init() gperr.Error {
|
||||
}
|
||||
|
||||
opts := []proxmox.Option{
|
||||
proxmox.WithAPIToken(c.TokenID, c.Secret),
|
||||
proxmox.WithAPIToken(c.TokenID, c.Secret.String()),
|
||||
proxmox.WithHTTPClient(&http.Client{
|
||||
Transport: tr,
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/luthermonson/go-proxmox"
|
||||
"github.com/yusing/godoxy/internal/utils/pool"
|
||||
"github.com/yusing/goutils/pool"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
@@ -19,6 +19,9 @@ type Node struct {
|
||||
var Nodes = pool.New[*Node]("proxmox_nodes")
|
||||
|
||||
func AvailableNodeNames() string {
|
||||
if Nodes.Size() == 0 {
|
||||
return ""
|
||||
}
|
||||
var sb strings.Builder
|
||||
for _, node := range Nodes.Iter {
|
||||
sb.WriteString(node.name)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
gphttp "github.com/yusing/godoxy/internal/net/gphttp"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
@@ -24,6 +25,8 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
var _ types.FileServerRoute = (*FileServer)(nil)
|
||||
|
||||
func handler(root string) http.Handler {
|
||||
return http.FileServer(http.Dir(root))
|
||||
}
|
||||
@@ -91,16 +94,12 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
|
||||
}
|
||||
|
||||
if s.UseHealthCheck() {
|
||||
s.HealthMon = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
|
||||
s.HealthMon = monitor.NewMonitor(s)
|
||||
if err := s.HealthMon.Start(s.task); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if s.ShouldExclude() {
|
||||
return nil
|
||||
}
|
||||
|
||||
routes.HTTP.Add(s)
|
||||
s.task.OnFinished("remove_route_from_http", func() {
|
||||
routes.HTTP.Del(s)
|
||||
@@ -108,6 +107,10 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileServer) RootPath() string {
|
||||
return s.Root
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
func (s *FileServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
s.handler.ServeHTTP(w, req)
|
||||
|
||||
@@ -3,8 +3,8 @@ package provider
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestParseDockerLabels(t *testing.T) {
|
||||
Names: []string{"container"},
|
||||
Labels: labels,
|
||||
State: "running",
|
||||
Ports: []container.Port{
|
||||
Ports: []container.PortSummary{
|
||||
{Type: "tcp", PrivatePort: 1234, PublicPort: 1234},
|
||||
},
|
||||
}, "/var/run/docker.sock"),
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
D "github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/route"
|
||||
routeTypes "github.com/yusing/godoxy/internal/route/types"
|
||||
@@ -274,7 +275,7 @@ func TestPrivateIPLocalhost(t *testing.T) {
|
||||
NetworkSettings: &container.NetworkSettingsSummary{
|
||||
Networks: map[string]*network.EndpointSettings{
|
||||
"network": {
|
||||
IPAddress: testDockerIP,
|
||||
IPAddress: netip.MustParseAddr(testDockerIP),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -292,7 +293,7 @@ func TestPrivateIPRemote(t *testing.T) {
|
||||
NetworkSettings: &container.NetworkSettingsSummary{
|
||||
Networks: map[string]*network.EndpointSettings{
|
||||
"network": {
|
||||
IPAddress: testDockerIP,
|
||||
IPAddress: netip.MustParseAddr(testDockerIP),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -314,11 +315,11 @@ func TestStreamDefaultValues(t *testing.T) {
|
||||
NetworkSettings: &container.NetworkSettingsSummary{
|
||||
Networks: map[string]*network.EndpointSettings{
|
||||
"network": {
|
||||
IPAddress: privIP,
|
||||
IPAddress: netip.MustParseAddr(privIP),
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: []container.Port{
|
||||
Ports: []container.PortSummary{
|
||||
{Type: "udp", PrivatePort: privPort, PublicPort: pubPort},
|
||||
},
|
||||
}
|
||||
@@ -371,7 +372,7 @@ func TestImplicitExcludeDatabase(t *testing.T) {
|
||||
t.Run("exposed port detection", func(t *testing.T) {
|
||||
r, ok := makeRoutes(&container.Summary{
|
||||
Names: dummyNames,
|
||||
Ports: []container.Port{
|
||||
Ports: []container.PortSummary{
|
||||
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
|
||||
},
|
||||
})["a"]
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
@@ -224,10 +224,13 @@ func (p *Provider) startRoute(parent task.Parent, r *route.Route) gperr.Error {
|
||||
p.lockDeleteRoute(r.Alias)
|
||||
return err.Subject(r.Alias)
|
||||
}
|
||||
|
||||
p.lockAddRoute(r)
|
||||
r.Task().OnCancel("remove_route_from_provider", func() {
|
||||
p.lockDeleteRoute(r.Alias)
|
||||
})
|
||||
if !r.ShouldExclude() {
|
||||
r.Task().OnCancel("remove_route_from_provider", func() {
|
||||
p.lockDeleteRoute(r.Alias)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -137,10 +137,6 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
|
||||
}
|
||||
}
|
||||
|
||||
if r.ShouldExclude() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.UseLoadBalance() {
|
||||
r.addToLoadBalancer(parent)
|
||||
} else {
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
homepagecfg "github.com/yusing/godoxy/internal/homepage/types"
|
||||
@@ -50,7 +50,7 @@ type (
|
||||
PathPatterns []string `json:"path_patterns,omitempty" extensions:"x-nullable"`
|
||||
Rules rules.Rules `json:"rules,omitempty" extension:"x-nullable"`
|
||||
RuleFile string `json:"rule_file,omitempty" extensions:"x-nullable"`
|
||||
HealthCheck *types.HealthCheckConfig `json:"healthcheck"`
|
||||
HealthCheck types.HealthCheckConfig `json:"healthcheck,omitempty" extensions:"x-nullable"` // null on load-balancer routes
|
||||
LoadBalance *types.LoadBalancerConfig `json:"load_balance,omitempty" extensions:"x-nullable"`
|
||||
Middlewares map[string]types.LabelMap `json:"middlewares,omitempty" extensions:"x-nullable"`
|
||||
Homepage *homepage.ItemConfig `json:"homepage"`
|
||||
@@ -123,24 +123,24 @@ func (r Routes) Contains(alias string) bool {
|
||||
}
|
||||
|
||||
func (r *Route) Validate() gperr.Error {
|
||||
pcs := make([]uintptr, 1)
|
||||
runtime.Callers(2, pcs)
|
||||
f := runtime.FuncForPC(pcs[0])
|
||||
fname := f.Name()
|
||||
// pcs := make([]uintptr, 1)
|
||||
// runtime.Callers(2, pcs)
|
||||
// f := runtime.FuncForPC(pcs[0])
|
||||
// fname := f.Name()
|
||||
r.onceValidate.Do(func() {
|
||||
filename, line := f.FileLine(pcs[0])
|
||||
if strings.HasPrefix(r.Alias, "godoxy") {
|
||||
log.Debug().Str("route", r.Alias).Str("caller", fname).Str("file", filename).Int("line", line).Msg("validating route")
|
||||
}
|
||||
// filename, line := f.FileLine(pcs[0])
|
||||
// if strings.HasPrefix(r.Alias, "godoxy") {
|
||||
// log.Debug().Str("route", r.Alias).Str("caller", fname).Str("file", filename).Int("line", line).Msg("validating route")
|
||||
// }
|
||||
r.valErr.Set(r.validate())
|
||||
})
|
||||
return r.valErr.Get()
|
||||
}
|
||||
|
||||
func (r *Route) validate() gperr.Error {
|
||||
if strings.HasPrefix(r.Alias, "godoxy") {
|
||||
log.Debug().Any("route", r).Msg("validating route")
|
||||
}
|
||||
// if strings.HasPrefix(r.Alias, "godoxy") {
|
||||
// log.Debug().Any("route", r).Msg("validating route")
|
||||
// }
|
||||
if r.Agent != "" {
|
||||
if r.Container != nil {
|
||||
return gperr.Errorf("specifying agent is not allowed for docker container routes")
|
||||
@@ -379,7 +379,8 @@ func (r *Route) start(parent task.Parent) gperr.Error {
|
||||
defer close(r.started)
|
||||
|
||||
// skip checking for excluded routes
|
||||
if !r.ShouldExclude() {
|
||||
excluded := r.ShouldExclude()
|
||||
if !excluded {
|
||||
if err := checkExists(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -389,8 +390,12 @@ func (r *Route) start(parent task.Parent) gperr.Error {
|
||||
docker.SetDockerHostByContainerID(cont.ContainerID, cont.DockerHost)
|
||||
}
|
||||
|
||||
if err := r.impl.Start(parent); err != nil {
|
||||
return err
|
||||
if !excluded {
|
||||
if err := r.impl.Start(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
} else { // required by idlewatcher
|
||||
r.task = parent.Subtask("excluded."+r.Name(), false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -495,7 +500,7 @@ func (r *Route) IdlewatcherConfig() *types.IdlewatcherConfig {
|
||||
return r.Idlewatcher
|
||||
}
|
||||
|
||||
func (r *Route) HealthCheckConfig() *types.HealthCheckConfig {
|
||||
func (r *Route) HealthCheckConfig() types.HealthCheckConfig {
|
||||
return r.HealthCheck
|
||||
}
|
||||
|
||||
@@ -578,30 +583,10 @@ func (r *Route) IsZeroPort() bool {
|
||||
}
|
||||
|
||||
func (r *Route) ShouldExclude() bool {
|
||||
if r.valErr.Get() != nil {
|
||||
if r.ExcludedReason != ExcludedReasonNone {
|
||||
return true
|
||||
}
|
||||
if r.Excluded {
|
||||
return true
|
||||
}
|
||||
if r.Container != nil {
|
||||
switch {
|
||||
case r.Container.IsExcluded:
|
||||
return true
|
||||
case r.IsZeroPort() && !r.UseIdleWatcher():
|
||||
return true
|
||||
case !r.Container.IsExplicit && docker.IsBlacklisted(r.Container):
|
||||
return true
|
||||
case strings.HasPrefix(r.Container.ContainerName, "buildx_"):
|
||||
return true
|
||||
}
|
||||
} else if r.IsZeroPort() && r.Scheme != route.SchemeFileServer {
|
||||
return true
|
||||
}
|
||||
if strings.HasSuffix(r.Alias, "-old") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return r.findExcludedReason() != ExcludedReasonNone
|
||||
}
|
||||
|
||||
type ExcludedReason uint8
|
||||
@@ -789,17 +774,7 @@ func (r *Route) Finalize() {
|
||||
}
|
||||
|
||||
r.Port.Listening, r.Port.Proxy = lp, pp
|
||||
|
||||
if r.HealthCheck == nil {
|
||||
r.HealthCheck = types.DefaultHealthConfig()
|
||||
}
|
||||
|
||||
if r.HealthCheck.Interval == 0 {
|
||||
r.HealthCheck.Interval = common.HealthCheckIntervalDefault
|
||||
}
|
||||
if r.HealthCheck.Timeout == 0 {
|
||||
r.HealthCheck.Timeout = common.HealthCheckTimeoutDefault
|
||||
}
|
||||
r.HealthCheck.ApplyDefaults(config.ActiveConfig.Load().Defaults.HealthCheck)
|
||||
}
|
||||
|
||||
func (r *Route) FinalizeHomepageConfig() {
|
||||
@@ -812,7 +787,6 @@ func (r *Route) FinalizeHomepageConfig() {
|
||||
if r.Homepage == nil {
|
||||
r.Homepage = &homepage.ItemConfig{
|
||||
Show: true,
|
||||
Name: r.Alias,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -878,7 +852,7 @@ var preferredPortOrder = []int{
|
||||
8443,
|
||||
}
|
||||
|
||||
func preferredPort(portMapping map[int]container.Port) (res int) {
|
||||
func preferredPort(portMapping types.PortMapping) (res int) {
|
||||
for _, port := range preferredPortOrder {
|
||||
if _, ok := portMapping[port]; ok {
|
||||
return port
|
||||
|
||||
@@ -2,8 +2,8 @@ package route
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
route "github.com/yusing/godoxy/internal/route/types"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
@@ -41,7 +41,7 @@ func TestRouteValidate(t *testing.T) {
|
||||
Scheme: route.SchemeHTTP,
|
||||
Host: "example.com",
|
||||
Port: route.Port{Proxy: 80},
|
||||
HealthCheck: &types.HealthCheckConfig{
|
||||
HealthCheck: types.HealthCheckConfig{
|
||||
Disable: true,
|
||||
},
|
||||
LoadBalance: &types.LoadBalancerConfig{
|
||||
@@ -137,7 +137,7 @@ func TestRouteValidate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPreferredPort(t *testing.T) {
|
||||
ports := map[int]container.Port{
|
||||
ports := types.PortMapping{
|
||||
22: {PrivatePort: 22},
|
||||
1000: {PrivatePort: 1000},
|
||||
3000: {PrivatePort: 80},
|
||||
@@ -180,3 +180,14 @@ func TestRouteAgent(t *testing.T) {
|
||||
expect.NoError(t, err, "Validate should not return error for valid route with agent")
|
||||
expect.NotNil(t, r.GetAgent(), "GetAgent should return agent")
|
||||
}
|
||||
|
||||
func TestRouteApplyingHealthCheckDefaults(t *testing.T) {
|
||||
hc := types.HealthCheckConfig{}
|
||||
hc.ApplyDefaults(types.HealthCheckConfig{
|
||||
Interval: 15 * time.Second,
|
||||
Timeout: 10 * time.Second,
|
||||
})
|
||||
|
||||
expect.Equal(t, hc.Interval, 15*time.Second)
|
||||
expect.Equal(t, hc.Timeout, 10*time.Second)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,6 @@ package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -34,7 +30,8 @@ func (r *RouteContext) Value(key any) any {
|
||||
func WithRouteContext(r *http.Request, route types.HTTPRoute) *http.Request {
|
||||
// we don't want to copy the request object every fucking requests
|
||||
// return r.WithContext(context.WithValue(r.Context(), routeContextKey, route))
|
||||
(*requestInternal)(unsafe.Pointer(r)).ctx = &RouteContext{
|
||||
ctxFieldPtr := (*context.Context)(unsafe.Pointer(uintptr(unsafe.Pointer(r)) + ctxFieldOffset))
|
||||
*ctxFieldPtr = &RouteContext{
|
||||
Context: r.Context(),
|
||||
Route: route,
|
||||
}
|
||||
@@ -107,43 +104,12 @@ func TryGetUpstreamURL(r *http.Request) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type requestInternal struct {
|
||||
Method string
|
||||
URL *url.URL
|
||||
Proto string
|
||||
ProtoMajor int
|
||||
ProtoMinor int
|
||||
Header http.Header
|
||||
Body io.ReadCloser
|
||||
GetBody func() (io.ReadCloser, error)
|
||||
ContentLength int64
|
||||
TransferEncoding []string
|
||||
Close bool
|
||||
Host string
|
||||
Form url.Values
|
||||
PostForm url.Values
|
||||
MultipartForm *multipart.Form
|
||||
Trailer http.Header
|
||||
RemoteAddr string
|
||||
RequestURI string
|
||||
TLS *tls.ConnectionState
|
||||
Cancel <-chan struct{}
|
||||
Response *http.Response
|
||||
Pattern string
|
||||
ctx context.Context
|
||||
}
|
||||
var ctxFieldOffset uintptr
|
||||
|
||||
func init() {
|
||||
// make sure ctx has the same offset as http.Request
|
||||
f, ok := reflect.TypeFor[requestInternal]().FieldByName("ctx")
|
||||
f, ok := reflect.TypeFor[http.Request]().FieldByName("ctx")
|
||||
if !ok {
|
||||
panic("ctx field not found")
|
||||
}
|
||||
f2, ok := reflect.TypeFor[http.Request]().FieldByName("ctx")
|
||||
if !ok {
|
||||
panic("ctx field not found")
|
||||
}
|
||||
if f.Offset != f2.Offset {
|
||||
panic(fmt.Sprintf("ctx has different offset than http.Request: %d != %d", f.Offset, f2.Offset))
|
||||
}
|
||||
ctxFieldOffset = f.Offset
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package routes
|
||||
|
||||
import (
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/utils/pool"
|
||||
"github.com/yusing/goutils/pool"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Cache is a map of cached values for a request.
|
||||
// It prevents the same value from being parsed multiple times.
|
||||
type (
|
||||
Cache map[string]any
|
||||
UpdateFunc[T any] func(T) T
|
||||
)
|
||||
|
||||
const (
|
||||
cacheKeyQueries = "queries"
|
||||
cacheKeyCookies = "cookies"
|
||||
cacheKeyRemoteIP = "remote_ip"
|
||||
cacheKeyBasicAuth = "basic_auth"
|
||||
)
|
||||
|
||||
var cachePool = sync.Pool{
|
||||
New: func() any {
|
||||
return make(Cache)
|
||||
},
|
||||
}
|
||||
|
||||
// NewCache returns a new Cached.
|
||||
func NewCache() Cache {
|
||||
return cachePool.Get().(Cache)
|
||||
}
|
||||
|
||||
// Release clear the contents of the Cached and returns it to the pool.
|
||||
func (c Cache) Release() {
|
||||
clear(c)
|
||||
cachePool.Put(c)
|
||||
}
|
||||
|
||||
// GetQueries returns the queries.
|
||||
// If r does not have queries, an empty map is returned.
|
||||
func (c Cache) GetQueries(r *http.Request) url.Values {
|
||||
v, ok := c[cacheKeyQueries]
|
||||
if !ok {
|
||||
v = r.URL.Query()
|
||||
c[cacheKeyQueries] = v
|
||||
}
|
||||
return v.(url.Values)
|
||||
}
|
||||
|
||||
func (c Cache) UpdateQueries(r *http.Request, update func(url.Values)) {
|
||||
queries := c.GetQueries(r)
|
||||
update(queries)
|
||||
r.URL.RawQuery = queries.Encode()
|
||||
}
|
||||
|
||||
// GetCookies returns the cookies.
|
||||
// If r does not have cookies, an empty slice is returned.
|
||||
func (c Cache) GetCookies(r *http.Request) []*http.Cookie {
|
||||
v, ok := c[cacheKeyCookies]
|
||||
if !ok {
|
||||
v = r.Cookies()
|
||||
c[cacheKeyCookies] = v
|
||||
}
|
||||
return v.([]*http.Cookie)
|
||||
}
|
||||
|
||||
func (c Cache) UpdateCookies(r *http.Request, update UpdateFunc[[]*http.Cookie]) {
|
||||
cookies := update(c.GetCookies(r))
|
||||
c[cacheKeyCookies] = cookies
|
||||
r.Header.Del("Cookie")
|
||||
for _, cookie := range cookies {
|
||||
r.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// GetRemoteIP returns the remote ip address.
|
||||
// If r.RemoteAddr is not a valid ip address, nil is returned.
|
||||
func (c Cache) GetRemoteIP(r *http.Request) net.IP {
|
||||
v, ok := c[cacheKeyRemoteIP]
|
||||
if !ok {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
host = r.RemoteAddr
|
||||
}
|
||||
v = net.ParseIP(host)
|
||||
c[cacheKeyRemoteIP] = v
|
||||
}
|
||||
return v.(net.IP)
|
||||
}
|
||||
|
||||
// GetBasicAuth returns *Credentials the basic auth username and password.
|
||||
// If r does not have basic auth, nil is returned.
|
||||
func (c Cache) GetBasicAuth(r *http.Request) *Credentials {
|
||||
v, ok := c[cacheKeyBasicAuth]
|
||||
if !ok {
|
||||
u, p, ok := r.BasicAuth()
|
||||
if ok {
|
||||
v = &Credentials{u, []byte(p)}
|
||||
c[cacheKeyBasicAuth] = v
|
||||
} else {
|
||||
c[cacheKeyBasicAuth] = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return v.(*Credentials)
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
package rules
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
import (
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type (
|
||||
HashedCrendentials struct {
|
||||
Username string
|
||||
CheckMatch func(inputPwd []byte) bool
|
||||
}
|
||||
Credentials struct {
|
||||
Username string
|
||||
Password []byte
|
||||
}
|
||||
)
|
||||
|
||||
func BCryptCrendentials(username string, hashedPassword []byte) *HashedCrendentials {
|
||||
@@ -19,7 +18,7 @@ func BCryptCrendentials(username string, hashedPassword []byte) *HashedCrendenti
|
||||
}}
|
||||
}
|
||||
|
||||
func (hc *HashedCrendentials) Match(cred *Credentials) bool {
|
||||
func (hc *HashedCrendentials) Match(cred *httputils.Credentials) bool {
|
||||
if cred == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/logging"
|
||||
gphttp "github.com/yusing/godoxy/internal/net/gphttp"
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
@@ -50,6 +49,14 @@ const (
|
||||
CommandPassAlt = "bypass"
|
||||
)
|
||||
|
||||
type AuthHandler func(w http.ResponseWriter, r *http.Request) (proceed bool)
|
||||
|
||||
var authHandler AuthHandler
|
||||
|
||||
func InitAuthHandler(handler AuthHandler) {
|
||||
authHandler = handler
|
||||
}
|
||||
|
||||
var commands = map[string]struct {
|
||||
help Help
|
||||
validate ValidateFunc
|
||||
@@ -70,7 +77,7 @@ var commands = map[string]struct {
|
||||
},
|
||||
build: func(args any) CommandHandler {
|
||||
return NonTerminatingCommand(func(w http.ResponseWriter, r *http.Request) error {
|
||||
if !auth.AuthOrProceed(w, r) {
|
||||
if !authHandler(w, r) {
|
||||
return errTerminated
|
||||
}
|
||||
return nil
|
||||
@@ -198,7 +205,7 @@ var commands = map[string]struct {
|
||||
code, textTmpl := args.(*Tuple[int, templateString]).Unpack()
|
||||
return TerminatingCommand(func(w http.ResponseWriter, r *http.Request) error {
|
||||
// error command should overwrite the response body
|
||||
GetInitResponseModifier(w).ResetBody()
|
||||
httputils.GetInitResponseModifier(w).ResetBody()
|
||||
w.WriteHeader(code)
|
||||
err := textTmpl.ExpandVars(w, r, w)
|
||||
return err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
ioutils "github.com/yusing/goutils/io"
|
||||
)
|
||||
|
||||
@@ -128,7 +129,7 @@ var modFields = map[string]struct {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
GetSharedData(w).UpdateQueries(r, func(queries url.Values) {
|
||||
httputils.GetSharedData(w).UpdateQueries(r, func(queries url.Values) {
|
||||
queries.Set(k, v)
|
||||
})
|
||||
return nil
|
||||
@@ -138,13 +139,13 @@ var modFields = map[string]struct {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
GetSharedData(w).UpdateQueries(r, func(queries url.Values) {
|
||||
httputils.GetSharedData(w).UpdateQueries(r, func(queries url.Values) {
|
||||
queries.Add(k, v)
|
||||
})
|
||||
return nil
|
||||
}),
|
||||
remove: NonTerminatingCommand(func(w http.ResponseWriter, r *http.Request) error {
|
||||
GetSharedData(w).UpdateQueries(r, func(queries url.Values) {
|
||||
httputils.GetSharedData(w).UpdateQueries(r, func(queries url.Values) {
|
||||
queries.Del(k)
|
||||
})
|
||||
return nil
|
||||
@@ -169,7 +170,7 @@ var modFields = map[string]struct {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
GetSharedData(w).UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
|
||||
httputils.GetSharedData(w).UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
|
||||
for i, c := range cookies {
|
||||
if c.Name == k {
|
||||
cookies[i].Value = v
|
||||
@@ -185,13 +186,13 @@ var modFields = map[string]struct {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
GetSharedData(w).UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
|
||||
httputils.GetSharedData(w).UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
|
||||
return append(cookies, &http.Cookie{Name: k, Value: v})
|
||||
})
|
||||
return nil
|
||||
}),
|
||||
remove: NonTerminatingCommand(func(w http.ResponseWriter, r *http.Request) error {
|
||||
GetSharedData(w).UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
|
||||
httputils.GetSharedData(w).UpdateCookies(r, func(cookies []*http.Cookie) []*http.Cookie {
|
||||
index := -1
|
||||
for i, c := range cookies {
|
||||
if c.Name == k {
|
||||
@@ -242,7 +243,7 @@ var modFields = map[string]struct {
|
||||
r.Body = nil
|
||||
}
|
||||
|
||||
bufPool := GetInitResponseModifier(w).BufPool()
|
||||
bufPool := httputils.GetInitResponseModifier(w).BufPool()
|
||||
b := bufPool.GetBuffer()
|
||||
err := tmpl.ExpandVars(w, r, b)
|
||||
if err != nil {
|
||||
@@ -282,7 +283,7 @@ var modFields = map[string]struct {
|
||||
tmpl := args.(templateString)
|
||||
return &FieldHandler{
|
||||
set: OnResponseCommand(func(w http.ResponseWriter, r *http.Request) error {
|
||||
rm := GetInitResponseModifier(w)
|
||||
rm := httputils.GetInitResponseModifier(w)
|
||||
rm.ResetBody()
|
||||
return tmpl.ExpandVars(w, r, rm)
|
||||
}),
|
||||
@@ -317,7 +318,7 @@ var modFields = map[string]struct {
|
||||
status := args.(int)
|
||||
return &FieldHandler{
|
||||
set: NonTerminatingCommand(func(w http.ResponseWriter, r *http.Request) error {
|
||||
GetInitResponseModifier(w).WriteHeader(status)
|
||||
httputils.GetInitResponseModifier(w).WriteHeader(status)
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
func TestFieldHandler_Header(t *testing.T) {
|
||||
@@ -420,7 +421,7 @@ func TestFieldHandler_ResponseBody(t *testing.T) {
|
||||
name string
|
||||
template string
|
||||
setup func(*http.Request)
|
||||
verify func(*ResponseModifier)
|
||||
verify func(*httputils.ResponseModifier)
|
||||
}{
|
||||
{
|
||||
name: "set response body with template",
|
||||
@@ -429,8 +430,8 @@ func TestFieldHandler_ResponseBody(t *testing.T) {
|
||||
r.Method = "GET"
|
||||
r.URL.Path = "/api/test"
|
||||
},
|
||||
verify: func(rm *ResponseModifier) {
|
||||
content := rm.buf.String()
|
||||
verify: func(rm *httputils.ResponseModifier) {
|
||||
content := string(rm.Content())
|
||||
expected := "Response: GET /api/test"
|
||||
assert.Equal(t, expected, content, "Expected response body")
|
||||
},
|
||||
@@ -444,7 +445,7 @@ func TestFieldHandler_ResponseBody(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Create ResponseModifier wrapper
|
||||
rm := NewResponseModifier(w)
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
|
||||
tmpl, tErr := validateTemplate(tt.template, false)
|
||||
if tErr != nil {
|
||||
@@ -495,7 +496,7 @@ func TestFieldHandler_StatusCode(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
rm := NewResponseModifier(w)
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
var cmd Command
|
||||
err := cmd.Parse(fmt.Sprintf("set %s %d", FieldStatusCode, tt.status))
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
type RuleOn struct {
|
||||
@@ -95,11 +96,11 @@ var checkers = map[string]struct {
|
||||
k, matcher := args.(*MapValueMatcher).Unpack()
|
||||
if matcher == nil {
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
return len(GetInitResponseModifier(w).Header()[k]) > 0
|
||||
return len(httputils.GetInitResponseModifier(w).Header()[k]) > 0
|
||||
}
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
return slices.ContainsFunc(GetInitResponseModifier(w).Header()[k], matcher)
|
||||
return slices.ContainsFunc(httputils.GetInitResponseModifier(w).Header()[k], matcher)
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -122,11 +123,11 @@ var checkers = map[string]struct {
|
||||
k, matcher := args.(*MapValueMatcher).Unpack()
|
||||
if matcher == nil {
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
return len(GetSharedData(w).GetQueries(r)[k]) > 0
|
||||
return len(httputils.GetSharedData(w).GetQueries(r)[k]) > 0
|
||||
}
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
return slices.ContainsFunc(GetSharedData(w).GetQueries(r)[k], matcher)
|
||||
return slices.ContainsFunc(httputils.GetSharedData(w).GetQueries(r)[k], matcher)
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -149,7 +150,7 @@ var checkers = map[string]struct {
|
||||
k, matcher := args.(*MapValueMatcher).Unpack()
|
||||
if matcher == nil {
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
cookies := GetSharedData(w).GetCookies(r)
|
||||
cookies := httputils.GetSharedData(w).GetCookies(r)
|
||||
for _, cookie := range cookies {
|
||||
if cookie.Name == k {
|
||||
return true
|
||||
@@ -159,7 +160,7 @@ var checkers = map[string]struct {
|
||||
}
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
cookies := GetSharedData(w).GetCookies(r)
|
||||
cookies := httputils.GetSharedData(w).GetCookies(r)
|
||||
for _, cookie := range cookies {
|
||||
if cookie.Name == k {
|
||||
if matcher(cookie.Value) {
|
||||
@@ -302,7 +303,7 @@ var checkers = map[string]struct {
|
||||
if ones, bits := ipnet.Mask.Size(); ones == bits {
|
||||
wantIP := ipnet.IP
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
ip := GetSharedData(w).GetRemoteIP(r)
|
||||
ip := httputils.GetSharedData(w).GetRemoteIP(r)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
@@ -310,7 +311,7 @@ var checkers = map[string]struct {
|
||||
}
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
ip := GetSharedData(w).GetRemoteIP(r)
|
||||
ip := httputils.GetSharedData(w).GetRemoteIP(r)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
@@ -330,7 +331,7 @@ var checkers = map[string]struct {
|
||||
builder: func(args any) CheckFunc {
|
||||
cred := args.(*HashedCrendentials)
|
||||
return func(w http.ResponseWriter, r *http.Request) bool {
|
||||
return cred.Match(GetSharedData(w).GetBasicAuth(r))
|
||||
return cred.Match(httputils.GetSharedData(w).GetBasicAuth(r))
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -378,11 +379,11 @@ var checkers = map[string]struct {
|
||||
beg, end := args.(*IntTuple).Unpack()
|
||||
if beg == end {
|
||||
return func(w http.ResponseWriter, _ *http.Request) bool {
|
||||
return GetInitResponseModifier(w).StatusCode() == beg
|
||||
return httputils.GetInitResponseModifier(w).StatusCode() == beg
|
||||
}
|
||||
}
|
||||
return func(w http.ResponseWriter, _ *http.Request) bool {
|
||||
statusCode := GetInitResponseModifier(w).StatusCode()
|
||||
statusCode := httputils.GetInitResponseModifier(w).StatusCode()
|
||||
return statusCode >= beg && statusCode <= end
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
!path glob("/auth/*")
|
||||
!path regex("[A-Za-z0-9_-]+\.(svg|png|jpg|jpeg|gif|ico|webp|woff2?|eot|ttf|otf|txt)(\?.+)?")
|
||||
!path /api/v1/version
|
||||
!path /manifest.json
|
||||
do: require_auth
|
||||
- name: proxy to backend
|
||||
on: path glob("/api/v1/*")
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/synk"
|
||||
)
|
||||
|
||||
type ResponseModifier struct {
|
||||
bufPool synk.UnsizedBytesPool
|
||||
|
||||
w http.ResponseWriter
|
||||
buf *bytes.Buffer
|
||||
statusCode int
|
||||
shared Cache
|
||||
|
||||
hijacked bool
|
||||
|
||||
errs gperr.Builder
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
StatusCode int
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
func unwrapResponseModifier(w http.ResponseWriter) *ResponseModifier {
|
||||
for {
|
||||
switch ww := w.(type) {
|
||||
case *ResponseModifier:
|
||||
return ww
|
||||
case interface{ Unwrap() http.ResponseWriter }:
|
||||
w = ww.Unwrap()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type responseAsRW struct {
|
||||
resp *http.Response
|
||||
}
|
||||
|
||||
func (r responseAsRW) WriteHeader(code int) {
|
||||
log.Error().Msg("write header after response has been created")
|
||||
}
|
||||
|
||||
func (r responseAsRW) Write(b []byte) (int, error) {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
func (r responseAsRW) Header() http.Header {
|
||||
return r.resp.Header
|
||||
}
|
||||
|
||||
func ResponseAsRW(resp *http.Response) *ResponseModifier {
|
||||
return &ResponseModifier{
|
||||
statusCode: resp.StatusCode,
|
||||
w: responseAsRW{resp},
|
||||
}
|
||||
}
|
||||
|
||||
// GetInitResponseModifier returns the response modifier for the given response writer.
|
||||
// If the response writer is already wrapped, it will return the wrapped response modifier.
|
||||
// Otherwise, it will return a new response modifier.
|
||||
func GetInitResponseModifier(w http.ResponseWriter) *ResponseModifier {
|
||||
if rm := unwrapResponseModifier(w); rm != nil {
|
||||
return rm
|
||||
}
|
||||
return NewResponseModifier(w)
|
||||
}
|
||||
|
||||
// GetSharedData returns the shared data for the given response writer.
|
||||
// It will initialize the shared data if not initialized.
|
||||
func GetSharedData(w http.ResponseWriter) Cache {
|
||||
rm := GetInitResponseModifier(w)
|
||||
if rm.shared == nil {
|
||||
rm.shared = NewCache()
|
||||
}
|
||||
return rm.shared
|
||||
}
|
||||
|
||||
// NewResponseModifier returns a new response modifier for the given response writer.
|
||||
//
|
||||
// It should only be called once, at the very beginning of the request.
|
||||
func NewResponseModifier(w http.ResponseWriter) *ResponseModifier {
|
||||
return &ResponseModifier{
|
||||
bufPool: synk.GetUnsizedBytesPool(),
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) BufPool() synk.UnsizedBytesPool {
|
||||
return rm.bufPool
|
||||
}
|
||||
|
||||
// func (rm *ResponseModifier) Unwrap() http.ResponseWriter {
|
||||
// return rm.w
|
||||
// }
|
||||
|
||||
func (rm *ResponseModifier) WriteHeader(code int) {
|
||||
rm.statusCode = code
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) ResetBody() {
|
||||
if rm.buf == nil {
|
||||
return
|
||||
}
|
||||
rm.buf.Reset()
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) ContentLength() int {
|
||||
if rm.buf == nil {
|
||||
return 0
|
||||
}
|
||||
return rm.buf.Len()
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) Content() []byte {
|
||||
if rm.buf == nil {
|
||||
return nil
|
||||
}
|
||||
return rm.buf.Bytes()
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) StatusCode() int {
|
||||
if rm.statusCode == 0 {
|
||||
return http.StatusOK
|
||||
}
|
||||
return rm.statusCode
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) Header() http.Header {
|
||||
return rm.w.Header()
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) Response() Response {
|
||||
return Response{StatusCode: rm.StatusCode(), Header: rm.Header()}
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) Write(b []byte) (int, error) {
|
||||
if rm.buf == nil {
|
||||
rm.buf = rm.bufPool.GetBuffer()
|
||||
}
|
||||
return rm.buf.Write(b)
|
||||
}
|
||||
|
||||
// AppendError appends an error to the response modifier
|
||||
// the error will be formatted as "rule <rule.Name> error: <err>"
|
||||
//
|
||||
// It will be aggregated and returned in FlushRelease.
|
||||
func (rm *ResponseModifier) AppendError(rule Rule, err error) {
|
||||
rm.errs.Addf("rule %q error: %w", rule.Name, err)
|
||||
}
|
||||
|
||||
func (rm *ResponseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if hijacker, ok := rm.w.(http.Hijacker); ok {
|
||||
rm.hijacked = true
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
return nil, nil, errors.New("hijack not supported")
|
||||
}
|
||||
|
||||
// FlushRelease flushes the response modifier and releases the resources
|
||||
// it returns the number of bytes written and the aggregated error
|
||||
// if there is any error (rule errors or write error), it will be returned
|
||||
func (rm *ResponseModifier) FlushRelease() (int, error) {
|
||||
n := 0
|
||||
if !rm.hijacked {
|
||||
h := rm.w.Header()
|
||||
// for k := range h {
|
||||
// if strings.EqualFold(k, "content-length") {
|
||||
// h.Del(k)
|
||||
// }
|
||||
// }
|
||||
contentLength := rm.ContentLength()
|
||||
h.Set("Content-Length", strconv.Itoa(rm.ContentLength()))
|
||||
h.Del("Transfer-Encoding")
|
||||
h.Del("Trailer")
|
||||
rm.w.WriteHeader(rm.StatusCode())
|
||||
|
||||
if contentLength > 0 {
|
||||
nn, werr := rm.w.Write(rm.Content())
|
||||
n += nn
|
||||
if werr != nil {
|
||||
rm.errs.Addf("write error: %w", werr)
|
||||
}
|
||||
if err := http.NewResponseController(rm.w).Flush(); err != nil {
|
||||
rm.errs.Addf("flush error: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// release the buffer and reset the pointers
|
||||
if rm.buf != nil {
|
||||
rm.bufPool.PutBuffer(rm.buf)
|
||||
rm.buf = nil
|
||||
}
|
||||
|
||||
// release the shared data
|
||||
if rm.shared != nil {
|
||||
rm.shared.Release()
|
||||
rm.shared = nil
|
||||
}
|
||||
|
||||
return n, rm.errs.Error()
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"github.com/rs/zerolog/log"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
_ "unsafe"
|
||||
@@ -92,7 +92,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
if defaultRule.IsResponseRule() {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
rm := NewResponseModifier(w)
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
defer func() {
|
||||
if _, err := rm.FlushRelease(); err != nil {
|
||||
logError(err, r)
|
||||
@@ -102,12 +102,12 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
up(w, r)
|
||||
err := defaultRule.Do.exec.Handle(w, r)
|
||||
if err != nil && !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(defaultRule, err)
|
||||
appendRuleError(rm, &defaultRule, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
rm := NewResponseModifier(w)
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
defer func() {
|
||||
if _, err := rm.FlushRelease(); err != nil {
|
||||
logError(err, r)
|
||||
@@ -120,7 +120,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(defaultRule, err)
|
||||
appendRuleError(rm, &defaultRule, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
defaultTerminates := isTerminatingHandler(defaultRule.Do.exec)
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
rm := NewResponseModifier(w)
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
defer func() {
|
||||
if _, err := rm.FlushRelease(); err != nil {
|
||||
logError(err, r)
|
||||
@@ -158,7 +158,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
err := defaultRule.Handle(w, r)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(defaultRule, err)
|
||||
appendRuleError(rm, &defaultRule, err)
|
||||
}
|
||||
shouldCallUpstream = false
|
||||
}
|
||||
@@ -175,7 +175,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
err := rule.Handle(w, r)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(rule, err)
|
||||
appendRuleError(rm, &rule, err)
|
||||
}
|
||||
shouldCallUpstream = false
|
||||
break
|
||||
@@ -191,7 +191,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
err := defaultRule.Handle(w, r)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(defaultRule, err)
|
||||
appendRuleError(rm, &defaultRule, err)
|
||||
return
|
||||
}
|
||||
shouldCallUpstream = false
|
||||
@@ -213,7 +213,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
err := rule.Handle(w, r)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(rule, err)
|
||||
appendRuleError(rm, &rule, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -223,12 +223,16 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc {
|
||||
if isDefaultRulePost {
|
||||
err := defaultRule.Handle(w, r)
|
||||
if err != nil && !errors.Is(err, errTerminated) {
|
||||
rm.AppendError(defaultRule, err)
|
||||
appendRuleError(rm, &defaultRule, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendRuleError(rm *httputils.ResponseModifier, rule *Rule, err error) {
|
||||
rm.AppendError("rule: %s, error: %w", rule.Name, err)
|
||||
}
|
||||
|
||||
func isTerminatingHandler(handler CommandHandler) bool {
|
||||
switch h := handler.(type) {
|
||||
case TerminatingCommand:
|
||||
@@ -243,14 +247,6 @@ func isTerminatingHandler(handler CommandHandler) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (rules Rules) MarshalJSON() ([]byte, error) {
|
||||
names := make([]string, len(rules))
|
||||
for i, rule := range rules {
|
||||
names[i] = rule.Name
|
||||
}
|
||||
return sonic.Marshal(names)
|
||||
}
|
||||
|
||||
func (rule *Rule) String() string {
|
||||
return rule.Name
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
type templateString struct {
|
||||
@@ -27,7 +29,7 @@ func (tmpl *templateString) ExpandVars(w http.ResponseWriter, req *http.Request,
|
||||
return err
|
||||
}
|
||||
|
||||
return ExpandVars(GetInitResponseModifier(w), req, tmpl.string, dstW)
|
||||
return ExpandVars(httputils.GetInitResponseModifier(w), req, tmpl.string, dstW)
|
||||
}
|
||||
|
||||
func (tmpl *templateString) ExpandVarsToString(w http.ResponseWriter, req *http.Request) (string, error) {
|
||||
@@ -36,7 +38,7 @@ func (tmpl *templateString) ExpandVarsToString(w http.ResponseWriter, req *http.
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
err := ExpandVars(GetInitResponseModifier(w), req, tmpl.string, &buf)
|
||||
err := ExpandVars(httputils.GetInitResponseModifier(w), req, tmpl.string, &buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
func BenchmarkExpandVars(b *testing.B) {
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier.WriteHeader(200)
|
||||
testResponseModifier.Write([]byte("Hello, world!"))
|
||||
testRequest := httptest.NewRequest("GET", "/", nil)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
ioutils "github.com/yusing/goutils/io"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
|
||||
type (
|
||||
reqVarGetter func(*http.Request) string
|
||||
respVarGetter func(*ResponseModifier) string
|
||||
respVarGetter func(*httputils.ResponseModifier) string
|
||||
)
|
||||
|
||||
var reVar = regexp.MustCompile(`\$[\w_]+`)
|
||||
@@ -36,7 +37,7 @@ func NeedExpandVars(s string) bool {
|
||||
}
|
||||
|
||||
var (
|
||||
voidResponseModifier = NewResponseModifier(httptest.NewRecorder())
|
||||
voidResponseModifier = httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
dummyRequest = http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{Path: "/"},
|
||||
@@ -50,7 +51,7 @@ func ValidateVars(s string) error {
|
||||
return ExpandVars(voidResponseModifier, &dummyRequest, s, io.Discard)
|
||||
}
|
||||
|
||||
func ExpandVars(w *ResponseModifier, req *http.Request, src string, dstW io.Writer) error {
|
||||
func ExpandVars(w *httputils.ResponseModifier, req *http.Request, src string, dstW io.Writer) error {
|
||||
dst := ioutils.NewBufferedWriter(dstW, 1024)
|
||||
defer dst.Close()
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -14,31 +16,31 @@ var (
|
||||
VarPostForm = "postform"
|
||||
)
|
||||
|
||||
type dynamicVarGetter func(args []string, w *ResponseModifier, req *http.Request) (string, error)
|
||||
type dynamicVarGetter func(args []string, w *httputils.ResponseModifier, req *http.Request) (string, error)
|
||||
|
||||
var dynamicVarSubsMap = map[string]dynamicVarGetter{
|
||||
VarHeader: func(args []string, w *ResponseModifier, req *http.Request) (string, error) {
|
||||
VarHeader: func(args []string, w *httputils.ResponseModifier, req *http.Request) (string, error) {
|
||||
key, index, err := getKeyAndIndex(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return getValueByKeyAtIndex(req.Header, key, index)
|
||||
},
|
||||
VarResponseHeader: func(args []string, w *ResponseModifier, req *http.Request) (string, error) {
|
||||
VarResponseHeader: func(args []string, w *httputils.ResponseModifier, req *http.Request) (string, error) {
|
||||
key, index, err := getKeyAndIndex(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return getValueByKeyAtIndex(w.Header(), key, index)
|
||||
},
|
||||
VarQuery: func(args []string, w *ResponseModifier, req *http.Request) (string, error) {
|
||||
VarQuery: func(args []string, w *httputils.ResponseModifier, req *http.Request) (string, error) {
|
||||
key, index, err := getKeyAndIndex(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return getValueByKeyAtIndex(GetSharedData(w).GetQueries(req), key, index)
|
||||
return getValueByKeyAtIndex(httputils.GetSharedData(w).GetQueries(req), key, index)
|
||||
},
|
||||
VarForm: func(args []string, w *ResponseModifier, req *http.Request) (string, error) {
|
||||
VarForm: func(args []string, w *httputils.ResponseModifier, req *http.Request) (string, error) {
|
||||
key, index, err := getKeyAndIndex(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -50,7 +52,7 @@ var dynamicVarSubsMap = map[string]dynamicVarGetter{
|
||||
}
|
||||
return getValueByKeyAtIndex(req.Form, key, index)
|
||||
},
|
||||
VarPostForm: func(args []string, w *ResponseModifier, req *http.Request) (string, error) {
|
||||
VarPostForm: func(args []string, w *httputils.ResponseModifier, req *http.Request) (string, error) {
|
||||
key, index, err := getKeyAndIndex(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -87,9 +88,9 @@ var staticReqVarSubsMap = map[string]reqVarGetter{
|
||||
}
|
||||
|
||||
var staticRespVarSubsMap = map[string]respVarGetter{
|
||||
VarRespContentType: func(resp *ResponseModifier) string { return resp.Header().Get("Content-Type") },
|
||||
VarRespContentLen: func(resp *ResponseModifier) string { return strconv.Itoa(resp.ContentLength()) },
|
||||
VarRespStatusCode: func(resp *ResponseModifier) string { return strconv.Itoa(resp.StatusCode()) },
|
||||
VarRespContentType: func(resp *httputils.ResponseModifier) string { return resp.Header().Get("Content-Type") },
|
||||
VarRespContentLen: func(resp *httputils.ResponseModifier) string { return resp.ContentLengthStr() },
|
||||
VarRespStatusCode: func(resp *httputils.ResponseModifier) string { return strconv.Itoa(resp.StatusCode()) },
|
||||
}
|
||||
|
||||
func stripFragment(s string) string {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
func TestExtractArgs(t *testing.T) {
|
||||
@@ -214,7 +215,7 @@ func TestExpandVars(t *testing.T) {
|
||||
testRequest.PostForm = postFormData
|
||||
|
||||
// Create response modifier with headers
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier.Header().Set("Content-Type", "text/html")
|
||||
testResponseModifier.Header().Set("X-Custom-Resp", "resp-value")
|
||||
testResponseModifier.WriteHeader(200)
|
||||
@@ -251,7 +252,7 @@ func TestExpandVars(t *testing.T) {
|
||||
{
|
||||
name: "req_uri",
|
||||
input: "$req_uri",
|
||||
want: "/api/users?param1=value1¶m2=value2#fragment",
|
||||
want: "/api/users?param1=value1¶m2=value2",
|
||||
},
|
||||
{
|
||||
name: "req_host",
|
||||
@@ -483,7 +484,7 @@ func TestExpandVars(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var out strings.Builder
|
||||
err := ExpandVars(testResponseModifier, testRequest, tt.input, &out)
|
||||
err := ExpandVars(httputils.NewResponseModifier(httptest.NewRecorder()), testRequest, tt.input, &out)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
@@ -501,11 +502,11 @@ func TestExpandVars_Integration(t *testing.T) {
|
||||
testRequest.Header.Set("User-Agent", "curl/7.68.0")
|
||||
testRequest.RemoteAddr = "10.0.0.1:54321"
|
||||
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier.WriteHeader(200)
|
||||
|
||||
var out strings.Builder
|
||||
err := ExpandVars(testResponseModifier, testRequest,
|
||||
err := ExpandVars(httputils.NewResponseModifier(httptest.NewRecorder()), testRequest,
|
||||
"$req_method $req_url $status_code User-Agent=$header(User-Agent)",
|
||||
&out)
|
||||
|
||||
@@ -516,7 +517,7 @@ func TestExpandVars_Integration(t *testing.T) {
|
||||
t.Run("with query parameters", func(t *testing.T) {
|
||||
testRequest := httptest.NewRequest("GET", "http://example.com/search?q=test&page=1", nil)
|
||||
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
|
||||
var out strings.Builder
|
||||
err := ExpandVars(testResponseModifier, testRequest,
|
||||
@@ -530,13 +531,13 @@ func TestExpandVars_Integration(t *testing.T) {
|
||||
t.Run("response headers", func(t *testing.T) {
|
||||
testRequest := httptest.NewRequest("GET", "/", nil)
|
||||
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier.Header().Set("Cache-Control", "no-cache")
|
||||
testResponseModifier.Header().Set("X-Rate-Limit", "100")
|
||||
testResponseModifier.WriteHeader(200)
|
||||
|
||||
var out strings.Builder
|
||||
err := ExpandVars(testResponseModifier, testRequest,
|
||||
err := ExpandVars(httputils.NewResponseModifier(httptest.NewRecorder()), testRequest,
|
||||
"Status: $status_code, Cache: $resp_header(Cache-Control), Limit: $resp_header(X-Rate-Limit)",
|
||||
&out)
|
||||
|
||||
@@ -569,7 +570,7 @@ func TestExpandVars_RequestSchemes(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
var out strings.Builder
|
||||
err := ExpandVars(testResponseModifier, tt.request, "$req_scheme", &out)
|
||||
require.NoError(t, err)
|
||||
@@ -582,7 +583,7 @@ func TestExpandVars_UpstreamVariables(t *testing.T) {
|
||||
// Upstream variables require context from routes package
|
||||
testRequest := httptest.NewRequest("GET", "/", nil)
|
||||
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
|
||||
// Test that upstream variables don't cause errors even when not set
|
||||
upstreamVars := []string{
|
||||
@@ -609,7 +610,7 @@ func TestExpandVars_NoHostPort(t *testing.T) {
|
||||
testRequest := httptest.NewRequest("GET", "/", nil)
|
||||
testRequest.Host = "example.com" // No port
|
||||
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
|
||||
t.Run("req_host without port", func(t *testing.T) {
|
||||
var out strings.Builder
|
||||
@@ -631,7 +632,7 @@ func TestExpandVars_NoRemotePort(t *testing.T) {
|
||||
testRequest := httptest.NewRequest("GET", "/", nil)
|
||||
testRequest.RemoteAddr = "192.168.1.1" // No port
|
||||
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
|
||||
t.Run("remote_host without port", func(t *testing.T) {
|
||||
var out strings.Builder
|
||||
@@ -650,7 +651,7 @@ func TestExpandVars_NoRemotePort(t *testing.T) {
|
||||
|
||||
func TestExpandVars_WhitespaceHandling(t *testing.T) {
|
||||
testRequest := httptest.NewRequest("GET", "/test", nil)
|
||||
testResponseModifier := NewResponseModifier(httptest.NewRecorder())
|
||||
testResponseModifier := httputils.NewResponseModifier(httptest.NewRecorder())
|
||||
|
||||
var out strings.Builder
|
||||
err := ExpandVars(testResponseModifier, testRequest, "$req_method $req_path", &out)
|
||||
|
||||
@@ -69,10 +69,6 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
|
||||
}
|
||||
}
|
||||
|
||||
if r.ShouldExclude() {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.ListenAndServe(r.task.Context(), nil, nil)
|
||||
r.l = r.l.With().Stringer("rurl", r.ProxyURL).Stringer("laddr", r.LocalAddr()).Logger()
|
||||
r.l.Info().Msg("stream started")
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gi "github.com/yusing/gointernals"
|
||||
"github.com/yusing/goutils/env"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
@@ -22,6 +21,22 @@ import (
|
||||
|
||||
type SerializedObject = map[string]any
|
||||
|
||||
// ToSerializedObject converts a map[string]VT to a SerializedObject.
|
||||
func ToSerializedObject[VT any](m map[string]VT) SerializedObject {
|
||||
so := make(SerializedObject, len(m))
|
||||
for k, v := range m {
|
||||
so[k] = v
|
||||
}
|
||||
return so
|
||||
}
|
||||
|
||||
func init() {
|
||||
strutils.SetJSONMarshaler(sonic.Marshal)
|
||||
strutils.SetJSONUnmarshaler(sonic.Unmarshal)
|
||||
strutils.SetYAMLMarshaler(yaml.Marshal)
|
||||
strutils.SetYAMLUnmarshaler(yaml.Unmarshal)
|
||||
}
|
||||
|
||||
type MapUnmarshaller interface {
|
||||
UnmarshalMap(m map[string]any) gperr.Error
|
||||
}
|
||||
@@ -300,7 +315,7 @@ func mapUnmarshalValidate(src SerializedObject, dstV reflect.Value, checkValidat
|
||||
errs.Add(err.Subject(k))
|
||||
}
|
||||
} else {
|
||||
errs.Add(ErrUnknownField.Subject(k).With(gperr.DoYouMean(utils.NearestField(k, info.fieldNames))))
|
||||
errs.Add(ErrUnknownField.Subject(k).With(gperr.DoYouMeanField(k, info.fieldNames)))
|
||||
}
|
||||
}
|
||||
if info.hasValidateTag && checkValidateTag {
|
||||
|
||||
@@ -2,20 +2,17 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/yusing/ds/ordered"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type (
|
||||
LabelMap = map[string]any
|
||||
|
||||
PortMapping = map[int]container.Port
|
||||
PortMapping = map[int]container.PortSummary
|
||||
Container struct {
|
||||
_ utils.NoCopy
|
||||
|
||||
DockerHost string `json:"docker_host"`
|
||||
Image *ContainerImage `json:"image"`
|
||||
ContainerName string `json:"container_name"`
|
||||
|
||||
@@ -3,25 +3,39 @@ package types
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
)
|
||||
|
||||
type HealthCheckConfig struct {
|
||||
Disable bool `json:"disable,omitempty" aliases:"disabled"`
|
||||
Path string `json:"path,omitempty" validate:"omitempty,uri,startswith=/"`
|
||||
UseGet bool `json:"use_get,omitempty"`
|
||||
Path string `json:"path,omitempty" validate:"omitempty,uri,startswith=/"`
|
||||
Interval time.Duration `json:"interval" validate:"omitempty,min=1s" swaggertype:"primitive,integer"`
|
||||
Timeout time.Duration `json:"timeout" validate:"omitempty,min=1s" swaggertype:"primitive,integer"`
|
||||
Retries int64 `json:"retries"` // <0: immediate, >=0: threshold
|
||||
Retries int64 `json:"retries"` // <0: immediate, 0: default, >0: threshold
|
||||
|
||||
BaseContext func() context.Context `json:"-"`
|
||||
} // @name HealthCheckConfig
|
||||
|
||||
func DefaultHealthConfig() *HealthCheckConfig {
|
||||
return &HealthCheckConfig{
|
||||
Interval: common.HealthCheckIntervalDefault,
|
||||
Timeout: common.HealthCheckTimeoutDefault,
|
||||
Retries: int64(common.HealthCheckDownNotifyDelayDefault / common.HealthCheckIntervalDefault),
|
||||
const (
|
||||
HealthCheckIntervalDefault = 5 * time.Second
|
||||
HealthCheckTimeoutDefault = 5 * time.Second
|
||||
HealthCheckDownNotifyDelayDefault = 15 * time.Second
|
||||
)
|
||||
|
||||
func (hc *HealthCheckConfig) ApplyDefaults(defaults HealthCheckConfig) {
|
||||
if hc.Interval == 0 {
|
||||
hc.Interval = defaults.Interval
|
||||
if hc.Interval == 0 {
|
||||
hc.Interval = HealthCheckIntervalDefault
|
||||
}
|
||||
}
|
||||
if hc.Timeout == 0 {
|
||||
hc.Timeout = defaults.Timeout
|
||||
if hc.Timeout == 0 {
|
||||
hc.Timeout = HealthCheckTimeoutDefault
|
||||
}
|
||||
}
|
||||
if hc.Retries == 0 {
|
||||
hc.Retries = int64(HealthCheckDownNotifyDelayDefault / hc.Interval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
provider "github.com/yusing/godoxy/internal/route/provider/types"
|
||||
"github.com/yusing/godoxy/internal/utils/pool"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/reverseproxy"
|
||||
"github.com/yusing/goutils/pool"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ type (
|
||||
Started() <-chan struct{}
|
||||
|
||||
IdlewatcherConfig() *IdlewatcherConfig
|
||||
HealthCheckConfig() *HealthCheckConfig
|
||||
HealthCheckConfig() HealthCheckConfig
|
||||
LoadBalanceConfig() *LoadBalancerConfig
|
||||
HomepageItem() homepage.Item
|
||||
DisplayName() string
|
||||
@@ -52,6 +52,10 @@ type (
|
||||
HTTPRoute
|
||||
ReverseProxy() *reverseproxy.ReverseProxy
|
||||
}
|
||||
FileServerRoute interface {
|
||||
HTTPRoute
|
||||
RootPath() string
|
||||
}
|
||||
StreamRoute interface {
|
||||
Route
|
||||
nettypes.Stream
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Recursively lists all files in a directory until `maxDepth` is reached
|
||||
// Returns a slice of file paths relative to `dir`.
|
||||
func ListFiles(dir string, maxDepth int, hideHidden ...bool) ([]string, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing directory %s: %w", dir, err)
|
||||
}
|
||||
hideHiddenFiles := len(hideHidden) > 0 && hideHidden[0]
|
||||
files := make([]string, 0)
|
||||
for _, entry := range entries {
|
||||
if hideHiddenFiles && entry.Name()[0] == '.' {
|
||||
continue
|
||||
}
|
||||
if entry.IsDir() {
|
||||
if maxDepth <= 0 {
|
||||
continue
|
||||
}
|
||||
subEntries, err := ListFiles(path.Join(dir, entry.Name()), maxDepth-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, subEntries...)
|
||||
} else {
|
||||
files = append(files, path.Join(dir, entry.Name()))
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
func NearestField(input string, s any) string {
|
||||
minDistance := -1
|
||||
nearestField := ""
|
||||
var fields []string
|
||||
switch s := s.(type) {
|
||||
case []string:
|
||||
fields = s
|
||||
default:
|
||||
t := reflect.TypeOf(s)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Struct:
|
||||
fields = make([]string, 0)
|
||||
for i := range t.NumField() {
|
||||
jsonTag, ok := t.Field(i).Tag.Lookup("json")
|
||||
if ok {
|
||||
fields = append(fields, jsonTag)
|
||||
} else {
|
||||
fields = append(fields, t.Field(i).Name)
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
keys := reflect.ValueOf(s).MapKeys()
|
||||
fields = make([]string, len(keys))
|
||||
for i, key := range keys {
|
||||
fields[i] = key.String()
|
||||
}
|
||||
default:
|
||||
panic("NearestField unsupported type: " + t.String())
|
||||
}
|
||||
}
|
||||
for _, field := range fields {
|
||||
distance := strutils.LevenshteinDistance(input, field)
|
||||
if minDistance == -1 || distance < minDistance {
|
||||
minDistance = distance
|
||||
nearestField = field
|
||||
}
|
||||
}
|
||||
return nearestField
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type (
|
||||
Pool[T Object] struct {
|
||||
m *xsync.Map[string, T]
|
||||
name string
|
||||
disableLog atomic.Bool
|
||||
}
|
||||
// Preferable allows an object to express deterministic replacement preference
|
||||
// when multiple objects with the same key are added to the pool.
|
||||
// If new.PreferOver(old) returns true, the new object replaces the old one.
|
||||
Preferable interface {
|
||||
PreferOver(other any) bool
|
||||
}
|
||||
Object interface {
|
||||
Key() string
|
||||
Name() string
|
||||
}
|
||||
ObjectWithDisplayName interface {
|
||||
Object
|
||||
DisplayName() string
|
||||
}
|
||||
)
|
||||
|
||||
func New[T Object](name string) Pool[T] {
|
||||
return Pool[T]{m: xsync.NewMap[string, T](), name: name}
|
||||
}
|
||||
|
||||
func (p *Pool[T]) ToggleLog(v bool) {
|
||||
p.disableLog.Store(v)
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Name() string {
|
||||
return p.name
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Add(obj T) {
|
||||
p.AddKey(obj.Key(), obj)
|
||||
}
|
||||
|
||||
func (p *Pool[T]) AddKey(key string, obj T) {
|
||||
if cur, exists := p.m.Load(key); exists {
|
||||
if newPref, ok := any(obj).(Preferable); ok {
|
||||
if !newPref.PreferOver(cur) {
|
||||
// keep existing
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
p.checkExists(key)
|
||||
p.m.Store(key, obj)
|
||||
p.logAction("added", obj)
|
||||
}
|
||||
|
||||
func (p *Pool[T]) AddIfNotExists(obj T) (actual T, added bool) {
|
||||
actual, loaded := p.m.LoadOrStore(obj.Key(), obj)
|
||||
if !loaded {
|
||||
p.logAction("added", obj)
|
||||
}
|
||||
return actual, !loaded
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Del(obj T) {
|
||||
p.m.Delete(obj.Key())
|
||||
p.logAction("removed", obj)
|
||||
}
|
||||
|
||||
func (p *Pool[T]) DelKey(key string) {
|
||||
if v, exists := p.m.LoadAndDelete(key); exists {
|
||||
p.logAction("removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Get(key string) (T, bool) {
|
||||
return p.m.Load(key)
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Size() int {
|
||||
return p.m.Size()
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Clear() {
|
||||
p.m.Clear()
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Iter(fn func(k string, v T) bool) {
|
||||
p.m.Range(fn)
|
||||
}
|
||||
|
||||
func (p *Pool[T]) Slice() []T {
|
||||
slice := make([]T, 0, p.m.Size())
|
||||
for _, v := range p.m.Range {
|
||||
slice = append(slice, v)
|
||||
}
|
||||
sort.Slice(slice, func(i, j int) bool {
|
||||
return slice[i].Name() < slice[j].Name()
|
||||
})
|
||||
return slice
|
||||
}
|
||||
|
||||
func (p *Pool[T]) logAction(action string, obj T) {
|
||||
if p.disableLog.Load() {
|
||||
return
|
||||
}
|
||||
if withName, ok := any(obj).(ObjectWithDisplayName); ok {
|
||||
disp, name := withName.DisplayName(), withName.Name()
|
||||
if disp != name {
|
||||
log.Info().Msgf("%s: %s %s (%s)", p.name, action, disp, name)
|
||||
} else {
|
||||
log.Info().Msgf("%s: %s %s", p.name, action, name)
|
||||
}
|
||||
} else {
|
||||
log.Info().Msgf("%s: %s %s", p.name, action, obj.Name())
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build debug
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (p Pool[T]) checkExists(key string) {
|
||||
if _, ok := p.m.Load(key); ok {
|
||||
log.Warn().Msgf("%s: key %s already exists\nstacktrace: %s", p.name, key, string(debug.Stack()))
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
//go:build !debug
|
||||
|
||||
package pool
|
||||
|
||||
func (p Pool[T]) checkExists(key string) {
|
||||
// no-op in production
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
TimeNow = DefaultTimeNow
|
||||
shouldCallTimeNow atomic.Bool
|
||||
timeNowTicker = time.NewTicker(shouldCallTimeNowInterval)
|
||||
lastTimeNow = atomic.NewTime(time.Now())
|
||||
)
|
||||
|
||||
const shouldCallTimeNowInterval = 100 * time.Millisecond
|
||||
|
||||
func MockTimeNow(t time.Time) {
|
||||
TimeNow = func() time.Time {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultTimeNow is a time.Now wrapper that reduces the number of calls to time.Now
|
||||
// by caching the result and only allow calling time.Now when the ticker fires.
|
||||
//
|
||||
// Returned value may have +-100ms error.
|
||||
func DefaultTimeNow() time.Time {
|
||||
swapped := shouldCallTimeNow.CompareAndSwap(false, true)
|
||||
if swapped { // first call
|
||||
now := time.Now()
|
||||
lastTimeNow.Store(now)
|
||||
return now
|
||||
}
|
||||
return lastTimeNow.Load()
|
||||
}
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
for range timeNowTicker.C {
|
||||
shouldCallTimeNow.Store(true)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var sink time.Time
|
||||
|
||||
func BenchmarkTimeNow(b *testing.B) {
|
||||
b.Run("default", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
sink = time.Now()
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("reduced_call", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
sink = DefaultTimeNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultTimeNow(t *testing.T) {
|
||||
// Get initial time
|
||||
t1 := DefaultTimeNow()
|
||||
|
||||
// Second call should return the same time without calling time.Now
|
||||
t2 := DefaultTimeNow()
|
||||
|
||||
if !t1.Equal(t2) {
|
||||
t.Errorf("Expected t1 == t2, got t1 = %v, t2 = %v", t1, t2)
|
||||
}
|
||||
|
||||
// Set shouldCallTimeNow to true
|
||||
shouldCallTimeNow.Store(true)
|
||||
|
||||
// This should update the lastTimeNow
|
||||
t3 := DefaultTimeNow()
|
||||
|
||||
// The time should have changed
|
||||
if t2.Equal(t3) {
|
||||
t.Errorf("Expected t2 != t3, got t2 = %v, t3 = %v", t2, t3)
|
||||
}
|
||||
|
||||
// Fourth call should return the same time as third call
|
||||
t4 := DefaultTimeNow()
|
||||
|
||||
if !t3.Equal(t4) {
|
||||
t.Errorf("Expected t3 == t4, got t3 = %v, t4 = %v", t3, t4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockTimeNow(t *testing.T) {
|
||||
// Save the original TimeNow function to restore later
|
||||
originalTimeNow := TimeNow
|
||||
defer func() {
|
||||
TimeNow = originalTimeNow
|
||||
}()
|
||||
|
||||
// Create a fixed time
|
||||
fixedTime := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
// Mock the time
|
||||
MockTimeNow(fixedTime)
|
||||
|
||||
// TimeNow should return the fixed time
|
||||
result := TimeNow()
|
||||
|
||||
if !result.Equal(fixedTime) {
|
||||
t.Errorf("Expected %v, got %v", fixedTime, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeNowTicker(t *testing.T) {
|
||||
// This test verifies that the ticker properly updates shouldCallTimeNow
|
||||
|
||||
// Reset the flag
|
||||
shouldCallTimeNow.Store(false)
|
||||
|
||||
// Wait for the ticker to tick (slightly more than the interval)
|
||||
time.Sleep(shouldCallTimeNowInterval + 10*time.Millisecond)
|
||||
|
||||
// The ticker should have set shouldCallTimeNow to true
|
||||
if !shouldCallTimeNow.Load() {
|
||||
t.Error("Expected shouldCallTimeNow to be true after ticker interval")
|
||||
}
|
||||
|
||||
// Call DefaultTimeNow which should reset the flag
|
||||
DefaultTimeNow()
|
||||
|
||||
// Check that the flag is reset
|
||||
if shouldCallTimeNow.Load() {
|
||||
t.Error("Expected shouldCallTimeNow to be false after calling DefaultTimeNow")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BenchmarkTimeNow
|
||||
BenchmarkTimeNow/default
|
||||
BenchmarkTimeNow/default-20 48158628 24.86 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTimeNow/reduced_call
|
||||
BenchmarkTimeNow/reduced_call-20 1000000000 1.000 ns/op 0 B/op 0 allocs/op
|
||||
*/
|
||||
@@ -5,9 +5,8 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
dockerEvents "github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
dockerEvents "github.com/moby/moby/api/types/events"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
@@ -16,23 +15,42 @@ import (
|
||||
|
||||
type (
|
||||
DockerWatcher string
|
||||
DockerListOptions = dockerEvents.ListOptions
|
||||
DockerListOptions = client.EventsListOptions
|
||||
DockerFilters = client.Filters
|
||||
)
|
||||
|
||||
type DockerFilter struct {
|
||||
Term string
|
||||
Values []string
|
||||
}
|
||||
|
||||
func NewDockerFilter(term string, values ...string) DockerFilter {
|
||||
return DockerFilter{
|
||||
Term: term,
|
||||
Values: values,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDockerFilters(filters ...DockerFilter) client.Filters {
|
||||
f := make(client.Filters, len(filters))
|
||||
for _, filter := range filters {
|
||||
f.Add(filter.Term, filter.Values...)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// https://docs.docker.com/reference/api/engine/version/v1.47/#tag/System/operation/SystemPingHead
|
||||
var (
|
||||
DockerFilterContainer = filters.Arg("type", string(dockerEvents.ContainerEventType))
|
||||
DockerFilterStart = filters.Arg("event", string(dockerEvents.ActionStart))
|
||||
DockerFilterStop = filters.Arg("event", string(dockerEvents.ActionStop))
|
||||
DockerFilterDie = filters.Arg("event", string(dockerEvents.ActionDie))
|
||||
DockerFilterDestroy = filters.Arg("event", string(dockerEvents.ActionDestroy))
|
||||
DockerFilterKill = filters.Arg("event", string(dockerEvents.ActionKill))
|
||||
DockerFilterPause = filters.Arg("event", string(dockerEvents.ActionPause))
|
||||
DockerFilterUnpause = filters.Arg("event", string(dockerEvents.ActionUnPause))
|
||||
DockerFilterContainer = NewDockerFilter("type", string(dockerEvents.ContainerEventType))
|
||||
DockerFilterStart = NewDockerFilter("event", string(dockerEvents.ActionStart))
|
||||
DockerFilterStop = NewDockerFilter("event", string(dockerEvents.ActionStop))
|
||||
DockerFilterDie = NewDockerFilter("event", string(dockerEvents.ActionDie))
|
||||
DockerFilterDestroy = NewDockerFilter("event", string(dockerEvents.ActionDestroy))
|
||||
DockerFilterKill = NewDockerFilter("event", string(dockerEvents.ActionKill))
|
||||
DockerFilterPause = NewDockerFilter("event", string(dockerEvents.ActionPause))
|
||||
DockerFilterUnpause = NewDockerFilter("event", string(dockerEvents.ActionUnPause))
|
||||
|
||||
NewDockerFilter = filters.NewArgs
|
||||
|
||||
optionsDefault = DockerListOptions{Filters: NewDockerFilter(
|
||||
optionsDefault = DockerListOptions{Filters: NewDockerFilters(
|
||||
DockerFilterContainer,
|
||||
DockerFilterStart,
|
||||
// DockerFilterStop,
|
||||
@@ -51,8 +69,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func DockerFilterContainerNameID(nameOrID string) filters.KeyValuePair {
|
||||
return filters.Arg("container", nameOrID)
|
||||
func DockerFilterContainerNameID(nameOrID string) DockerFilter {
|
||||
return NewDockerFilter("container", nameOrID)
|
||||
}
|
||||
|
||||
func NewDockerWatcher(host string) DockerWatcher {
|
||||
@@ -80,15 +98,15 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList
|
||||
client.Close()
|
||||
}()
|
||||
|
||||
cEventCh, cErrCh := client.Events(ctx, options)
|
||||
chs := client.Events(ctx, options)
|
||||
defer log.Debug().Str("host", client.Address()).Msg("docker watcher closed")
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case msg := <-cEventCh:
|
||||
case msg := <-chs.Messages:
|
||||
w.handleEvent(msg, eventCh)
|
||||
case err := <-cErrCh:
|
||||
case err := <-chs.Err:
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
@@ -117,7 +135,7 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList
|
||||
// connection successful, trigger reload (reload routes)
|
||||
eventCh <- reloadTrigger
|
||||
// reopen event channel
|
||||
cEventCh, cErrCh = client.Events(ctx, options)
|
||||
chs = client.Events(ctx, options)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -3,7 +3,7 @@ package events
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
dockerEvents "github.com/docker/docker/api/types/events"
|
||||
dockerEvents "github.com/moby/moby/api/types/events"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -45,7 +45,7 @@ func (target *AgentCheckHealthTarget) displayURL() *url.URL {
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config *types.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
||||
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config types.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
||||
mon := &AgentProxiedMonitor{
|
||||
agent: agent,
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user