Compare commits

...

6 Commits

Author SHA1 Message Date
yusing
a03ef50f6e chore(deps): upgrade dependencies 2026-03-21 10:39:16 +08:00
yusing
65fba24170 fix(route): update path exclusion rules in webui_dev.yml
Add exclusion for all paths under /src/* and modify the websocket protocol header to 'vite-hmr' for improved compatibility with development environments.
2026-03-21 10:37:21 +08:00
yusing
be0017dd63 feat(auth): add CSRF protection middleware
Implement Signed Double Submit Cookie pattern to prevent CSRF attacks.
Adds CSRF token generation, validation, and middleware for API endpoints.
Safe methods (GET/HEAD/OPTIONS) automatically receive CSRF cookies, while
unsafe methods require X-CSRF-Token header matching the cookie value with
valid HMAC signature. Includes same-origin exemption for login/callback
endpoints to support browser-based authentication flows.
2026-03-21 10:37:15 +08:00
yusing
892ee95c81 fix(api/file): prevent path traversal in file API
Use os.OpenRoot to restrict file access to the application root,
preventing directory traversal attacks through the file download endpoint.

Also add test to verify path traversal attempts are blocked.
2026-03-21 10:36:45 +08:00
yusing
d77f6960ec fix(route): handle synthetic load balancer routes consistently
Synthetic load balancer routes were created with SchemeNone and a zero
proxy port, so the embedded Route logic treated them as excluded routes.
That caused them to be keyed like excluded routes instead of by alias,
which broke HTTP route lookup in reverse proxy load balancer tests.

Override Key and ShouldExclude for synthetic load balancer routes so
they stay addressable through the HTTP route pool while preserving the
existing behavior for normal backend routes.

Also guard addToLoadBalancer against a nil Homepage on an existing
linked route, and update the reverse proxy test to use the in-memory
test entrypoint rather than depending on real listener setup.
2026-03-11 11:45:53 +08:00
yusing
0bd134cf47 Apply compat patch 2026-03-10 15:01:07 +08:00
73 changed files with 1205 additions and 422 deletions

View File

@@ -22,22 +22,23 @@ replace (
exclude github.com/containerd/nerdctl/mod/tigron v0.0.0
require (
github.com/bytedance/sonic v1.15.0
github.com/gin-gonic/gin v1.12.0
github.com/gorilla/websocket v1.5.3
github.com/pion/dtls/v3 v3.1.2
github.com/pion/transport/v3 v3.1.1
github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.11.1
github.com/yusing/godoxy v0.27.2
github.com/yusing/godoxy v0.27.5
github.com/yusing/godoxy/socketproxy v0.0.0-00010101000000-000000000000
github.com/yusing/goutils v0.7.0
)
require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
@@ -46,6 +47,7 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v29.3.0+incompatible // indirect
github.com/docker/docker v28.5.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.10.0 // indirect
@@ -58,11 +60,11 @@ 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.30.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect
@@ -70,7 +72,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/moby/api v1.52.0 // indirect
github.com/moby/moby/client v0.2.1 // indirect
github.com/moby/sys/sequential v0.6.0 // 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
@@ -78,6 +80,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/transport/v4 v4.0.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/puzpuzpuz/xsync/v4 v4.4.0 // indirect
@@ -93,20 +96,22 @@ require (
github.com/valyala/fasthttp v1.69.0 // indirect
github.com/yusing/ds v0.4.1 // indirect
github.com/yusing/gointernals v0.2.0 // indirect
github.com/yusing/goutils/http/reverseproxy v0.0.0-20260228084019-4912690d409d // indirect
github.com/yusing/goutils/http/websocket v0.0.0-20260228084019-4912690d409d // indirect
github.com/yusing/goutils/http/reverseproxy v0.0.0-20260319022356-c0bbdc984e13 // indirect
github.com/yusing/goutils/http/websocket v0.0.0-20260319022356-c0bbdc984e13 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
go.opentelemetry.io/otel/metric v1.42.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
golang.org/x/arch v0.25.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -1,15 +1,17 @@
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.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo=
github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ=
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=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
@@ -24,6 +26,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.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=
@@ -31,14 +35,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
github.com/diskfs/go-diskfs v1.8.0 h1:YynvepHpU7xpl8z2RqiV/x3NPAj3zU0ki0vdjPty0U4=
github.com/diskfs/go-diskfs v1.8.0/go.mod h1:rW9+4MPN1tbMpQqRZlcM3YQsh3Ucc+Q1k1iIqzzmZcg=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
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 v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk=
github.com/docker/cli v29.3.0+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/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=
@@ -55,8 +61,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.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-acme/lego/v4 v4.32.0 h1:z7Ss7aa1noabhKj+DBzhNCO2SM96xhE3b0ucVW3x8Tc=
github.com/go-acme/lego/v4 v4.32.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
github.com/go-acme/lego/v4 v4.33.0 h1:2KrRKieG+VczT4zvVXKAY7Tp/S3XLvh/QImofBALRAM=
github.com/go-acme/lego/v4 v4.33.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
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=
@@ -77,8 +83,8 @@ github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy0
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
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-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -95,12 +101,14 @@ 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.9.1 h1:wsQUCdYJ4ZvP7RIRKDLtAtmFQc3kxbrv3QqccO5RWzs=
github.com/gotify/server/v2 v2.9.1/go.mod h1:8scw0hiExomp4rJDrXBwRIcgQm7kv74P4Z4B+iM4l8w=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
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.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
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=
@@ -115,8 +123,8 @@ github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/luthermonson/go-proxmox v0.4.0 h1:LKXpG9d64zTaQF79wV0kfOnnSwIcdG39m7sc4ga+XZs=
github.com/luthermonson/go-proxmox v0.4.0/go.mod h1:U6dAkJ+iiwaeb1g/LMWpWuWN4nmvWeXhmoMuYJMumS4=
github.com/magefile/mage v1.16.0 h1:2naaPmNwrMicCdLBCRDw288hcyClO9lmnm6FMpXyJ5I=
github.com/magefile/mage v1.16.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magefile/mage v1.16.1 h1:j5UwkdA48xTlGs0Hcm1Q3sSAcxBorntQjiewDNMsqlo=
github.com/magefile/mage v1.16.1/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
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=
@@ -130,13 +138,19 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
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/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/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.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
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=
@@ -155,6 +169,7 @@ github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
github.com/pires/go-proxyproto v0.11.0/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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
@@ -222,6 +237,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:Oyrsyzu
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 h1:uLXP+3mghfMf7XmV4PkGfFhFKuNWoCvvx5wP/wOXo0o=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0/go.mod h1:v0Tj04armyT59mnURNUJf7RCKcKzq+lgJs6QSjHjaTc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
@@ -230,36 +249,45 @@ go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
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.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -270,5 +298,3 @@ 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=

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
@@ -15,7 +16,6 @@ import (
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/agent/pkg/agent/common"
@@ -366,7 +366,7 @@ func (cfg *AgentConfig) fetchJSON(ctx context.Context, endpoint string, out any)
return resp.StatusCode, nil
}
err = sonic.Unmarshal(data, out)
err = json.Unmarshal(data, out)
if err != nil {
return 0, err
}

View File

@@ -2,11 +2,11 @@ package agentproxy
import (
"encoding/base64"
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/bytedance/sonic"
route "github.com/yusing/godoxy/internal/route/types"
)
@@ -53,7 +53,7 @@ func proxyConfigFromHeaders(h http.Header) (cfg Config, err error) {
return cfg, err
}
err = sonic.Unmarshal(cfgJSON, &cfg)
err = json.Unmarshal(cfgJSON, &cfg)
return cfg, err
}
@@ -67,7 +67,7 @@ func (cfg *Config) SetAgentProxyConfigHeadersLegacy(h http.Header) {
func (cfg *Config) SetAgentProxyConfigHeaders(h http.Header) {
h.Set(HeaderXProxyHost, cfg.Host)
h.Set(HeaderXProxyScheme, string(cfg.Scheme))
cfgJSON, _ := sonic.Marshal(cfg.HTTPConfig)
cfgJSON, _ := json.Marshal(cfg.HTTPConfig)
cfgBase64 := base64.StdEncoding.EncodeToString(cfgJSON)
h.Set(HeaderXProxyConfig, cfgBase64)
}

View File

@@ -1,6 +1,7 @@
package handler
import (
"encoding/json"
"net"
"net/http"
"net/url"
@@ -8,7 +9,6 @@ import (
"strings"
"time"
"github.com/bytedance/sonic"
healthcheck "github.com/yusing/godoxy/internal/health/check"
"github.com/yusing/godoxy/internal/types"
)
@@ -73,7 +73,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
sonic.ConfigDefault.NewEncoder(w).Encode(result)
json.NewEncoder(w).Encode(result)
}
func parseMsOrDefault(msStr string) time.Duration {

View File

@@ -1,9 +1,9 @@
package handler
import (
"encoding/json"
"net/http"
"github.com/bytedance/sonic"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/yusing/godoxy/agent/pkg/agent"
@@ -51,7 +51,7 @@ func NewAgentHandler() http.Handler {
Runtime: env.Runtime,
}
w.Header().Set("Content-Type", "application/json")
sonic.ConfigDefault.NewEncoder(w).Encode(agentInfo)
json.NewEncoder(w).Encode(agentInfo)
})
mux.HandleEndpoint("GET", agent.EndpointHealth, CheckHealth)
mux.HandleEndpoint("GET", agent.EndpointSystemInfo, metricsHandler.ServeHTTP)

View File

@@ -2,6 +2,6 @@ module github.com/yusing/godoxy/cmd/h2c_test_server
go 1.26.1
require golang.org/x/net v0.51.0
require golang.org/x/net v0.52.0
require golang.org/x/text v0.34.0 // indirect
require golang.org/x/text v0.35.0 // indirect

View File

@@ -1,4 +1,4 @@
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=

60
go.mod
View File

@@ -22,12 +22,12 @@ replace (
)
require (
github.com/PuerkitoBio/goquery v1.11.0 // parsing HTML for extract fav icon; modify_html middleware
github.com/PuerkitoBio/goquery v1.12.0 // parsing HTML for extract fav icon; modify_html middleware
github.com/cenkalti/backoff/v5 v5.0.3 // backoff for retrying operations
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.12.0 // api server
github.com/go-acme/lego/v4 v4.32.0 // acme client
github.com/go-acme/lego/v4 v4.33.0 // acme client
github.com/go-playground/validator/v10 v10.30.1 // 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
@@ -37,22 +37,21 @@ require (
github.com/puzpuzpuz/xsync/v4 v4.4.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.48.0 // encrypting password with bcrypt
golang.org/x/net v0.51.0 // HTTP header utilities
golang.org/x/crypto v0.49.0 // encrypting password with bcrypt
golang.org/x/net v0.52.0 // HTTP header utilities
golang.org/x/oauth2 v0.36.0 // oauth2 authentication
golang.org/x/sync v0.20.0 // errgroup and singleflight for concurrent operations
golang.org/x/time v0.15.0 // time utilities
)
require (
github.com/bytedance/gopkg v0.1.3 // xxhash64 for fast hash
github.com/bytedance/sonic v1.15.0 // fast json parsing
github.com/bytedance/gopkg v0.1.4 // xxhash64 for fast hash
github.com/bytedance/sonic v1.15.0 // indirect; fast json parsing
github.com/docker/cli v29.3.0+incompatible // needs docker/cli/cli/connhelper connection helper for docker client
github.com/goccy/go-yaml v1.19.2 // yaml parsing for different config files
github.com/golang-jwt/jwt/v5 v5.3.1 // jwt authentication
github.com/luthermonson/go-proxmox v0.4.0 // 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.59.0 // http3 support
github.com/shirou/gopsutil/v4 v4.26.2 // system information
@@ -60,13 +59,13 @@ require (
github.com/stretchr/testify v1.11.1 // testing framework
github.com/valyala/fasthttp v1.69.0 // fast http for health check
github.com/yusing/ds v0.4.1 // data structures and algorithms
github.com/yusing/godoxy/agent v0.0.0-20260228194043-59238adb5b6e
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20260228194043-59238adb5b6e
github.com/yusing/godoxy/agent v0.0.0-20260321022848-11e3a9231f23
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20260321022848-11e3a9231f23
github.com/yusing/gointernals v0.2.0
github.com/yusing/goutils v0.7.0
github.com/yusing/goutils/http/reverseproxy v0.0.0-20260228084019-4912690d409d
github.com/yusing/goutils/http/websocket v0.0.0-20260228084019-4912690d409d
github.com/yusing/goutils/server v0.0.0-20260228084019-4912690d409d
github.com/yusing/goutils/http/reverseproxy v0.0.0-20260319022356-c0bbdc984e13
github.com/yusing/goutils/http/websocket v0.0.0-20260319022356-c0bbdc984e13
github.com/yusing/goutils/server v0.0.0-20260319022356-c0bbdc984e13
)
require (
@@ -85,7 +84,7 @@ require (
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/buger/goterm v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/diskfs/go-diskfs v1.7.0 // indirect
github.com/diskfs/go-diskfs v1.8.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/djherbis/times v1.6.0 // indirect
github.com/docker/go-connections v0.6.0
@@ -102,14 +101,14 @@ require (
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/magefile/mage v1.16.0 // indirect
github.com/magefile/mage v1.16.1 // 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.72 // indirect
@@ -140,13 +139,13 @@ require (
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.uber.org/atomic v1.11.0
go.uber.org/ratelimit v0.3.1 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/tools v0.42.0 // indirect
google.golang.org/api v0.270.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/grpc v1.79.2 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/tools v0.43.0 // indirect
google.golang.org/api v0.272.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/ini.v1 v1.67.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
@@ -162,6 +161,7 @@ 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/docker/docker v28.5.2+incompatible
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
@@ -169,16 +169,16 @@ require (
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
github.com/go-resty/resty/v2 v2.17.2 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/linode/linodego v1.66.0 // indirect
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect
github.com/nrdcg/goinwx v0.12.0 // indirect
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.0 // indirect
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.0 // indirect
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.2 // indirect
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.2 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pion/dtls/v3 v3.1.2 // indirect
github.com/pion/logging v0.2.4 // indirect
@@ -190,10 +190,18 @@ require (
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.15 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/vultr/govultr/v3 v3.28.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
golang.org/x/arch v0.25.0 // indirect
)
require (
github.com/containerd/log v0.1.0 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/morikuni/aec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect
)

111
go.sum
View File

@@ -23,14 +23,16 @@ 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.7.0 h1:4iB+IesclUXdP0ICgAabvq2FYLXrJWKx1fJQ+GxSo3Y=
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.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.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo=
github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ=
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=
@@ -49,8 +51,8 @@ github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVk
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
@@ -65,19 +67,23 @@ 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=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
github.com/diskfs/go-diskfs v1.8.0 h1:YynvepHpU7xpl8z2RqiV/x3NPAj3zU0ki0vdjPty0U4=
github.com/diskfs/go-diskfs v1.8.0/go.mod h1:rW9+4MPN1tbMpQqRZlcM3YQsh3Ucc+Q1k1iIqzzmZcg=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
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 v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk=
github.com/docker/cli v29.3.0+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/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=
@@ -100,8 +106,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.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-acme/lego/v4 v4.32.0 h1:z7Ss7aa1noabhKj+DBzhNCO2SM96xhE3b0ucVW3x8Tc=
github.com/go-acme/lego/v4 v4.32.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
github.com/go-acme/lego/v4 v4.33.0 h1:2KrRKieG+VczT4zvVXKAY7Tp/S3XLvh/QImofBALRAM=
github.com/go-acme/lego/v4 v4.33.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
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=
@@ -130,8 +136,8 @@ github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPE
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
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-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -153,12 +159,14 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gotify/server/v2 v2.9.1 h1:wsQUCdYJ4ZvP7RIRKDLtAtmFQc3kxbrv3QqccO5RWzs=
github.com/gotify/server/v2 v2.9.1/go.mod h1:8scw0hiExomp4rJDrXBwRIcgQm7kv74P4Z4B+iM4l8w=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
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 +185,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.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
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/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
@@ -197,8 +205,8 @@ github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM=
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/magefile/mage v1.16.0 h1:2naaPmNwrMicCdLBCRDw288hcyClO9lmnm6FMpXyJ5I=
github.com/magefile/mage v1.16.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magefile/mage v1.16.1 h1:j5UwkdA48xTlGs0Hcm1Q3sSAcxBorntQjiewDNMsqlo=
github.com/magefile/mage v1.16.1/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
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=
@@ -216,21 +224,27 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
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/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/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.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
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/goinwx v0.12.0 h1:ujdUqDBnaRSFwzVnImvPHYw3w3m9XgmGImNUw1GyMb4=
github.com/nrdcg/goinwx v0.12.0/go.mod h1:IrVKd3ZDbFiMjdPgML4CSxZAY9wOoqLvH44zv3NodJ0=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.0 h1:r/RTdSUa7oUSTkDku6Bz8QTcYr5nhoiCURAgFZ0Z7EY=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.0/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.0 h1:dia9msUR1RHXMn4RHiyOI/8Ud2v3EZyDoHGVRTaq3/I=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.0/go.mod h1:xUUiSvfkzsJIhCtjNE15qvfdHORt+9C2uRRtbz10R4Q=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.2 h1:SlJJlU2lgrB8dB9UFopLUNaO+JxtzLK4UWHYY3e3gvo=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.2/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.2 h1:c8B4nduK77OvP6WnsfzfZgnK6uHhZETZGYnxNfu23Go=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.2/go.mod h1:twxIRVoHRrrMZp1A8Mh46CqCG/gyzLnImEb4dpzleIE=
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=
@@ -255,9 +269,10 @@ github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG6
github.com/pires/go-proxyproto v0.11.0/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=
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/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=
@@ -343,6 +358,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:Oyrsyzu
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 h1:uLXP+3mghfMf7XmV4PkGfFhFKuNWoCvvx5wP/wOXo0o=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0/go.mod h1:v0Tj04armyT59mnURNUJf7RCKcKzq+lgJs6QSjHjaTc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
@@ -351,6 +370,8 @@ go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
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=
@@ -365,15 +386,15 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
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.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
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.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
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,8 +404,8 @@ 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.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -434,8 +455,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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -444,21 +465,21 @@ 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.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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.270.0 h1:4rJZbIuWSTohczG9mG2ukSDdt9qKx4sSSHIydTN26L4=
google.golang.org/api v0.270.0/go.mod h1:5+H3/8DlXpQWrSz4RjGGwz5HfJAQSEI8Bc6JqQNH77U=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=
google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -474,5 +495,3 @@ 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=

Submodule goutils updated: e48e337bd1...635feb302e

View File

@@ -2,12 +2,12 @@ package agentpool
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/bytedance/sonic"
"github.com/gorilla/websocket"
"github.com/valyala/fasthttp"
agentPkg "github.com/yusing/godoxy/agent/pkg/agent"
@@ -63,7 +63,7 @@ func (agent *Agent) DoHealthCheck(timeout time.Duration, query string) (ret Heal
ret.Detail = fmt.Sprintf("HTTP %d %s", status, resp.Body())
return ret, nil
} else {
err = sonic.Unmarshal(resp.Body(), &ret)
err = json.Unmarshal(resp.Body(), &ret)
if err != nil {
return ret, err
}

109
internal/api/csrf.go Normal file
View File

@@ -0,0 +1,109 @@
package api
import (
"net"
"net/http"
"net/url"
"strings"
"github.com/gin-gonic/gin"
"github.com/yusing/godoxy/internal/auth"
apitypes "github.com/yusing/goutils/apitypes"
)
// CSRFMiddleware implements the Signed Double Submit Cookie pattern.
//
// Safe methods (GET/HEAD/OPTIONS): ensure a signed CSRF cookie exists.
// Unsafe methods (POST/PUT/DELETE/PATCH): require X-CSRF-Token header
// matching the cookie value, with a valid HMAC signature.
func CSRFMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
switch c.Request.Method {
case http.MethodGet, http.MethodHead, http.MethodOptions:
ensureCSRFCookie(c)
c.Next()
return
}
if allowSameOriginAuthBootstrap(c.Request) {
ensureCSRFCookie(c)
c.Next()
return
}
cookie, err := c.Request.Cookie(auth.CSRFCookieName)
if err != nil {
// No cookie at all — issue one so the frontend can retry.
reissueCSRFCookie(c)
c.JSON(http.StatusForbidden, apitypes.Error("missing CSRF token"))
c.Abort()
return
}
cookieToken := canonicalCSRFToken(cookie.Value)
headerToken := canonicalCSRFToken(c.GetHeader(auth.CSRFHeaderName))
if headerToken == "" || cookieToken != headerToken || !auth.ValidateCSRFToken(cookieToken) {
// Stale or forged token — issue a fresh one so the
// frontend can read the new cookie and retry.
reissueCSRFCookie(c)
c.JSON(http.StatusForbidden, apitypes.Error("invalid CSRF token"))
c.Abort()
return
}
c.Next()
}
}
func ensureCSRFCookie(c *gin.Context) {
if _, err := c.Request.Cookie(auth.CSRFCookieName); err == nil {
return
}
reissueCSRFCookie(c)
}
func reissueCSRFCookie(c *gin.Context) {
token, err := auth.GenerateCSRFToken()
if err != nil {
return
}
auth.SetCSRFCookie(c.Writer, c.Request, token)
}
func allowSameOriginAuthBootstrap(r *http.Request) bool {
if r.Method != http.MethodPost {
return false
}
switch r.URL.Path {
case "/api/v1/auth/login", "/api/v1/auth/callback":
return requestSourceMatchesHost(r)
default:
return false
}
}
func requestSourceMatchesHost(r *http.Request) bool {
for _, header := range []string{"Origin", "Referer"} {
value := r.Header.Get(header)
if value == "" {
continue
}
u, err := url.Parse(value)
if err != nil || u.Host == "" {
return false
}
return normalizeHost(u.Hostname()) == normalizeHost(r.Host)
}
return false
}
func normalizeHost(host string) string {
host = strings.ToLower(host)
if h, _, err := net.SplitHostPort(host); err == nil {
return h
}
return host
}
func canonicalCSRFToken(token string) string {
return strings.Trim(strings.TrimSpace(token), "\"")
}

280
internal/api/csrf_test.go Normal file
View File

@@ -0,0 +1,280 @@
package api
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/yusing/godoxy/internal/auth"
autocert "github.com/yusing/godoxy/internal/autocert/types"
"github.com/yusing/godoxy/internal/common"
"github.com/yusing/goutils/task"
)
func TestAuthCheckIssuesCSRFCookie(t *testing.T) {
handler := newAuthenticatedHandler(t)
req := httptest.NewRequest(http.MethodHead, "/api/v1/auth/check", nil)
req.Host = "app.example.com"
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusFound, rec.Code)
csrfCookie := findCookie(rec.Result().Cookies(), auth.CSRFCookieName)
require.NotNil(t, csrfCookie)
assert.NotEmpty(t, csrfCookie.Value)
assert.Empty(t, csrfCookie.Domain)
assert.Equal(t, "/", csrfCookie.Path)
assert.Equal(t, http.SameSiteStrictMode, csrfCookie.SameSite)
}
func TestUserPassCallbackAllowsSameOriginFormPostWithoutCSRFCookie(t *testing.T) {
handler := newAuthenticatedHandler(t)
req := newJSONRequest(t, http.MethodPost, "/api/v1/auth/callback", map[string]string{
"username": common.APIUser,
"password": common.APIPassword,
})
req.Host = "app.example.com"
req.Header.Set("Origin", "https://app.example.com")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
tokenCookie := findCookie(rec.Result().Cookies(), "godoxy_token")
require.NotNil(t, tokenCookie)
assert.NotEmpty(t, tokenCookie.Value)
csrfCookie := findCookie(rec.Result().Cookies(), auth.CSRFCookieName)
require.NotNil(t, csrfCookie)
assert.NotEmpty(t, csrfCookie.Value)
}
func TestUserPassCallbackRejectsCrossOriginPostWithoutCSRFCookie(t *testing.T) {
handler := newAuthenticatedHandler(t)
req := newJSONRequest(t, http.MethodPost, "/api/v1/auth/callback", map[string]string{
"username": common.APIUser,
"password": common.APIPassword,
})
req.Host = "app.example.com"
req.Header.Set("Origin", "https://evil.example.com")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusForbidden, rec.Code)
csrfCookie := findCookie(rec.Result().Cookies(), auth.CSRFCookieName)
require.NotNil(t, csrfCookie)
assert.NotEmpty(t, csrfCookie.Value)
}
func TestUserPassCallbackAcceptsValidCSRFCookie(t *testing.T) {
handler := newAuthenticatedHandler(t)
csrfCookie := issueCSRFCookie(t, handler)
req := newJSONRequest(t, http.MethodPost, "/api/v1/auth/callback", map[string]string{
"username": common.APIUser,
"password": common.APIPassword,
})
req.Host = "app.example.com"
req.AddCookie(csrfCookie)
req.Header.Set(auth.CSRFHeaderName, csrfCookie.Value)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
tokenCookie := findCookie(rec.Result().Cookies(), "godoxy_token")
require.NotNil(t, tokenCookie)
assert.NotEmpty(t, tokenCookie.Value)
}
func TestUnsafeRequestAcceptsQuotedCSRFCookieValue(t *testing.T) {
handler := newAuthenticatedHandler(t)
csrfCookie := issueCSRFCookie(t, handler)
sessionToken := issueSessionToken(t)
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/logout", nil)
req.Host = "app.example.com"
req.Header.Set("Cookie", `godoxy_token=`+sessionToken+`; godoxy_csrf="`+csrfCookie.Value+`"`)
req.Header.Set(auth.CSRFHeaderName, csrfCookie.Value)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusFound, rec.Code)
}
func TestLogoutRequiresCSRFCookie(t *testing.T) {
handler := newAuthenticatedHandler(t)
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/logout", nil)
req.Host = "app.example.com"
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusForbidden, rec.Code)
}
func TestLoginAllowsSameOriginPostWithoutCSRFCookie(t *testing.T) {
handler := newAuthenticatedHandler(t)
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", nil)
req.Host = "app.example.com"
req.Header.Set("Origin", "https://app.example.com")
req.Header.Set("Accept", "text/html")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusFound, rec.Code)
csrfCookie := findCookie(rec.Result().Cookies(), auth.CSRFCookieName)
require.NotNil(t, csrfCookie)
assert.NotEmpty(t, csrfCookie.Value)
}
func TestGetLogoutRouteStillAvailableForFrontend(t *testing.T) {
handler := newAuthenticatedHandler(t)
req := httptest.NewRequest(http.MethodGet, "/api/v1/auth/logout", nil)
req.Host = "app.example.com"
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusFound, rec.Code)
}
func TestCertRenewRejectsCrossOriginWebSocketRequest(t *testing.T) {
handler := newAuthenticatedHandler(t)
provider := &stubAutocertProvider{}
sessionToken := issueSessionToken(t)
req := httptest.NewRequest(http.MethodGet, "/api/v1/cert/renew", nil)
req.Host = "app.example.com"
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", "websocket")
req.Header.Set("Sec-WebSocket-Version", "13")
req.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
req.Header.Set("Origin", "https://evil.example.com")
req.AddCookie(&http.Cookie{Name: "godoxy_token", Value: sessionToken})
req = req.WithContext(context.WithValue(req.Context(), autocert.ContextKey{}, provider))
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusForbidden, rec.Code)
assert.Zero(t, provider.forceExpiryCalls)
}
func newAuthenticatedHandler(t *testing.T) *gin.Engine {
t.Helper()
gin.SetMode(gin.TestMode)
prevSecret := common.APIJWTSecret
prevUser := common.APIUser
prevPassword := common.APIPassword
prevDisableAuth := common.DebugDisableAuth
prevIssuerURL := common.OIDCIssuerURL
common.APIJWTSecret = []byte("0123456789abcdef0123456789abcdef")
common.APIUser = "username"
common.APIPassword = "password"
common.DebugDisableAuth = false
common.OIDCIssuerURL = ""
t.Cleanup(func() {
common.APIJWTSecret = prevSecret
common.APIUser = prevUser
common.APIPassword = prevPassword
common.DebugDisableAuth = prevDisableAuth
common.OIDCIssuerURL = prevIssuerURL
})
require.NoError(t, auth.Initialize())
return NewHandler(true)
}
func issueCSRFCookie(t *testing.T, handler http.Handler) *http.Cookie {
t.Helper()
req := httptest.NewRequest(http.MethodHead, "/api/v1/auth/check", nil)
req.Host = "app.example.com"
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
csrfCookie := findCookie(rec.Result().Cookies(), auth.CSRFCookieName)
require.NotNil(t, csrfCookie)
return csrfCookie
}
func issueSessionToken(t *testing.T) string {
t.Helper()
userpass, ok := auth.GetDefaultAuth().(*auth.UserPassAuth)
require.True(t, ok)
token, err := userpass.NewToken()
require.NoError(t, err)
return token
}
func newJSONRequest(t *testing.T, method, target string, body any) *http.Request {
t.Helper()
encoded, err := json.Marshal(body)
require.NoError(t, err)
req := httptest.NewRequest(method, target, bytes.NewReader(encoded))
req.Header.Set("Content-Type", "application/json")
return req
}
func findCookie(cookies []*http.Cookie, name string) *http.Cookie {
for _, cookie := range cookies {
if cookie.Name == name {
return cookie
}
}
return nil
}
type stubAutocertProvider struct {
forceExpiryCalls int
}
func (p *stubAutocertProvider) GetCert(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return nil, nil
}
func (p *stubAutocertProvider) GetCertInfos() ([]autocert.CertInfo, error) {
return nil, nil
}
func (p *stubAutocertProvider) ScheduleRenewalAll(task.Parent) {}
func (p *stubAutocertProvider) ObtainCertAll() error {
return nil
}
func (p *stubAutocertProvider) ForceExpiryAll() bool {
p.forceExpiryCalls++
return true
}
func (p *stubAutocertProvider) WaitRenewalDone(context.Context) bool {
return true
}

View File

@@ -2,10 +2,8 @@ package api
import (
"net/http"
"reflect"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/codec/json"
"github.com/gorilla/websocket"
"github.com/rs/zerolog/log"
apiV1 "github.com/yusing/godoxy/internal/api/v1"
@@ -49,18 +47,16 @@ func NewHandler(requireAuth bool) *gin.Engine {
r.Use(ErrorLoggingMiddleware())
r.Use(NoCache())
log.Debug().Msg("gin codec json.API: " + reflect.TypeOf(json.API).Name())
r.GET("/api/v1/version", apiV1.Version)
if auth.IsEnabled() && requireAuth {
v1Auth := r.Group("/api/v1/auth")
{
v1Auth.HEAD("/check", authApi.Check)
v1Auth.POST("/login", authApi.Login)
v1Auth.HEAD("/check", CSRFMiddleware(), authApi.Check)
v1Auth.POST("/login", CSRFMiddleware(), authApi.Login)
v1Auth.GET("/callback", authApi.Callback)
v1Auth.POST("/callback", authApi.Callback)
v1Auth.POST("/logout", authApi.Logout)
v1Auth.POST("/callback", CSRFMiddleware(), authApi.Callback)
v1Auth.POST("/logout", CSRFMiddleware(), authApi.Logout)
v1Auth.GET("/logout", authApi.Logout)
}
}
@@ -68,6 +64,7 @@ func NewHandler(requireAuth bool) *gin.Engine {
v1 := r.Group("/api/v1")
if auth.IsEnabled() && requireAuth {
v1.Use(AuthMiddleware())
v1.Use(CSRFMiddleware())
}
if common.APISkipOriginCheck {
v1.Use(SkipOriginCheckMiddleware())

View File

@@ -19,6 +19,7 @@ import (
// @Tags cert,websocket
// @Produce plain
// @Success 200 {object} apitypes.SuccessResponse
// @Failure 400 {object} apitypes.ErrorResponse
// @Failure 403 {object} apitypes.ErrorResponse
// @Failure 500 {object} apitypes.ErrorResponse
// @Router /cert/renew [get]

View File

@@ -4,7 +4,6 @@ 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"
)
@@ -43,22 +42,22 @@ func GetContainer(c *gin.Context) {
defer dockerClient.Close()
cont, err := dockerClient.ContainerInspect(c.Request.Context(), id, client.ContainerInspectOptions{})
cont, err := dockerClient.ContainerInspect(c.Request.Context(), id)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to inspect container"))
return
}
var state ContainerState
if cont.Container.State != nil {
state = cont.Container.State.Status
if cont.State != nil {
state = cont.State.Status
}
c.JSON(http.StatusOK, &Container{
Server: dockerCfg.URL,
Name: cont.Container.Name,
ID: cont.Container.ID,
Image: cont.Container.Image,
Name: cont.Name,
ID: cont.ID,
Image: cont.Image,
State: state,
})
}

View File

@@ -4,9 +4,8 @@ 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"
"github.com/rs/zerolog/log"
gperr "github.com/yusing/goutils/errs"
@@ -41,12 +40,12 @@ func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Containe
errs := gperr.NewBuilder("failed to get containers")
containers := make([]Container, 0)
for name, dockerClient := range dockerClients {
conts, err := dockerClient.ContainerList(ctx, client.ContainerListOptions{All: true})
conts, err := dockerClient.ContainerList(ctx, container.ListOptions{All: true})
if err != nil {
errs.AddSubject(err, name)
continue
}
for _, cont := range conts.Items {
for _, cont := range conts {
containers = append(containers, Container{
Server: name,
Name: cont.Names[0],

View File

@@ -4,9 +4,8 @@ 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"
@@ -65,13 +64,13 @@ func GetDockerInfo(ctx context.Context, dockerClients DockerClients) ([]dockerIn
i := 0
for name, dockerClient := range dockerClients {
info, err := dockerClient.Info(ctx, client.InfoOptions{})
info, err := dockerClient.Info(ctx)
if err != nil {
errs.AddSubject(err, name)
continue
}
info.Info.Name = name
dockerInfos[i] = toDockerInfo(info.Info)
info.Name = name
dockerInfos[i] = toDockerInfo(info)
i++
}

View File

@@ -7,9 +7,9 @@ import (
"net/http"
"strconv"
"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"
@@ -73,7 +73,7 @@ func Logs(c *gin.Context) {
}
defer dockerClient.Close()
opts := client.ContainerLogsOptions{
opts := container.LogsOptions{
ShowStdout: queryParams.Stdout,
ShowStderr: queryParams.Stderr,
Since: queryParams.Since,

View File

@@ -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 RestartRequest struct {
ID string `json:"id" binding:"required"`
client.ContainerRestartOptions
container.StopOptions
}
// @x-id "restart"
@@ -20,7 +20,7 @@ type RestartRequest struct {
// @Description Restart container by container id
// @Tags docker
// @Produce json
// @Param request body RestartRequest true "Request"
// @Param request body StopRequest true "Request"
// @Success 200 {object} apitypes.SuccessResponse
// @Failure 400 {object} apitypes.ErrorResponse "Invalid request"
// @Failure 403 {object} apitypes.ErrorResponse
@@ -48,7 +48,7 @@ func Restart(c *gin.Context) {
defer client.Close()
_, err = client.ContainerRestart(c.Request.Context(), req.ID, req.ContainerRestartOptions)
err = client.ContainerRestart(c.Request.Context(), req.ID, req.StopOptions)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to restart container"))
return

View File

@@ -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"`
client.ContainerStartOptions
container.StartOptions
}
// @x-id "start"
@@ -48,7 +48,7 @@ func Start(c *gin.Context) {
defer client.Close()
_, err = client.ContainerStart(c.Request.Context(), req.ID, req.ContainerStartOptions)
err = client.ContainerStart(c.Request.Context(), req.ID, req.StartOptions)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to start container"))
return

View File

@@ -8,7 +8,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/yusing/godoxy/internal/docker"
entrypoint "github.com/yusing/godoxy/internal/entrypoint/types"
"github.com/yusing/godoxy/internal/types"
@@ -68,7 +67,7 @@ func Stats(c *gin.Context) {
defer dockerClient.Close()
if httpheaders.IsWebsocket(c.Request.Header) {
stats, err := dockerClient.ContainerStats(c.Request.Context(), id, client.ContainerStatsOptions{Stream: true})
stats, err := dockerClient.ContainerStats(c.Request.Context(), id, true)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to get container stats"))
return
@@ -102,7 +101,7 @@ func Stats(c *gin.Context) {
}
}
stats, err := dockerClient.ContainerStats(c.Request.Context(), id, client.ContainerStatsOptions{Stream: false})
stats, err := dockerClient.ContainerStats(c.Request.Context(), id, false)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to get container stats"))
return

View File

@@ -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"`
client.ContainerStopOptions
container.StopOptions
}
// @x-id "stop"
@@ -48,7 +48,7 @@ func Stop(c *gin.Context) {
defer client.Close()
_, err = client.ContainerStop(c.Request.Context(), req.ID, req.ContainerStopOptions)
err = client.ContainerStop(c.Request.Context(), req.ID, req.StopOptions)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to stop container"))
return

View File

@@ -1,6 +1,7 @@
package fileapi
import (
"io"
"net/http"
"os"
"path"
@@ -44,7 +45,14 @@ func Get(c *gin.Context) {
return
}
content, err := os.ReadFile(request.FileType.GetPath(request.Filename))
f, err := os.OpenInRoot(".", request.FileType.GetPath(request.Filename))
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to open root"))
return
}
defer f.Close()
content, err := io.ReadAll(f)
if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to read file"))
return

View File

@@ -0,0 +1,68 @@
package fileapi_test
import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
api "github.com/yusing/godoxy/internal/api"
fileapi "github.com/yusing/godoxy/internal/api/v1/file"
"github.com/yusing/goutils/fs"
)
func TestGet_PathTraversalBlocked(t *testing.T) {
gin.SetMode(gin.TestMode)
files, err := fs.ListFiles("..", 1, false)
require.NoError(t, err)
require.Greater(t, len(files), 0, "no files found")
relativePath := files[0]
fileContent, err := os.ReadFile(relativePath)
require.NoError(t, err)
r := gin.New()
r.Use(api.ErrorHandler())
r.GET("/api/v1/file/content", fileapi.Get)
tests := []struct {
name string
filename string
queryEscaped bool
}{
{
name: "dotdot_traversal",
filename: relativePath,
},
{
name: "url_encoded_dotdot_traversal",
filename: relativePath,
queryEscaped: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filename := tt.filename
if tt.queryEscaped {
filename = url.QueryEscape(filename)
}
url := "/api/v1/file/content?type=config&filename=" + filename
req := httptest.NewRequest(http.MethodGet, url, nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
// "Blocked" means we should never successfully read the outside file.
assert.NotEqual(t, http.StatusOK, w.Code)
assert.NotEqual(t, fileContent, w.Body.String())
})
}
}

View File

@@ -8,7 +8,6 @@ import (
"sync/atomic"
"time"
"github.com/bytedance/sonic"
"github.com/cenkalti/backoff/v5"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
@@ -241,7 +240,7 @@ func marshalSystemInfo(ws *websocket.Manager, agentName string, systemInfo any)
defer bufFromPool.release(bufFromPool.RawMessage)
}
err := sonic.ConfigDefault.NewEncoder(buf).Encode(map[string]any{
err := json.NewEncoder(buf).Encode(map[string]any{
agentName: systemInfo,
})
if err != nil {

84
internal/auth/csrf.go Normal file
View File

@@ -0,0 +1,84 @@
package auth
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"net/http"
"strings"
"github.com/yusing/godoxy/internal/common"
"golang.org/x/crypto/hkdf"
)
const (
CSRFCookieName = "godoxy_csrf"
CSRFHKDFSalt = "godoxy-csrf"
CSRFHeaderName = "X-CSRF-Token"
csrfTokenLength = 32
)
// csrfSecret is derived from API_JWT_SECRET via HKDF for cryptographic
// separation from JWT signing. Falls back to an ephemeral random key
// for OIDC-only setups where no JWT secret is configured.
var csrfSecret = func() []byte {
if common.APIJWTSecret != nil {
return hkdf.Extract(sha256.New, common.APIJWTSecret, []byte(CSRFHKDFSalt))
}
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic("failed to generate CSRF secret: " + err.Error())
}
return b
}()
func GenerateCSRFToken() (string, error) {
nonce := make([]byte, csrfTokenLength)
if _, err := rand.Read(nonce); err != nil {
return "", err
}
nonceHex := hex.EncodeToString(nonce)
return nonceHex + "." + csrfSign(nonceHex), nil
}
// ValidateCSRFToken checks the HMAC signature embedded in the token.
// This prevents subdomain cookie-injection attacks where an attacker
// sets a forged CSRF cookie — they cannot produce a valid signature
// without the ephemeral secret.
func ValidateCSRFToken(token string) bool {
nonce, sig, ok := strings.Cut(token, ".")
if !ok || len(nonce) != csrfTokenLength*2 {
return false
}
return hmac.Equal([]byte(sig), []byte(csrfSign(nonce)))
}
func csrfSign(nonce string) string {
mac := hmac.New(sha256.New, csrfSecret)
mac.Write([]byte(nonce))
return hex.EncodeToString(mac.Sum(nil))
}
func SetCSRFCookie(w http.ResponseWriter, r *http.Request, token string) {
http.SetCookie(w, &http.Cookie{
Name: CSRFCookieName,
Value: token,
HttpOnly: false,
Secure: common.APIJWTSecure,
SameSite: http.SameSiteStrictMode,
Path: "/",
})
}
func ClearCSRFCookie(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: CSRFCookieName,
Value: "",
MaxAge: -1,
HttpOnly: false,
Secure: common.APIJWTSecure,
SameSite: http.SameSiteStrictMode,
Path: "/",
})
}

View File

@@ -1,12 +1,12 @@
package auth
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"github.com/bytedance/sonic"
"github.com/golang-jwt/jwt/v5"
"github.com/yusing/godoxy/internal/common"
httputils "github.com/yusing/goutils/http"
@@ -107,7 +107,7 @@ type UserPassAuthCallbackRequest struct {
func (auth *UserPassAuth) PostAuthCallbackHandler(w http.ResponseWriter, r *http.Request) {
var creds UserPassAuthCallbackRequest
err := sonic.ConfigDefault.NewDecoder(r.Body).Decode(&creds)
err := json.NewDecoder(r.Body).Decode(&creds)
if err != nil {
http.Error(w, "invalid request", http.StatusBadRequest)
return

View File

@@ -5,8 +5,8 @@ go 1.26.1
replace github.com/yusing/godoxy => ../..
require (
github.com/go-acme/lego/v4 v4.32.0
github.com/yusing/godoxy v0.27.2
github.com/go-acme/lego/v4 v4.33.0
github.com/yusing/godoxy v0.27.5
)
require (
@@ -23,12 +23,8 @@ require (
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/boombuler/barcode v1.1.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -49,11 +45,10 @@ require (
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
github.com/gotify/server/v2 v2.9.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
@@ -65,8 +60,8 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/nrdcg/goacmedns v0.2.0 // indirect
github.com/nrdcg/goinwx v0.12.0 // indirect
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.0 // indirect
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.0 // indirect
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.2 // indirect
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.2 // 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
@@ -78,7 +73,6 @@ require (
github.com/sony/gobreaker v1.0.0 // indirect
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.28.1 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/yusing/gointernals v0.2.0 // indirect
@@ -89,18 +83,17 @@ require (
go.opentelemetry.io/otel/metric v1.42.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
golang.org/x/arch v0.25.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/tools v0.42.0 // indirect
google.golang.org/api v0.270.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/grpc v1.79.2 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/tools v0.43.0 // indirect
google.golang.org/api v0.272.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/ini.v1 v1.67.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@@ -37,18 +37,10 @@ github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
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=
@@ -62,8 +54,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.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-acme/lego/v4 v4.32.0 h1:z7Ss7aa1noabhKj+DBzhNCO2SM96xhE3b0ucVW3x8Tc=
github.com/go-acme/lego/v4 v4.32.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
github.com/go-acme/lego/v4 v4.33.0 h1:2KrRKieG+VczT4zvVXKAY7Tp/S3XLvh/QImofBALRAM=
github.com/go-acme/lego/v4 v4.33.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
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=
@@ -105,8 +97,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
github.com/gotify/server/v2 v2.9.1 h1:wsQUCdYJ4ZvP7RIRKDLtAtmFQc3kxbrv3QqccO5RWzs=
github.com/gotify/server/v2 v2.9.1/go.mod h1:8scw0hiExomp4rJDrXBwRIcgQm7kv74P4Z4B+iM4l8w=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
@@ -119,8 +111,6 @@ github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2
github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
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/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -150,10 +140,10 @@ 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/goinwx v0.12.0 h1:ujdUqDBnaRSFwzVnImvPHYw3w3m9XgmGImNUw1GyMb4=
github.com/nrdcg/goinwx v0.12.0/go.mod h1:IrVKd3ZDbFiMjdPgML4CSxZAY9wOoqLvH44zv3NodJ0=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.0 h1:r/RTdSUa7oUSTkDku6Bz8QTcYr5nhoiCURAgFZ0Z7EY=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.0/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.0 h1:dia9msUR1RHXMn4RHiyOI/8Ud2v3EZyDoHGVRTaq3/I=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.0/go.mod h1:xUUiSvfkzsJIhCtjNE15qvfdHORt+9C2uRRtbz10R4Q=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.2 h1:SlJJlU2lgrB8dB9UFopLUNaO+JxtzLK4UWHYY3e3gvo=
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.109.2/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.2 h1:c8B4nduK77OvP6WnsfzfZgnK6uHhZETZGYnxNfu23Go=
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.109.2/go.mod h1:twxIRVoHRrrMZp1A8Mh46CqCG/gyzLnImEb4dpzleIE=
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=
@@ -188,11 +178,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
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/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.28.1 h1:KR3LhppYARlBujY7+dcrE7YKL0Yo9qXL+msxykKQrLI=
github.com/vultr/govultr/v3 v3.28.1/go.mod h1:2zyUw9yADQaGwKnwDesmIOlBNLrm7edsCfWHFJpWKf8=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
@@ -221,14 +208,12 @@ 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.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
@@ -240,25 +225,25 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
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.270.0 h1:4rJZbIuWSTohczG9mG2ukSDdt9qKx4sSSHIydTN26L4=
google.golang.org/api v0.270.0/go.mod h1:5+H3/8DlXpQWrSz4RjGGwz5HfJAQSEI8Bc6JqQNH77U=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=
google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -14,7 +14,7 @@ import (
"unsafe"
"github.com/docker/cli/cli/connhelper"
"github.com/moby/moby/client"
"github.com/docker/docker/client"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/agent/pkg/agent"
"github.com/yusing/godoxy/internal/agentpool"
@@ -198,7 +198,9 @@ func NewClient(cfg types.DockerProviderConfig, unique ...bool) (*SharedClient, e
opt = append(opt, client.WithTLSClientConfig(cfg.TLS.CAFile, cfg.TLS.CertFile, cfg.TLS.KeyFile))
}
client, err := client.New(opt...)
opt = append(opt, client.WithAPIVersionNegotiation())
client, err := client.NewClientWithOpts(opt...)
if err != nil {
return nil, err
}

View File

@@ -11,9 +11,8 @@ 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/agentpool"
"github.com/yusing/godoxy/internal/serialization"
@@ -99,18 +98,18 @@ func UpdatePorts(ctx context.Context, c *types.Container) error {
}
defer dockerClient.Close()
inspect, err := dockerClient.ContainerInspect(ctx, c.ContainerID, client.ContainerInspectOptions{})
inspect, err := dockerClient.ContainerInspect(ctx, c.ContainerID)
if err != nil {
return err
}
for port := range inspect.Container.Config.ExposedPorts {
proto, portStr := nat.SplitProtoPort(port.String())
for port := range inspect.Config.ExposedPorts {
proto, portStr := nat.SplitProtoPort(string(port))
portInt, _ := nat.ParsePort(portStr)
if portInt == 0 {
continue
}
c.PublicPortMapping[portInt] = container.PortSummary{
c.PublicPortMapping[portInt] = container.Port{
PublicPort: uint16(portInt), //nolint:gosec
PrivatePort: uint16(portInt), //nolint:gosec
Type: proto,
@@ -211,8 +210,8 @@ func setPrivateHostname(c *types.Container, helper containerHelper) {
}
if c.Network != "" {
v, hasNetwork := helper.NetworkSettings.Networks[c.Network]
if hasNetwork && v.IPAddress.IsValid() {
c.PrivateHostname = v.IPAddress.String()
if hasNetwork && v.IPAddress != "" {
c.PrivateHostname = v.IPAddress
return
}
var hasComposeNetwork bool
@@ -220,9 +219,9 @@ func setPrivateHostname(c *types.Container, helper containerHelper) {
if proj := DockerComposeProject(c); proj != "" {
newNetwork := fmt.Sprintf("%s_%s", proj, c.Network)
v, hasComposeNetwork = helper.NetworkSettings.Networks[newNetwork]
if hasComposeNetwork && v.IPAddress.IsValid() {
if hasComposeNetwork && v.IPAddress != "" {
c.Network = newNetwork // update network to the new one
c.PrivateHostname = v.IPAddress.String()
c.PrivateHostname = v.IPAddress
return
}
}
@@ -235,9 +234,9 @@ func setPrivateHostname(c *types.Container, helper containerHelper) {
}
// fallback to first network if no network is specified
for k, v := range helper.NetworkSettings.Networks {
if v.IPAddress.IsValid() {
if v.IPAddress != "" {
c.Network = k // update network to the first network
c.PrivateHostname = v.IPAddress.String()
c.PrivateHostname = v.IPAddress
return
}
}

View File

@@ -3,7 +3,7 @@ package docker
import (
"strings"
"github.com/moby/moby/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/yusing/ds/ordered"
"github.com/yusing/godoxy/internal/types"
strutils "github.com/yusing/goutils/strings"

View File

@@ -3,7 +3,7 @@ package docker
import (
"testing"
"github.com/moby/moby/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/yusing/godoxy/internal/types"
expect "github.com/yusing/goutils/testing"
)

View File

@@ -3,12 +3,12 @@ package docker
import (
"context"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/yusing/godoxy/internal/types"
)
var listOptions = client.ContainerListOptions{
var listOptions = container.ListOptions{
// created|restarting|running|removing|paused|exited|dead
// Filters: filters.NewArgs(
// filters.Arg("status", "created"),
@@ -31,7 +31,7 @@ func ListContainers(ctx context.Context, dockerCfg types.DockerProviderConfig) (
if err != nil {
return nil, err
}
return containers.Items, nil
return containers, nil
}
func IsErrConnectionFailed(err error) bool {

View File

@@ -2,13 +2,12 @@ package healthcheck
import (
"context"
"encoding/json"
"errors"
"net/http"
"time"
"github.com/bytedance/sonic"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/yusing/godoxy/internal/docker"
"github.com/yusing/godoxy/internal/types"
httputils "github.com/yusing/goutils/http"
@@ -46,7 +45,7 @@ func Docker(ctx context.Context, state *DockerHealthcheckState, timeout time.Dur
defer cancel()
// the actual inspect response is intercepted and returned as RequestInterceptedError
_, err := state.client.ContainerInspect(ctx, state.containerID, client.ContainerInspectOptions{})
_, err := state.client.ContainerInspect(ctx, state.containerID)
var interceptedErr *httputils.RequestInterceptedError
if !httputils.AsRequestInterceptedError(err, &interceptedErr) {
@@ -108,7 +107,7 @@ func interceptDockerInspectResponse(resp *http.Response) (intercepted bool, err
}
var state container.State
err = sonic.Unmarshal(body, &state)
err = json.Unmarshal(body, &state)
release(body)
if err != nil {
return false, err

View File

@@ -2,12 +2,12 @@ package iconlist
import (
"context"
"encoding/json"
"net/http"
"slices"
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/lithammer/fuzzysearch/fuzzy"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/internal/common"
@@ -55,7 +55,7 @@ func init() {
func InitCache() {
m := make(IconMap)
err := serialization.LoadFileIfExist(common.IconListCachePath, &m, sonic.Unmarshal)
err := serialization.LoadFileIfExist(common.IconListCachePath, &m, json.Unmarshal)
switch {
case err != nil:
// backward compatible
@@ -63,13 +63,13 @@ func InitCache() {
Icons IconMap
LastUpdate time.Time
}{}
err = serialization.LoadFileIfExist(common.IconListCachePath, &oldFormat, sonic.Unmarshal)
err = serialization.LoadFileIfExist(common.IconListCachePath, &oldFormat, json.Unmarshal)
if err != nil {
log.Error().Err(err).Msg("failed to load icons")
} else {
m = oldFormat.Icons
// store it to disk immediately
_ = serialization.SaveFile(common.IconListCachePath, &m, 0o644, sonic.Marshal)
_ = serialization.SaveFile(common.IconListCachePath, &m, 0o644, json.Marshal)
}
case len(m) > 0:
log.Info().
@@ -85,7 +85,7 @@ func InitCache() {
task.OnProgramExit("save_icons_cache", func() {
icons := iconsCache.Load()
_ = serialization.SaveFile(common.IconListCachePath, &icons, 0o644, sonic.Marshal)
_ = serialization.SaveFile(common.IconListCachePath, &icons, 0o644, json.Marshal)
})
go backgroundUpdateIcons()
@@ -106,7 +106,7 @@ func backgroundUpdateIcons() {
// swap old cache with new cache
iconsCache.Store(newCache)
// save it to disk
err := serialization.SaveFile(common.IconListCachePath, &newCache, 0o644, sonic.Marshal)
err := serialization.SaveFile(common.IconListCachePath, &newCache, 0o644, json.Marshal)
if err != nil {
log.Warn().Err(err).Msg("failed to save icons")
}
@@ -286,7 +286,7 @@ func UpdateWalkxCodeIcons(m IconMap) error {
}
data := make(map[string][]string)
err = sonic.Unmarshal(body, &data)
err = json.Unmarshal(body, &data)
release(body)
if err != nil {
return err
@@ -365,7 +365,7 @@ func UpdateSelfhstIcons(m IconMap) error {
}
data := make([]SelfhStIcon, 0)
err = sonic.Unmarshal(body, &data) //nolint:musttag
err = json.Unmarshal(body, &data) //nolint:musttag
release(body)
if err != nil {
return err

View File

@@ -2,13 +2,13 @@ package qbittorrent
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"github.com/bytedance/sonic"
"github.com/yusing/godoxy/internal/homepage/widgets"
strutils "github.com/yusing/goutils/strings"
)
@@ -71,7 +71,7 @@ func jsonRequest[T any](ctx context.Context, client *Client, endpoint string, qu
}
defer resp.Body.Close()
err = sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&result)
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return result, err
}

View File

@@ -2,11 +2,10 @@ package qbittorrent
import (
"context"
"encoding/json"
"net/url"
"strconv"
"time"
"github.com/bytedance/sonic"
)
const endpointLogs = "/api/v2/log/main"
@@ -45,7 +44,7 @@ func (l *LogEntry) Level() string {
}
func (l *LogEntry) MarshalJSON() ([]byte, error) {
return sonic.Marshal(map[string]any{
return json.Marshal(map[string]any{
"id": l.ID,
"timestamp": l.Timestamp,
"level": l.Level(),

View File

@@ -1,10 +1,10 @@
package idlewatcher
import (
"encoding/json"
"iter"
"strconv"
"github.com/bytedance/sonic"
strutils "github.com/yusing/goutils/strings"
)
@@ -14,7 +14,7 @@ type watcherDebug struct {
func (w watcherDebug) MarshalJSON() ([]byte, error) {
state := w.state.Load()
return sonic.Marshal(map[string]any{
return json.Marshal(map[string]any{
"name": w.Name(),
"state": map[string]string{
"status": string(state.status),

View File

@@ -1,11 +1,11 @@
package idlewatcher
import (
"encoding/json"
"fmt"
"io"
"github.com/bytedance/sonic"
gevents "github.com/yusing/goutils/events"
"github.com/yusing/goutils/events"
)
type WakeEvent struct {
@@ -26,7 +26,7 @@ const (
)
func writeSSE(w io.Writer, v any) error {
data, err := sonic.Marshal(v)
data, err := json.Marshal(v)
if err != nil {
return err
}
@@ -58,12 +58,12 @@ func (w *Watcher) sendEvent(eventType WakeEventType, message string, err error)
w.l.Debug().Str("event", string(eventType)).Str("message", message).Err(err).Msg("sending event")
level := gevents.LevelInfo
level := events.LevelInfo
if eventType == WakeEventError {
level = gevents.LevelError
level = events.LevelError
}
w.events.Add(gevents.NewEvent(
w.events.Add(events.NewEvent(
level,
w.cfg.ContainerName(),
string(eventType),

View File

@@ -9,12 +9,12 @@ import (
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
"github.com/yusing/godoxy/internal/types"
gevents "github.com/yusing/goutils/events"
"github.com/yusing/goutils/events"
)
func DebugHandler(rw http.ResponseWriter, r *http.Request) {
w := &Watcher{
events: gevents.NewHistory(),
events: events.NewHistory(),
cfg: &types.IdlewatcherConfig{
IdlewatcherProviderConfig: types.IdlewatcherProviderConfig{
Docker: &types.DockerConfig{

View File

@@ -4,8 +4,7 @@ import (
"context"
"fmt"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/docker/docker/api/types/container"
"github.com/yusing/godoxy/internal/docker"
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
"github.com/yusing/godoxy/internal/types"
@@ -18,7 +17,7 @@ type DockerProvider struct {
containerID string
}
var startOptions = client.ContainerStartOptions{}
var startOptions = container.StartOptions{}
func NewDockerProvider(dockerCfg types.DockerProviderConfig, containerID string) (idlewatcher.Provider, error) {
client, err := docker.NewClient(dockerCfg)
@@ -33,41 +32,34 @@ func NewDockerProvider(dockerCfg types.DockerProviderConfig, containerID string)
}
func (p *DockerProvider) ContainerPause(ctx context.Context) error {
_, err := p.client.ContainerPause(ctx, p.containerID, client.ContainerPauseOptions{})
return err
return p.client.ContainerPause(ctx, p.containerID)
}
func (p *DockerProvider) ContainerUnpause(ctx context.Context) error {
_, err := p.client.ContainerUnpause(ctx, p.containerID, client.ContainerUnpauseOptions{})
return err
return p.client.ContainerUnpause(ctx, p.containerID)
}
func (p *DockerProvider) ContainerStart(ctx context.Context) error {
_, err := p.client.ContainerStart(ctx, p.containerID, startOptions)
return err
return p.client.ContainerStart(ctx, p.containerID, startOptions)
}
func (p *DockerProvider) ContainerStop(ctx context.Context, signal types.ContainerSignal, timeout int) error {
_, err := p.client.ContainerStop(ctx, p.containerID, client.ContainerStopOptions{
return p.client.ContainerStop(ctx, p.containerID, container.StopOptions{
Signal: string(signal),
Timeout: &timeout,
})
return err
}
func (p *DockerProvider) ContainerKill(ctx context.Context, signal types.ContainerSignal) error {
_, err := p.client.ContainerKill(ctx, p.containerID, client.ContainerKillOptions{
Signal: string(signal),
})
return err
return p.client.ContainerKill(ctx, p.containerID, string(signal))
}
func (p *DockerProvider) ContainerStatus(ctx context.Context) (idlewatcher.ContainerStatus, error) {
status, err := p.client.ContainerInspect(ctx, p.containerID, client.ContainerInspectOptions{})
status, err := p.client.ContainerInspect(ctx, p.containerID)
if err != nil {
return idlewatcher.ContainerStatusError, err
}
switch status.Container.State.Status {
switch status.State.Status {
case container.StateRunning:
return idlewatcher.ContainerStatusRunning, nil
case container.StateExited, container.StateDead, container.StateRestarting:
@@ -75,7 +67,7 @@ func (p *DockerProvider) ContainerStatus(ctx context.Context) (idlewatcher.Conta
case container.StatePaused:
return idlewatcher.ContainerStatusPaused, nil
}
return idlewatcher.ContainerStatusError, fmt.Errorf("%w: %s", idlewatcher.ErrUnexpectedContainerStatus, status.Container.State.Status)
return idlewatcher.ContainerStatusError, fmt.Errorf("%w: %s", idlewatcher.ErrUnexpectedContainerStatus, status.State.Status)
}
func (p *DockerProvider) Watch(ctx context.Context) (eventCh <-chan watcher.Event, errCh <-chan error) {

View File

@@ -22,7 +22,7 @@ import (
"github.com/yusing/godoxy/internal/types"
watcherEvents "github.com/yusing/godoxy/internal/watcher/events"
gperr "github.com/yusing/goutils/errs"
gevents "github.com/yusing/goutils/events"
"github.com/yusing/goutils/events"
"github.com/yusing/goutils/http/reverseproxy"
strutils "github.com/yusing/goutils/strings"
"github.com/yusing/goutils/synk"
@@ -67,7 +67,7 @@ type (
task *task.Task
// Per-watcher event history (for SSE and debug)
events *gevents.History
events *events.History
dependsOn []*dependency
}
@@ -132,7 +132,7 @@ func NewWatcher(parent task.Parent, r types.Route, cfg *Config) (*Watcher, error
idleTicker: time.NewTicker(cfg.IdleTimeout),
healthTicker: time.NewTicker(idleWakerCheckInterval),
readyNotifyCh: make(chan struct{}, 1), // buffered to avoid blocking
events: gevents.NewHistory(),
events: events.NewHistory(),
cfg: cfg,
routeHelper: routeHelper{
hc: monitor.NewMonitor(r),

View File

@@ -6,7 +6,6 @@ import (
"path/filepath"
"reflect"
"github.com/bytedance/sonic"
"github.com/puzpuzpuz/xsync/v4"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/internal/common"
@@ -66,7 +65,7 @@ func loadNS[T store](ns namespace) T {
}
} else {
defer file.Close()
if err := sonic.ConfigDefault.NewDecoder(file).Decode(&store); err != nil {
if err := json.NewDecoder(file).Decode(&store); err != nil {
log.Err(err).
Str("path", path).
Msg("failed to load store")
@@ -84,7 +83,7 @@ func save() error {
errs := gperr.NewBuilder("failed to save data stores")
for ns, store := range stores {
path := filepath.Join(storesPath, string(ns)+".json")
if err := serialization.SaveFile(path, &store, 0o644, sonic.Marshal); err != nil {
if err := serialization.SaveFile(path, &store, 0o644, json.Marshal); err != nil {
errs.Add(err)
}
}
@@ -114,12 +113,12 @@ func (s *MapStore[VT]) Initialize() {
}
func (s MapStore[VT]) MarshalJSON() ([]byte, error) {
return sonic.Marshal(xsync.ToPlainMap(s.Map))
return json.Marshal(xsync.ToPlainMap(s.Map))
}
func (s *MapStore[VT]) UnmarshalJSON(data []byte) error {
tmp := make(map[string]VT)
if err := sonic.Unmarshal(data, &tmp); err != nil {
if err := json.Unmarshal(data, &tmp); err != nil {
return err
}
s.Map = xsync.NewMap[string, VT](xsync.WithPresize(len(tmp)))
@@ -135,10 +134,10 @@ func (obj *ObjectStore[Ptr]) Initialize() {
}
func (obj ObjectStore[Ptr]) MarshalJSON() ([]byte, error) {
return sonic.Marshal(obj.ptr)
return json.Marshal(obj.ptr)
}
func (obj *ObjectStore[Ptr]) UnmarshalJSON(data []byte) error {
obj.Initialize()
return sonic.Unmarshal(data, obj.ptr)
return json.Unmarshal(data, obj.ptr)
}

View File

@@ -3,8 +3,6 @@ package period
import (
"encoding/json"
"time"
"github.com/bytedance/sonic"
)
type Entries[T any] struct {
@@ -75,7 +73,7 @@ type entriesJSON[T any] struct {
}
func (e *Entries[T]) MarshalJSON() ([]byte, error) {
return sonic.Marshal(entriesJSON[T]{
return json.Marshal(entriesJSON[T]{
Entries: e.Get(),
Interval: e.interval,
})

View File

@@ -9,7 +9,6 @@ import (
"sync"
"time"
"github.com/bytedance/sonic"
"github.com/rs/zerolog/log"
gperr "github.com/yusing/goutils/errs"
"github.com/yusing/goutils/synk"
@@ -97,7 +96,7 @@ func (p *Poller[T, AggregateT]) save() error {
}
defer f.Close()
err = sonic.ConfigDefault.NewEncoder(f).Encode(p.period)
err = json.NewEncoder(f).Encode(p.period)
if err != nil {
return err
}

View File

@@ -6,7 +6,6 @@ import (
"reflect"
"testing"
"github.com/bytedance/sonic"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
@@ -81,12 +80,12 @@ var (
func TestSystemInfo(t *testing.T) {
// Test marshaling
data, err := sonic.Marshal(testInfo)
data, err := json.Marshal(testInfo)
expect.NoError(t, err)
// Test unmarshaling back
var decoded SystemInfo
err = sonic.Unmarshal(data, &decoded)
err = json.Unmarshal(data, &decoded)
expect.NoError(t, err)
// Compare original and decoded
@@ -138,7 +137,7 @@ func TestSerialize(t *testing.T) {
for _, query := range allQueries {
t.Run(string(query), func(t *testing.T) {
_, result := aggregate(entries, url.Values{"aggregate": []string{string(query)}})
s, err := sonic.Marshal(result)
s, err := json.Marshal(result)
expect.NoError(t, err)
var v []map[string]any
expect.NoError(t, json.Unmarshal(s, &v))

View File

@@ -2,13 +2,13 @@ package uptime
import (
"context"
"encoding/json"
"errors"
"math"
"net/url"
"slices"
"time"
"github.com/bytedance/sonic"
"github.com/lithammer/fuzzysearch/fuzzy"
config "github.com/yusing/godoxy/internal/config/types"
entrypoint "github.com/yusing/godoxy/internal/entrypoint/types"
@@ -54,7 +54,7 @@ func getStatuses(ctx context.Context, _ StatusByAlias) (StatusByAlias, error) {
}
func (s *Status) MarshalJSON() ([]byte, error) {
return sonic.Marshal(map[string]any{
return json.Marshal(map[string]any{
"status": s.Status.String(),
"latency": s.Latency,
"timestamp": s.Timestamp,
@@ -158,5 +158,5 @@ func (rs RouteStatuses) aggregate(limit int, offset int) Aggregated {
}
func (result Aggregated) MarshalJSON() ([]byte, error) {
return sonic.Marshal([]RouteAggregate(result))
return json.Marshal([]RouteAggregate(result))
}

View File

@@ -3,6 +3,7 @@ package captcha
import (
"bytes"
"context"
"encoding/json"
"errors"
"net"
"net/http"
@@ -11,7 +12,6 @@ import (
_ "embed"
"github.com/bytedance/sonic"
gperr "github.com/yusing/goutils/errs"
strutils "github.com/yusing/goutils/strings"
)
@@ -73,7 +73,7 @@ func (p *HcaptchaProvider) Verify(r *http.Request) error {
Success bool `json:"success"`
Error []string `json:"error-codes"`
}
if err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&respData); err != nil {
if err := json.NewDecoder(resp.Body).Decode(&respData); err != nil {
return err
}

View File

@@ -1,6 +1,7 @@
package middleware
import (
"encoding/json"
"fmt"
"maps"
"mime"
@@ -10,7 +11,6 @@ import (
"strconv"
"strings"
"github.com/bytedance/sonic"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/internal/serialization"
@@ -163,7 +163,7 @@ func (m *Middleware) MarshalJSON() ([]byte, error) {
commonOptions
any
}
return sonic.MarshalIndent(map[string]any{
return json.MarshalIndent(map[string]any{
"name": m.name,
"options": allOptions{
commonOptions: m.commonOptions,

View File

@@ -3,13 +3,13 @@ package middleware
import (
"bytes"
_ "embed"
"encoding/json"
"io"
"maps"
"net/http"
"net/http/httptest"
"strings"
"github.com/bytedance/sonic"
"github.com/yusing/godoxy/internal/common"
nettypes "github.com/yusing/godoxy/internal/net/types"
"github.com/yusing/goutils/http/reverseproxy"
@@ -24,7 +24,7 @@ func init() {
return
}
tmp := map[string]string{}
err := sonic.Unmarshal(testHeadersRaw, &tmp)
err := json.Unmarshal(testHeadersRaw, &tmp)
if err != nil {
panic(err)
}

View File

@@ -1,9 +1,8 @@
package nettypes
import (
"encoding/json"
urlPkg "net/url"
"github.com/bytedance/sonic"
)
type URL struct {
@@ -47,7 +46,7 @@ func (u *URL) MarshalJSON() (text []byte, err error) {
if u == nil {
return []byte("null"), nil
}
return sonic.Marshal(u.URL.String())
return json.Marshal(u.URL.String())
}
func (u *URL) Equals(other *URL) bool {

View File

@@ -2,9 +2,9 @@ package notif
import (
"bytes"
"encoding/json"
"strings"
"github.com/bytedance/sonic"
gperr "github.com/yusing/goutils/errs"
)
@@ -70,7 +70,7 @@ func (f FieldsBody) Format(format LogFormat) ([]byte, error) {
}
return msg.Bytes(), nil
case LogFormatRawJSON:
return sonic.Marshal(f)
return json.Marshal(f)
}
return f.Format(LogFormatMarkdown)
}
@@ -88,7 +88,7 @@ func (l ListBody) Format(format LogFormat) ([]byte, error) {
}
return msg.Bytes(), nil
case LogFormatRawJSON:
return sonic.Marshal(l)
return json.Marshal(l)
}
return l.Format(LogFormatMarkdown)
}
@@ -98,7 +98,7 @@ func (m MessageBody) Format(format LogFormat) ([]byte, error) {
case LogFormatPlain, LogFormatMarkdown:
return []byte(m), nil
case LogFormatRawJSON:
return sonic.Marshal(m)
return json.Marshal(m)
}
return []byte(m), nil
}
@@ -106,7 +106,7 @@ func (m MessageBody) Format(format LogFormat) ([]byte, error) {
func (m MessageBodyBytes) Format(format LogFormat) ([]byte, error) {
switch format {
case LogFormatRawJSON:
return sonic.Marshal(string(m))
return json.Marshal(string(m))
default:
}
return m, nil
@@ -115,7 +115,7 @@ func (m MessageBodyBytes) Format(format LogFormat) ([]byte, error) {
func (e errorBody) Format(format LogFormat) ([]byte, error) {
switch format {
case LogFormatRawJSON:
return sonic.Marshal(e.Error)
return json.Marshal(e.Error)
case LogFormatPlain:
return gperr.Plain(e.Error), nil
case LogFormatMarkdown:

View File

@@ -1,10 +1,10 @@
package notif
import (
"encoding/json"
"fmt"
"io"
"github.com/bytedance/sonic"
"github.com/gotify/server/v2/model"
"github.com/rs/zerolog"
gperr "github.com/yusing/goutils/errs"
@@ -66,7 +66,7 @@ func (client *GotifyClient) MarshalMessage(logMsg *LogMessage) ([]byte, error) {
}
}
data, err := sonic.Marshal(msg)
data, err := json.Marshal(msg)
if err != nil {
return nil, err
}
@@ -77,7 +77,7 @@ func (client *GotifyClient) MarshalMessage(logMsg *LogMessage) ([]byte, error) {
// fmtError implements Provider.
func (client *GotifyClient) fmtError(respBody io.Reader) error {
var errm model.Error
err := sonic.ConfigDefault.NewDecoder(respBody).Decode(&errm)
err := json.NewDecoder(respBody).Decode(&errm)
if err != nil {
return fmt.Errorf("failed to decode err response: %w", err)
}

View File

@@ -2,12 +2,12 @@ package notif
import (
_ "embed"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"github.com/bytedance/sonic"
gperr "github.com/yusing/goutils/errs"
)
@@ -101,7 +101,7 @@ func (webhook *Webhook) fmtError(respBody io.Reader) error {
}
func (webhook *Webhook) MarshalMessage(logMsg *LogMessage) ([]byte, error) {
title, err := sonic.Marshal(logMsg.Title)
title, err := json.Marshal(logMsg.Title)
if err != nil {
return nil, err
}
@@ -120,7 +120,7 @@ func (webhook *Webhook) MarshalMessage(logMsg *LogMessage) ([]byte, error) {
return nil, err
}
if webhook.MIMEType == MimeTypeJSON {
message, err = sonic.Marshal(string(message))
message, err = json.Marshal(string(message))
if err != nil {
return nil, err
}
@@ -149,5 +149,5 @@ func validateJSONPayload(payload string) bool {
"$color", "",
)
payload = replacer.Replace(payload)
return sonic.Valid([]byte(payload))
return json.Valid([]byte(payload))
}

View File

@@ -2,6 +2,7 @@ package proxmox
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
@@ -12,7 +13,6 @@ import (
"strings"
"sync"
"github.com/bytedance/sonic"
"github.com/luthermonson/go-proxmox"
"golang.org/x/sync/errgroup"
)
@@ -203,7 +203,7 @@ func (c *Client) Name() string {
}
func (c *Client) MarshalJSON() ([]byte, error) {
return sonic.Marshal(map[string]any{
return json.Marshal(map[string]any{
"version": c.Version,
"cluster": map[string]any{
"name": c.Cluster.Name,

View File

@@ -2,10 +2,10 @@ package proxmox
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/bytedance/sonic"
gperr "github.com/yusing/goutils/errs"
"github.com/yusing/goutils/pool"
)
@@ -82,7 +82,7 @@ func (n *Node) String() string {
}
func (n *Node) MarshalJSON() ([]byte, error) {
return sonic.Marshal(map[string]any{
return json.Marshal(map[string]any{
"name": n.name,
"id": n.id,
})

View File

@@ -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"
"github.com/yusing/godoxy/internal/types"
expect "github.com/yusing/goutils/testing"
@@ -26,7 +26,7 @@ func TestParseDockerLabels(t *testing.T) {
Names: []string{"container"},
Labels: labels,
State: "running",
Ports: []container.PortSummary{
Ports: []container.Port{
{Type: "tcp", PrivatePort: 1234, PublicPort: 1234},
},
}, types.DockerProviderConfig{URL: "unix:///var/run/docker.sock"}),

View File

@@ -1,13 +1,12 @@
package provider
import (
"net/netip"
"testing"
"time"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
D "github.com/yusing/godoxy/internal/docker"
"github.com/yusing/godoxy/internal/route"
routeTypes "github.com/yusing/godoxy/internal/route/types"
@@ -276,7 +275,7 @@ func TestPrivateIPLocalhost(t *testing.T) {
NetworkSettings: &container.NetworkSettingsSummary{
Networks: map[string]*network.EndpointSettings{
"network": {
IPAddress: netip.MustParseAddr(testDockerIP),
IPAddress: testDockerIP,
},
},
},
@@ -294,7 +293,7 @@ func TestPrivateIPRemote(t *testing.T) {
NetworkSettings: &container.NetworkSettingsSummary{
Networks: map[string]*network.EndpointSettings{
"network": {
IPAddress: netip.MustParseAddr(testDockerIP),
IPAddress: testDockerIP,
},
},
},
@@ -316,11 +315,11 @@ func TestStreamDefaultValues(t *testing.T) {
NetworkSettings: &container.NetworkSettingsSummary{
Networks: map[string]*network.EndpointSettings{
"network": {
IPAddress: netip.MustParseAddr(privIP),
IPAddress: privIP,
},
},
},
Ports: []container.PortSummary{
Ports: []container.Port{
{Type: "udp", PrivatePort: privPort, PublicPort: pubPort},
},
}
@@ -373,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.PortSummary{
Ports: []container.Port{
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
},
})["a"]

View File

@@ -123,6 +123,24 @@ func (r *ReverseProxyRoute) ReverseProxy() *reverseproxy.ReverseProxy {
return r.rp
}
func (r *ReverseProxyRoute) isSyntheticLoadBalancerRoute() bool {
return r.loadBalancer != nil && r.rp == nil
}
func (r *ReverseProxyRoute) Key() string {
if r.isSyntheticLoadBalancerRoute() {
return r.Alias
}
return r.Route.Key()
}
func (r *ReverseProxyRoute) ShouldExclude() bool {
if r.isSyntheticLoadBalancerRoute() {
return false
}
return r.Route.ShouldExclude()
}
// Start implements task.TaskStarter.
func (r *ReverseProxyRoute) Start(parent task.Parent) error {
r.task = parent.Subtask("http."+r.Name(), false)
@@ -206,7 +224,7 @@ func (r *ReverseProxyRoute) addToLoadBalancer(parent task.Parent, ep entrypoint.
linked = l.(*ReverseProxyRoute) // it must be a reverse proxy route
lb = linked.loadBalancer
lb.UpdateConfigIfNeeded(cfg)
if linked.Homepage.Name == "" {
if linked.Homepage == nil || linked.Homepage.Name == "" {
linked.Homepage = r.Homepage
}
} else {

View File

@@ -1,16 +1,165 @@
package route
import (
"fmt"
"net"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
entrypoint "github.com/yusing/godoxy/internal/entrypoint/types"
"github.com/yusing/godoxy/internal/homepage"
route "github.com/yusing/godoxy/internal/route/types"
"github.com/yusing/godoxy/internal/types"
"github.com/yusing/goutils/task"
)
type testPool[T interface{ Key() string }] struct {
mu sync.RWMutex
items map[string]T
}
func newTestPool[T interface{ Key() string }]() *testPool[T] {
return &testPool[T]{items: make(map[string]T)}
}
func (p *testPool[T]) Get(alias string) (T, bool) {
p.mu.RLock()
defer p.mu.RUnlock()
v, ok := p.items[alias]
return v, ok
}
func (p *testPool[T]) Iter(yield func(alias string, r T) bool) {
p.mu.RLock()
defer p.mu.RUnlock()
for alias, r := range p.items {
if !yield(alias, r) {
return
}
}
}
func (p *testPool[T]) Size() int {
p.mu.RLock()
defer p.mu.RUnlock()
return len(p.items)
}
func (p *testPool[T]) Add(r T) {
p.mu.Lock()
defer p.mu.Unlock()
p.items[r.Key()] = r
}
func (p *testPool[T]) Del(r T) {
p.mu.Lock()
defer p.mu.Unlock()
delete(p.items, r.Key())
}
type testEntrypoint struct {
httpRoutes *testPool[types.HTTPRoute]
streamRoutes *testPool[types.StreamRoute]
excludedRoutes *testPool[types.Route]
}
func newTestEntrypoint() *testEntrypoint {
return &testEntrypoint{
httpRoutes: newTestPool[types.HTTPRoute](),
streamRoutes: newTestPool[types.StreamRoute](),
excludedRoutes: newTestPool[types.Route](),
}
}
func (ep *testEntrypoint) SupportProxyProtocol() bool { return false }
func (ep *testEntrypoint) DisablePoolsLog(bool) {}
func (ep *testEntrypoint) GetRoute(alias string) (types.Route, bool) {
if r, ok := ep.httpRoutes.Get(alias); ok {
return r, true
}
if r, ok := ep.streamRoutes.Get(alias); ok {
return r, true
}
if r, ok := ep.excludedRoutes.Get(alias); ok {
return r, true
}
return nil, false
}
func (ep *testEntrypoint) StartAddRoute(r types.Route) error {
if r.ShouldExclude() {
ep.excludedRoutes.Add(r)
return nil
}
switch rt := r.(type) {
case types.HTTPRoute:
ep.httpRoutes.Add(rt)
return nil
case types.StreamRoute:
ep.streamRoutes.Add(rt)
return nil
default:
return fmt.Errorf("unknown route type: %T", r)
}
}
func (ep *testEntrypoint) IterRoutes(yield func(r types.Route) bool) {
ep.httpRoutes.Iter(func(_ string, r types.HTTPRoute) bool {
return yield(r)
})
ep.streamRoutes.Iter(func(_ string, r types.StreamRoute) bool {
return yield(r)
})
ep.excludedRoutes.Iter(func(_ string, r types.Route) bool {
return yield(r)
})
}
func (ep *testEntrypoint) NumRoutes() int {
return ep.httpRoutes.Size() + ep.streamRoutes.Size() + ep.excludedRoutes.Size()
}
func (ep *testEntrypoint) RoutesByProvider() map[string][]types.Route {
return map[string][]types.Route{}
}
func (ep *testEntrypoint) HTTPRoutes() entrypoint.PoolLike[types.HTTPRoute] {
return ep.httpRoutes
}
func (ep *testEntrypoint) StreamRoutes() entrypoint.PoolLike[types.StreamRoute] {
return ep.streamRoutes
}
func (ep *testEntrypoint) ExcludedRoutes() entrypoint.RWPoolLike[types.Route] {
return ep.excludedRoutes
}
func (ep *testEntrypoint) GetHealthInfo() map[string]types.HealthInfo {
return nil
}
func (ep *testEntrypoint) GetHealthInfoWithoutDetail() map[string]types.HealthInfoWithoutDetail {
return nil
}
func (ep *testEntrypoint) GetHealthInfoSimple() map[string]types.HealthStatus {
return nil
}
func TestReverseProxyRoute(t *testing.T) {
t.Run("LinkToLoadBalancer", func(t *testing.T) {
testTask := task.GetTestTask(t)
entrypoint.SetCtx(testTask, newTestEntrypoint())
cfg := Route{
Alias: "test",
Scheme: route.SchemeHTTP,
@@ -36,4 +185,75 @@ func TestReverseProxyRoute(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, r2)
})
t.Run("LoadBalancerRoute", func(t *testing.T) {
testTask := task.GetTestTask(t)
entrypoint.SetCtx(testTask, newTestEntrypoint())
newServer := func() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
}
srv1 := newServer()
t.Cleanup(srv1.Close)
srv2 := newServer()
t.Cleanup(srv2.Close)
srv3 := newServer()
t.Cleanup(srv3.Close)
makeRoute := func(alias string, target *httptest.Server) *Route {
t.Helper()
targetURL, err := url.Parse(target.URL)
require.NoError(t, err)
host, portStr, err := net.SplitHostPort(targetURL.Host)
require.NoError(t, err)
port, err := strconv.Atoi(portStr)
require.NoError(t, err)
return &Route{
Alias: alias,
Scheme: route.SchemeHTTP,
Host: host,
Port: Port{Proxy: port},
Homepage: &homepage.ItemConfig{
Show: true,
},
LoadBalance: &types.LoadBalancerConfig{
Link: "lb-test",
},
HealthCheck: types.HealthCheckConfig{
Path: "/",
Interval: 2 * time.Second,
Timeout: time.Second,
UseGet: true,
},
}
}
_, err := NewStartedTestRoute(t, makeRoute("lb-1", srv1))
require.NoError(t, err)
_, err = NewStartedTestRoute(t, makeRoute("lb-2", srv2))
require.NoError(t, err)
_, err = NewStartedTestRoute(t, makeRoute("lb-3", srv3))
require.NoError(t, err)
ep := entrypoint.FromCtx(testTask.Context())
require.NotNil(t, ep)
lbRoute, ok := ep.HTTPRoutes().Get("lb-test")
require.True(t, ok)
lb, ok := lbRoute.(*ReverseProxyRoute)
require.True(t, ok)
require.False(t, lb.ShouldExclude())
require.NotNil(t, lb.loadBalancer)
require.NotNil(t, lb.HealthMonitor())
assert.Equal(t, route.SchemeNone, lb.Scheme)
assert.Empty(t, lb.Host)
assert.Zero(t, lb.Port.Proxy)
assert.Equal(t, "3/3 servers are healthy", lb.HealthMonitor().Detail())
})
}

View File

@@ -6,9 +6,10 @@
!path glob("/@tanstack-start/*")
!path glob("/@vite-plugin-pwa/*")
!path glob("/__tsd/*")
!path glob("/src/*")
!path /@react-refresh
!path /@vite/client
!path regex("/\?token=[a-zA-Z0-9-_]+")
!header Sec-Websocket-Protocol vite-hmr
!path glob("/@id/*")
!path glob("/api/v1/auth/*")
!path glob("/auth/*")

View File

@@ -1,10 +1,10 @@
package route
import (
"encoding/json"
"errors"
"strconv"
"github.com/bytedance/sonic"
gperr "github.com/yusing/goutils/errs"
)
@@ -59,7 +59,7 @@ func (s Scheme) MarshalJSON() ([]byte, error) {
func (s *Scheme) UnmarshalJSON(data []byte) error {
var v string
if err := sonic.Unmarshal(data, &v); err != nil {
if err := json.Unmarshal(data, &v); err != nil {
return err
}
return s.Parse(v)

View File

@@ -1,9 +1,9 @@
package serialization
import (
"encoding/json"
"net/http"
"github.com/bytedance/sonic"
"github.com/goccy/go-yaml"
)
@@ -18,7 +18,7 @@ func (b GinJSONBinding) Name() string {
func (b GinJSONBinding) Bind(req *http.Request, obj any) error {
m := make(map[string]any)
if err := sonic.ConfigDefault.NewDecoder(NewSubstituteEnvReader(req.Body)).Decode(&m); err != nil {
if err := json.NewDecoder(NewSubstituteEnvReader(req.Body)).Decode(&m); err != nil {
return err
}
return MapUnmarshalValidate(m, obj)

View File

@@ -1,6 +1,7 @@
package serialization
import (
"encoding/json"
"errors"
"fmt"
"io"
@@ -12,7 +13,6 @@ import (
"time"
"unsafe"
"github.com/bytedance/sonic"
"github.com/go-playground/validator/v10"
"github.com/goccy/go-yaml"
"github.com/puzpuzpuz/xsync/v4"
@@ -34,8 +34,8 @@ func ToSerializedObject[VT any](m map[string]VT) SerializedObject {
}
func init() {
strutils.SetJSONMarshaler(sonic.Marshal)
strutils.SetJSONUnmarshaler(sonic.Unmarshal)
strutils.SetJSONMarshaler(json.Marshal)
strutils.SetJSONUnmarshaler(json.Unmarshal)
strutils.SetYAMLMarshaler(yaml.Marshal)
strutils.SetYAMLUnmarshaler(yaml.Unmarshal)
}

View File

@@ -1,8 +1,9 @@
package types
import (
"github.com/bytedance/sonic"
"github.com/moby/moby/api/types/container"
"encoding/json"
"github.com/docker/docker/api/types/container"
"github.com/yusing/ds/ordered"
"github.com/yusing/godoxy/internal/agentpool"
gperr "github.com/yusing/goutils/errs"
@@ -11,7 +12,7 @@ import (
type (
LabelMap = map[string]any
PortMapping = map[int]container.PortSummary
PortMapping = map[int]container.Port
Container struct {
DockerCfg DockerProviderConfig `json:"docker_cfg"`
Image *ContainerImage `json:"image"`
@@ -69,5 +70,5 @@ func (e *ContainerError) Unwrap() error {
func (e *ContainerError) MarshalJSON() ([]byte, error) {
err := e.errs.Error().(gperr.PlainError)
return sonic.Marshal(string(err.Plain()))
return json.Marshal(string(err.Plain()))
}

View File

@@ -7,7 +7,6 @@ import (
"strconv"
"time"
"github.com/bytedance/sonic"
"github.com/yusing/goutils/task"
)
@@ -175,7 +174,7 @@ func (s HealthStatus) MarshalJSON() ([]byte, error) {
func (s *HealthStatus) UnmarshalJSON(data []byte) error {
var v string
if err := sonic.Unmarshal(data, &v); err != nil {
if err := json.Unmarshal(data, &v); err != nil {
return fmt.Errorf("failed to unmarshal health status: %w", err)
}
@@ -191,7 +190,7 @@ func (jsonRepr *HealthJSONRepr) MarshalJSON() ([]byte, error) {
if url == "http://:0" {
url = ""
}
return sonic.Marshal(HealthJSON{
return json.Marshal(HealthJSON{
Name: jsonRepr.Name,
Config: jsonRepr.Config,
Started: jsonRepr.Started.Unix(),

View File

@@ -6,8 +6,9 @@ import (
"fmt"
"time"
dockerEvents "github.com/moby/moby/api/types/events"
"github.com/moby/moby/client"
dockerEvents "github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/rs/zerolog/log"
"github.com/yusing/godoxy/internal/docker"
"github.com/yusing/godoxy/internal/types"
@@ -18,40 +19,21 @@ type (
DockerWatcher struct {
cfg types.DockerProviderConfig
}
DockerListOptions = client.EventsListOptions
DockerFilters = client.Filters
DockerListOptions = dockerEvents.ListOptions
)
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 = 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))
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))
NewDockerFilters = filters.NewArgs
optionsDefault = DockerListOptions{Filters: NewDockerFilters(
DockerFilterContainer,
@@ -72,8 +54,8 @@ var (
}
)
func DockerFilterContainerNameID(nameOrID string) DockerFilter {
return NewDockerFilter("container", nameOrID)
func DockerFilterContainerNameID(nameOrID string) filters.KeyValuePair {
return filters.Arg("container", nameOrID)
}
func NewDockerWatcher(dockerCfg types.DockerProviderConfig) DockerWatcher {
@@ -106,15 +88,15 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList
client.Close()
}()
chs := client.Events(ctx, options)
cEventCh, cErrCh := client.Events(ctx, options)
defer log.Debug().Str("host", client.DaemonHost()).Msg("docker watcher closed")
for {
select {
case <-ctx.Done():
return
case msg := <-chs.Messages:
case msg := <-cEventCh:
w.handleEvent(msg, eventCh)
case err := <-chs.Err:
case err := <-cErrCh:
if err == nil {
continue
}
@@ -142,7 +124,7 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList
// connection successful, trigger reload (reload routes)
eventCh <- reloadTrigger
// reopen event channel
chs = client.Events(ctx, options)
cEventCh, cErrCh = client.Events(ctx, options)
}
}
}()

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"maps"
dockerEvents "github.com/moby/moby/api/types/events"
dockerEvents "github.com/docker/docker/api/types/events"
)
type (

View File

@@ -7,7 +7,7 @@ replace github.com/yusing/goutils => ../goutils
require (
github.com/gorilla/mux v1.8.1
github.com/yusing/goutils v0.7.0
golang.org/x/net v0.51.0
golang.org/x/net v0.52.0
)
require (
@@ -18,5 +18,5 @@ require (
github.com/puzpuzpuz/xsync/v4 v4.4.0 // indirect
github.com/rs/zerolog v1.34.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/text v0.35.0 // indirect
)

View File

@@ -21,14 +21,14 @@ github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=