mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 20:52:33 +02:00
Compare commits
149 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cee6eaecff | ||
![]() |
67a6b89ea5 | ||
![]() |
78be9b1c71 | ||
![]() |
26856b612a | ||
![]() |
36ceba3ae7 | ||
![]() |
f45f3fba79 | ||
![]() |
4bbff323e3 | ||
![]() |
2e68baa93e | ||
![]() |
a162371ec5 | ||
![]() |
8f9c76daa5 | ||
![]() |
8b3e058885 | ||
![]() |
023cbc81bc | ||
![]() |
b490e8c475 | ||
![]() |
8e27886235 | ||
![]() |
7435b8e485 | ||
![]() |
21724c037f | ||
![]() |
44b4cff35e | ||
![]() |
1e24765b17 | ||
![]() |
a1f2a84a16 | ||
![]() |
453262832a | ||
![]() |
99e975145c | ||
![]() |
e300170c51 | ||
![]() |
1382137f20 | ||
![]() |
54d7508f5d | ||
![]() |
71ca8c738e | ||
![]() |
f1eefde964 | ||
![]() |
84e7a6591e | ||
![]() |
30c76cfc5f | ||
![]() |
a8ba42e360 | ||
![]() |
cd291556fc | ||
![]() |
0d41809630 | ||
![]() |
53acf75c04 | ||
![]() |
cf30fe6cfc | ||
![]() |
55bbcae911 | ||
![]() |
b30c0d7dc0 | ||
![]() |
198ae2cd02 | ||
![]() |
26938eb6ed | ||
![]() |
48823a860f | ||
![]() |
985ff0a74d | ||
![]() |
43b493c60e | ||
![]() |
e0e0fab127 | ||
![]() |
fc0dbd940c | ||
![]() |
0208e6286f | ||
![]() |
2c0b68c8c2 | ||
![]() |
c05059765d | ||
![]() |
a06787593c | ||
![]() |
8fe94d6d14 | ||
![]() |
4ddfb48b9d | ||
![]() |
31dc112591 | ||
![]() |
6797897814 | ||
![]() |
99eccd0b95 | ||
![]() |
0387739b94 | ||
![]() |
ead27c72f1 | ||
![]() |
455a85e6a0 | ||
![]() |
8424fd9f1a | ||
![]() |
75ee0e63bd | ||
![]() |
1ce607029a | ||
![]() |
1e80ad2a44 | ||
![]() |
4daefa19d1 | ||
![]() |
491231e439 | ||
![]() |
c90ec8caa1 | ||
![]() |
9eb674029e | ||
![]() |
e41c6530ab | ||
![]() |
afd35c183d | ||
![]() |
f190483b4e | ||
![]() |
7b0ed09772 | ||
![]() |
4415bffc35 | ||
![]() |
ddab2766b4 | ||
![]() |
ef95682116 | ||
![]() |
dd65a8d04b | ||
![]() |
aa23b5b595 | ||
![]() |
c55c6c84bc | ||
![]() |
a45e5e17db | ||
![]() |
b8c0961de3 | ||
![]() |
62d3d200e6 | ||
![]() |
bf32cafd90 | ||
![]() |
1c182b5a7d | ||
![]() |
ad60f377ba | ||
![]() |
75db09b1f3 | ||
![]() |
6dd849f480 | ||
![]() |
e2ae29795d | ||
![]() |
92fa0f8168 | ||
![]() |
b090598b68 | ||
![]() |
2cec88d3ce | ||
![]() |
4df31263b5 | ||
![]() |
9eae809690 | ||
![]() |
f1ba554a24 | ||
![]() |
f9a8aede20 | ||
![]() |
e275ee634c | ||
![]() |
797d88772f | ||
![]() |
8ef8015a7f | ||
![]() |
5fce4b445b | ||
![]() |
7552a706a7 | ||
![]() |
e1bc6d1f44 | ||
![]() |
56850a9580 | ||
![]() |
5f780f4902 | ||
![]() |
ccb4639f43 | ||
![]() |
ac1470d81d | ||
![]() |
efaabfa63a | ||
![]() |
9043cf25c5 | ||
![]() |
98e90d7a0b | ||
![]() |
82c829de18 | ||
![]() |
2fe4fef779 | ||
![]() |
91302ceed7 | ||
![]() |
7fa7b55b18 | ||
![]() |
69ee8495d8 | ||
![]() |
28d9a72908 | ||
![]() |
770c698332 | ||
![]() |
cd4c843025 | ||
![]() |
f0cf89060b | ||
![]() |
f79a15bac6 | ||
![]() |
2b4a70a550 | ||
![]() |
f06741428c | ||
![]() |
16e6e72454 | ||
![]() |
100d2c392f | ||
![]() |
829eb08e37 | ||
![]() |
53d54a09b0 | ||
![]() |
62c551c7fe | ||
![]() |
80e59bb481 | ||
![]() |
7a5afc3612 | ||
![]() |
2c0349c11c | ||
![]() |
8e3c2cc8d4 | ||
![]() |
d35afdb3c9 | ||
![]() |
ae093ebf40 | ||
![]() |
aa8af4185b | ||
![]() |
0029cf69d6 | ||
![]() |
33e400a17e | ||
![]() |
1d22bcfed9 | ||
![]() |
978d82060e | ||
![]() |
7aa1215491 | ||
![]() |
0b69589586 | ||
![]() |
bca3cd84d1 | ||
![]() |
ce4bf2f646 | ||
![]() |
c49016f22c | ||
![]() |
8da63daf02 | ||
![]() |
c5fd21552e | ||
![]() |
27409abc24 | ||
![]() |
21c9e46274 | ||
![]() |
22a12d3116 | ||
![]() |
89d93dd878 | ||
![]() |
66853dfc52 | ||
![]() |
c72f66d64b | ||
![]() |
59bc342a40 | ||
![]() |
e11579df10 | ||
![]() |
6a8f6fb4b5 | ||
![]() |
8f20bd3840 | ||
![]() |
f1abb745fe | ||
![]() |
cb2990f6e8 | ||
![]() |
fb2f850311 |
288 changed files with 7825 additions and 6612 deletions
26
.env.example
26
.env.example
|
@ -1,6 +1,13 @@
|
||||||
|
# docker image tag (latest, nightly)
|
||||||
|
TAG=latest
|
||||||
|
|
||||||
# set timezone to get correct log timestamp
|
# set timezone to get correct log timestamp
|
||||||
TZ=ETC/UTC
|
TZ=ETC/UTC
|
||||||
|
|
||||||
|
# container uid and gid (must match the owner of mounted directories)
|
||||||
|
GODOXY_UID=1000
|
||||||
|
GODOXY_GID=1000
|
||||||
|
|
||||||
# API JWT Configuration (common)
|
# API JWT Configuration (common)
|
||||||
# generate secret with `openssl rand -base64 32`
|
# generate secret with `openssl rand -base64 32`
|
||||||
GODOXY_API_JWT_SECRET=
|
GODOXY_API_JWT_SECRET=
|
||||||
|
@ -41,14 +48,29 @@ GODOXY_API_PASSWORD=password
|
||||||
GODOXY_HTTP_ADDR=:80
|
GODOXY_HTTP_ADDR=:80
|
||||||
GODOXY_HTTPS_ADDR=:443
|
GODOXY_HTTPS_ADDR=:443
|
||||||
|
|
||||||
|
# Enable HTTP3
|
||||||
|
GODOXY_HTTP3_ENABLED=true
|
||||||
|
|
||||||
# API listening address
|
# API listening address
|
||||||
GODOXY_API_ADDR=127.0.0.1:8888
|
GODOXY_API_ADDR=127.0.0.1:8888
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
GODOXY_METRICS_DISABLE_CPU=false
|
||||||
|
GODOXY_METRICS_DISABLE_MEMORY=false
|
||||||
|
GODOXY_METRICS_DISABLE_DISK=false
|
||||||
|
GODOXY_METRICS_DISABLE_NETWORK=false
|
||||||
|
GODOXY_METRICS_DISABLE_SENSORS=false
|
||||||
|
|
||||||
# Frontend listening port
|
# Frontend listening port
|
||||||
GODOXY_FRONTEND_PORT=3000
|
GODOXY_FRONTEND_PORT=3000
|
||||||
|
|
||||||
# Prometheus Metrics
|
# Frontend aliases (subdomains / FQDNs, e.g. godoxy, godoxy.domain.com)
|
||||||
GODOXY_PROMETHEUS_ENABLED=true
|
GODOXY_FRONTEND_ALIASES=godoxy
|
||||||
|
|
||||||
|
# Docker socket
|
||||||
|
# /var/run/podman/podman.sock for podman
|
||||||
|
DOCKER_SOCKET=/var/run/docker.sock
|
||||||
|
SOCKET_PROXY_LISTEN_ADDR=127.0.0.1:2375
|
||||||
|
|
||||||
# Debug mode
|
# Debug mode
|
||||||
GODOXY_DEBUG=false
|
GODOXY_DEBUG=false
|
3
.github/workflows/docker-image-nightly.yml
vendored
3
.github/workflows/docker-image-nightly.yml
vendored
|
@ -15,9 +15,10 @@ jobs:
|
||||||
with:
|
with:
|
||||||
image_name: ${{ github.repository_owner }}/godoxy
|
image_name: ${{ github.repository_owner }}/godoxy
|
||||||
tag: nightly
|
tag: nightly
|
||||||
|
target: main
|
||||||
build-nightly-agent:
|
build-nightly-agent:
|
||||||
uses: ./.github/workflows/docker-image.yml
|
uses: ./.github/workflows/docker-image.yml
|
||||||
with:
|
with:
|
||||||
image_name: ${{ github.repository_owner }}/godoxy-agent
|
image_name: ${{ github.repository_owner }}/godoxy-agent
|
||||||
tag: nightly
|
tag: nightly
|
||||||
agent: true
|
target: agent
|
||||||
|
|
3
.github/workflows/docker-image-prod.yml
vendored
3
.github/workflows/docker-image-prod.yml
vendored
|
@ -12,9 +12,10 @@ jobs:
|
||||||
image_name: ${{ github.repository_owner }}/godoxy
|
image_name: ${{ github.repository_owner }}/godoxy
|
||||||
old_image_name: ${{ github.repository_owner }}/go-proxy
|
old_image_name: ${{ github.repository_owner }}/go-proxy
|
||||||
tag: latest
|
tag: latest
|
||||||
|
target: main
|
||||||
build-prod-agent:
|
build-prod-agent:
|
||||||
uses: ./.github/workflows/docker-image.yml
|
uses: ./.github/workflows/docker-image.yml
|
||||||
with:
|
with:
|
||||||
image_name: ${{ github.repository_owner }}/godoxy-agent
|
image_name: ${{ github.repository_owner }}/godoxy-agent
|
||||||
tag: latest
|
tag: latest
|
||||||
agent: true
|
target: agent
|
||||||
|
|
23
.github/workflows/docker-image-socket-proxy.yml
vendored
Normal file
23
.github/workflows/docker-image-socket-proxy.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: Docker Image CI (socket-proxy)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "socket-proxy/**"
|
||||||
|
tags-ignore:
|
||||||
|
- '**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
uses: ./.github/workflows/docker-image.yml
|
||||||
|
with:
|
||||||
|
image_name: ${{ github.repository_owner }}/socket-proxy
|
||||||
|
tag: latest
|
||||||
|
target: socket-proxy
|
||||||
|
dockerfile: socket-proxy.Dockerfile
|
23
.github/workflows/docker-image.yml
vendored
23
.github/workflows/docker-image.yml
vendored
|
@ -12,16 +12,20 @@ on:
|
||||||
old_image_name:
|
old_image_name:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
agent:
|
target:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
dockerfile:
|
||||||
required: false
|
required: false
|
||||||
default: false
|
type: string
|
||||||
type: boolean
|
default: Dockerfile
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
MAKE_ARGS: agent=${{ inputs.agent && '1' || '0' }}
|
MAKE_ARGS: ${{ inputs.target }}=1
|
||||||
DIGEST_PATH: /tmp/digests/${{ inputs.agent && 'agent' || 'main' }}
|
DIGEST_PATH: /tmp/digests/${{ inputs.target }}
|
||||||
DIGEST_NAME_SUFFIX: ${{ inputs.agent && 'agent' || 'main' }}
|
DIGEST_NAME_SUFFIX: ${{ inputs.target }}
|
||||||
|
DOCKERFILE: ${{ inputs.dockerfile }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -76,11 +80,14 @@ jobs:
|
||||||
with:
|
with:
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
file: ${{ env.DOCKERFILE }}
|
||||||
outputs: type=image,name=${{ env.REGISTRY }}/${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
outputs: type=image,name=${{ env.REGISTRY }}/${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||||
cache-from: |
|
cache-from: |
|
||||||
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }}-${{ inputs.tag }}
|
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }}
|
||||||
|
type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }}
|
||||||
cache-to: |
|
cache-to: |
|
||||||
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }}-${{ inputs.tag }},mode=max
|
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max
|
||||||
|
type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }},mode=max
|
||||||
build-args: |
|
build-args: |
|
||||||
VERSION=${{ github.ref_name }}
|
VERSION=${{ github.ref_name }}
|
||||||
MAKE_ARGS=${{ env.MAKE_ARGS }}
|
MAKE_ARGS=${{ env.MAKE_ARGS }}
|
||||||
|
|
204
.golangci.yml
204
.golangci.yml
|
@ -1,43 +1,77 @@
|
||||||
run:
|
version: "2"
|
||||||
timeout: 10m
|
linters:
|
||||||
|
default: all
|
||||||
linters-settings:
|
|
||||||
govet:
|
|
||||||
enable-all: true
|
|
||||||
disable:
|
disable:
|
||||||
- shadow
|
- bodyclose
|
||||||
- fieldalignment
|
- containedctx
|
||||||
gocyclo:
|
- contextcheck
|
||||||
min-complexity: 14
|
- cyclop
|
||||||
misspell:
|
- depguard
|
||||||
locale: US
|
- dupl
|
||||||
|
- err113
|
||||||
|
- exhaustive
|
||||||
|
- exhaustruct
|
||||||
|
- forcetypeassert
|
||||||
|
- gochecknoglobals
|
||||||
|
- gochecknoinits
|
||||||
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- gocyclo
|
||||||
|
- gomoddirectives
|
||||||
|
- gosec
|
||||||
|
- gosmopolitan
|
||||||
|
- ireturn
|
||||||
|
- lll
|
||||||
|
- maintidx
|
||||||
|
- makezero
|
||||||
|
- mnd
|
||||||
|
- nakedret
|
||||||
|
- nestif
|
||||||
|
- nilnil
|
||||||
|
- nlreturn
|
||||||
|
- noctx
|
||||||
|
- nonamedreturns
|
||||||
|
- paralleltest
|
||||||
|
- prealloc
|
||||||
|
- rowserrcheck
|
||||||
|
- sqlclosecheck
|
||||||
|
- tagliatelle
|
||||||
|
- testpackage
|
||||||
|
- tparallel
|
||||||
|
- varnamelen
|
||||||
|
- wrapcheck
|
||||||
|
- wsl
|
||||||
|
settings:
|
||||||
|
errcheck:
|
||||||
|
exclude-functions:
|
||||||
|
- fmt.Fprintln
|
||||||
|
forbidigo:
|
||||||
|
forbid:
|
||||||
|
- pattern: ^print(ln)?$
|
||||||
funlen:
|
funlen:
|
||||||
lines: -1
|
lines: -1
|
||||||
statements: 120
|
statements: 120
|
||||||
forbidigo:
|
gocyclo:
|
||||||
forbid:
|
min-complexity: 14
|
||||||
- ^print(ln)?$
|
|
||||||
godox:
|
godox:
|
||||||
keywords:
|
keywords:
|
||||||
- FIXME
|
- FIXME
|
||||||
tagalign:
|
gomoddirectives:
|
||||||
align: false
|
replace-allow-list:
|
||||||
sort: true
|
- github.com/abbot/go-http-auth
|
||||||
order:
|
- github.com/gorilla/mux
|
||||||
- description
|
- github.com/mailgun/minheap
|
||||||
- json
|
- github.com/mailgun/multibuf
|
||||||
- toml
|
- github.com/jaguilar/vt100
|
||||||
- yaml
|
- github.com/cucumber/godog
|
||||||
- yml
|
- github.com/http-wasm/http-wasm-host-go
|
||||||
- label
|
govet:
|
||||||
- label-slice-as-struct
|
disable:
|
||||||
- file
|
- shadow
|
||||||
- kv
|
- fieldalignment
|
||||||
- export
|
enable-all: true
|
||||||
stylecheck:
|
misspell:
|
||||||
dot-import-whitelist:
|
locale: US
|
||||||
- github.com/yusing/go-proxy/internal/utils/testing # go tests only
|
|
||||||
- github.com/yusing/go-proxy/internal/api/v1/utils # api only
|
|
||||||
revive:
|
revive:
|
||||||
rules:
|
rules:
|
||||||
- name: struct-tag
|
- name: struct-tag
|
||||||
|
@ -67,69 +101,51 @@ linters-settings:
|
||||||
disabled: true
|
disabled: true
|
||||||
- name: unreachable-code
|
- name: unreachable-code
|
||||||
- name: redefines-builtin-id
|
- name: redefines-builtin-id
|
||||||
gomoddirectives:
|
staticcheck:
|
||||||
replace-allow-list:
|
checks:
|
||||||
- github.com/abbot/go-http-auth
|
- all
|
||||||
- github.com/gorilla/mux
|
- -SA1019
|
||||||
- github.com/mailgun/minheap
|
dot-import-whitelist:
|
||||||
- github.com/mailgun/multibuf
|
- github.com/yusing/go-proxy/internal/utils/testing
|
||||||
- github.com/jaguilar/vt100
|
- github.com/yusing/go-proxy/internal/api/v1/utils
|
||||||
- github.com/cucumber/godog
|
tagalign:
|
||||||
- github.com/http-wasm/http-wasm-host-go
|
align: false
|
||||||
|
sort: true
|
||||||
|
order:
|
||||||
|
- description
|
||||||
|
- json
|
||||||
|
- toml
|
||||||
|
- yaml
|
||||||
|
- yml
|
||||||
|
- label
|
||||||
|
- label-slice-as-struct
|
||||||
|
- file
|
||||||
|
- kv
|
||||||
|
- export
|
||||||
testifylint:
|
testifylint:
|
||||||
disable:
|
disable:
|
||||||
- suite-dont-use-pkg
|
- suite-dont-use-pkg
|
||||||
- require-error
|
- require-error
|
||||||
- go-require
|
- go-require
|
||||||
staticcheck:
|
exclusions:
|
||||||
checks:
|
generated: lax
|
||||||
- all
|
presets:
|
||||||
- -SA1019
|
- comments
|
||||||
errcheck:
|
- common-false-positives
|
||||||
exclude-functions:
|
- legacy
|
||||||
- fmt.Fprintln
|
- std-error-handling
|
||||||
linters:
|
paths:
|
||||||
enable-all: true
|
- third_party$
|
||||||
disable:
|
- builtin$
|
||||||
- execinquery # deprecated
|
- examples$
|
||||||
- gomnd # deprecated
|
formatters:
|
||||||
- sqlclosecheck # not relevant (SQL)
|
enable:
|
||||||
- rowserrcheck # not relevant (SQL)
|
- gofmt
|
||||||
- cyclop # duplicate of gocyclo
|
- gofumpt
|
||||||
- depguard # Not relevant
|
- goimports
|
||||||
- nakedret # Too strict
|
exclusions:
|
||||||
- lll # Not relevant
|
generated: lax
|
||||||
- gocyclo # must be fixed
|
paths:
|
||||||
- gocognit # Too strict
|
- third_party$
|
||||||
- nestif # Too many false-positive.
|
- builtin$
|
||||||
- prealloc # Too many false-positive.
|
- examples$
|
||||||
- makezero # Not relevant
|
|
||||||
- dupl # Too strict
|
|
||||||
- gci # I don't care
|
|
||||||
- goconst # Too annoying
|
|
||||||
- gosec # Too strict
|
|
||||||
- gochecknoinits
|
|
||||||
- gochecknoglobals
|
|
||||||
- wsl # Too strict
|
|
||||||
- nlreturn # Not relevant
|
|
||||||
- mnd # Too strict
|
|
||||||
- testpackage # Too strict
|
|
||||||
- tparallel # Not relevant
|
|
||||||
- paralleltest # Not relevant
|
|
||||||
- exhaustive # Not relevant
|
|
||||||
- exhaustruct # Not relevant
|
|
||||||
- err113 # Too strict
|
|
||||||
- wrapcheck # Too strict
|
|
||||||
- noctx # Too strict
|
|
||||||
- bodyclose # too many false-positive
|
|
||||||
- forcetypeassert # Too strict
|
|
||||||
- tagliatelle # Too strict
|
|
||||||
- varnamelen # Not relevant
|
|
||||||
- nilnil # Not relevant
|
|
||||||
- ireturn # Not relevant
|
|
||||||
- contextcheck # too many false-positive
|
|
||||||
- containedctx # too many false-positive
|
|
||||||
- maintidx # kind of duplicate of gocyclo
|
|
||||||
- nonamedreturns # Too strict
|
|
||||||
- gosmopolitan # not relevant
|
|
||||||
- exportloopref # Not relevant since go1.22
|
|
||||||
|
|
|
@ -2,36 +2,37 @@
|
||||||
# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml
|
# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml
|
||||||
version: 0.1
|
version: 0.1
|
||||||
cli:
|
cli:
|
||||||
version: 1.22.10
|
version: 1.22.15
|
||||||
# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins)
|
# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins)
|
||||||
plugins:
|
plugins:
|
||||||
sources:
|
sources:
|
||||||
- id: trunk
|
- id: trunk
|
||||||
ref: v1.6.7
|
ref: v1.6.8
|
||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes)
|
# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes)
|
||||||
runtimes:
|
runtimes:
|
||||||
enabled:
|
enabled:
|
||||||
- node@18.20.5
|
- node@18.20.5
|
||||||
- python@3.10.8
|
- python@3.10.8
|
||||||
- go@1.23.2
|
- go@1.24.3
|
||||||
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
|
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
|
||||||
lint:
|
lint:
|
||||||
disabled:
|
disabled:
|
||||||
- markdownlint
|
- markdownlint
|
||||||
- yamllint
|
- yamllint
|
||||||
enabled:
|
enabled:
|
||||||
|
- checkov@3.2.416
|
||||||
|
- golangci-lint2@2.1.6
|
||||||
- hadolint@2.12.1-beta
|
- hadolint@2.12.1-beta
|
||||||
- actionlint@1.7.7
|
- actionlint@1.7.7
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- gofmt@1.20.4
|
- gofmt@1.20.4
|
||||||
- golangci-lint@1.64.5
|
- osv-scanner@2.0.2
|
||||||
- osv-scanner@1.9.2
|
- oxipng@9.1.5
|
||||||
- oxipng@9.1.4
|
- prettier@3.5.3
|
||||||
- prettier@3.5.1
|
|
||||||
- shellcheck@0.10.0
|
- shellcheck@0.10.0
|
||||||
- shfmt@3.6.0
|
- shfmt@3.6.0
|
||||||
- trufflehog@3.88.9
|
- trufflehog@3.88.29
|
||||||
actions:
|
actions:
|
||||||
disabled:
|
disabled:
|
||||||
- trunk-announce
|
- trunk-announce
|
||||||
|
|
4
.vscode/settings.example.json
vendored
4
.vscode/settings.example.json
vendored
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"yaml.schemas": {
|
"yaml.schemas": {
|
||||||
"https://github.com/yusing/go-proxy/raw/main/schemas/config.schema.json": [
|
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/src/types/godoxy/config.schema.json": [
|
||||||
"config.example.yml",
|
"config.example.yml",
|
||||||
"config.yml"
|
"config.yml"
|
||||||
],
|
],
|
||||||
"https://github.com/yusing/go-proxy/raw/main/schemas/routes.schema.json": [
|
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/src/types/godoxy/routes.schema.json": [
|
||||||
"providers.example.yml"
|
"providers.example.yml"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
32
Dockerfile
32
Dockerfile
|
@ -1,27 +1,33 @@
|
||||||
# Stage 1: deps
|
# Stage 1: deps
|
||||||
FROM golang:1.24.2-alpine AS deps
|
FROM golang:1.24.3-alpine AS deps
|
||||||
HEALTHCHECK NONE
|
HEALTHCHECK NONE
|
||||||
|
|
||||||
# package version does not matter
|
# package version does not matter
|
||||||
# trunk-ignore(hadolint/DL3018)
|
# trunk-ignore(hadolint/DL3018)
|
||||||
RUN apk add --no-cache tzdata make libcap-setcap
|
RUN apk add --no-cache tzdata make libcap-setcap
|
||||||
|
|
||||||
|
ENV GOPATH=/root/go
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
# remove godoxy stuff from go.mod first
|
||||||
|
RUN sed -i '/^module github\.com\/yusing\/go-proxy/!{/github\.com\/yusing\/go-proxy/d}' go.mod && \
|
||||||
|
go mod download -x
|
||||||
|
|
||||||
# Stage 2: builder
|
# Stage 2: builder
|
||||||
FROM deps AS builder
|
FROM deps AS builder
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
COPY Makefile ./
|
COPY Makefile ./
|
||||||
COPY cmd ./cmd
|
COPY cmd ./cmd
|
||||||
COPY internal ./internal
|
COPY internal ./internal
|
||||||
COPY pkg ./pkg
|
COPY pkg ./pkg
|
||||||
COPY agent ./agent
|
COPY agent ./agent
|
||||||
|
COPY socket-proxy ./socket-proxy
|
||||||
# Only copy go.mod and go.sum initially for better caching
|
|
||||||
COPY go.mod go.sum /src/
|
|
||||||
|
|
||||||
ENV GOPATH=/root/go
|
|
||||||
RUN go mod download -x
|
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ENV VERSION=${VERSION}
|
ENV VERSION=${VERSION}
|
||||||
|
@ -31,9 +37,10 @@ ENV MAKE_ARGS=${MAKE_ARGS}
|
||||||
|
|
||||||
ENV GOCACHE=/root/.cache/go-build
|
ENV GOCACHE=/root/.cache/go-build
|
||||||
ENV GOPATH=/root/go
|
ENV GOPATH=/root/go
|
||||||
RUN make ${MAKE_ARGS} build link-binary && \
|
|
||||||
mv bin /app/ && \
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||||
mkdir -p /app/error_pages /app/certs
|
--mount=type=cache,target=/root/go/pkg/mod \
|
||||||
|
make ${MAKE_ARGS} docker=1 build
|
||||||
|
|
||||||
# Stage 3: Final image
|
# Stage 3: Final image
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
@ -45,10 +52,7 @@ LABEL proxy.exclude=1
|
||||||
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
|
||||||
# copy binary
|
# copy binary
|
||||||
COPY --from=builder /app /app
|
COPY --from=builder /app/run /app/run
|
||||||
|
|
||||||
# copy example config
|
|
||||||
COPY config.example.yml /app/config/config.yml
|
|
||||||
|
|
||||||
# copy certs
|
# copy certs
|
||||||
COPY --from=builder /etc/ssl/certs /etc/ssl/certs
|
COPY --from=builder /etc/ssl/certs /etc/ssl/certs
|
||||||
|
|
26
LICENSE
26
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 [fullname]
|
Copyright (c) 2024 - present Yusing
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -19,3 +19,27 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
internal/net/gphttp/reverseproxy/reverse_proxy_mod.go is copied from et/http/httputil/reverseproxy.go with modifications to adapt to this project.
|
||||||
|
|
||||||
|
Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
internal/utils/io.go has a modified version of io.Copy with context and HTTP flusher handling.
|
||||||
|
|
||||||
|
Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
internal/utils/strutils/split_join.go is copied from strings.Split and strings.Join with modifications to adapt to this project.
|
||||||
|
|
||||||
|
Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
|
63
Makefile
63
Makefile
|
@ -1,3 +1,4 @@
|
||||||
|
shell := /bin/sh
|
||||||
export VERSION ?= $(shell git describe --tags --abbrev=0)
|
export VERSION ?= $(shell git describe --tags --abbrev=0)
|
||||||
export BUILD_DATE ?= $(shell date -u +'%Y%m%d-%H%M')
|
export BUILD_DATE ?= $(shell date -u +'%Y%m%d-%H%M')
|
||||||
export GOOS = linux
|
export GOOS = linux
|
||||||
|
@ -7,11 +8,12 @@ LDFLAGS = -X github.com/yusing/go-proxy/pkg.version=${VERSION}
|
||||||
|
|
||||||
ifeq ($(agent), 1)
|
ifeq ($(agent), 1)
|
||||||
NAME = godoxy-agent
|
NAME = godoxy-agent
|
||||||
CMD_PATH = ./cmd
|
|
||||||
PWD = ${shell pwd}/agent
|
PWD = ${shell pwd}/agent
|
||||||
|
else ifeq ($(socket-proxy), 1)
|
||||||
|
NAME = godoxy-socket-proxy
|
||||||
|
PWD = ${shell pwd}/socket-proxy
|
||||||
else
|
else
|
||||||
NAME = godoxy
|
NAME = godoxy
|
||||||
CMD_PATH = ./cmd
|
|
||||||
PWD = ${shell pwd}
|
PWD = ${shell pwd}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -45,7 +47,6 @@ BUILD_FLAGS += -ldflags='$(LDFLAGS)'
|
||||||
BIN_PATH := $(shell pwd)/bin/${NAME}
|
BIN_PATH := $(shell pwd)/bin/${NAME}
|
||||||
|
|
||||||
export NAME
|
export NAME
|
||||||
export CMD_PATH
|
|
||||||
export CGO_ENABLED
|
export CGO_ENABLED
|
||||||
export GODOXY_DEBUG
|
export GODOXY_DEBUG
|
||||||
export GODOXY_TRACE
|
export GODOXY_TRACE
|
||||||
|
@ -59,30 +60,63 @@ else
|
||||||
SETCAP_CMD = sudo setcap
|
SETCAP_CMD = sudo setcap
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# CAP_NET_BIND_SERVICE: permission for binding to :80 and :443
|
||||||
|
POST_BUILD = $(SETCAP_CMD) CAP_NET_BIND_SERVICE=+ep ${BIN_PATH};
|
||||||
|
ifeq ($(docker), 1)
|
||||||
|
POST_BUILD += mkdir -p /app && mv ${BIN_PATH} /app/run;
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: debug
|
.PHONY: debug
|
||||||
|
|
||||||
test:
|
test:
|
||||||
GODOXY_TEST=1 go test ./internal/...
|
GODOXY_TEST=1 go test ./internal/...
|
||||||
|
|
||||||
get:
|
docker-build-test:
|
||||||
for dir in ${PWD} ${PWD}/agent; do cd $$dir && go get -u ./... && go mod tidy; done
|
docker build -t godoxy .
|
||||||
|
docker build --build-arg=MAKE_ARGS=agent=1 -t godoxy-agent .
|
||||||
|
|
||||||
|
go_ver := $(shell go version | cut -d' ' -f3 | cut -d'o' -f2)
|
||||||
|
files := $(shell find . -name go.mod -type f -or -name Dockerfile -type f)
|
||||||
|
gomod_paths := $(shell find . -name go.mod -type f | xargs dirname)
|
||||||
|
|
||||||
|
update-go:
|
||||||
|
for file in ${files}; do \
|
||||||
|
echo "updating $$file"; \
|
||||||
|
sed -i 's|go \([0-9]\+\.[0-9]\+\.[0-9]\+\)|go ${go_ver}|g' $$file; \
|
||||||
|
sed -i 's|FROM golang:.*-alpine|FROM golang:${go_ver}-alpine|g' $$file; \
|
||||||
|
done
|
||||||
|
for path in ${gomod_paths}; do \
|
||||||
|
echo "go mod tidy $$path"; \
|
||||||
|
cd ${PWD}/$$path && go mod tidy; \
|
||||||
|
done
|
||||||
|
|
||||||
|
update-deps:
|
||||||
|
for path in ${gomod_paths}; do \
|
||||||
|
echo "go get -u $$path"; \
|
||||||
|
cd ${PWD}/$$path && go get -u ./... && go mod tidy; \
|
||||||
|
done
|
||||||
|
|
||||||
|
mod-tidy:
|
||||||
|
for path in ${gomod_paths}; do \
|
||||||
|
echo "go mod tidy $$path"; \
|
||||||
|
cd ${PWD}/$$path && go mod tidy; \
|
||||||
|
done
|
||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p bin
|
mkdir -p $(shell dirname ${BIN_PATH})
|
||||||
cd ${PWD} && go build ${BUILD_FLAGS} -o ${BIN_PATH} ${CMD_PATH}
|
cd ${PWD} && go build ${BUILD_FLAGS} -o ${BIN_PATH} ./cmd
|
||||||
|
${POST_BUILD}
|
||||||
# CAP_NET_BIND_SERVICE: permission for binding to :80 and :443
|
|
||||||
$(SETCAP_CMD) CAP_NET_BIND_SERVICE=+ep ${BIN_PATH}
|
|
||||||
|
|
||||||
run:
|
run:
|
||||||
[ -f .env ] && godotenv -f .env go run ${BUILD_FLAGS} ${CMD_PATH}
|
cd ${PWD} && [ -f .env ] && godotenv -f .env go run ${BUILD_FLAGS} ./cmd
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
make NAME="godoxy-test" debug=1 build
|
make NAME="godoxy-test" debug=1 build
|
||||||
sh -c 'HTTP_ADDR=:81 HTTPS_ADDR=:8443 API_ADDR=:8899 DEBUG=1 bin/godoxy-test'
|
sh -c 'HTTP_ADDR=:81 HTTPS_ADDR=:8443 API_ADDR=:8899 DEBUG=1 bin/godoxy-test'
|
||||||
|
|
||||||
mtrace:
|
mtrace:
|
||||||
bin/godoxy debug-ls-mtrace > mtrace.json
|
${BIN_PATH} debug-ls-mtrace > mtrace.json
|
||||||
|
|
||||||
rapid-crash:
|
rapid-crash:
|
||||||
docker run --restart=always --name test_crash -p 80 debian:bookworm-slim /bin/cat &&\
|
docker run --restart=always --name test_crash -p 80 debian:bookworm-slim /bin/cat &&\
|
||||||
|
@ -97,10 +131,7 @@ ci-test:
|
||||||
act -n --artifact-server-path /tmp/artifacts -s GITHUB_TOKEN="$$(gh auth token)"
|
act -n --artifact-server-path /tmp/artifacts -s GITHUB_TOKEN="$$(gh auth token)"
|
||||||
|
|
||||||
cloc:
|
cloc:
|
||||||
cloc --not-match-f '_test.go$$' cmd internal pkg
|
cloc --include-lang=Go --not-match-f '_test.go$$' .
|
||||||
|
|
||||||
link-binary:
|
|
||||||
ln -s /app/${NAME} bin/run
|
|
||||||
|
|
||||||
push-github:
|
push-github:
|
||||||
git push origin $(shell git rev-parse --abbrev-ref HEAD)
|
git push origin $(shell git rev-parse --abbrev-ref HEAD)
|
30
README.md
30
README.md
|
@ -2,17 +2,19 @@
|
||||||
|
|
||||||
# GoDoxy
|
# GoDoxy
|
||||||
|
|
||||||
[](https://sonarcloud.io/summary/new_code?id=yusing_godoxy)
|
[](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy)
|
||||||

|

|
||||||
[](https://sonarcloud.io/summary/new_code?id=yusing_godoxy)
|
[](https://sonarcloud.io/summary/new_code?id=go-proxy)
|
||||||

|

|
||||||
[](https://discord.gg/umReR62nRd)
|
[](https://discord.gg/umReR62nRd)
|
||||||
|
|
||||||
A lightweight, simple, and [performant](https://github.com/yusing/godoxy/wiki/Benchmarks) reverse proxy with WebUI.
|
A lightweight, simple, and performant reverse proxy with WebUI.
|
||||||
|
|
||||||
For full documentation, check out **[Wiki](https://github.com/yusing/godoxy/wiki)**
|
<h5>
|
||||||
|
<a href="https://docs.godoxy.dev">Website</a> | <a href="https://docs.godoxy.dev/Home.html">Wiki</a> | <a href="https://discord.gg/umReR62nRd">Discord</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
**EN** | <a href="README_CHT.md">中文</a>
|
<h5>EN | <a href="README_CHT.md">中文</a></h5>
|
||||||
|
|
||||||
<img src="screenshots/webui.jpg" style="max-width: 650">
|
<img src="screenshots/webui.jpg" style="max-width: 650">
|
||||||
|
|
||||||
|
@ -45,8 +47,8 @@ For full documentation, check out **[Wiki](https://github.com/yusing/godoxy/wiki
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
- **Simple**
|
- **Simple**
|
||||||
- Effortless configuration with [simple labels](https://github.com/yusing/godoxy/wiki/Docker-labels-and-Route-Files) or WebUI
|
- Effortless configuration with [simple labels](https://docs.godoxy.dev/Docker-labels-and-Route-Files) or WebUI
|
||||||
- [Simple multi-node setup](https://github.com/yusing/godoxy/wiki/Configurations#multi-docker-nodes-setup)
|
- [Simple multi-node setup](https://docs.godoxy.dev/Configurations#multi-docker-nodes-setup)
|
||||||
- Detailed error messages for easy troubleshooting.
|
- Detailed error messages for easy troubleshooting.
|
||||||
- **ACL**: connection / request level access control
|
- **ACL**: connection / request level access control
|
||||||
- IP/CIDR
|
- IP/CIDR
|
||||||
|
@ -54,7 +56,7 @@ For full documentation, check out **[Wiki](https://github.com/yusing/godoxy/wiki
|
||||||
- Timezone **(Maxmind account required)**
|
- Timezone **(Maxmind account required)**
|
||||||
- **Access logging**
|
- **Access logging**
|
||||||
- **Advanced Automation**
|
- **Advanced Automation**
|
||||||
- Automatic SSL certificate management with Let's Encrypt ([using DNS-01 Challenge](https://github.com/yusing/go-proxy/wiki/Supported-DNS%E2%80%9001-Providers))
|
- Automatic SSL certificate management with Let's Encrypt ([using DNS-01 Challenge](https://docs.godoxy.dev/DNS-01-Providers))
|
||||||
- Auto-configuration for Docker containers
|
- Auto-configuration for Docker containers
|
||||||
- Hot-reloading of configurations and container state changes
|
- Hot-reloading of configurations and container state changes
|
||||||
- **Idle-sleep**: stop and wake containers based on traffic _(see [screenshots](#idlesleeper))_
|
- **Idle-sleep**: stop and wake containers based on traffic _(see [screenshots](#idlesleeper))_
|
||||||
|
@ -65,8 +67,8 @@ For full documentation, check out **[Wiki](https://github.com/yusing/godoxy/wiki
|
||||||
- TCP/UDP port forwarding
|
- TCP/UDP port forwarding
|
||||||
- **OpenID Connect support**: SSO and secure your apps easily
|
- **OpenID Connect support**: SSO and secure your apps easily
|
||||||
- **Customization**
|
- **Customization**
|
||||||
- [HTTP middlewares](https://github.com/yusing/go-proxy/wiki/Middlewares)
|
- [HTTP middlewares](https://docs.godoxy.dev/Middlewares)
|
||||||
- [Custom error pages support](https://github.com/yusing/go-proxy/wiki/Middlewares#custom-error-pages)
|
- [Custom error pages support](https://docs.godoxy.dev/Custom-Error-Pages)
|
||||||
- **Web UI**
|
- **Web UI**
|
||||||
- App Dashboard
|
- App Dashboard
|
||||||
- Config Editor
|
- Config Editor
|
||||||
|
@ -99,7 +101,13 @@ Configure Wildcard DNS Record(s) to point to machine running `GoDoxy`, e.g.
|
||||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/yusing/godoxy/main/scripts/setup.sh)"
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/yusing/godoxy/main/scripts/setup.sh)"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. You may now do some extra configuration on WebUI `https://godoxy.yourdomain.com`
|
3. Start the docker compose service from generated `compose.yml`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
4. You may now do some extra configuration on WebUI `https://godoxy.yourdomain.com`
|
||||||
|
|
||||||
## How does GoDoxy work
|
## How does GoDoxy work
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||

|

|
||||||
[](https://discord.gg/umReR62nRd)
|
[](https://discord.gg/umReR62nRd)
|
||||||
|
|
||||||
輕量、易用、 [高效能](https://github.com/yusing/godoxy/wiki/Benchmarks),且帶有主頁和配置面板的反向代理
|
輕量、易用、 高效能,且帶有主頁和配置面板的反向代理
|
||||||
|
|
||||||
完整文檔請查閱 **[Wiki](https://github.com/yusing/godoxy/wiki)**(暫未有中文翻譯)
|
<h5>
|
||||||
|
<a href="https://docs.godoxy.dev">網站</a> | <a href="https://docs.godoxy.dev/Home.html">文檔</a> | <a href="https://discord.gg/umReR62nRd">Discord</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
<a href="README.md">EN</a> | **中文**
|
<h5><a href="README.md">EN</a> | 中文</h5>
|
||||||
|
|
||||||
<img src="https://github.com/user-attachments/assets/4bb371f4-6e4c-425c-89b2-b9e962bdd46f" style="max-width: 650">
|
<img src="https://github.com/user-attachments/assets/4bb371f4-6e4c-425c-89b2-b9e962bdd46f" style="max-width: 650">
|
||||||
|
|
||||||
|
@ -43,20 +45,38 @@
|
||||||
|
|
||||||
## 主要特點
|
## 主要特點
|
||||||
|
|
||||||
- 容易使用
|
- **簡單易用**
|
||||||
- 輕鬆配置
|
- 透過 Docker[標籤](https://docs.godoxy.dev/Docker-labels-and-Route-Files)或 WebUI 輕鬆設定
|
||||||
- 簡單的多節點設置
|
- [簡單的多節點設置](https://docs.godoxy.dev/Configurations#multi-docker-nodes-setup)
|
||||||
- 錯誤訊息清晰詳細,易於排除故障
|
- 詳細的錯誤訊息,便於故障排除
|
||||||
- 自動 SSL 憑證管理(參見 [支援的 DNS-01 驗證提供商](https://github.com/yusing/godoxy/wiki/Supported-DNS%E2%80%9001-Providers))
|
- **存取控制 (ACL)**:連線/請求層級存取控制
|
||||||
- 自動配置 Docker 容器
|
- IP/CIDR
|
||||||
- 容器狀態/配置文件變更時自動熱重載
|
- 國家 **(需要 Maxmind 帳戶)**
|
||||||
- **閒置休眠**:在閒置時停止容器,有流量時喚醒(_可選,參見[截圖](#閒置休眠)_)
|
- 時區 **(需要 Maxmind 帳戶)**
|
||||||
- OpenID Connect:輕鬆實現單點登入
|
- **存取日誌記錄**
|
||||||
- HTTP(s) 反向代理和 TCP 和 UDP 埠轉發
|
- **自動化**
|
||||||
- [HTTP 中介軟體](https://github.com/yusing/godoxy/wiki/Middlewares) 和 [自定義錯誤頁面](https://github.com/yusing/godoxy/wiki/Middlewares#custom-error-pages)
|
- 使用 Let's Encrypt 自動管理 SSL 憑證 ([使用 DNS-01 驗證](https://docs.godoxy.dev/DNS-01-Providers))
|
||||||
- **網頁介面,具有應用儀表板和配置編輯器**
|
- Docker 容器自動配置
|
||||||
- 支援 linux/amd64、linux/arm64
|
- 設定檔與容器狀態變更時自動熱重載
|
||||||
- 使用 **[Go](https://go.dev)** 編寫
|
- **閒置休眠**:根據流量停止和喚醒容器 _(參見[截圖](#閒置休眠))_
|
||||||
|
- Docker 容器
|
||||||
|
- Proxmox LXC 容器
|
||||||
|
- **流量管理**
|
||||||
|
- HTTP 反向代理
|
||||||
|
- TCP/UDP 連接埠轉送
|
||||||
|
- **OpenID Connect 支援**:輕鬆實現單點登入 (SSO) 並保護您的應用程式
|
||||||
|
- **客製化**
|
||||||
|
- [HTTP 中介軟體](https://docs.godoxy.dev/Middlewares)
|
||||||
|
- [支援自訂錯誤頁面](https://docs.godoxy.dev/Custom-Error-Pages)
|
||||||
|
- **網頁使用者介面 (Web UI)**
|
||||||
|
- 應用程式一覽
|
||||||
|
- 設定編輯器
|
||||||
|
- 執行時間與系統指標
|
||||||
|
- Docker 日誌檢視器
|
||||||
|
- **跨平台支援**
|
||||||
|
- 支援 **linux/amd64** 與 **linux/arm64**
|
||||||
|
- **高效能**
|
||||||
|
- 以 **[Go](https://go.dev)** 語言編寫
|
||||||
|
|
||||||
[🔼 回到頂部](#目錄)
|
[🔼 回到頂部](#目錄)
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/env"
|
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/server"
|
"github.com/yusing/go-proxy/agent/pkg/server"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
|
||||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||||
|
httpServer "github.com/yusing/go-proxy/internal/net/gphttp/server"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/pkg"
|
"github.com/yusing/go-proxy/pkg"
|
||||||
|
socketproxy "github.com/yusing/go-proxy/socketproxy/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logging.InitLogger(os.Stderr, memlogger.GetMemLogger())
|
|
||||||
|
|
||||||
ca := &agent.PEMPair{}
|
ca := &agent.PEMPair{}
|
||||||
err := ca.Load(env.AgentCACert)
|
err := ca.Load(env.AgentCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -55,6 +52,17 @@ Tips:
|
||||||
}
|
}
|
||||||
|
|
||||||
server.StartAgentServer(t, opts)
|
server.StartAgentServer(t, opts)
|
||||||
|
|
||||||
|
if socketproxy.ListenAddr != "" {
|
||||||
|
logging.Info().Msgf("Docker socket listening on: %s", socketproxy.ListenAddr)
|
||||||
|
opts := httpServer.Options{
|
||||||
|
Name: "docker",
|
||||||
|
HTTPAddr: socketproxy.ListenAddr,
|
||||||
|
Handler: socketproxy.NewHandler(),
|
||||||
|
}
|
||||||
|
httpServer.StartServer(t, opts)
|
||||||
|
}
|
||||||
|
|
||||||
systeminfo.Poller.Start()
|
systeminfo.Poller.Start()
|
||||||
|
|
||||||
task.WaitExit(3)
|
task.WaitExit(3)
|
||||||
|
|
60
agent/go.mod
60
agent/go.mod
|
@ -1,33 +1,38 @@
|
||||||
module github.com/yusing/go-proxy/agent
|
module github.com/yusing/go-proxy/agent
|
||||||
|
|
||||||
go 1.24.2
|
go 1.24.3
|
||||||
|
|
||||||
replace github.com/yusing/go-proxy => ..
|
replace github.com/yusing/go-proxy => ..
|
||||||
|
|
||||||
|
replace github.com/yusing/go-proxy/socketproxy => ../socket-proxy
|
||||||
|
|
||||||
|
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1
|
||||||
|
|
||||||
|
replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250502022742-408a348f1b97
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coder/websocket v1.8.13
|
github.com/coder/websocket v1.8.13
|
||||||
github.com/docker/docker v28.1.1+incompatible
|
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/yusing/go-proxy v0.11.1
|
github.com/yusing/go-proxy v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/yusing/go-proxy/socketproxy v0.0.0-00010101000000-000000000000
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250418000134-7af8fd7b079e
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/bytedance/sonic v1.13.2 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/diskfs/go-diskfs v1.6.0 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
github.com/docker/cli v28.1.1+incompatible // indirect
|
github.com/docker/cli v28.1.1+incompatible // indirect
|
||||||
|
github.com/docker/docker v28.1.1+incompatible // indirect
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/ebitengine/purego v0.8.2 // indirect
|
github.com/ebitengine/purego v0.8.3 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
github.com/go-acme/lego/v4 v4.23.1 // indirect
|
github.com/go-acme/lego/v4 v4.23.1 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
|
||||||
|
@ -36,18 +41,21 @@ require (
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
|
||||||
github.com/goccy/go-yaml v1.17.1 // indirect
|
github.com/goccy/go-yaml v1.17.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect
|
||||||
github.com/gotify/server/v2 v2.6.1 // indirect
|
github.com/gorilla/mux v1.8.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/gotify/server/v2 v2.6.3 // indirect
|
||||||
|
github.com/jinzhu/copier v0.4.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||||
|
github.com/luthermonson/go-proxmox v0.2.2 // indirect
|
||||||
|
github.com/magefile/mage v1.15.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/miekg/dns v1.1.65 // indirect
|
github.com/miekg/dns v1.1.66 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
@ -56,33 +64,29 @@ require (
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
github.com/puzpuzpuz/xsync/v4 v4.1.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.51.0 // indirect
|
github.com/quic-go/quic-go v0.51.0 // indirect
|
||||||
github.com/samber/lo v1.49.1 // indirect
|
github.com/samber/lo v1.50.0 // indirect
|
||||||
github.com/samber/slog-common v0.18.1 // indirect
|
github.com/samber/slog-common v0.18.1 // indirect
|
||||||
github.com/samber/slog-zerolog/v2 v2.7.3 // indirect
|
github.com/samber/slog-zerolog/v2 v2.7.3 // indirect
|
||||||
github.com/shirou/gopsutil/v4 v4.25.3 // indirect
|
github.com/shirou/gopsutil/v4 v4.25.4 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||||
github.com/spf13/afero v1.14.0 // indirect
|
github.com/spf13/afero v1.14.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
|
||||||
github.com/vincent-petithory/dataurl v1.0.0 // indirect
|
github.com/vincent-petithory/dataurl v1.0.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
go.uber.org/mock v0.5.1 // indirect
|
go.uber.org/mock v0.5.2 // indirect
|
||||||
golang.org/x/arch v0.16.0 // indirect
|
golang.org/x/crypto v0.38.0 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.40.0 // indirect
|
||||||
golang.org/x/sync v0.13.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/time v0.11.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
golang.org/x/tools v0.32.0 // indirect
|
golang.org/x/tools v0.33.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
140
agent/go.sum
140
agent/go.sum
|
@ -8,22 +8,12 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk
|
||||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
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 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
|
||||||
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
||||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
|
||||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
|
||||||
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
|
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
|
||||||
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
|
||||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
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.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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -41,10 +31,10 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
|
||||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||||
github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4=
|
github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4=
|
||||||
|
@ -68,38 +58,42 @@ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc
|
||||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
||||||
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godoxy-app/docker v0.0.0-20250418000134-7af8fd7b079e h1:LEbMtJ6loEubxetD+Aw8+1x0rShor5iMoy9WuFQ8hN8=
|
github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1 h1:fsSqE28vU0PRkq9FdekirRoDBeYJ+UaJ9dTErdXflWg=
|
||||||
github.com/godoxy-app/docker v0.0.0-20250418000134-7af8fd7b079e/go.mod h1:3tMTnTkH7IN5smn7PX83XdmRnNj4Nw2/Pt8GgReqnKM=
|
github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1/go.mod h1:av6ggKWQz6SEkFyShjDEgVqiIB0RHvEQNIkPeqgJEeE=
|
||||||
|
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250502022742-408a348f1b97 h1:i52gBYamrKs4DHT1+SiobW2im5UgTMVXK1KIL1djSeA=
|
||||||
|
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250502022742-408a348f1b97/go.mod h1:XvbfPmmrdpLrsKwj3irYkxt5ygyMcDsTQTJ7cnZ9RNQ=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
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/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gotify/server/v2 v2.6.1 h1:Kf7v5fzBxzELzZa/jonWfwJMkqYqh1LBzBpCmt5QIAI=
|
github.com/gotify/server/v2 v2.6.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I=
|
||||||
github.com/gotify/server/v2 v2.6.1/go.mod h1:Dk8HLyTVDqmXM8YEg6tjROBen6mxyHZFRggJFHTwZLc=
|
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||||
|
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||||
|
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||||
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||||
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
@ -119,8 +113,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||||
|
@ -143,8 +137,12 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
|
||||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||||
|
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.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 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
@ -152,8 +150,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
github.com/puzpuzpuz/xsync/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
||||||
|
@ -161,33 +159,26 @@ github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||||
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
|
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
|
||||||
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
||||||
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
|
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
|
||||||
github.com/samber/slog-zerolog/v2 v2.7.3/go.mod h1:oWU7WHof4Xp8VguiNO02r1a4VzkgoOyOZhY5CuRke60=
|
github.com/samber/slog-zerolog/v2 v2.7.3/go.mod h1:oWU7WHof4Xp8VguiNO02r1a4VzkgoOyOZhY5CuRke60=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
|
||||||
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
|
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
@ -201,24 +192,22 @@ go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
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/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||||
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
|
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||||
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||||
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
|
||||||
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
@ -227,8 +216,8 @@ 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.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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
@ -250,10 +239,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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
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.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
|
||||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -263,16 +250,18 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -284,8 +273,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
@ -304,8 +293,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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.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.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -316,19 +305,19 @@ 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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
|
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@ -339,4 +328,3 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ var (
|
||||||
AGENT_PORT="{{.Port}}" \
|
AGENT_PORT="{{.Port}}" \
|
||||||
AGENT_CA_CERT="{{.CACert}}" \
|
AGENT_CA_CERT="{{.CACert}}" \
|
||||||
AGENT_SSL_CERT="{{.SSLCert}}" \
|
AGENT_SSL_CERT="{{.SSLCert}}" \
|
||||||
bash -c "$(curl -fsSL https://raw.githubusercontent.com/yusing/go-proxy/main/scripts/install-agent.sh)"`
|
bash -c "$(curl -fsSL https://raw.githubusercontent.com/yusing/godoxy/main/scripts/install-agent.sh)"`
|
||||||
installScriptTemplate = template.Must(template.New("install.sh").Parse(installScript))
|
installScriptTemplate = template.Must(template.New("install.sh").Parse(installScript))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -14,10 +16,7 @@ import (
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
|
||||||
"github.com/yusing/go-proxy/pkg"
|
"github.com/yusing/go-proxy/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,6 +26,7 @@ type AgentConfig struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
name string
|
name string
|
||||||
|
version string
|
||||||
l zerolog.Logger
|
l zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,9 @@ func (cfg *AgentConfig) Parse(addr string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte) error {
|
var serverVersion = pkg.GetVersion()
|
||||||
|
|
||||||
|
func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte) error {
|
||||||
clientCert, err := tls.X509KeyPair(crt, key)
|
clientCert, err := tls.X509KeyPair(crt, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -90,7 +92,7 @@ func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte)
|
||||||
caCertPool := x509.NewCertPool()
|
caCertPool := x509.NewCertPool()
|
||||||
ok := caCertPool.AppendCertsFromPEM(ca)
|
ok := caCertPool.AppendCertsFromPEM(ca)
|
||||||
if !ok {
|
if !ok {
|
||||||
return gperr.New("invalid ca certificate")
|
return errors.New("invalid ca certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.tlsConfig = &tls.Config{
|
cfg.tlsConfig = &tls.Config{
|
||||||
|
@ -102,7 +104,7 @@ func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte)
|
||||||
// create transport and http client
|
// create transport and http client
|
||||||
cfg.httpClient = cfg.NewHTTPClient()
|
cfg.httpClient = cfg.NewHTTPClient()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(parent.Context(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// get agent name
|
// get agent name
|
||||||
|
@ -121,33 +123,34 @@ func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
agentVersion := string(agentVersionBytes)
|
cfg.version = string(agentVersionBytes)
|
||||||
|
agentVersion := pkg.ParseVersion(cfg.version)
|
||||||
|
|
||||||
if pkg.GetVersion().IsNewerMajorThan(pkg.ParseVersion(agentVersion)) {
|
if serverVersion.IsNewerMajorThan(agentVersion) {
|
||||||
logging.Warn().Msgf("agent %s major version mismatch: server: %s, agent: %s", cfg.name, pkg.GetVersion(), agentVersion)
|
logging.Warn().Msgf("agent %s major version mismatch: server: %s, agent: %s", cfg.name, serverVersion, agentVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.Info().Msgf("agent %q initialized", cfg.name)
|
logging.Info().Msgf("agent %q initialized", cfg.name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *AgentConfig) Start(parent task.Parent) gperr.Error {
|
func (cfg *AgentConfig) Start(ctx context.Context) error {
|
||||||
filepath, ok := certs.AgentCertsFilepath(cfg.Addr)
|
filepath, ok := certs.AgentCertsFilepath(cfg.Addr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return gperr.New("invalid agent host").Subject(cfg.Addr)
|
return fmt.Errorf("invalid agent host: %s", cfg.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
certData, err := os.ReadFile(filepath)
|
certData, err := os.ReadFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gperr.Wrap(err, "failed to read agent certs")
|
return fmt.Errorf("failed to read agent certs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ca, crt, key, err := certs.ExtractCert(certData)
|
ca, crt, key, err := certs.ExtractCert(certData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gperr.Wrap(err, "failed to extract agent certs")
|
return fmt.Errorf("failed to extract agent certs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gperr.Wrap(cfg.StartWithCerts(parent, ca, crt, key))
|
return cfg.StartWithCerts(ctx, ca, crt, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *AgentConfig) NewHTTPClient() *http.Client {
|
func (cfg *AgentConfig) NewHTTPClient() *http.Client {
|
||||||
|
@ -171,8 +174,10 @@ func (cfg *AgentConfig) Transport() *http.Transport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dialer = &net.Dialer{Timeout: 5 * time.Second}
|
||||||
|
|
||||||
func (cfg *AgentConfig) DialContext(ctx context.Context) (net.Conn, error) {
|
func (cfg *AgentConfig) DialContext(ctx context.Context) (net.Conn, error) {
|
||||||
return gphttp.DefaultDialer.DialContext(ctx, "tcp", cfg.Addr)
|
return dialer.DialContext(ctx, "tcp", cfg.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *AgentConfig) Name() string {
|
func (cfg *AgentConfig) Name() string {
|
||||||
|
@ -187,5 +192,6 @@ func (cfg *AgentConfig) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(map[string]string{
|
return json.Marshal(map[string]string{
|
||||||
"name": cfg.Name(),
|
"name": cfg.Name(),
|
||||||
"addr": cfg.Addr,
|
"addr": cfg.Addr,
|
||||||
|
"version": cfg.version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
@ -12,20 +11,37 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CertsDNSName = "godoxy.agent"
|
CertsDNSName = "godoxy.agent"
|
||||||
KeySize = 2048
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func toPEMPair(certDER []byte, key *rsa.PrivateKey) *PEMPair {
|
func toPEMPair(certDER []byte, key *ecdsa.PrivateKey) *PEMPair {
|
||||||
|
marshaledKey, err := marshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
// This is a critical internal error during PEM encoding of a newly generated key.
|
||||||
|
// Panicking is acceptable here as it indicates a fundamental issue.
|
||||||
|
panic(fmt.Sprintf("failed to marshal EC private key for PEM encoding: %v", err))
|
||||||
|
}
|
||||||
return &PEMPair{
|
return &PEMPair{
|
||||||
Cert: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}),
|
Cert: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}),
|
||||||
Key: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}),
|
Key: pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: marshaledKey}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func marshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||||
|
derBytes, err := x509.MarshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal EC private key: %w", err)
|
||||||
|
}
|
||||||
|
return derBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
func b64Encode(data []byte) string {
|
func b64Encode(data []byte) string {
|
||||||
return base64.StdEncoding.EncodeToString(data)
|
return base64.StdEncoding.EncodeToString(data)
|
||||||
}
|
}
|
||||||
|
@ -63,10 +79,23 @@ func (p *PEMPair) ToTLSCert() (*tls.Certificate, error) {
|
||||||
return &cert, err
|
return &cert, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newSerialNumber() (*big.Int, error) {
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) // 128-bit random number
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate serial number: %w", err)
|
||||||
|
}
|
||||||
|
return serialNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewAgent() (ca, srv, client *PEMPair, err error) {
|
func NewAgent() (ca, srv, client *PEMPair, err error) {
|
||||||
|
caSerialNumber, err := newSerialNumber()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
// Create the CA's certificate
|
// Create the CA's certificate
|
||||||
caTemplate := &x509.Certificate{
|
caTemplate := &x509.Certificate{
|
||||||
SerialNumber: big.NewInt(1),
|
SerialNumber: caSerialNumber,
|
||||||
Subject: pkix.Name{
|
Subject: pkix.Name{
|
||||||
Organization: []string{"GoDoxy"},
|
Organization: []string{"GoDoxy"},
|
||||||
CommonName: CertsDNSName,
|
CommonName: CertsDNSName,
|
||||||
|
@ -76,9 +105,12 @@ func NewAgent() (ca, srv, client *PEMPair, err error) {
|
||||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
IsCA: true,
|
IsCA: true,
|
||||||
|
MaxPathLen: 0,
|
||||||
|
MaxPathLenZero: true,
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||||
}
|
}
|
||||||
|
|
||||||
caKey, err := rsa.GenerateKey(rand.Reader, KeySize)
|
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -91,20 +123,29 @@ func NewAgent() (ca, srv, client *PEMPair, err error) {
|
||||||
ca = toPEMPair(caDER, caKey)
|
ca = toPEMPair(caDER, caKey)
|
||||||
|
|
||||||
// Generate a new private key for the server certificate
|
// Generate a new private key for the server certificate
|
||||||
serverKey, err := rsa.GenerateKey(rand.Reader, KeySize)
|
serverKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serverSerialNumber, err := newSerialNumber()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
srvTemplate := &x509.Certificate{
|
srvTemplate := &x509.Certificate{
|
||||||
SerialNumber: big.NewInt(2),
|
SerialNumber: serverSerialNumber,
|
||||||
Issuer: caTemplate.Subject,
|
Issuer: caTemplate.Subject,
|
||||||
Subject: caTemplate.Subject,
|
Subject: pkix.Name{
|
||||||
|
Organization: caTemplate.Subject.Organization,
|
||||||
|
OrganizationalUnit: []string{"Server"},
|
||||||
|
CommonName: CertsDNSName,
|
||||||
|
},
|
||||||
DNSNames: []string{CertsDNSName},
|
DNSNames: []string{CertsDNSName},
|
||||||
NotBefore: time.Now(),
|
NotBefore: time.Now(),
|
||||||
NotAfter: time.Now().AddDate(1000, 0, 0), // Add validity period
|
NotAfter: time.Now().AddDate(1000, 0, 0), // Add validity period
|
||||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||||
}
|
}
|
||||||
|
|
||||||
srvCertDER, err := x509.CreateCertificate(rand.Reader, srvTemplate, caTemplate, &serverKey.PublicKey, caKey)
|
srvCertDER, err := x509.CreateCertificate(rand.Reader, srvTemplate, caTemplate, &serverKey.PublicKey, caKey)
|
||||||
|
@ -114,20 +155,29 @@ func NewAgent() (ca, srv, client *PEMPair, err error) {
|
||||||
|
|
||||||
srv = toPEMPair(srvCertDER, serverKey)
|
srv = toPEMPair(srvCertDER, serverKey)
|
||||||
|
|
||||||
clientKey, err := rsa.GenerateKey(rand.Reader, KeySize)
|
clientKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientSerialNumber, err := newSerialNumber()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
clientTemplate := &x509.Certificate{
|
clientTemplate := &x509.Certificate{
|
||||||
SerialNumber: big.NewInt(3),
|
SerialNumber: clientSerialNumber,
|
||||||
Issuer: caTemplate.Subject,
|
Issuer: caTemplate.Subject,
|
||||||
Subject: caTemplate.Subject,
|
Subject: pkix.Name{
|
||||||
|
Organization: caTemplate.Subject.Organization,
|
||||||
|
OrganizationalUnit: []string{"Client"},
|
||||||
|
CommonName: CertsDNSName,
|
||||||
|
},
|
||||||
DNSNames: []string{CertsDNSName},
|
DNSNames: []string{CertsDNSName},
|
||||||
NotBefore: time.Now(),
|
NotBefore: time.Now(),
|
||||||
NotAfter: time.Now().AddDate(1000, 0, 0),
|
NotAfter: time.Now().AddDate(1000, 0, 0),
|
||||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
||||||
}
|
}
|
||||||
clientCertDER, err := x509.CreateCertificate(rand.Reader, clientTemplate, caTemplate, &clientKey.PublicKey, caKey)
|
clientCertDER, err := x509.CreateCertificate(rand.Reader, clientTemplate, caTemplate, &clientKey.PublicKey, caKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,59 +8,59 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewAgent(t *testing.T) {
|
func TestNewAgent(t *testing.T) {
|
||||||
ca, srv, client, err := NewAgent()
|
ca, srv, client, err := NewAgent()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
ExpectTrue(t, ca != nil)
|
require.NotNil(t, ca)
|
||||||
ExpectTrue(t, srv != nil)
|
require.NotNil(t, srv)
|
||||||
ExpectTrue(t, client != nil)
|
require.NotNil(t, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPEMPair(t *testing.T) {
|
func TestPEMPair(t *testing.T) {
|
||||||
ca, srv, client, err := NewAgent()
|
ca, srv, client, err := NewAgent()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i, p := range []*PEMPair{ca, srv, client} {
|
for i, p := range []*PEMPair{ca, srv, client} {
|
||||||
t.Run(fmt.Sprintf("load-%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("load-%d", i), func(t *testing.T) {
|
||||||
var pp PEMPair
|
var pp PEMPair
|
||||||
err := pp.Load(p.String())
|
err := pp.Load(p.String())
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
ExpectEqual(t, p.Cert, pp.Cert)
|
require.Equal(t, p.Cert, pp.Cert)
|
||||||
ExpectEqual(t, p.Key, pp.Key)
|
require.Equal(t, p.Key, pp.Key)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPEMPairToTLSCert(t *testing.T) {
|
func TestPEMPairToTLSCert(t *testing.T) {
|
||||||
ca, srv, client, err := NewAgent()
|
ca, srv, client, err := NewAgent()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i, p := range []*PEMPair{ca, srv, client} {
|
for i, p := range []*PEMPair{ca, srv, client} {
|
||||||
t.Run(fmt.Sprintf("toTLSCert-%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("toTLSCert-%d", i), func(t *testing.T) {
|
||||||
cert, err := p.ToTLSCert()
|
cert, err := p.ToTLSCert()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
ExpectTrue(t, cert != nil)
|
require.NotNil(t, cert)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerClient(t *testing.T) {
|
func TestServerClient(t *testing.T) {
|
||||||
ca, srv, client, err := NewAgent()
|
ca, srv, client, err := NewAgent()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
srvTLS, err := srv.ToTLSCert()
|
srvTLS, err := srv.ToTLSCert()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
ExpectTrue(t, srvTLS != nil)
|
require.NotNil(t, srvTLS)
|
||||||
|
|
||||||
clientTLS, err := client.ToTLSCert()
|
clientTLS, err := client.ToTLSCert()
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
ExpectTrue(t, clientTLS != nil)
|
require.NotNil(t, clientTLS)
|
||||||
|
|
||||||
caPool := x509.NewCertPool()
|
caPool := x509.NewCertPool()
|
||||||
ExpectTrue(t, caPool.AppendCertsFromPEM(ca.Cert))
|
require.True(t, caPool.AppendCertsFromPEM(ca.Cert))
|
||||||
|
|
||||||
srvTLSConfig := &tls.Config{
|
srvTLSConfig := &tls.Config{
|
||||||
Certificates: []tls.Certificate{*srvTLS},
|
Certificates: []tls.Certificate{*srvTLS},
|
||||||
|
@ -86,6 +86,6 @@ func TestServerClient(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := httpClient.Get(server.URL)
|
resp, err := httpClient.Get(server.URL)
|
||||||
ExpectNoError(t, err)
|
require.NoError(t, err)
|
||||||
ExpectEqual(t, resp.StatusCode, http.StatusOK)
|
require.Equal(t, resp.StatusCode, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,36 @@ services:
|
||||||
AGENT_PORT: "{{.Port}}"
|
AGENT_PORT: "{{.Port}}"
|
||||||
AGENT_CA_CERT: "{{.CACert}}"
|
AGENT_CA_CERT: "{{.CACert}}"
|
||||||
AGENT_SSL_CERT: "{{.SSLCert}}"
|
AGENT_SSL_CERT: "{{.SSLCert}}"
|
||||||
|
# use agent as a docker socket proxy: [host]:port
|
||||||
|
# set LISTEN_ADDR to enable (e.g. 127.0.0.1:2375)
|
||||||
|
LISTEN_ADDR:
|
||||||
|
POST: false
|
||||||
|
ALLOW_RESTARTS: false
|
||||||
|
ALLOW_START: false
|
||||||
|
ALLOW_STOP: false
|
||||||
|
AUTH: false
|
||||||
|
BUILD: false
|
||||||
|
COMMIT: false
|
||||||
|
CONFIGS: false
|
||||||
|
CONTAINERS: false
|
||||||
|
DISTRIBUTION: false
|
||||||
|
EVENTS: true
|
||||||
|
EXEC: false
|
||||||
|
GRPC: false
|
||||||
|
IMAGES: false
|
||||||
|
INFO: false
|
||||||
|
NETWORKS: false
|
||||||
|
NODES: false
|
||||||
|
PING: true
|
||||||
|
PLUGINS: false
|
||||||
|
SECRETS: false
|
||||||
|
SERVICES: false
|
||||||
|
SESSION: false
|
||||||
|
SWARM: false
|
||||||
|
SYSTEM: false
|
||||||
|
TASKS: false
|
||||||
|
VERSION: true
|
||||||
|
VOLUMES: false
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
|
|
16
agent/pkg/env/env.go
vendored
16
agent/pkg/env/env.go
vendored
|
@ -15,10 +15,24 @@ func DefaultAgentName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
AgentName string
|
||||||
|
AgentPort int
|
||||||
|
AgentSkipClientCertCheck bool
|
||||||
|
AgentCACert string
|
||||||
|
AgentSSLCert string
|
||||||
|
DockerSocket string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load() {
|
||||||
|
DockerSocket = common.GetEnvString("DOCKER_SOCKET", "/var/run/docker.sock")
|
||||||
AgentName = common.GetEnvString("AGENT_NAME", DefaultAgentName())
|
AgentName = common.GetEnvString("AGENT_NAME", DefaultAgentName())
|
||||||
AgentPort = common.GetEnvInt("AGENT_PORT", 8890)
|
AgentPort = common.GetEnvInt("AGENT_PORT", 8890)
|
||||||
AgentSkipClientCertCheck = common.GetEnvBool("AGENT_SKIP_CLIENT_CERT_CHECK", false)
|
AgentSkipClientCertCheck = common.GetEnvBool("AGENT_SKIP_CLIENT_CERT_CHECK", false)
|
||||||
|
|
||||||
AgentCACert = common.GetEnvString("AGENT_CA_CERT", "")
|
AgentCACert = common.GetEnvString("AGENT_CA_CERT", "")
|
||||||
AgentSSLCert = common.GetEnvString("AGENT_SSL_CERT", "")
|
AgentSSLCert = common.GetEnvString("AGENT_SSL_CERT", "")
|
||||||
)
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||||
)
|
)
|
||||||
|
@ -18,7 +18,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
query := r.URL.Query()
|
query := r.URL.Query()
|
||||||
scheme := query.Get("scheme")
|
scheme := query.Get("scheme")
|
||||||
if scheme == "" {
|
if scheme == "" {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, "missing scheme", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
case "fileserver":
|
case "fileserver":
|
||||||
path := query.Get("path")
|
path := query.Get("path")
|
||||||
if path == "" {
|
if path == "" {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, "missing path", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
|
@ -40,7 +40,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
host := query.Get("host")
|
host := query.Get("host")
|
||||||
path := query.Get("path")
|
path := query.Get("path")
|
||||||
if host == "" {
|
if host == "" {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, "missing host", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result, err = monitor.NewHTTPHealthMonitor(&url.URL{
|
result, err = monitor.NewHTTPHealthMonitor(&url.URL{
|
||||||
|
@ -51,17 +51,18 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
case "tcp", "udp":
|
case "tcp", "udp":
|
||||||
host := query.Get("host")
|
host := query.Get("host")
|
||||||
if host == "" {
|
if host == "" {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, "missing host", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hasPort := strings.Contains(host, ":")
|
hasPort := strings.Contains(host, ":")
|
||||||
port := query.Get("port")
|
port := query.Get("port")
|
||||||
if port != "" && !hasPort {
|
if port != "" && hasPort {
|
||||||
host = fmt.Sprintf("%s:%s", host, port)
|
http.Error(w, "port and host with port cannot both be provided", http.StatusBadRequest)
|
||||||
} else {
|
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if port != "" {
|
||||||
|
host = fmt.Sprintf("%s:%s", host, port)
|
||||||
|
}
|
||||||
result, err = monitor.NewRawHealthMonitor(&url.URL{
|
result, err = monitor.NewRawHealthMonitor(&url.URL{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Host: host,
|
Host: host,
|
||||||
|
@ -73,5 +74,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gphttp.RespondJSON(w, r, result)
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
|
||||||
"github.com/yusing/go-proxy/internal/docker"
|
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func serviceUnavailable(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.Error(w, "docker socket is not available", http.StatusServiceUnavailable)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DockerSocketHandler() http.HandlerFunc {
|
|
||||||
dockerClient, err := docker.NewClient(common.DockerHostFromEnv)
|
|
||||||
if err != nil {
|
|
||||||
logging.Warn().Err(err).Msg("failed to connect to docker client")
|
|
||||||
return serviceUnavailable
|
|
||||||
}
|
|
||||||
rp := reverseproxy.NewReverseProxy("docker", types.NewURL(&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: client.DummyHost,
|
|
||||||
}), dockerClient.HTTPClient().Transport)
|
|
||||||
|
|
||||||
return rp.ServeHTTP
|
|
||||||
}
|
|
|
@ -1,49 +1,57 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/env"
|
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
|
||||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
|
||||||
"github.com/yusing/go-proxy/pkg"
|
"github.com/yusing/go-proxy/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServeMux struct{ *http.ServeMux }
|
type ServeMux struct{ *http.ServeMux }
|
||||||
|
|
||||||
func (mux ServeMux) HandleMethods(methods, endpoint string, handler http.HandlerFunc) {
|
func (mux ServeMux) HandleEndpoint(method, endpoint string, handler http.HandlerFunc) {
|
||||||
for _, m := range strutils.CommaSeperatedList(methods) {
|
mux.ServeMux.HandleFunc(method+" "+agent.APIEndpointBase+endpoint, handler)
|
||||||
mux.ServeMux.HandleFunc(m+" "+agent.APIEndpointBase+endpoint, handler)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mux ServeMux) HandleFunc(endpoint string, handler http.HandlerFunc) {
|
func (mux ServeMux) HandleFunc(endpoint string, handler http.HandlerFunc) {
|
||||||
mux.ServeMux.HandleFunc(agent.APIEndpointBase+endpoint, handler)
|
mux.ServeMux.HandleFunc(agent.APIEndpointBase+endpoint, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NopWriteCloser struct {
|
var dialer = &net.Dialer{KeepAlive: 1 * time.Second}
|
||||||
io.Writer
|
|
||||||
|
func dialDockerSocket(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, "unix", env.DockerSocket)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (NopWriteCloser) Close() error {
|
func dockerSocketHandler() http.HandlerFunc {
|
||||||
return nil
|
rp := httputil.NewSingleHostReverseProxy(&url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: "api.moby.localhost",
|
||||||
|
})
|
||||||
|
rp.Transport = &http.Transport{
|
||||||
|
DialContext: dialDockerSocket,
|
||||||
|
}
|
||||||
|
return rp.ServeHTTP
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAgentHandler() http.Handler {
|
func NewAgentHandler() http.Handler {
|
||||||
mux := ServeMux{http.NewServeMux()}
|
mux := ServeMux{http.NewServeMux()}
|
||||||
|
|
||||||
mux.HandleFunc(agent.EndpointProxyHTTP+"/{path...}", ProxyHTTP)
|
mux.HandleFunc(agent.EndpointProxyHTTP+"/{path...}", ProxyHTTP)
|
||||||
mux.HandleMethods("GET", agent.EndpointVersion, pkg.GetVersionHTTPHandler())
|
mux.HandleEndpoint("GET", agent.EndpointVersion, pkg.GetVersionHTTPHandler())
|
||||||
mux.HandleMethods("GET", agent.EndpointName, func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleEndpoint("GET", agent.EndpointName, func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprint(w, env.AgentName)
|
fmt.Fprint(w, env.AgentName)
|
||||||
})
|
})
|
||||||
mux.HandleMethods("GET", agent.EndpointHealth, CheckHealth)
|
mux.HandleEndpoint("GET", agent.EndpointHealth, CheckHealth)
|
||||||
mux.HandleMethods("GET", agent.EndpointLogs, memlogger.HandlerFunc())
|
mux.HandleEndpoint("GET", agent.EndpointSystemInfo, systeminfo.Poller.ServeHTTP)
|
||||||
mux.HandleMethods("GET", agent.EndpointSystemInfo, systeminfo.Poller.ServeHTTP)
|
mux.ServeMux.HandleFunc("/", dockerSocketHandler())
|
||||||
mux.ServeMux.HandleFunc("/", DockerSocketHandler())
|
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,26 @@ package handler
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/http/httputil"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func NewTransport() *http.Transport {
|
||||||
|
return &http.Transport{
|
||||||
|
MaxIdleConnsPerHost: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
ResponseHeaderTimeout: 60 * time.Second,
|
||||||
|
WriteBufferSize: 16 * 1024, // 16KB
|
||||||
|
ReadBufferSize: 16 * 1024, // 16KB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
host := r.Header.Get(agentproxy.HeaderXProxyHost)
|
host := r.Header.Get(agentproxy.HeaderXProxyHost)
|
||||||
isHTTPS, _ := strconv.ParseBool(r.Header.Get(agentproxy.HeaderXProxyHTTPS))
|
isHTTPS, _ := strconv.ParseBool(r.Header.Get(agentproxy.HeaderXProxyHTTPS))
|
||||||
|
@ -34,11 +42,9 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
var transport *http.Transport
|
transport := NewTransport()
|
||||||
if skipTLSVerify {
|
if skipTLSVerify {
|
||||||
transport = gphttp.NewTransportWithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
} else {
|
|
||||||
transport = gphttp.NewTransport()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if responseHeaderTimeout > 0 {
|
if responseHeaderTimeout > 0 {
|
||||||
|
@ -49,14 +55,13 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
r.URL.Host = ""
|
r.URL.Host = ""
|
||||||
r.URL.Path = r.URL.Path[agent.HTTPProxyURLPrefixLen:] // strip the {API_BASE}/proxy/http prefix
|
r.URL.Path = r.URL.Path[agent.HTTPProxyURLPrefixLen:] // strip the {API_BASE}/proxy/http prefix
|
||||||
r.RequestURI = r.URL.String()
|
r.RequestURI = r.URL.String()
|
||||||
r.URL.Host = host
|
|
||||||
|
rp := &httputil.ReverseProxy{
|
||||||
|
Director: func(r *http.Request) {
|
||||||
r.URL.Scheme = scheme
|
r.URL.Scheme = scheme
|
||||||
|
r.URL.Host = host
|
||||||
logging.Debug().Msgf("proxy http request: %s %s", r.Method, r.URL.String())
|
},
|
||||||
|
Transport: transport,
|
||||||
rp := reverseproxy.NewReverseProxy("agent", types.NewURL(&url.URL{
|
}
|
||||||
Scheme: scheme,
|
|
||||||
Host: host,
|
|
||||||
}), transport)
|
|
||||||
rp.ServeHTTP(w, r)
|
rp.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
94
cmd/main.go
94
cmd/main.go
|
@ -1,30 +1,24 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal"
|
|
||||||
"github.com/yusing/go-proxy/internal/api/v1/query"
|
|
||||||
"github.com/yusing/go-proxy/internal/auth"
|
"github.com/yusing/go-proxy/internal/auth"
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
"github.com/yusing/go-proxy/internal/config"
|
"github.com/yusing/go-proxy/internal/config"
|
||||||
"github.com/yusing/go-proxy/internal/dnsproviders"
|
"github.com/yusing/go-proxy/internal/dnsproviders"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
|
"github.com/yusing/go-proxy/internal/homepage"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
||||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||||
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||||
"github.com/yusing/go-proxy/internal/route/routes"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/pkg"
|
"github.com/yusing/go-proxy/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rawLogger = log.New(os.Stdout, "", 0)
|
|
||||||
|
|
||||||
func parallel(fns ...func()) {
|
func parallel(fns ...func()) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, fn := range fns {
|
for _, fn := range fns {
|
||||||
|
@ -39,97 +33,29 @@ func parallel(fns ...func()) {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
initProfiling()
|
initProfiling()
|
||||||
dnsproviders.InitProviders()
|
|
||||||
args := pkg.GetArgs(common.MainServerCommandValidator{})
|
|
||||||
|
|
||||||
switch args.Command {
|
|
||||||
case common.CommandReload:
|
|
||||||
if err := query.ReloadServer(); err != nil {
|
|
||||||
gperr.LogFatal("server reload error", err)
|
|
||||||
}
|
|
||||||
rawLogger.Println("ok")
|
|
||||||
return
|
|
||||||
case common.CommandListIcons:
|
|
||||||
icons, err := internal.ListAvailableIcons()
|
|
||||||
if err != nil {
|
|
||||||
rawLogger.Fatal(err)
|
|
||||||
}
|
|
||||||
printJSON(icons)
|
|
||||||
return
|
|
||||||
case common.CommandListRoutes:
|
|
||||||
routes, err := query.ListRoutes()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to connect to api server: %s", err)
|
|
||||||
log.Printf("falling back to config file")
|
|
||||||
} else {
|
|
||||||
printJSON(routes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case common.CommandDebugListMTrace:
|
|
||||||
trace, err := query.ListMiddlewareTraces()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
printJSON(trace)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Command == common.CommandStart {
|
|
||||||
logging.InitLogger(os.Stderr, memlogger.GetMemLogger())
|
logging.InitLogger(os.Stderr, memlogger.GetMemLogger())
|
||||||
logging.Info().Msgf("GoDoxy version %s", pkg.GetVersion())
|
logging.Info().Msgf("GoDoxy version %s", pkg.GetVersion())
|
||||||
logging.Trace().Msg("trace enabled")
|
logging.Trace().Msg("trace enabled")
|
||||||
parallel(
|
parallel(
|
||||||
internal.InitIconListCache,
|
dnsproviders.InitProviders,
|
||||||
|
homepage.InitIconListCache,
|
||||||
systeminfo.Poller.Start,
|
systeminfo.Poller.Start,
|
||||||
|
middleware.LoadComposeFiles,
|
||||||
)
|
)
|
||||||
|
|
||||||
if common.APIJWTSecret == nil {
|
if common.APIJWTSecret == nil {
|
||||||
logging.Warn().Msg("API_JWT_SECRET is not set, using random key")
|
logging.Warn().Msg("API_JWT_SECRET is not set, using random key")
|
||||||
common.APIJWTSecret = common.RandomJWTKey()
|
common.APIJWTSecret = common.RandomJWTKey()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logging.DiscardLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Command == common.CommandValidate {
|
|
||||||
data, err := os.ReadFile(common.ConfigPath)
|
|
||||||
if err == nil {
|
|
||||||
err = config.Validate(data)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("config error: ", err)
|
|
||||||
}
|
|
||||||
log.Print("config OK")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dir := range common.RequiredDirectories {
|
for _, dir := range common.RequiredDirectories {
|
||||||
prepareDirectory(dir)
|
prepareDirectory(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware.LoadComposeFiles()
|
cfg, err := config.Load()
|
||||||
|
if err != nil {
|
||||||
var cfg *config.Config
|
|
||||||
var err gperr.Error
|
|
||||||
if cfg, err = config.Load(); err != nil {
|
|
||||||
gperr.LogWarn("errors in config", err)
|
gperr.LogWarn("errors in config", err)
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args.Command {
|
|
||||||
case common.CommandListRoutes:
|
|
||||||
cfg.StartProxyProviders()
|
|
||||||
printJSON(routes.ByAlias())
|
|
||||||
return
|
|
||||||
case common.CommandListConfigs:
|
|
||||||
printJSON(cfg.Value())
|
|
||||||
return
|
|
||||||
case common.CommandDebugListEntries:
|
|
||||||
printJSON(cfg.DumpRoutes())
|
|
||||||
return
|
|
||||||
case common.CommandDebugListProviders:
|
|
||||||
printJSON(cfg.DumpRouteProviders())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.Start(&config.StartServersOptions{
|
cfg.Start(&config.StartServersOptions{
|
||||||
|
@ -156,11 +82,3 @@ func prepareDirectory(dir string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printJSON(obj any) {
|
|
||||||
j, err := json.MarshalIndent(obj, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
logging.Fatal().Err(err).Send()
|
|
||||||
}
|
|
||||||
rawLogger.Print(string(j)) // raw output for convenience using "jq"
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +1,46 @@
|
||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
|
socket-proxy:
|
||||||
|
container_name: socket-proxy
|
||||||
|
image: ghcr.io/yusing/socket-proxy:latest
|
||||||
|
environment:
|
||||||
|
- ALLOW_START=1
|
||||||
|
- ALLOW_STOP=1
|
||||||
|
- ALLOW_RESTARTS=1
|
||||||
|
- CONTAINERS=1
|
||||||
|
- EVENTS=1
|
||||||
|
- INFO=1
|
||||||
|
- PING=1
|
||||||
|
- POST=1
|
||||||
|
- VERSION=1
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
|
||||||
|
restart: unless-stopped
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
ports:
|
||||||
|
- ${SOCKET_PROXY_LISTEN_ADDR:-127.0.0.1:2375}:2375
|
||||||
frontend:
|
frontend:
|
||||||
image: ghcr.io/yusing/godoxy-frontend:latest
|
image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}
|
||||||
container_name: godoxy-frontend
|
container_name: godoxy-frontend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host # do not change this
|
network_mode: host # do not change this
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
user: ${GODOXY_UID:-1000}:${GODOXY_GID:-1000}
|
||||||
|
read_only: true
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- all
|
||||||
depends_on:
|
depends_on:
|
||||||
- app
|
- app
|
||||||
environment:
|
environment:
|
||||||
|
HOSTNAME: 127.0.0.1
|
||||||
PORT: ${GODOXY_FRONTEND_PORT:-3000}
|
PORT: ${GODOXY_FRONTEND_PORT:-3000}
|
||||||
|
|
||||||
# modify below to fit your needs
|
|
||||||
labels:
|
labels:
|
||||||
proxy.aliases: godoxy
|
proxy.aliases: ${GODOXY_FRONTEND_ALIASES:-godoxy}
|
||||||
proxy.godoxy.port: ${GODOXY_FRONTEND_PORT:-3000}
|
proxy.#1.port: ${GODOXY_FRONTEND_PORT:-3000}
|
||||||
# proxy.godoxy.middlewares.cidr_whitelist: |
|
# proxy.#1.middlewares.cidr_whitelist: |
|
||||||
# status: 403
|
# status: 403
|
||||||
# message: IP not allowed
|
# message: IP not allowed
|
||||||
# allow:
|
# allow:
|
||||||
|
@ -24,16 +49,27 @@ services:
|
||||||
# - 192.168.0.0/16
|
# - 192.168.0.0/16
|
||||||
# - 172.16.0.0/12
|
# - 172.16.0.0/12
|
||||||
app:
|
app:
|
||||||
image: ghcr.io/yusing/godoxy:latest
|
image: ghcr.io/yusing/godoxy:${TAG:-latest}
|
||||||
container_name: godoxy
|
container_name: godoxy
|
||||||
restart: always
|
restart: always
|
||||||
network_mode: host # do not change this
|
network_mode: host # do not change this
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
user: ${GODOXY_UID:-1000}:${GODOXY_GID:-1000}
|
||||||
|
depends_on:
|
||||||
|
socket-proxy:
|
||||||
|
condition: service_started
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- all
|
||||||
|
cap_add:
|
||||||
|
- NET_BIND_SERVICE
|
||||||
|
environment:
|
||||||
|
- DOCKER_HOST=tcp://${SOCKET_PROXY_LISTEN_ADDR:-127.0.0.1:2375}
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
- ./error_pages:/app/error_pages
|
- ./error_pages:/app/error_pages:ro
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
|
|
||||||
# To use autocert, certs will be stored in "./certs".
|
# To use autocert, certs will be stored in "./certs".
|
||||||
|
|
|
@ -38,10 +38,25 @@
|
||||||
|
|
||||||
entrypoint:
|
entrypoint:
|
||||||
# Below define an example of middleware config
|
# Below define an example of middleware config
|
||||||
# 1. block non local IP connections
|
# 1. set security headers
|
||||||
# 2. redirect HTTP to HTTPS
|
# 2. block non local IP connections
|
||||||
|
# 3. redirect HTTP to HTTPS
|
||||||
#
|
#
|
||||||
# middlewares:
|
middlewares:
|
||||||
|
- use: CloudflareRealIP
|
||||||
|
- use: ModifyResponse
|
||||||
|
set_headers:
|
||||||
|
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
|
||||||
|
Access-Control-Allow-Headers: "*"
|
||||||
|
Access-Control-Allow-Origin: "*"
|
||||||
|
Access-Control-Max-Age: 180
|
||||||
|
Vary: "*"
|
||||||
|
X-XSS-Protection: 1; mode=block
|
||||||
|
Content-Security-Policy: "object-src 'self'; frame-ancestors 'self';"
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: SAMEORIGIN
|
||||||
|
Referrer-Policy: same-origin
|
||||||
|
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
|
||||||
# - use: CIDRWhitelist
|
# - use: CIDRWhitelist
|
||||||
# allow:
|
# allow:
|
||||||
# - "127.0.0.1"
|
# - "127.0.0.1"
|
||||||
|
|
95
go.mod
95
go.mod
|
@ -1,11 +1,17 @@
|
||||||
module github.com/yusing/go-proxy
|
module github.com/yusing/go-proxy
|
||||||
|
|
||||||
go 1.24.2
|
go 1.24.3
|
||||||
|
|
||||||
replace github.com/yusing/go-proxy/agent => ./agent
|
replace github.com/yusing/go-proxy/agent => ./agent
|
||||||
|
|
||||||
replace github.com/yusing/go-proxy/internal/dnsproviders => ./internal/dnsproviders
|
replace github.com/yusing/go-proxy/internal/dnsproviders => ./internal/dnsproviders
|
||||||
|
|
||||||
|
replace github.com/coreos/go-oidc/v3 => github.com/godoxy-app/go-oidc/v3 v3.14.2
|
||||||
|
|
||||||
|
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1
|
||||||
|
|
||||||
|
replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250502022742-408a348f1b97
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.10.3 // parsing HTML for extract fav icon
|
github.com/PuerkitoBio/goquery v1.10.3 // parsing HTML for extract fav icon
|
||||||
github.com/coder/websocket v1.8.13 // websocket for API and agent
|
github.com/coder/websocket v1.8.13 // websocket for API and agent
|
||||||
|
@ -15,25 +21,21 @@ require (
|
||||||
github.com/go-acme/lego/v4 v4.23.1 // acme client
|
github.com/go-acme/lego/v4 v4.23.1 // acme client
|
||||||
github.com/go-playground/validator/v10 v10.26.0 // validator
|
github.com/go-playground/validator/v10 v10.26.0 // validator
|
||||||
github.com/gobwas/glob v0.2.3 // glob matcher for route rules
|
github.com/gobwas/glob v0.2.3 // glob matcher for route rules
|
||||||
github.com/gotify/server/v2 v2.6.1 // reference the Message struct for json response
|
github.com/gotify/server/v2 v2.6.3 // reference the Message struct for json response
|
||||||
github.com/lithammer/fuzzysearch v1.1.8 // fuzzy search for searching icons and filtering metrics
|
github.com/lithammer/fuzzysearch v1.1.8 // fuzzy search for searching icons and filtering metrics
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // lock free map for concurrent operations
|
github.com/puzpuzpuz/xsync/v4 v4.1.0 // lock free map for concurrent operations
|
||||||
github.com/rs/zerolog v1.34.0 // logging
|
github.com/rs/zerolog v1.34.0 // logging
|
||||||
github.com/shirou/gopsutil/v4 v4.25.3 // system info metrics
|
github.com/shirou/gopsutil/v4 v4.25.4 // system info metrics
|
||||||
github.com/vincent-petithory/dataurl v1.0.0 // data url for fav icon
|
github.com/vincent-petithory/dataurl v1.0.0 // data url for fav icon
|
||||||
golang.org/x/crypto v0.37.0 // encrypting password with bcrypt
|
golang.org/x/crypto v0.38.0 // encrypting password with bcrypt
|
||||||
golang.org/x/net v0.39.0 // HTTP header utilities
|
golang.org/x/net v0.40.0 // HTTP header utilities
|
||||||
golang.org/x/oauth2 v0.29.0 // oauth2 authentication
|
golang.org/x/oauth2 v0.30.0 // oauth2 authentication
|
||||||
golang.org/x/text v0.24.0 // string utilities
|
|
||||||
golang.org/x/time v0.11.0 // time utilities
|
golang.org/x/time v0.11.0 // time utilities
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect; yaml parsing for different config files
|
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/coreos/go-oidc/v3 => github.com/godoxy-app/go-oidc/v3 v3.14.2
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/docker/cli v28.1.1+incompatible
|
github.com/docker/cli v28.1.1+incompatible
|
||||||
github.com/goccy/go-yaml v1.17.1
|
github.com/goccy/go-yaml v1.17.1 // yaml parsing for different config files
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||||
github.com/luthermonson/go-proxmox v0.2.2
|
github.com/luthermonson/go-proxmox v0.2.2
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1
|
github.com/oschwald/maxminddb-golang v1.13.1
|
||||||
|
@ -46,21 +48,13 @@ require (
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/auth v0.16.1 // indirect
|
cloud.google.com/go/auth v0.16.1 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||||
|
@ -86,22 +80,22 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
|
||||||
github.com/aws/smithy-go v1.22.3 // indirect
|
github.com/aws/smithy-go v1.22.3 // indirect
|
||||||
github.com/baidubce/bce-sdk-go v0.9.224 // indirect
|
github.com/baidubce/bce-sdk-go v0.9.226 // indirect
|
||||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
github.com/boombuler/barcode v1.0.2 // indirect
|
github.com/boombuler/barcode v1.0.2 // indirect
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/civo/civogo v0.3.99 // indirect
|
github.com/civo/civogo v0.5.0 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/diskfs/go-diskfs v1.6.0 // indirect
|
github.com/diskfs/go-diskfs v1.6.0 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/djherbis/times v1.6.0 // indirect
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/ebitengine/purego v0.8.2 // indirect
|
github.com/ebitengine/purego v0.8.3 // indirect
|
||||||
github.com/exoscale/egoscale/v3 v3.1.15 // indirect
|
github.com/exoscale/egoscale/v3 v3.1.17 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||||
|
@ -120,18 +114,18 @@ require (
|
||||||
github.com/gofrs/flock v0.12.1 // indirect
|
github.com/gofrs/flock v0.12.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect
|
||||||
github.com/google/s2a-go v0.1.9 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||||
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 // indirect
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149 // indirect
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||||
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
|
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
|
||||||
github.com/jinzhu/copier v0.4.0 // indirect
|
github.com/jinzhu/copier v0.4.0 // indirect
|
||||||
|
@ -143,14 +137,14 @@ require (
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/linode/linodego v1.49.0 // indirect
|
github.com/linode/linodego v1.50.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||||
github.com/magefile/mage v1.15.0 // indirect
|
github.com/magefile/mage v1.15.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/miekg/dns v1.1.65 // indirect
|
github.com/miekg/dns v1.1.66 // indirect
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
@ -173,7 +167,7 @@ require (
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.89.2 // indirect
|
github.com/oracle/oci-go-sdk/v65 v65.91.0 // indirect
|
||||||
github.com/ovh/go-ovh v1.7.0 // indirect
|
github.com/ovh/go-ovh v1.7.0 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
@ -188,10 +182,10 @@ require (
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/sacloud/api-client-go v0.2.10 // indirect
|
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||||
github.com/sacloud/go-http v0.1.9 // indirect
|
github.com/sacloud/go-http v0.1.9 // indirect
|
||||||
github.com/sacloud/iaas-api-go v1.14.0 // indirect
|
github.com/sacloud/iaas-api-go v1.15.0 // indirect
|
||||||
github.com/sacloud/packages-go v0.0.11 // indirect
|
github.com/sacloud/packages-go v0.0.11 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||||
github.com/samber/lo v1.49.1 // indirect
|
github.com/samber/lo v1.50.0 // indirect
|
||||||
github.com/samber/slog-common v0.18.1 // indirect
|
github.com/samber/slog-common v0.18.1 // indirect
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
|
||||||
github.com/selectel/domains-go v1.1.0 // indirect
|
github.com/selectel/domains-go v1.1.0 // indirect
|
||||||
|
@ -203,11 +197,11 @@ require (
|
||||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||||
github.com/sony/gobreaker v1.0.0 // indirect
|
github.com/sony/gobreaker v1.0.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/cast v1.8.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/spf13/viper v1.20.1 // indirect
|
github.com/spf13/viper v1.20.1 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
|
@ -215,8 +209,8 @@ require (
|
||||||
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||||
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.206 // indirect
|
github.com/volcengine/volc-sdk-golang v1.0.207 // indirect
|
||||||
github.com/vultr/govultr/v3 v3.19.1 // indirect
|
github.com/vultr/govultr/v3 v3.20.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
|
@ -224,29 +218,32 @@ require (
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
go.uber.org/mock v0.5.1 // indirect
|
go.uber.org/mock v0.5.2 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/ratelimit v0.3.1 // indirect
|
go.uber.org/ratelimit v0.3.1 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.13.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/tools v0.32.0 // indirect
|
golang.org/x/text v0.25.0
|
||||||
google.golang.org/api v0.230.0 // indirect
|
golang.org/x/tools v0.33.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
google.golang.org/api v0.233.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect
|
||||||
google.golang.org/grpc v1.72.0 // indirect
|
google.golang.org/grpc v1.72.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.14.2 // indirect
|
gopkg.in/ns1/ns1-go.v2 v2.14.3 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/api v0.33.0 // indirect
|
k8s.io/api v0.33.0 // indirect
|
||||||
k8s.io/apimachinery v0.33.0 // indirect
|
k8s.io/apimachinery v0.33.0 // indirect
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||||
|
|
167
go.sum
167
go.sum
|
@ -179,8 +179,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ
|
||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||||
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
||||||
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
||||||
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
|
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
|
||||||
|
@ -606,8 +606,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesn
|
||||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||||
|
@ -710,8 +710,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjK
|
||||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||||
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||||
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||||
github.com/baidubce/bce-sdk-go v0.9.224 h1:z2L8alGw/y3IUHjrLRyrxrgCvMssYTjgCd7OQdb4gt0=
|
github.com/baidubce/bce-sdk-go v0.9.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI=
|
||||||
github.com/baidubce/bce-sdk-go v0.9.224/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
@ -749,8 +749,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/civo/civogo v0.3.99 h1:ijv3ecaz9Ju82J72kbQwQvzlSxn9fLMlklu8C8pXGjI=
|
github.com/civo/civogo v0.5.0 h1:YDG38z+hmgaAhpDad1n/9sZrBtwRTeKVvMgdrS8Gwy4=
|
||||||
github.com/civo/civogo v0.3.99/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
|
github.com/civo/civogo v0.5.0/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
|
||||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
|
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
|
||||||
|
@ -811,8 +811,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
|
||||||
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
|
||||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||||
|
@ -831,8 +831,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||||
github.com/exoscale/egoscale/v3 v3.1.15 h1:L2p9jWZhOeSBEwXCP12LPAoclBUv4LqzSN4RgNJtMdg=
|
github.com/exoscale/egoscale/v3 v3.1.17 h1:+T6+GP/k3tFNsYIQzpF3Sou4ecH//FkERDsJze/OZ00=
|
||||||
github.com/exoscale/egoscale/v3 v3.1.15/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco=
|
github.com/exoscale/egoscale/v3 v3.1.17/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
@ -933,6 +933,8 @@ github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1 h1:fsSqE28vU0PRk
|
||||||
github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1/go.mod h1:av6ggKWQz6SEkFyShjDEgVqiIB0RHvEQNIkPeqgJEeE=
|
github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1/go.mod h1:av6ggKWQz6SEkFyShjDEgVqiIB0RHvEQNIkPeqgJEeE=
|
||||||
github.com/godoxy-app/go-oidc/v3 v3.14.2 h1:y1sosR6N7IpMiREM8I8w68zrUhh5P0Hg+6wERmuhFAc=
|
github.com/godoxy-app/go-oidc/v3 v3.14.2 h1:y1sosR6N7IpMiREM8I8w68zrUhh5P0Hg+6wERmuhFAc=
|
||||||
github.com/godoxy-app/go-oidc/v3 v3.14.2/go.mod h1:ZRZLrEz7MmMe1kRzRsYqYmWKN2EHlPVGn71GMbrLLt4=
|
github.com/godoxy-app/go-oidc/v3 v3.14.2/go.mod h1:ZRZLrEz7MmMe1kRzRsYqYmWKN2EHlPVGn71GMbrLLt4=
|
||||||
|
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250502022742-408a348f1b97 h1:i52gBYamrKs4DHT1+SiobW2im5UgTMVXK1KIL1djSeA=
|
||||||
|
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250502022742-408a348f1b97/go.mod h1:XvbfPmmrdpLrsKwj3irYkxt5ygyMcDsTQTJ7cnZ9RNQ=
|
||||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||||
|
@ -1033,8 +1035,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||||
|
@ -1063,8 +1065,8 @@ github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMd
|
||||||
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
||||||
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||||
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
||||||
|
@ -1082,16 +1084,17 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
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/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gotify/server/v2 v2.6.1 h1:Kf7v5fzBxzELzZa/jonWfwJMkqYqh1LBzBpCmt5QIAI=
|
github.com/gotify/server/v2 v2.6.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I=
|
||||||
github.com/gotify/server/v2 v2.6.1/go.mod h1:Dk8HLyTVDqmXM8YEg6tjROBen6mxyHZFRggJFHTwZLc=
|
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||||
|
@ -1147,8 +1150,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
||||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 h1:ld5s5UeA9zgyFsZskVD2Tr6k6VnJWkvaLm5nqvfOEf4=
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149 h1:gDzo/eYE8/mwF5fi7v10pdBW2027PUvaHvRPA5aOPoM=
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
||||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
@ -1161,8 +1164,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod
|
||||||
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 h1:AKsihjFT/t6Y0keEv3p59DACcOuh0inWXdUB0ZOzYH0=
|
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 h1:AKsihjFT/t6Y0keEv3p59DACcOuh0inWXdUB0ZOzYH0=
|
||||||
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg=
|
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg=
|
||||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
||||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||||
|
@ -1232,8 +1235,8 @@ github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk=
|
||||||
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/linode/linodego v1.49.0 h1:MNd3qwvQzbXB5mCpvdCqlUIu1RPA9oC+50LyB9kK+GQ=
|
github.com/linode/linodego v1.50.0 h1:5y79VvvQnWb5JyPIjTwyUrU3ArHcs7XZQFdkPS/lNpw=
|
||||||
github.com/linode/linodego v1.49.0/go.mod h1:B+HAM3//4w1wOS0BwdaQBKwBxlfe6kYJ7bSC6jJ/xtc=
|
github.com/linode/linodego v1.50.0/go.mod h1:9S+REoPCtUNWCm63D1vjjxIJZfwEL2t2kTDnwt620FM=
|
||||||
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
||||||
|
@ -1287,8 +1290,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
|
github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
|
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
|
||||||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
||||||
|
@ -1395,8 +1398,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||||
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.89.2 h1:w0GwID9NlT+eg3InbAwkWsazDtxVLYQ8rJb4E33Yb14=
|
github.com/oracle/oci-go-sdk/v65 v65.91.0 h1:maO6AxKxVfszH0X4tbbtN21jOk03lCRR3IqiA8/FzZc=
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.89.2/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
github.com/oracle/oci-go-sdk/v65 v65.91.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||||
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
|
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
|
||||||
|
@ -1477,16 +1480,16 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
github.com/puzpuzpuz/xsync/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
||||||
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
||||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||||
github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE=
|
github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE=
|
||||||
github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY=
|
github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
@ -1510,15 +1513,15 @@ github.com/sacloud/api-client-go v0.2.10 h1:+rv3jDohD+pkdYwOTBiB+jZsM0xK3AxadXRz
|
||||||
github.com/sacloud/api-client-go v0.2.10/go.mod h1:Jj3CTy2+O4bcMedVDXlbHuqqche85HEPuVXoQFhLaRc=
|
github.com/sacloud/api-client-go v0.2.10/go.mod h1:Jj3CTy2+O4bcMedVDXlbHuqqche85HEPuVXoQFhLaRc=
|
||||||
github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
|
github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
|
||||||
github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE=
|
github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE=
|
||||||
github.com/sacloud/iaas-api-go v1.14.0 h1:xjkFWqdo4ilTrKPNNYBNWR/CZ/kVRsJrdAHAad6J/AQ=
|
github.com/sacloud/iaas-api-go v1.15.0 h1:G88U69OOVUSjOZ2fEP5QWMg7r3VOVsfNkl5Mxl+/wMk=
|
||||||
github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFnpe69kyA4M3V0=
|
github.com/sacloud/iaas-api-go v1.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA=
|
||||||
github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo=
|
github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo=
|
||||||
github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
|
github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
|
||||||
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
|
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
|
||||||
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
|
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
|
||||||
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||||
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
|
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
|
||||||
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
||||||
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
|
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
|
||||||
|
@ -1530,8 +1533,6 @@ github.com/selectel/domains-go v1.1.0 h1:futG50J43ALLKQAnZk9H9yOtLGnSUh7c5hSvuC5
|
||||||
github.com/selectel/domains-go v1.1.0/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA=
|
github.com/selectel/domains-go v1.1.0/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA=
|
||||||
github.com/selectel/go-selvpcclient/v3 v3.2.1 h1:ny6WIAMiHzKxOgOEnwcWE79wIQij1AHHylzPA41MXCw=
|
github.com/selectel/go-selvpcclient/v3 v3.2.1 h1:ny6WIAMiHzKxOgOEnwcWE79wIQij1AHHylzPA41MXCw=
|
||||||
github.com/selectel/go-selvpcclient/v3 v3.2.1/go.mod h1:3EfSf8aEWyhspOGbvZ6mvnFg7JN5uckxNyBFPGWsXNQ=
|
github.com/selectel/go-selvpcclient/v3 v3.2.1/go.mod h1:3EfSf8aEWyhspOGbvZ6mvnFg7JN5uckxNyBFPGWsXNQ=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
|
||||||
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
@ -1572,8 +1573,8 @@ github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZ
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
@ -1615,8 +1616,8 @@ github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNG
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151 h1:SBbEaeCwhqmyAEEF5ubpg/2vv3RO6SdBsOSYhpnJaL4=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164 h1:qEzZCZf1sgvvrZ8ngws0gZlyW+sOdY0K9VXGm4AcvTE=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
|
@ -1642,10 +1643,10 @@ github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8A
|
||||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
|
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
|
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.206 h1:7NG8FCpvu9wbx+Z4I/p3tcTS2zdBqTZtJXgydunGy6g=
|
github.com/volcengine/volc-sdk-golang v1.0.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18=
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.206/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
|
github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
|
||||||
github.com/vultr/govultr/v3 v3.19.1 h1:31rOP5Tz40AOc8h6Ws4ryzqAniUBffgRhy9uMG/EFvs=
|
github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA=
|
||||||
github.com/vultr/govultr/v3 v3.19.1/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
|
github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
@ -1701,8 +1702,8 @@ go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||||
|
@ -1714,8 +1715,8 @@ go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
@ -1725,8 +1726,8 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
|
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||||
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||||
|
@ -1771,8 +1772,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
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.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -1916,8 +1917,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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
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.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -1947,8 +1948,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
|
||||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -1969,8 +1970,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -2092,8 +2093,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
@ -2111,8 +2112,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -2134,8 +2135,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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.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.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -2217,8 +2218,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -2293,8 +2294,8 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
|
||||||
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
|
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
|
||||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||||
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
|
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
|
||||||
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
|
google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI=
|
||||||
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
|
google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -2435,10 +2436,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
|
||||||
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||||
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
|
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
|
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -2481,8 +2484,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
|
||||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
@ -2523,8 +2526,8 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.14.2 h1:wz/toj9U20wBrmYxW4vTz7sZWED+JJVRjUBBJ7CKrzI=
|
gopkg.in/ns1/ns1-go.v2 v2.14.3 h1:Yn72GgB6AA9I4602AsLMtbC1ZKT5EUrKiG+IPS+Ovr0=
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.14.2/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
|
gopkg.in/ns1/ns1-go.v2 v2.14.3/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
@ -2560,8 +2563,8 @@ k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
|
||||||
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg=
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||||
|
|
|
@ -2,16 +2,14 @@ package acl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/oschwald/maxminddb-golang"
|
"github.com/puzpuzpuz/xsync/v4"
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
acl "github.com/yusing/go-proxy/internal/acl/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/logging/accesslog"
|
"github.com/yusing/go-proxy/internal/logging/accesslog"
|
||||||
|
"github.com/yusing/go-proxy/internal/maxmind"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
)
|
)
|
||||||
|
@ -19,43 +17,24 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Default string `json:"default" validate:"omitempty,oneof=allow deny"` // default: allow
|
Default string `json:"default" validate:"omitempty,oneof=allow deny"` // default: allow
|
||||||
AllowLocal *bool `json:"allow_local"` // default: true
|
AllowLocal *bool `json:"allow_local"` // default: true
|
||||||
Allow []string `json:"allow"`
|
Allow Matchers `json:"allow"`
|
||||||
Deny []string `json:"deny"`
|
Deny Matchers `json:"deny"`
|
||||||
Log *accesslog.ACLLoggerConfig `json:"log"`
|
Log *accesslog.ACLLoggerConfig `json:"log"`
|
||||||
|
|
||||||
MaxMind *MaxMindConfig `json:"maxmind" validate:"omitempty"`
|
|
||||||
|
|
||||||
config
|
config
|
||||||
|
valErr gperr.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
|
||||||
MaxMindDatabaseType string
|
|
||||||
MaxMindConfig struct {
|
|
||||||
AccountID string `json:"account_id" validate:"required"`
|
|
||||||
LicenseKey string `json:"license_key" validate:"required"`
|
|
||||||
Database MaxMindDatabaseType `json:"database" validate:"required,oneof=geolite geoip2"`
|
|
||||||
|
|
||||||
logger zerolog.Logger
|
|
||||||
lastUpdate time.Time
|
|
||||||
db struct {
|
|
||||||
*maxminddb.Reader
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
defaultAllow bool
|
defaultAllow bool
|
||||||
allowLocal bool
|
allowLocal bool
|
||||||
allow []matcher
|
ipCache *xsync.Map[string, *checkCache]
|
||||||
deny []matcher
|
|
||||||
ipCache *xsync.MapOf[string, *checkCache]
|
|
||||||
logAllowed bool
|
logAllowed bool
|
||||||
logger *accesslog.AccessLogger
|
logger *accesslog.AccessLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkCache struct {
|
type checkCache struct {
|
||||||
*acl.IPInfo
|
*maxmind.IPInfo
|
||||||
allow bool
|
allow bool
|
||||||
created time.Time
|
created time.Time
|
||||||
}
|
}
|
||||||
|
@ -63,7 +42,7 @@ type checkCache struct {
|
||||||
const cacheTTL = 1 * time.Minute
|
const cacheTTL = 1 * time.Minute
|
||||||
|
|
||||||
func (c *checkCache) Expired() bool {
|
func (c *checkCache) Expired() bool {
|
||||||
return c.created.Add(cacheTTL).After(utils.TimeNow())
|
return c.created.Add(cacheTTL).Before(utils.TimeNow())
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: add stats
|
//TODO: add stats
|
||||||
|
@ -73,11 +52,6 @@ const (
|
||||||
ACLDeny = "deny"
|
ACLDeny = "deny"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
MaxMindGeoLite MaxMindDatabaseType = "geolite"
|
|
||||||
MaxMindGeoIP2 MaxMindDatabaseType = "geoip2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Config) Validate() gperr.Error {
|
func (c *Config) Validate() gperr.Error {
|
||||||
switch c.Default {
|
switch c.Default {
|
||||||
case "", ACLAllow:
|
case "", ACLAllow:
|
||||||
|
@ -85,7 +59,8 @@ func (c *Config) Validate() gperr.Error {
|
||||||
case ACLDeny:
|
case ACLDeny:
|
||||||
c.defaultAllow = false
|
c.defaultAllow = false
|
||||||
default:
|
default:
|
||||||
return gperr.New("invalid default value").Subject(c.Default)
|
c.valErr = gperr.New("invalid default value").Subject(c.Default)
|
||||||
|
return c.valErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.AllowLocal != nil {
|
if c.AllowLocal != nil {
|
||||||
|
@ -94,55 +69,24 @@ func (c *Config) Validate() gperr.Error {
|
||||||
c.allowLocal = true
|
c.allowLocal = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.MaxMind != nil {
|
|
||||||
c.MaxMind.logger = logging.With().Str("type", string(c.MaxMind.Database)).Logger()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Log != nil {
|
if c.Log != nil {
|
||||||
c.logAllowed = c.Log.LogAllowed
|
c.logAllowed = c.Log.LogAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := gperr.NewBuilder("syntax error")
|
if !c.allowLocal && !c.defaultAllow && len(c.Allow) == 0 {
|
||||||
c.allow = make([]matcher, 0, len(c.Allow))
|
c.valErr = gperr.New("allow_local is false and default is deny, but no allow rules are configured")
|
||||||
c.deny = make([]matcher, 0, len(c.Deny))
|
return c.valErr
|
||||||
|
|
||||||
for _, s := range c.Allow {
|
|
||||||
m, err := c.parseMatcher(s)
|
|
||||||
if err != nil {
|
|
||||||
errs.Add(err.Subject(s))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.allow = append(c.allow, m)
|
|
||||||
}
|
|
||||||
for _, s := range c.Deny {
|
|
||||||
m, err := c.parseMatcher(s)
|
|
||||||
if err != nil {
|
|
||||||
errs.Add(err.Subject(s))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.deny = append(c.deny, m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if errs.HasError() {
|
c.ipCache = xsync.NewMap[string, *checkCache]()
|
||||||
c.allow = nil
|
|
||||||
c.deny = nil
|
|
||||||
return errMatcherFormat.With(errs.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ipCache = xsync.NewMapOf[string, *checkCache]()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Valid() bool {
|
func (c *Config) Valid() bool {
|
||||||
return c != nil && (len(c.allow) > 0 || len(c.deny) > 0 || c.allowLocal)
|
return c != nil && c.valErr == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Start(parent *task.Task) gperr.Error {
|
func (c *Config) Start(parent *task.Task) gperr.Error {
|
||||||
if c.MaxMind != nil {
|
|
||||||
if err := c.MaxMind.LoadMaxMindDB(parent); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.Log != nil {
|
if c.Log != nil {
|
||||||
logger, err := accesslog.NewAccessLogger(parent, c.Log)
|
logger, err := accesslog.NewAccessLogger(parent, c.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,10 +94,22 @@ func (c *Config) Start(parent *task.Task) gperr.Error {
|
||||||
}
|
}
|
||||||
c.logger = logger
|
c.logger = logger
|
||||||
}
|
}
|
||||||
|
if c.valErr != nil {
|
||||||
|
return c.valErr
|
||||||
|
}
|
||||||
|
logging.Info().
|
||||||
|
Str("default", c.Default).
|
||||||
|
Bool("allow_local", c.allowLocal).
|
||||||
|
Int("allow_rules", len(c.Allow)).
|
||||||
|
Int("deny_rules", len(c.Deny)).
|
||||||
|
Msg("ACL started")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *config) cacheRecord(info *acl.IPInfo, allow bool) {
|
func (c *Config) cacheRecord(info *maxmind.IPInfo, allow bool) {
|
||||||
|
if common.ForceResolveCountry && info.City == nil {
|
||||||
|
maxmind.LookupCity(info)
|
||||||
|
}
|
||||||
c.ipCache.Store(info.Str, &checkCache{
|
c.ipCache.Store(info.Str, &checkCache{
|
||||||
IPInfo: info,
|
IPInfo: info,
|
||||||
allow: allow,
|
allow: allow,
|
||||||
|
@ -161,7 +117,7 @@ func (c *config) cacheRecord(info *acl.IPInfo, allow bool) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *config) log(info *acl.IPInfo, allowed bool) {
|
func (c *config) log(info *maxmind.IPInfo, allowed bool) {
|
||||||
if c.logger == nil {
|
if c.logger == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -175,14 +131,13 @@ func (c *Config) IPAllowed(ip net.IP) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// always allow private and loopback
|
// always allow loopback, not logged
|
||||||
// loopback is not logged
|
|
||||||
if ip.IsLoopback() {
|
if ip.IsLoopback() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.allowLocal && ip.IsPrivate() {
|
if c.allowLocal && ip.IsPrivate() {
|
||||||
c.log(&acl.IPInfo{IP: ip, Str: ip.String()}, true)
|
c.log(&maxmind.IPInfo{IP: ip, Str: ip.String()}, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,21 +148,17 @@ func (c *Config) IPAllowed(ip net.IP) bool {
|
||||||
return record.allow
|
return record.allow
|
||||||
}
|
}
|
||||||
|
|
||||||
ipAndStr := &acl.IPInfo{IP: ip, Str: ipStr}
|
ipAndStr := &maxmind.IPInfo{IP: ip, Str: ipStr}
|
||||||
for _, m := range c.allow {
|
if c.Allow.Match(ipAndStr) {
|
||||||
if m(ipAndStr) {
|
|
||||||
c.log(ipAndStr, true)
|
c.log(ipAndStr, true)
|
||||||
c.cacheRecord(ipAndStr, true)
|
c.cacheRecord(ipAndStr, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
if c.Deny.Match(ipAndStr) {
|
||||||
for _, m := range c.deny {
|
|
||||||
if m(ipAndStr) {
|
|
||||||
c.log(ipAndStr, false)
|
c.log(ipAndStr, false)
|
||||||
c.cacheRecord(ipAndStr, false)
|
c.cacheRecord(ipAndStr, false)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
c.log(ipAndStr, c.defaultAllow)
|
c.log(ipAndStr, c.defaultAllow)
|
||||||
c.cacheRecord(ipAndStr, c.defaultAllow)
|
c.cacheRecord(ipAndStr, c.defaultAllow)
|
||||||
|
|
|
@ -4,11 +4,17 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
acl "github.com/yusing/go-proxy/internal/acl/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
|
"github.com/yusing/go-proxy/internal/maxmind"
|
||||||
)
|
)
|
||||||
|
|
||||||
type matcher func(*acl.IPInfo) bool
|
type MatcherFunc func(*maxmind.IPInfo) bool
|
||||||
|
|
||||||
|
type Matcher struct {
|
||||||
|
match MatcherFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type Matchers []Matcher
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MatcherTypeIP = "ip"
|
MatcherTypeIP = "ip"
|
||||||
|
@ -17,6 +23,9 @@ const (
|
||||||
MatcherTypeCountry = "country"
|
MatcherTypeCountry = "country"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: use this error in the future
|
||||||
|
//
|
||||||
|
//nolint:unused
|
||||||
var errMatcherFormat = gperr.Multiline().AddLines(
|
var errMatcherFormat = gperr.Multiline().AddLines(
|
||||||
"invalid matcher format, expect {type}:{value}",
|
"invalid matcher format, expect {type}:{value}",
|
||||||
"Available types: ip|cidr|tz|country",
|
"Available types: ip|cidr|tz|country",
|
||||||
|
@ -25,62 +34,66 @@ var errMatcherFormat = gperr.Multiline().AddLines(
|
||||||
"tz:Asia/Shanghai",
|
"tz:Asia/Shanghai",
|
||||||
"country:GB",
|
"country:GB",
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errSyntax = gperr.New("syntax error")
|
errSyntax = gperr.New("syntax error")
|
||||||
errInvalidIP = gperr.New("invalid IP")
|
errInvalidIP = gperr.New("invalid IP")
|
||||||
errInvalidCIDR = gperr.New("invalid CIDR")
|
errInvalidCIDR = gperr.New("invalid CIDR")
|
||||||
errMaxMindNotConfigured = gperr.New("MaxMind not configured")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cfg *Config) parseMatcher(s string) (matcher, gperr.Error) {
|
func (matcher *Matcher) Parse(s string) error {
|
||||||
parts := strings.Split(s, ":")
|
parts := strings.Split(s, ":")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return nil, errSyntax
|
return errSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
switch parts[0] {
|
switch parts[0] {
|
||||||
case MatcherTypeIP:
|
case MatcherTypeIP:
|
||||||
ip := net.ParseIP(parts[1])
|
ip := net.ParseIP(parts[1])
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, errInvalidIP
|
return errInvalidIP
|
||||||
}
|
}
|
||||||
return matchIP(ip), nil
|
matcher.match = matchIP(ip)
|
||||||
case MatcherTypeCIDR:
|
case MatcherTypeCIDR:
|
||||||
_, net, err := net.ParseCIDR(parts[1])
|
_, net, err := net.ParseCIDR(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errInvalidCIDR
|
return errInvalidCIDR
|
||||||
}
|
}
|
||||||
return matchCIDR(net), nil
|
matcher.match = matchCIDR(net)
|
||||||
case MatcherTypeTimeZone:
|
case MatcherTypeTimeZone:
|
||||||
if cfg.MaxMind == nil {
|
matcher.match = matchTimeZone(parts[1])
|
||||||
return nil, errMaxMindNotConfigured
|
|
||||||
}
|
|
||||||
return cfg.MaxMind.matchTimeZone(parts[1]), nil
|
|
||||||
case MatcherTypeCountry:
|
case MatcherTypeCountry:
|
||||||
if cfg.MaxMind == nil {
|
matcher.match = matchISOCode(parts[1])
|
||||||
return nil, errMaxMindNotConfigured
|
|
||||||
}
|
|
||||||
return cfg.MaxMind.matchISOCode(parts[1]), nil
|
|
||||||
default:
|
default:
|
||||||
return nil, errSyntax
|
return errSyntax
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchIP(ip net.IP) matcher {
|
func (matchers Matchers) Match(ip *maxmind.IPInfo) bool {
|
||||||
return func(ip2 *acl.IPInfo) bool {
|
for _, m := range matchers {
|
||||||
|
if m.match(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchIP(ip net.IP) MatcherFunc {
|
||||||
|
return func(ip2 *maxmind.IPInfo) bool {
|
||||||
return ip.Equal(ip2.IP)
|
return ip.Equal(ip2.IP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchCIDR(n *net.IPNet) matcher {
|
func matchCIDR(n *net.IPNet) MatcherFunc {
|
||||||
return func(ip *acl.IPInfo) bool {
|
return func(ip *maxmind.IPInfo) bool {
|
||||||
return n.Contains(ip.IP)
|
return n.Contains(ip.IP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) matchTimeZone(tz string) matcher {
|
func matchTimeZone(tz string) MatcherFunc {
|
||||||
return func(ip *acl.IPInfo) bool {
|
return func(ip *maxmind.IPInfo) bool {
|
||||||
city, ok := cfg.lookupCity(ip)
|
city, ok := maxmind.LookupCity(ip)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -88,9 +101,9 @@ func (cfg *MaxMindConfig) matchTimeZone(tz string) matcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) matchISOCode(iso string) matcher {
|
func matchISOCode(iso string) MatcherFunc {
|
||||||
return func(ip *acl.IPInfo) bool {
|
return func(ip *maxmind.IPInfo) bool {
|
||||||
city, ok := cfg.lookupCity(ip)
|
city, ok := maxmind.LookupCity(ip)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
49
internal/acl/matcher_test.go
Normal file
49
internal/acl/matcher_test.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
maxmind "github.com/yusing/go-proxy/internal/maxmind/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMatchers(t *testing.T) {
|
||||||
|
strMatchers := []string{
|
||||||
|
"ip:127.0.0.1",
|
||||||
|
"cidr:10.0.0.0/8",
|
||||||
|
}
|
||||||
|
|
||||||
|
var mathers Matchers
|
||||||
|
err := utils.Convert(reflect.ValueOf(strMatchers), reflect.ValueOf(&mathers), false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ip string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"127.0.0.1", true},
|
||||||
|
{"10.0.0.1", true},
|
||||||
|
{"127.0.0.2", false},
|
||||||
|
{"192.168.0.1", false},
|
||||||
|
{"11.0.0.1", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
ip := net.ParseIP(test.ip)
|
||||||
|
if ip == nil {
|
||||||
|
t.Fatalf("invalid ip: %s", test.ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := mathers.Match(&maxmind.IPInfo{
|
||||||
|
IP: ip,
|
||||||
|
Str: test.ip,
|
||||||
|
})
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("mathers.Match(%s) = %v, want %v", test.ip, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,281 +0,0 @@
|
||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/tar"
|
|
||||||
"compress/gzip"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/oschwald/maxminddb-golang"
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
updateInterval = 24 * time.Hour
|
|
||||||
httpClient = &http.Client{
|
|
||||||
Timeout: 10 * time.Second,
|
|
||||||
}
|
|
||||||
ErrResponseNotOK = gperr.New("response not OK")
|
|
||||||
ErrDownloadFailure = gperr.New("download failure")
|
|
||||||
)
|
|
||||||
|
|
||||||
func dbPathImpl(dbType MaxMindDatabaseType) string {
|
|
||||||
if dbType == MaxMindGeoLite {
|
|
||||||
return filepath.Join(dataDir, "GeoLite2-City.mmdb")
|
|
||||||
}
|
|
||||||
return filepath.Join(dataDir, "GeoIP2-City.mmdb")
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbURLimpl(dbType MaxMindDatabaseType) string {
|
|
||||||
if dbType == MaxMindGeoLite {
|
|
||||||
return "https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz"
|
|
||||||
}
|
|
||||||
return "https://download.maxmind.com/geoip/databases/GeoIP2-City/download?suffix=tar.gz"
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbFilename(dbType MaxMindDatabaseType) string {
|
|
||||||
if dbType == MaxMindGeoLite {
|
|
||||||
return "GeoLite2-City.mmdb"
|
|
||||||
}
|
|
||||||
return "GeoIP2-City.mmdb"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) LoadMaxMindDB(parent task.Parent) gperr.Error {
|
|
||||||
if cfg.Database == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
path := dbPath(cfg.Database)
|
|
||||||
reader, err := maxmindDBOpen(path)
|
|
||||||
exists := true
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, os.ErrNotExist):
|
|
||||||
default:
|
|
||||||
// ignore invalid error, just download it again
|
|
||||||
var invalidErr maxminddb.InvalidDatabaseError
|
|
||||||
if !errors.As(err, &invalidErr) {
|
|
||||||
return gperr.Wrap(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exists = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
cfg.logger.Info().Msg("MaxMind DB not found/invalid, downloading...")
|
|
||||||
reader, err = cfg.download()
|
|
||||||
if err != nil {
|
|
||||||
return ErrDownloadFailure.With(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.logger.Info().Msg("MaxMind DB loaded")
|
|
||||||
|
|
||||||
cfg.db.Reader = reader
|
|
||||||
go cfg.scheduleUpdate(parent)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) loadLastUpdate() {
|
|
||||||
f, err := os.Stat(dbPath(cfg.Database))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.lastUpdate = f.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) setLastUpdate(t time.Time) {
|
|
||||||
cfg.lastUpdate = t
|
|
||||||
_ = os.Chtimes(dbPath(cfg.Database), t, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) scheduleUpdate(parent task.Parent) {
|
|
||||||
task := parent.Subtask("schedule_update", true)
|
|
||||||
ticker := time.NewTicker(updateInterval)
|
|
||||||
|
|
||||||
cfg.loadLastUpdate()
|
|
||||||
cfg.update()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ticker.Stop()
|
|
||||||
if cfg.db.Reader != nil {
|
|
||||||
cfg.db.Reader.Close()
|
|
||||||
}
|
|
||||||
task.Finish(nil)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-task.Context().Done():
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
cfg.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) update() {
|
|
||||||
// check for update
|
|
||||||
cfg.logger.Info().Msg("checking for MaxMind DB update...")
|
|
||||||
remoteLastModified, err := cfg.checkLastest()
|
|
||||||
if err != nil {
|
|
||||||
cfg.logger.Err(err).Msg("failed to check MaxMind DB update")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if remoteLastModified.Equal(cfg.lastUpdate) {
|
|
||||||
cfg.logger.Info().Msg("MaxMind DB is up to date")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.logger.Info().
|
|
||||||
Time("latest", remoteLastModified.Local()).
|
|
||||||
Time("current", cfg.lastUpdate).
|
|
||||||
Msg("MaxMind DB update available")
|
|
||||||
reader, err := cfg.download()
|
|
||||||
if err != nil {
|
|
||||||
cfg.logger.Err(err).Msg("failed to update MaxMind DB")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.db.Lock()
|
|
||||||
cfg.db.Close()
|
|
||||||
cfg.db.Reader = reader
|
|
||||||
cfg.setLastUpdate(*remoteLastModified)
|
|
||||||
cfg.db.Unlock()
|
|
||||||
|
|
||||||
cfg.logger.Info().Msg("MaxMind DB updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) newReq(method string) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest(method, dbURL(cfg.Database), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.SetBasicAuth(cfg.AccountID, cfg.LicenseKey)
|
|
||||||
resp, err := httpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) checkLastest() (lastModifiedT *time.Time, err error) {
|
|
||||||
resp, err := newReq(cfg, http.MethodHead)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("%w: %d", ErrResponseNotOK, resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastModified := resp.Header.Get("Last-Modified")
|
|
||||||
if lastModified == "" {
|
|
||||||
cfg.logger.Warn().Msg("MaxMind responded no last modified time, update skipped")
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
lastModifiedTime, err := time.Parse(http.TimeFormat, lastModified)
|
|
||||||
if err != nil {
|
|
||||||
cfg.logger.Warn().Err(err).Msg("MaxMind responded invalid last modified time, update skipped")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &lastModifiedTime, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *MaxMindConfig) download() (*maxminddb.Reader, error) {
|
|
||||||
resp, err := newReq(cfg, http.MethodGet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("%w: %d", ErrResponseNotOK, resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := dbPath(cfg.Database)
|
|
||||||
tmpPath := path + "-tmp.tar.gz"
|
|
||||||
file, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_WRONLY, 0o644)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.logger.Info().Msg("MaxMind DB downloading...")
|
|
||||||
|
|
||||||
_, err = io.Copy(file, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
file.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
// extract .tar.gz and move only the dbFilename to path
|
|
||||||
err = extractFileFromTarGz(tmpPath, dbFilename(cfg.Database), path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, gperr.New("failed to extract database from archive").With(err)
|
|
||||||
}
|
|
||||||
// cleanup the tar.gz file
|
|
||||||
_ = os.Remove(tmpPath)
|
|
||||||
|
|
||||||
db, err := maxmindDBOpen(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return db, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractFileFromTarGz(tarGzPath, targetFilename, destPath string) error {
|
|
||||||
f, err := os.Open(tarGzPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
gzr, err := gzip.NewReader(f)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer gzr.Close()
|
|
||||||
|
|
||||||
tr := tar.NewReader(gzr)
|
|
||||||
for {
|
|
||||||
hdr, err := tr.Next()
|
|
||||||
if err == io.EOF {
|
|
||||||
break // End of archive
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Only extract the file that matches targetFilename (basename match)
|
|
||||||
if filepath.Base(hdr.Name) == targetFilename {
|
|
||||||
outFile, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, hdr.FileInfo().Mode())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer outFile.Close()
|
|
||||||
_, err = io.Copy(outFile, tr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil // Done
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("file %s not found in archive", targetFilename)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
dataDir = common.DataDir
|
|
||||||
dbURL = dbURLimpl
|
|
||||||
dbPath = dbPathImpl
|
|
||||||
maxmindDBOpen = maxminddb.Open
|
|
||||||
newReq = (*MaxMindConfig).newReq
|
|
||||||
)
|
|
|
@ -1,213 +0,0 @@
|
||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/oschwald/maxminddb-golang"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_dbPath(t *testing.T) {
|
|
||||||
tmpDataDir := "/tmp/testdata"
|
|
||||||
oldDataDir := dataDir
|
|
||||||
dataDir = tmpDataDir
|
|
||||||
defer func() { dataDir = oldDataDir }()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
dbType MaxMindDatabaseType
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"GeoLite", MaxMindGeoLite, filepath.Join(tmpDataDir, "GeoLite2-City.mmdb")},
|
|
||||||
{"GeoIP2", MaxMindGeoIP2, filepath.Join(tmpDataDir, "GeoIP2-City.mmdb")},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := dbPath(tt.dbType); got != tt.want {
|
|
||||||
t.Errorf("dbPath() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_dbURL(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
dbType MaxMindDatabaseType
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"GeoLite", MaxMindGeoLite, "https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz"},
|
|
||||||
{"GeoIP2", MaxMindGeoIP2, "https://download.maxmind.com/geoip/databases/GeoIP2-City/download?suffix=tar.gz"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := dbURL(tt.dbType); got != tt.want {
|
|
||||||
t.Errorf("dbURL() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Helper for MaxMindConfig ---
|
|
||||||
type testLogger struct{ zerolog.Logger }
|
|
||||||
|
|
||||||
func (testLogger) Info() *zerolog.Event { return &zerolog.Event{} }
|
|
||||||
func (testLogger) Warn() *zerolog.Event { return &zerolog.Event{} }
|
|
||||||
func (testLogger) Err(_ error) *zerolog.Event { return &zerolog.Event{} }
|
|
||||||
|
|
||||||
func Test_MaxMindConfig_newReq(t *testing.T) {
|
|
||||||
cfg := &MaxMindConfig{
|
|
||||||
AccountID: "testid",
|
|
||||||
LicenseKey: "testkey",
|
|
||||||
Database: MaxMindGeoLite,
|
|
||||||
logger: zerolog.Nop(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch httpClient to use httptest
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if u, p, ok := r.BasicAuth(); !ok || u != "testid" || p != "testkey" {
|
|
||||||
t.Errorf("basic auth not set correctly")
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
oldURL := dbURL
|
|
||||||
dbURL = func(MaxMindDatabaseType) string { return server.URL }
|
|
||||||
defer func() { dbURL = oldURL }()
|
|
||||||
|
|
||||||
resp, err := cfg.newReq(http.MethodGet)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("newReq() error = %v", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
t.Errorf("unexpected status: %v", resp.StatusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_MaxMindConfig_checkUpdate(t *testing.T) {
|
|
||||||
cfg := &MaxMindConfig{
|
|
||||||
AccountID: "id",
|
|
||||||
LicenseKey: "key",
|
|
||||||
Database: MaxMindGeoLite,
|
|
||||||
logger: zerolog.Nop(),
|
|
||||||
}
|
|
||||||
lastMod := time.Now().UTC().Format(http.TimeFormat)
|
|
||||||
buildTime := time.Now().Add(-time.Hour)
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Last-Modified", lastMod)
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
oldURL := dbURL
|
|
||||||
dbURL = func(MaxMindDatabaseType) string { return server.URL }
|
|
||||||
defer func() { dbURL = oldURL }()
|
|
||||||
|
|
||||||
latest, err := cfg.checkLastest()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("checkUpdate() error = %v", err)
|
|
||||||
}
|
|
||||||
if latest.Equal(buildTime) {
|
|
||||||
t.Errorf("expected update needed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeReadCloser struct {
|
|
||||||
firstRead bool
|
|
||||||
closed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeReadCloser) Read(p []byte) (int, error) {
|
|
||||||
if !c.firstRead {
|
|
||||||
c.firstRead = true
|
|
||||||
return strings.NewReader("FAKEMMDB").Read(p)
|
|
||||||
}
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeReadCloser) Close() error {
|
|
||||||
c.closed = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_MaxMindConfig_download(t *testing.T) {
|
|
||||||
cfg := &MaxMindConfig{
|
|
||||||
AccountID: "id",
|
|
||||||
LicenseKey: "key",
|
|
||||||
Database: MaxMindGeoLite,
|
|
||||||
logger: zerolog.Nop(),
|
|
||||||
}
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
io.Copy(w, strings.NewReader("FAKEMMDB"))
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
oldURL := dbURL
|
|
||||||
dbURL = func(MaxMindDatabaseType) string { return server.URL }
|
|
||||||
defer func() { dbURL = oldURL }()
|
|
||||||
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
oldDataDir := dataDir
|
|
||||||
dataDir = tmpDir
|
|
||||||
defer func() { dataDir = oldDataDir }()
|
|
||||||
|
|
||||||
// Patch maxminddb.Open to always succeed
|
|
||||||
origOpen := maxmindDBOpen
|
|
||||||
maxmindDBOpen = func(path string) (*maxminddb.Reader, error) {
|
|
||||||
return &maxminddb.Reader{}, nil
|
|
||||||
}
|
|
||||||
defer func() { maxmindDBOpen = origOpen }()
|
|
||||||
|
|
||||||
rw := &fakeReadCloser{}
|
|
||||||
oldNewReq := newReq
|
|
||||||
newReq = func(cfg *MaxMindConfig, method string) (*http.Response, error) {
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: http.StatusOK,
|
|
||||||
Body: rw,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
defer func() { newReq = oldNewReq }()
|
|
||||||
|
|
||||||
db, err := cfg.download()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("download() error = %v", err)
|
|
||||||
}
|
|
||||||
if db == nil {
|
|
||||||
t.Error("expected db instance")
|
|
||||||
}
|
|
||||||
if !rw.closed {
|
|
||||||
t.Error("expected rw to be closed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_MaxMindConfig_loadMaxMindDB(t *testing.T) {
|
|
||||||
// This test should cover both the path where DB exists and where it does not
|
|
||||||
// For brevity, only the non-existing path is tested here
|
|
||||||
cfg := &MaxMindConfig{
|
|
||||||
AccountID: "id",
|
|
||||||
LicenseKey: "key",
|
|
||||||
Database: MaxMindGeoLite,
|
|
||||||
logger: zerolog.Nop(),
|
|
||||||
}
|
|
||||||
oldOpen := maxmindDBOpen
|
|
||||||
maxmindDBOpen = func(path string) (*maxminddb.Reader, error) {
|
|
||||||
return &maxminddb.Reader{}, nil
|
|
||||||
}
|
|
||||||
defer func() { maxmindDBOpen = oldOpen }()
|
|
||||||
|
|
||||||
oldDBPath := dbPath
|
|
||||||
dbPath = func(MaxMindDatabaseType) string { return filepath.Join(t.TempDir(), "maxmind.mmdb") }
|
|
||||||
defer func() { dbPath = oldDBPath }()
|
|
||||||
|
|
||||||
task := task.RootTask("test")
|
|
||||||
defer task.Finish(nil)
|
|
||||||
err := cfg.LoadMaxMindDB(task)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("loadMaxMindDB() error = %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,9 @@
|
||||||
package acl
|
package acl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TCPListener struct {
|
type TCPListener struct {
|
||||||
|
@ -9,6 +11,17 @@ type TCPListener struct {
|
||||||
lis net.Listener
|
lis net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noConn struct{}
|
||||||
|
|
||||||
|
func (noConn) Read(b []byte) (int, error) { return 0, io.EOF }
|
||||||
|
func (noConn) Write(b []byte) (int, error) { return 0, io.EOF }
|
||||||
|
func (noConn) Close() error { return nil }
|
||||||
|
func (noConn) LocalAddr() net.Addr { return nil }
|
||||||
|
func (noConn) RemoteAddr() net.Addr { return nil }
|
||||||
|
func (noConn) SetDeadline(t time.Time) error { return nil }
|
||||||
|
func (noConn) SetReadDeadline(t time.Time) error { return nil }
|
||||||
|
func (noConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||||
|
|
||||||
func (cfg *Config) WrapTCP(lis net.Listener) net.Listener {
|
func (cfg *Config) WrapTCP(lis net.Listener) net.Listener {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return lis
|
return lis
|
||||||
|
@ -32,11 +45,11 @@ func (s *TCPListener) Accept() (net.Conn, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
// Not a TCPAddr, drop
|
// Not a TCPAddr, drop
|
||||||
c.Close()
|
c.Close()
|
||||||
return nil, nil
|
return noConn{}, nil
|
||||||
}
|
}
|
||||||
if !s.acl.IPAllowed(addr.IP) {
|
if !s.acl.IPAllowed(addr.IP) {
|
||||||
c.Close()
|
c.Close()
|
||||||
return nil, nil
|
return noConn{}, nil
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ type UDPListener struct {
|
||||||
lis net.PacketConn
|
lis net.PacketConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) WrapUDP(lis net.PacketConn) net.PacketConn {
|
func (c *Config) WrapUDP(lis net.PacketConn) net.PacketConn {
|
||||||
if cfg == nil {
|
if c == nil {
|
||||||
return lis
|
return lis
|
||||||
}
|
}
|
||||||
return &UDPListener{
|
return &UDPListener{
|
||||||
acl: cfg,
|
acl: c,
|
||||||
lis: lis,
|
lis: lis,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
config "github.com/yusing/go-proxy/internal/config/types"
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
||||||
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
"github.com/yusing/go-proxy/pkg"
|
"github.com/yusing/go-proxy/pkg"
|
||||||
|
@ -45,7 +46,7 @@ func (mux ServeMux) HandleFunc(methods, endpoint string, h any, requireAuth ...b
|
||||||
origHandler := handler
|
origHandler := handler
|
||||||
handler = func(w http.ResponseWriter, r *http.Request) {
|
handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
if httpheaders.IsWebsocket(r.Header) {
|
if httpheaders.IsWebsocket(r.Header) {
|
||||||
httpheaders.SetWebsocketAllowedDomains(r.Header, matchDomains)
|
gpwebsocket.SetWebsocketAllowedDomains(r.Header, matchDomains)
|
||||||
}
|
}
|
||||||
origHandler(w, r)
|
origHandler(w, r)
|
||||||
}
|
}
|
||||||
|
@ -70,9 +71,15 @@ func NewHandler(cfg config.ConfigInstance) http.Handler {
|
||||||
|
|
||||||
mux.HandleFunc("GET", "/v1/stats", v1.Stats, true)
|
mux.HandleFunc("GET", "/v1/stats", v1.Stats, true)
|
||||||
mux.HandleFunc("POST", "/v1/reload", v1.Reload, true)
|
mux.HandleFunc("POST", "/v1/reload", v1.Reload, true)
|
||||||
mux.HandleFunc("GET", "/v1/list", v1.List, true)
|
mux.HandleFunc("GET", "/v1/list", v1.ListRoutesHandler, true)
|
||||||
mux.HandleFunc("GET", "/v1/list/{what}", v1.List, true)
|
mux.HandleFunc("GET", "/v1/list/routes", v1.ListRoutesHandler, true)
|
||||||
mux.HandleFunc("GET", "/v1/list/{what}/{which}", v1.List, true)
|
mux.HandleFunc("GET", "/v1/list/route/{which}", v1.ListRouteHandler, true)
|
||||||
|
mux.HandleFunc("GET", "/v1/list/routes_by_provider", v1.ListRoutesByProviderHandler, true)
|
||||||
|
mux.HandleFunc("GET", "/v1/list/files", v1.ListFilesHandler, true)
|
||||||
|
mux.HandleFunc("GET", "/v1/list/homepage_config", v1.ListHomepageConfigHandler, true)
|
||||||
|
mux.HandleFunc("GET", "/v1/list/route_providers", v1.ListRouteProvidersHandler, true)
|
||||||
|
mux.HandleFunc("GET", "/v1/list/homepage_categories", v1.ListHomepageCategoriesHandler, true)
|
||||||
|
mux.HandleFunc("GET", "/v1/list/icons", v1.ListIconsHandler, true)
|
||||||
mux.HandleFunc("GET", "/v1/file/{type}/{filename}", v1.GetFileContent, true)
|
mux.HandleFunc("GET", "/v1/file/{type}/{filename}", v1.GetFileContent, true)
|
||||||
mux.HandleFunc("POST,PUT", "/v1/file/{type}/{filename}", v1.SetFileContent, true)
|
mux.HandleFunc("POST,PUT", "/v1/file/{type}/{filename}", v1.SetFileContent, true)
|
||||||
mux.HandleFunc("POST", "/v1/file/validate/{type}", v1.ValidateFile, true)
|
mux.HandleFunc("POST", "/v1/file/validate/{type}", v1.ValidateFile, true)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -51,12 +52,12 @@ func (t FileType) GetPath(filename string) string {
|
||||||
func getArgs(r *http.Request) (fileType FileType, filename string, err error) {
|
func getArgs(r *http.Request) (fileType FileType, filename string, err error) {
|
||||||
fileType = FileType(r.PathValue("type"))
|
fileType = FileType(r.PathValue("type"))
|
||||||
if !fileType.IsValid() {
|
if !fileType.IsValid() {
|
||||||
err = gphttp.ErrInvalidKey("type")
|
err = fmt.Errorf("invalid file type: %s", fileType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
filename = r.PathValue("filename")
|
filename = r.PathValue("filename")
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
err = gphttp.ErrMissingKey("filename")
|
err = fmt.Errorf("missing filename")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package dockerapi
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
const reqTimeout = 10 * time.Second
|
|
|
@ -18,7 +18,7 @@ type Container struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Containers(w http.ResponseWriter, r *http.Request) {
|
func Containers(w http.ResponseWriter, r *http.Request) {
|
||||||
serveHTTP[Container, []Container](w, r, GetContainers)
|
serveHTTP[Container](w, r, GetContainers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Container, gperr.Error) {
|
func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Container, gperr.Error) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ func Logs(w http.ResponseWriter, r *http.Request) {
|
||||||
until := query.Get("to")
|
until := query.Get("to")
|
||||||
levels := query.Get("levels") // TODO: implement levels
|
levels := query.Get("levels") // TODO: implement levels
|
||||||
|
|
||||||
dockerClient, found, err := getDockerClient(w, server)
|
dockerClient, found, err := getDockerClient(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gphttp.BadRequest(w, err.Error())
|
gphttp.BadRequest(w, err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -56,7 +56,7 @@ func getDockerClients() (DockerClients, gperr.Error) {
|
||||||
return dockerClients, connErrs.Error()
|
return dockerClients, connErrs.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDockerClient(w http.ResponseWriter, server string) (*docker.SharedClient, bool, error) {
|
func getDockerClient(server string) (*docker.SharedClient, bool, error) {
|
||||||
cfg := config.GetInstance()
|
cfg := config.GetInstance()
|
||||||
var host string
|
var host string
|
||||||
for name, h := range cfg.Value().Providers.Docker {
|
for name, h := range cfg.Value().Providers.Docker {
|
||||||
|
@ -98,7 +98,7 @@ func handleResult[V any, T ResultType[V]](w http.ResponseWriter, errs error, res
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(result)
|
json.NewEncoder(w).Encode(result) //nolint
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveHTTP[V any, T ResultType[V]](w http.ResponseWriter, r *http.Request, getResult func(ctx context.Context, dockerClients DockerClients) (T, gperr.Error)) {
|
func serveHTTP[V any, T ResultType[V]](w http.ResponseWriter, r *http.Request, getResult func(ctx context.Context, dockerClients DockerClients) (T, gperr.Error)) {
|
||||||
|
@ -119,6 +119,6 @@ func serveHTTP[V any, T ResultType[V]](w http.ResponseWriter, r *http.Request, g
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
result, err := getResult(r.Context(), dockerClients)
|
result, err := getResult(r.Context(), dockerClients)
|
||||||
handleResult[V, T](w, err, result)
|
handleResult[V](w, err, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package favicon
|
package favicon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
|
||||||
"github.com/yusing/go-proxy/internal/homepage"
|
"github.com/yusing/go-proxy/internal/homepage"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/route/routes"
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
|
@ -21,11 +19,11 @@ import (
|
||||||
func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
||||||
url, alias := req.FormValue("url"), req.FormValue("alias")
|
url, alias := req.FormValue("url"), req.FormValue("alias")
|
||||||
if url == "" && alias == "" {
|
if url == "" && alias == "" {
|
||||||
gphttp.ClientError(w, gphttp.ErrMissingKey("url or alias"), http.StatusBadRequest)
|
gphttp.MissingKey(w, "url or alias")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if url != "" && alias != "" {
|
if url != "" && alias != "" {
|
||||||
gphttp.ClientError(w, gperr.New("url and alias are mutually exclusive"), http.StatusBadRequest)
|
gphttp.BadRequest(w, "url and alias are mutually exclusive")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +31,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
||||||
if url != "" {
|
if url != "" {
|
||||||
var iconURL homepage.IconURL
|
var iconURL homepage.IconURL
|
||||||
if err := iconURL.Parse(url); err != nil {
|
if err := iconURL.Parse(url); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, req, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fetchResult := homepage.FetchFavIconFromURL(req.Context(), &iconURL)
|
fetchResult := homepage.FetchFavIconFromURL(req.Context(), &iconURL)
|
||||||
|
@ -49,7 +47,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
||||||
// try with route.Icon
|
// try with route.Icon
|
||||||
r, ok := routes.HTTP.Get(alias)
|
r, ok := routes.HTTP.Get(alias)
|
||||||
if !ok {
|
if !ok {
|
||||||
gphttp.ClientError(w, errors.New("no such route"), http.StatusNotFound)
|
gphttp.ValueNotFound(w, "route", alias)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +55,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
||||||
hp := r.HomepageItem()
|
hp := r.HomepageItem()
|
||||||
if hp.Icon != nil {
|
if hp.Icon != nil {
|
||||||
if hp.Icon.IconSource == homepage.IconSourceRelative {
|
if hp.Icon.IconSource == homepage.IconSourceRelative {
|
||||||
result = homepage.FindIcon(req.Context(), r, hp.Icon.Value)
|
result = homepage.FindIcon(req.Context(), r, *hp.Icon.FullURL)
|
||||||
} else {
|
} else {
|
||||||
result = homepage.FetchFavIconFromURL(req.Context(), hp.Icon)
|
result = homepage.FetchFavIconFromURL(req.Context(), hp.Icon)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
data, err := io.ReadAll(r.Body)
|
data, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
|
@ -53,21 +53,21 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||||
case HomepageOverrideItem:
|
case HomepageOverrideItem:
|
||||||
var params HomepageOverrideItemParams
|
var params HomepageOverrideItemParams
|
||||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
overrides.OverrideItem(params.Which, ¶ms.Value)
|
overrides.OverrideItem(params.Which, ¶ms.Value)
|
||||||
case HomepageOverrideItemsBatch:
|
case HomepageOverrideItemsBatch:
|
||||||
var params HomepageOverrideItemsBatchParams
|
var params HomepageOverrideItemsBatchParams
|
||||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
overrides.OverrideItems(params.Value)
|
overrides.OverrideItems(params.Value)
|
||||||
case HomepageOverrideItemVisible: // POST /v1/item_visible [a,b,c], false => hide a, b, c
|
case HomepageOverrideItemVisible: // POST /v1/item_visible [a,b,c], false => hide a, b, c
|
||||||
var params HomepageOverrideItemVisibleParams
|
var params HomepageOverrideItemVisibleParams
|
||||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if params.Value {
|
if params.Value {
|
||||||
|
@ -78,7 +78,7 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||||
case HomepageOverrideCategoryOrder:
|
case HomepageOverrideCategoryOrder:
|
||||||
var params HomepageOverrideCategoryOrderParams
|
var params HomepageOverrideCategoryOrderParams
|
||||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
overrides.SetCategoryOrder(params.Which, params.Value)
|
overrides.SetCategoryOrder(params.Which, params.Value)
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal"
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
|
||||||
config "github.com/yusing/go-proxy/internal/config/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
|
||||||
"github.com/yusing/go-proxy/internal/route/routes"
|
|
||||||
route "github.com/yusing/go-proxy/internal/route/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ListRoute = "route"
|
|
||||||
ListRoutes = "routes"
|
|
||||||
ListFiles = "files"
|
|
||||||
ListMiddlewares = "middlewares"
|
|
||||||
ListMiddlewareTraces = "middleware_trace"
|
|
||||||
ListMatchDomains = "match_domains"
|
|
||||||
ListHomepageConfig = "homepage_config"
|
|
||||||
ListRouteProviders = "route_providers"
|
|
||||||
ListHomepageCategories = "homepage_categories"
|
|
||||||
ListIcons = "icons"
|
|
||||||
ListTasks = "tasks"
|
|
||||||
)
|
|
||||||
|
|
||||||
func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
|
||||||
what := r.PathValue("what")
|
|
||||||
if what == "" {
|
|
||||||
what = ListRoutes
|
|
||||||
}
|
|
||||||
which := r.PathValue("which")
|
|
||||||
|
|
||||||
switch what {
|
|
||||||
case ListRoute:
|
|
||||||
route := listRoute(which)
|
|
||||||
if route == nil {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
} else {
|
|
||||||
gphttp.RespondJSON(w, r, route)
|
|
||||||
}
|
|
||||||
case ListRoutes:
|
|
||||||
gphttp.RespondJSON(w, r, routes.ByAlias(route.RouteType(r.FormValue("type"))))
|
|
||||||
case ListFiles:
|
|
||||||
listFiles(w, r)
|
|
||||||
case ListMiddlewares:
|
|
||||||
gphttp.RespondJSON(w, r, middleware.All())
|
|
||||||
case ListMiddlewareTraces:
|
|
||||||
gphttp.RespondJSON(w, r, middleware.GetAllTrace())
|
|
||||||
case ListMatchDomains:
|
|
||||||
gphttp.RespondJSON(w, r, cfg.Value().MatchDomains)
|
|
||||||
case ListHomepageConfig:
|
|
||||||
gphttp.RespondJSON(w, r, routes.HomepageConfig(r.FormValue("category"), r.FormValue("provider")))
|
|
||||||
case ListRouteProviders:
|
|
||||||
gphttp.RespondJSON(w, r, cfg.RouteProviderList())
|
|
||||||
case ListHomepageCategories:
|
|
||||||
gphttp.RespondJSON(w, r, routes.HomepageCategories())
|
|
||||||
case ListIcons:
|
|
||||||
limit, err := strconv.Atoi(r.FormValue("limit"))
|
|
||||||
if err != nil {
|
|
||||||
limit = 0
|
|
||||||
}
|
|
||||||
icons, err := internal.SearchIcons(r.FormValue("keyword"), limit)
|
|
||||||
if err != nil {
|
|
||||||
gphttp.ClientError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if icons == nil {
|
|
||||||
icons = []string{}
|
|
||||||
}
|
|
||||||
gphttp.RespondJSON(w, r, icons)
|
|
||||||
case ListTasks:
|
|
||||||
gphttp.RespondJSON(w, r, task.DebugTaskList())
|
|
||||||
default:
|
|
||||||
gphttp.BadRequest(w, fmt.Sprintf("invalid what: %s", what))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if which is "all" or empty, return map[string]Route of all routes
|
|
||||||
// otherwise, return a single Route with alias which or nil if not found.
|
|
||||||
func listRoute(which string) any {
|
|
||||||
if which == "" || which == "all" {
|
|
||||||
return routes.ByAlias()
|
|
||||||
}
|
|
||||||
routes := routes.ByAlias()
|
|
||||||
route, ok := routes[which]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return route
|
|
||||||
}
|
|
||||||
|
|
||||||
func listFiles(w http.ResponseWriter, r *http.Request) {
|
|
||||||
files, err := utils.ListFiles(common.ConfigBasePath, 0, true)
|
|
||||||
if err != nil {
|
|
||||||
gphttp.ServerError(w, r, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp := map[FileType][]string{
|
|
||||||
FileTypeConfig: make([]string, 0),
|
|
||||||
FileTypeProvider: make([]string, 0),
|
|
||||||
FileTypeMiddleware: make([]string, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
t := fileType(file)
|
|
||||||
file = strings.TrimPrefix(file, common.ConfigBasePath+"/")
|
|
||||||
resp[t] = append(resp[t], file)
|
|
||||||
}
|
|
||||||
|
|
||||||
mids, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
|
||||||
if err != nil {
|
|
||||||
gphttp.ServerError(w, r, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, mid := range mids {
|
|
||||||
mid = strings.TrimPrefix(mid, common.MiddlewareComposeBasePath+"/")
|
|
||||||
resp[FileTypeMiddleware] = append(resp[FileTypeMiddleware], mid)
|
|
||||||
}
|
|
||||||
gphttp.RespondJSON(w, r, resp)
|
|
||||||
}
|
|
41
internal/api/v1/list_files.go
Normal file
41
internal/api/v1/list_files.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListFilesHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
files, err := utils.ListFiles(common.ConfigBasePath, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
gphttp.ServerError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := map[FileType][]string{
|
||||||
|
FileTypeConfig: make([]string, 0),
|
||||||
|
FileTypeProvider: make([]string, 0),
|
||||||
|
FileTypeMiddleware: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
t := fileType(file)
|
||||||
|
file = strings.TrimPrefix(file, common.ConfigBasePath+"/")
|
||||||
|
resp[t] = append(resp[t], file)
|
||||||
|
}
|
||||||
|
|
||||||
|
mids, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
gphttp.ServerError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, mid := range mids {
|
||||||
|
mid = strings.TrimPrefix(mid, common.MiddlewareComposeBasePath+"/")
|
||||||
|
resp[FileTypeMiddleware] = append(resp[FileTypeMiddleware], mid)
|
||||||
|
}
|
||||||
|
gphttp.RespondJSON(w, r, resp)
|
||||||
|
}
|
13
internal/api/v1/list_homepage_categories.go
Normal file
13
internal/api/v1/list_homepage_categories.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListHomepageCategoriesHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
gphttp.RespondJSON(w, r, routes.HomepageCategories())
|
||||||
|
}
|
13
internal/api/v1/list_homepage_config.go
Normal file
13
internal/api/v1/list_homepage_config.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListHomepageConfigHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
gphttp.RespondJSON(w, r, routes.HomepageConfig(r.FormValue("category"), r.FormValue("provider")))
|
||||||
|
}
|
23
internal/api/v1/list_icons.go
Normal file
23
internal/api/v1/list_icons.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/homepage"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListIconsHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
limit, err := strconv.Atoi(r.FormValue("limit"))
|
||||||
|
if err != nil {
|
||||||
|
limit = 0
|
||||||
|
}
|
||||||
|
icons, err := homepage.SearchIcons(r.FormValue("keyword"), limit)
|
||||||
|
if err != nil {
|
||||||
|
gphttp.ClientError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gphttp.RespondJSON(w, r, icons)
|
||||||
|
}
|
19
internal/api/v1/list_route.go
Normal file
19
internal/api/v1/list_route.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListRouteHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
which := r.PathValue("which")
|
||||||
|
route, ok := routes.Get(which)
|
||||||
|
if ok {
|
||||||
|
gphttp.RespondJSON(w, r, route)
|
||||||
|
} else {
|
||||||
|
gphttp.RespondJSON(w, r, nil)
|
||||||
|
}
|
||||||
|
}
|
23
internal/api/v1/list_route_providers.go
Normal file
23
internal/api/v1/list_route_providers.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coder/websocket"
|
||||||
|
"github.com/coder/websocket/wsjson"
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListRouteProvidersHandler(cfgInstance config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if httpheaders.IsWebsocket(r.Header) {
|
||||||
|
gpwebsocket.Periodic(w, r, 5*time.Second, func(conn *websocket.Conn) error {
|
||||||
|
return wsjson.Write(r.Context(), conn, cfgInstance.RouteProviderList())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
gphttp.RespondJSON(w, r, cfgInstance.RouteProviderList())
|
||||||
|
}
|
||||||
|
}
|
25
internal/api/v1/list_routes.go
Normal file
25
internal/api/v1/list_routes.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListRoutesHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
rts := make([]routes.Route, 0)
|
||||||
|
provider := r.FormValue("provider")
|
||||||
|
if provider == "" {
|
||||||
|
gphttp.RespondJSON(w, r, slices.Collect(routes.Iter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for r := range routes.Iter {
|
||||||
|
if r.ProviderName() == provider {
|
||||||
|
rts = append(rts, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gphttp.RespondJSON(w, r, rts)
|
||||||
|
}
|
13
internal/api/v1/list_routes_by_provider.go
Normal file
13
internal/api/v1/list_routes_by_provider.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListRoutesByProviderHandler(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||||
|
gphttp.RespondJSON(w, r, routes.ByProvider())
|
||||||
|
}
|
|
@ -20,27 +20,27 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
q := r.URL.Query()
|
q := r.URL.Query()
|
||||||
name := q.Get("name")
|
name := q.Get("name")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
gphttp.ClientError(w, gphttp.ErrMissingKey("name"))
|
gphttp.MissingKey(w, "name")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
host := q.Get("host")
|
host := q.Get("host")
|
||||||
if host == "" {
|
if host == "" {
|
||||||
gphttp.ClientError(w, gphttp.ErrMissingKey("host"))
|
gphttp.MissingKey(w, "host")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
portStr := q.Get("port")
|
portStr := q.Get("port")
|
||||||
if portStr == "" {
|
if portStr == "" {
|
||||||
gphttp.ClientError(w, gphttp.ErrMissingKey("port"))
|
gphttp.MissingKey(w, "port")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(portStr)
|
port, err := strconv.Atoi(portStr)
|
||||||
if err != nil || port < 1 || port > 65535 {
|
if err != nil || port < 1 || port > 65535 {
|
||||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("port"))
|
gphttp.InvalidKey(w, "port")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hostport := fmt.Sprintf("%s:%d", host, port)
|
hostport := fmt.Sprintf("%s:%d", host, port)
|
||||||
if _, ok := config.GetInstance().GetAgent(hostport); ok {
|
if _, ok := config.GetInstance().GetAgent(hostport); ok {
|
||||||
gphttp.ClientError(w, gphttp.ErrAlreadyExists("agent", hostport), http.StatusConflict)
|
gphttp.KeyAlreadyExists(w, "agent", hostport)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := q.Get("type")
|
t := q.Get("type")
|
||||||
|
@ -48,10 +48,10 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
case "docker", "system":
|
case "docker", "system":
|
||||||
break
|
break
|
||||||
case "":
|
case "":
|
||||||
gphttp.ClientError(w, gphttp.ErrMissingKey("type"))
|
gphttp.MissingKey(w, "type")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("type"))
|
gphttp.InvalidKey(w, "type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,13 +109,13 @@ func VerifyNewAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(clientPEMData, &data); err != nil {
|
if err := json.Unmarshal(clientPEMData, &data); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
gphttp.ClientError(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nRoutesAdded, err := config.GetInstance().VerifyNewAgent(data.Host, data.CA, data.Client)
|
nRoutesAdded, err := config.GetInstance().VerifyNewAgent(data.Host, data.CA, data.Client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gphttp.ClientError(w, err)
|
gphttp.ClientError(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ func VerifyNewAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
filename, ok := certs.AgentCertsFilepath(data.Host)
|
filename, ok := certs.AgentCertsFilepath(data.Host)
|
||||||
if !ok {
|
if !ok {
|
||||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("host"))
|
gphttp.InvalidKey(w, "host")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
package query
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
v1 "github.com/yusing/go-proxy/internal/api/v1"
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ReloadServer() gperr.Error {
|
|
||||||
resp, err := gphttp.Post(common.APIHTTPURL+"/v1/reload", "", nil)
|
|
||||||
if err != nil {
|
|
||||||
return gperr.Wrap(err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
failure := gperr.Errorf("server reload status %v", resp.StatusCode)
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return failure.With(err)
|
|
||||||
}
|
|
||||||
reloadErr := string(body)
|
|
||||||
return failure.Withf(reloadErr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func List[T any](what string) (_ T, outErr gperr.Error) {
|
|
||||||
resp, err := gphttp.Get(fmt.Sprintf("%s/v1/list/%s", common.APIHTTPURL, what))
|
|
||||||
if err != nil {
|
|
||||||
outErr = gperr.Wrap(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
outErr = gperr.Errorf("list %s: failed, status %v", what, resp.StatusCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var res T
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&res)
|
|
||||||
if err != nil {
|
|
||||||
outErr = gperr.Wrap(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListRoutes() (map[string]map[string]any, gperr.Error) {
|
|
||||||
return List[map[string]map[string]any](v1.ListRoutes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListMiddlewareTraces() (middleware.Traces, gperr.Error) {
|
|
||||||
return List[middleware.Traces](v1.ListMiddlewareTraces)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DebugListTasks() (map[string]any, gperr.Error) {
|
|
||||||
return List[map[string]any](v1.ListTasks)
|
|
||||||
}
|
|
|
@ -38,22 +38,35 @@ func IsOIDCEnabled() bool {
|
||||||
return common.OIDCIssuerURL != ""
|
return common.OIDCIssuerURL != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nextHandler struct{}
|
||||||
|
|
||||||
|
var nextHandlerContextKey = nextHandler{}
|
||||||
|
|
||||||
func RequireAuth(next http.HandlerFunc) http.HandlerFunc {
|
func RequireAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||||
if IsEnabled() {
|
if !IsEnabled() {
|
||||||
|
return next
|
||||||
|
}
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := defaultAuth.CheckToken(r); err != nil {
|
if err := defaultAuth.CheckToken(r); err != nil {
|
||||||
gphttp.ClientError(w, err, http.StatusUnauthorized)
|
gphttp.Unauthorized(w, err.Error())
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
next(w, r)
|
next(w, r)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return next
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
func ProceedNext(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := defaultAuth.CheckToken(r); err != nil {
|
next, ok := r.Context().Value(nextHandlerContextKey).(http.HandlerFunc)
|
||||||
http.Redirect(w, r, "/v1/auth/login", http.StatusFound)
|
if ok {
|
||||||
|
next(w, r)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := defaultAuth.CheckToken(r); err != nil {
|
||||||
|
defaultAuth.LoginHandler(w, r)
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
@ -19,6 +21,10 @@ type oauthRefreshToken struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
Expiry time.Time `json:"expiry"`
|
Expiry time.Time `json:"expiry"`
|
||||||
|
|
||||||
|
result *RefreshResult
|
||||||
|
err error
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
@ -27,6 +33,12 @@ type Session struct {
|
||||||
Groups []string `json:"groups"`
|
Groups []string `json:"groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RefreshResult struct {
|
||||||
|
newSession Session
|
||||||
|
jwt string
|
||||||
|
jwtExpiry time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type sessionClaims struct {
|
type sessionClaims struct {
|
||||||
Session
|
Session
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
|
@ -34,11 +46,11 @@ type sessionClaims struct {
|
||||||
|
|
||||||
type sessionID string
|
type sessionID string
|
||||||
|
|
||||||
var oauthRefreshTokens jsonstore.MapStore[oauthRefreshToken]
|
var oauthRefreshTokens jsonstore.MapStore[*oauthRefreshToken]
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultRefreshTokenExpiry = 30 * 24 * time.Hour // 1 month
|
defaultRefreshTokenExpiry = 30 * 24 * time.Hour // 1 month
|
||||||
refreshBefore = 30 * time.Second
|
sessionInvalidateDelay = 3 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -50,7 +62,7 @@ const sessionTokenIssuer = "GoDoxy"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if IsOIDCEnabled() {
|
if IsOIDCEnabled() {
|
||||||
oauthRefreshTokens = jsonstore.Store[oauthRefreshToken]("oauth_refresh_tokens")
|
oauthRefreshTokens = jsonstore.Store[*oauthRefreshToken]("oauth_refresh_tokens")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +73,7 @@ func (token *oauthRefreshToken) expired() bool {
|
||||||
func newSessionID() sessionID {
|
func newSessionID() sessionID {
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
_, _ = rand.Read(b)
|
_, _ = rand.Read(b)
|
||||||
return sessionID(base64.StdEncoding.EncodeToString(b))
|
return sessionID(hex.EncodeToString(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSession(username string, groups []string) Session {
|
func newSession(username string, groups []string) Session {
|
||||||
|
@ -72,26 +84,26 @@ func newSession(username string, groups []string) Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOnceOAuthRefreshToken returns the refresh token for the given session.
|
// getOAuthRefreshToken returns the refresh token for the given session.
|
||||||
//
|
func getOAuthRefreshToken(claims *Session) (*oauthRefreshToken, bool) {
|
||||||
// The token is removed from the store after retrieval.
|
|
||||||
func getOnceOAuthRefreshToken(claims *Session) (*oauthRefreshToken, bool) {
|
|
||||||
token, ok := oauthRefreshTokens.Load(string(claims.SessionID))
|
token, ok := oauthRefreshTokens.Load(string(claims.SessionID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
invalidateOAuthRefreshToken(claims.SessionID)
|
|
||||||
if token.expired() {
|
if token.expired() {
|
||||||
|
invalidateOAuthRefreshToken(claims.SessionID)
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims.Username != token.Username {
|
if claims.Username != token.Username {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return &token, true
|
return token, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func storeOAuthRefreshToken(sessionID sessionID, username, token string) {
|
func storeOAuthRefreshToken(sessionID sessionID, username, token string) {
|
||||||
oauthRefreshTokens.Store(string(sessionID), oauthRefreshToken{
|
oauthRefreshTokens.Store(string(sessionID), &oauthRefreshToken{
|
||||||
Username: username,
|
Username: username,
|
||||||
RefreshToken: token,
|
RefreshToken: token,
|
||||||
Expiry: time.Now().Add(defaultRefreshTokenExpiry),
|
Expiry: time.Now().Add(defaultRefreshTokenExpiry),
|
||||||
|
@ -118,7 +130,7 @@ func (auth *OIDCProvider) setSessionTokenCookie(w http.ResponseWriter, r *http.R
|
||||||
logging.Err(err).Msg("failed to sign session token")
|
logging.Err(err).Msg("failed to sign session token")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setTokenCookie(w, r, CookieOauthSessionToken, signed, common.APIJWTTokenTTL)
|
SetTokenCookie(w, r, CookieOauthSessionToken, signed, common.APIJWTTokenTTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OIDCProvider) parseSessionJWT(sessionJWT string) (claims *sessionClaims, valid bool, err error) {
|
func (auth *OIDCProvider) parseSessionJWT(sessionJWT string) (claims *sessionClaims, valid bool, err error) {
|
||||||
|
@ -135,51 +147,75 @@ func (auth *OIDCProvider) parseSessionJWT(sessionJWT string) (claims *sessionCla
|
||||||
return claims, sessionToken.Valid && claims.Issuer == sessionTokenIssuer, nil
|
return claims, sessionToken.Valid && claims.Issuer == sessionTokenIssuer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OIDCProvider) TryRefreshToken(w http.ResponseWriter, r *http.Request, sessionJWT string) error {
|
func (auth *OIDCProvider) TryRefreshToken(ctx context.Context, sessionJWT string) (*RefreshResult, error) {
|
||||||
// verify the session cookie
|
// verify the session cookie
|
||||||
claims, valid, err := auth.parseSessionJWT(sessionJWT)
|
claims, valid, err := auth.parseSessionJWT(sessionJWT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w: %w", ErrInvalidSessionToken, err)
|
return nil, fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrInvalidSessionToken, err)
|
||||||
}
|
}
|
||||||
if !valid {
|
if !valid {
|
||||||
return ErrInvalidSessionToken
|
return nil, ErrInvalidSessionToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if refresh is possible
|
// check if refresh is possible
|
||||||
refreshToken, ok := getOnceOAuthRefreshToken(&claims.Session)
|
refreshToken, ok := getOAuthRefreshToken(&claims.Session)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errNoRefreshToken
|
return nil, errNoRefreshToken
|
||||||
}
|
}
|
||||||
|
|
||||||
if !auth.checkAllowed(claims.Username, claims.Groups) {
|
if !auth.checkAllowed(claims.Username, claims.Groups) {
|
||||||
return ErrUserNotAllowed
|
return nil, ErrUserNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth.doRefreshToken(ctx, refreshToken, &claims.Session)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *OIDCProvider) doRefreshToken(ctx context.Context, refreshToken *oauthRefreshToken, claims *Session) (*RefreshResult, error) {
|
||||||
|
refreshToken.mu.Lock()
|
||||||
|
defer refreshToken.mu.Unlock()
|
||||||
|
|
||||||
|
// already refreshed
|
||||||
|
// this must be called after refresh but before invalidate
|
||||||
|
if refreshToken.result != nil || refreshToken.err != nil {
|
||||||
|
return refreshToken.result, refreshToken.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// this step refreshes the token
|
// this step refreshes the token
|
||||||
// see https://cs.opensource.google/go/x/oauth2/+/refs/tags/v0.29.0:oauth2.go;l=313
|
// see https://cs.opensource.google/go/x/oauth2/+/refs/tags/v0.29.0:oauth2.go;l=313
|
||||||
newToken, err := auth.oauthConfig.TokenSource(r.Context(), &oauth2.Token{
|
newToken, err := auth.oauthConfig.TokenSource(ctx, &oauth2.Token{
|
||||||
RefreshToken: refreshToken.RefreshToken,
|
RefreshToken: refreshToken.RefreshToken,
|
||||||
}).Token()
|
}).Token()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w: %w", ErrRefreshTokenFailure, err)
|
refreshToken.err = fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrRefreshTokenFailure, err)
|
||||||
|
return nil, refreshToken.err
|
||||||
}
|
}
|
||||||
|
|
||||||
idTokenJWT, idToken, err := auth.getIdToken(r.Context(), newToken)
|
idTokenJWT, idToken, err := auth.getIdToken(ctx, newToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
refreshToken.err = fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrRefreshTokenFailure, err)
|
||||||
|
return nil, refreshToken.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in case there're multiple requests for the same session to refresh
|
||||||
|
// invalidate the token after a short delay
|
||||||
|
go func() {
|
||||||
|
<-time.After(sessionInvalidateDelay)
|
||||||
|
invalidateOAuthRefreshToken(claims.SessionID)
|
||||||
|
}()
|
||||||
|
|
||||||
sessionID := newSessionID()
|
sessionID := newSessionID()
|
||||||
|
|
||||||
logging.Debug().Str("username", claims.Username).Time("expiry", newToken.Expiry).Msg("refreshed token")
|
logging.Debug().Str("username", claims.Username).Time("expiry", newToken.Expiry).Msg("refreshed token")
|
||||||
storeOAuthRefreshToken(sessionID, claims.Username, newToken.RefreshToken)
|
storeOAuthRefreshToken(sessionID, claims.Username, newToken.RefreshToken)
|
||||||
|
|
||||||
// set new idToken and new sessionToken
|
refreshToken.result = &RefreshResult{
|
||||||
auth.setIDTokenCookie(w, r, idTokenJWT, time.Until(idToken.Expiry))
|
newSession: Session{
|
||||||
auth.setSessionTokenCookie(w, r, Session{
|
|
||||||
SessionID: sessionID,
|
SessionID: sessionID,
|
||||||
Username: claims.Username,
|
Username: claims.Username,
|
||||||
Groups: claims.Groups,
|
Groups: claims.Groups,
|
||||||
})
|
},
|
||||||
return nil
|
jwt: idTokenJWT,
|
||||||
|
jwtExpiry: idToken.Expiry,
|
||||||
|
}
|
||||||
|
return refreshToken.result, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
|
@ -47,7 +48,12 @@ const (
|
||||||
OIDCLogoutPath = "/auth/logout"
|
OIDCLogoutPath = "/auth/logout"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errMissingIDToken = errors.New("missing id_token field from oauth token")
|
var (
|
||||||
|
errMissingIDToken = errors.New("missing id_token field from oauth token")
|
||||||
|
|
||||||
|
ErrMissingOAuthToken = gperr.New("missing oauth token")
|
||||||
|
ErrInvalidOAuthToken = gperr.New("invalid oauth token")
|
||||||
|
)
|
||||||
|
|
||||||
// generateState generates a random string for OIDC state.
|
// generateState generates a random string for OIDC state.
|
||||||
const oidcStateLength = 32
|
const oidcStateLength = 32
|
||||||
|
@ -62,7 +68,10 @@ func NewOIDCProvider(issuerURL, clientID, clientSecret string, allowedUsers, all
|
||||||
if len(allowedUsers)+len(allowedGroups) == 0 {
|
if len(allowedUsers)+len(allowedGroups) == 0 {
|
||||||
return nil, errors.New("oidc.allowed_users or oidc.allowed_groups are both empty")
|
return nil, errors.New("oidc.allowed_users or oidc.allowed_groups are both empty")
|
||||||
}
|
}
|
||||||
provider, err := oidc.NewProvider(context.Background(), issuerURL)
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
provider, err := oidc.NewProvider(ctx, issuerURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize OIDC provider: %w", err)
|
return nil, fmt.Errorf("failed to initialize OIDC provider: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -133,6 +142,14 @@ func (auth *OIDCProvider) getIdToken(ctx context.Context, oauthToken *oauth2.Tok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
|
func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == "" {
|
||||||
|
r.URL.Path = OIDCAuthInitPath
|
||||||
|
}
|
||||||
|
if r.TLS == nil && r.Header.Get("X-Forwarded-Proto") != "https" {
|
||||||
|
r.URL.Scheme = "https"
|
||||||
|
http.Redirect(w, r, r.URL.String(), http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
case OIDCAuthInitPath:
|
case OIDCAuthInitPath:
|
||||||
auth.LoginHandler(w, r)
|
auth.LoginHandler(w, r)
|
||||||
|
@ -148,18 +165,25 @@ func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// check for session token
|
// check for session token
|
||||||
sessionToken, err := r.Cookie(CookieOauthSessionToken)
|
sessionToken, err := r.Cookie(CookieOauthSessionToken)
|
||||||
|
if err == nil { // session token exists
|
||||||
|
result, err := auth.TryRefreshToken(r.Context(), sessionToken.Value)
|
||||||
|
// redirect back to where they requested
|
||||||
|
// when token refresh is ok
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = auth.TryRefreshToken(w, r, sessionToken.Value)
|
auth.setIDTokenCookie(w, r, result.jwt, time.Until(result.jwtExpiry))
|
||||||
if err != nil {
|
auth.setSessionTokenCookie(w, r, result.newSession)
|
||||||
logging.Debug().Err(err).Msg("failed to refresh token")
|
ProceedNext(w, r)
|
||||||
auth.clearCookie(w, r)
|
return
|
||||||
}
|
}
|
||||||
|
// clear cookies then redirect to home
|
||||||
|
logging.Err(err).Msg("failed to refresh token")
|
||||||
|
auth.clearCookie(w, r)
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := generateState()
|
state := generateState()
|
||||||
setTokenCookie(w, r, CookieOauthState, state, 300*time.Second)
|
SetTokenCookie(w, r, CookieOauthState, state, 300*time.Second)
|
||||||
// redirect user to Idp
|
// redirect user to Idp
|
||||||
http.Redirect(w, r, auth.oauthConfig.AuthCodeURL(state, optRedirectPostAuth(r)), http.StatusFound)
|
http.Redirect(w, r, auth.oauthConfig.AuthCodeURL(state, optRedirectPostAuth(r)), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
@ -170,7 +194,7 @@ func parseClaims(idToken *oidc.IDToken) (*IDTokenClaims, error) {
|
||||||
return nil, fmt.Errorf("failed to parse claims: %w", err)
|
return nil, fmt.Errorf("failed to parse claims: %w", err)
|
||||||
}
|
}
|
||||||
if claim.Username == "" {
|
if claim.Username == "" {
|
||||||
return nil, fmt.Errorf("missing username in ID token")
|
return nil, errors.New("missing username in ID token")
|
||||||
}
|
}
|
||||||
return &claim, nil
|
return &claim, nil
|
||||||
}
|
}
|
||||||
|
@ -284,12 +308,12 @@ func (auth *OIDCProvider) LogoutHandler(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OIDCProvider) setIDTokenCookie(w http.ResponseWriter, r *http.Request, jwt string, ttl time.Duration) {
|
func (auth *OIDCProvider) setIDTokenCookie(w http.ResponseWriter, r *http.Request, jwt string, ttl time.Duration) {
|
||||||
setTokenCookie(w, r, CookieOauthToken, jwt, ttl)
|
SetTokenCookie(w, r, CookieOauthToken, jwt, ttl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OIDCProvider) clearCookie(w http.ResponseWriter, r *http.Request) {
|
func (auth *OIDCProvider) clearCookie(w http.ResponseWriter, r *http.Request) {
|
||||||
clearTokenCookie(w, r, CookieOauthToken)
|
ClearTokenCookie(w, r, CookieOauthToken)
|
||||||
clearTokenCookie(w, r, CookieOauthSessionToken)
|
ClearTokenCookie(w, r, CookieOauthSessionToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleTestCallback handles OIDC callback in test environment.
|
// handleTestCallback handles OIDC callback in test environment.
|
||||||
|
@ -306,7 +330,7 @@ func (auth *OIDCProvider) handleTestCallback(w http.ResponseWriter, r *http.Requ
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create test JWT token
|
// Create test JWT token
|
||||||
setTokenCookie(w, r, CookieOauthToken, "test", time.Hour)
|
SetTokenCookie(w, r, CookieOauthToken, "test", time.Hour)
|
||||||
|
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -24,7 +23,7 @@ import (
|
||||||
func setupMockOIDC(t *testing.T) {
|
func setupMockOIDC(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
provider := (&oidc.ProviderConfig{}).NewProvider(context.TODO())
|
provider := (&oidc.ProviderConfig{}).NewProvider(t.Context())
|
||||||
defaultAuth = &OIDCProvider{
|
defaultAuth = &OIDCProvider{
|
||||||
oauthConfig: &oauth2.Config{
|
oauthConfig: &oauth2.Config{
|
||||||
ClientID: "test-client",
|
ClientID: "test-client",
|
||||||
|
@ -104,7 +103,7 @@ func setupProvider(t *testing.T) *provider {
|
||||||
t.Cleanup(ts.Close)
|
t.Cleanup(ts.Close)
|
||||||
|
|
||||||
// Create a test OIDCProvider.
|
// Create a test OIDCProvider.
|
||||||
providerCtx := oidc.ClientContext(context.Background(), ts.Client())
|
providerCtx := oidc.ClientContext(t.Context(), ts.Client())
|
||||||
keySet := oidc.NewRemoteKeySet(providerCtx, ts.URL+"/.well-known/jwks.json")
|
keySet := oidc.NewRemoteKeySet(providerCtx, ts.URL+"/.well-known/jwks.json")
|
||||||
|
|
||||||
return &provider{
|
return &provider{
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (auth *UserPassAuth) PostAuthCallbackHandler(w http.ResponseWriter, r *http
|
||||||
gphttp.ServerError(w, r, err)
|
gphttp.ServerError(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setTokenCookie(w, r, auth.TokenCookieName(), token, auth.tokenTTL)
|
SetTokenCookie(w, r, auth.TokenCookieName(), token, auth.tokenTTL)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ func (auth *UserPassAuth) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *UserPassAuth) LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
func (auth *UserPassAuth) LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
clearTokenCookie(w, r, auth.TokenCookieName())
|
ClearTokenCookie(w, r, auth.TokenCookieName())
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ func TestUserPassLoginCallbackHandler(t *testing.T) {
|
||||||
Host: "app.example.com",
|
Host: "app.example.com",
|
||||||
Body: io.NopCloser(bytes.NewReader(Must(json.Marshal(tt.creds)))),
|
Body: io.NopCloser(bytes.NewReader(Must(json.Marshal(tt.creds)))),
|
||||||
}
|
}
|
||||||
auth.LoginHandler(w, req)
|
auth.PostAuthCallbackHandler(w, req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
ExpectEqual(t, w.Code, http.StatusUnauthorized)
|
ExpectEqual(t, w.Code, http.StatusUnauthorized)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,22 +10,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMissingOAuthToken = gperr.New("missing oauth token")
|
|
||||||
ErrMissingSessionToken = gperr.New("missing session token")
|
ErrMissingSessionToken = gperr.New("missing session token")
|
||||||
ErrInvalidOAuthToken = gperr.New("invalid oauth token")
|
|
||||||
ErrInvalidSessionToken = gperr.New("invalid session token")
|
ErrInvalidSessionToken = gperr.New("invalid session token")
|
||||||
ErrUserNotAllowed = gperr.New("user not allowed")
|
ErrUserNotAllowed = gperr.New("user not allowed")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func IsFrontend(r *http.Request) bool {
|
||||||
|
return r.Host == common.APIHTTPAddr
|
||||||
|
}
|
||||||
|
|
||||||
func requestHost(r *http.Request) string {
|
func requestHost(r *http.Request) string {
|
||||||
// check if it's from backend
|
// check if it's from backend
|
||||||
switch r.Host {
|
if IsFrontend(r) {
|
||||||
case common.APIHTTPAddr:
|
|
||||||
// use XFH
|
|
||||||
return r.Header.Get("X-Forwarded-Host")
|
return r.Header.Get("X-Forwarded-Host")
|
||||||
default:
|
|
||||||
return r.Host
|
|
||||||
}
|
}
|
||||||
|
return r.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
// cookieDomain returns the fully qualified domain name of the request host
|
// cookieDomain returns the fully qualified domain name of the request host
|
||||||
|
@ -45,7 +44,7 @@ func cookieDomain(r *http.Request) string {
|
||||||
return strutils.JoinRune(parts, '.')
|
return strutils.JoinRune(parts, '.')
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTokenCookie(w http.ResponseWriter, r *http.Request, name, value string, ttl time.Duration) {
|
func SetTokenCookie(w http.ResponseWriter, r *http.Request, name, value string, ttl time.Duration) {
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: value,
|
Value: value,
|
||||||
|
@ -58,7 +57,7 @@ func setTokenCookie(w http.ResponseWriter, r *http.Request, name, value string,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearTokenCookie(w http.ResponseWriter, r *http.Request, name string) {
|
func ClearTokenCookie(w http.ResponseWriter, r *http.Request, name string) {
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: "",
|
Value: "",
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -70,7 +69,7 @@ func (cfg *Config) Validate() gperr.Error {
|
||||||
if !ok {
|
if !ok {
|
||||||
b.Add(ErrUnknownProvider.
|
b.Add(ErrUnknownProvider.
|
||||||
Subject(cfg.Provider).
|
Subject(cfg.Provider).
|
||||||
Withf(strutils.DoYouMean(utils.NearestField(cfg.Provider, Providers))))
|
With(gperr.DoYouMean(utils.NearestField(cfg.Provider, Providers))))
|
||||||
} else {
|
} else {
|
||||||
_, err := providerConstructor(cfg.Options)
|
_, err := providerConstructor(cfg.Options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,14 +9,15 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
|
"github.com/yusing/go-proxy/internal/notif"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
@ -31,8 +32,6 @@ type (
|
||||||
legoCert *certificate.Resource
|
legoCert *certificate.Resource
|
||||||
tlsCert *tls.Certificate
|
tlsCert *tls.Certificate
|
||||||
certExpiries CertExpiries
|
certExpiries CertExpiries
|
||||||
|
|
||||||
obtainMu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CertExpiries map[string]time.Time
|
CertExpiries map[string]time.Time
|
||||||
|
@ -193,8 +192,18 @@ func (p *Provider) ScheduleRenewal(parent task.Parent) {
|
||||||
if err := p.renewIfNeeded(); err != nil {
|
if err := p.renewIfNeeded(); err != nil {
|
||||||
gperr.LogWarn("cert renew failed", err)
|
gperr.LogWarn("cert renew failed", err)
|
||||||
lastErrOn = time.Now()
|
lastErrOn = time.Now()
|
||||||
|
notif.Notify(¬if.LogMessage{
|
||||||
|
Level: zerolog.ErrorLevel,
|
||||||
|
Title: "SSL certificate renewal failed",
|
||||||
|
Body: notif.MessageBody(err.Error()),
|
||||||
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
notif.Notify(¬if.LogMessage{
|
||||||
|
Level: zerolog.InfoLevel,
|
||||||
|
Title: "SSL certificate renewed",
|
||||||
|
Body: notif.ListBody(p.cfg.Domains),
|
||||||
|
})
|
||||||
// Reset on success
|
// Reset on success
|
||||||
lastErrOn = time.Time{}
|
lastErrOn = time.Time{}
|
||||||
renewalTime = p.ShouldRenewOn()
|
renewalTime = p.ShouldRenewOn()
|
||||||
|
|
|
@ -45,6 +45,6 @@ oauth2_config:
|
||||||
testYaml = testYaml[1:] // remove first \n
|
testYaml = testYaml[1:] // remove first \n
|
||||||
opt := make(map[string]any)
|
opt := make(map[string]any)
|
||||||
require.NoError(t, yaml.Unmarshal([]byte(testYaml), &opt))
|
require.NoError(t, yaml.Unmarshal([]byte(testYaml), &opt))
|
||||||
require.NoError(t, utils.Deserialize(opt, cfg))
|
require.NoError(t, utils.MapUnmarshalValidate(opt, cfg))
|
||||||
require.Equal(t, cfg, cfgExpected)
|
require.Equal(t, cfgExpected, cfg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ func DNSProvider[CT any, PT challenge.Provider](
|
||||||
) Generator {
|
) Generator {
|
||||||
return func(opt map[string]any) (challenge.Provider, gperr.Error) {
|
return func(opt map[string]any) (challenge.Provider, gperr.Error) {
|
||||||
cfg := defaultCfg()
|
cfg := defaultCfg()
|
||||||
err := utils.Deserialize(opt, &cfg)
|
err := utils.MapUnmarshalValidate(opt, &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ const (
|
||||||
ConfigExampleFileName = "config.example.yml"
|
ConfigExampleFileName = "config.example.yml"
|
||||||
ConfigPath = ConfigBasePath + "/" + ConfigFileName
|
ConfigPath = ConfigBasePath + "/" + ConfigFileName
|
||||||
|
|
||||||
IconListCachePath = ConfigBasePath + "/.icon_list_cache.json"
|
DataDir = "data"
|
||||||
IconCachePath = ConfigBasePath + "/.icon_cache.json"
|
IconListCachePath = DataDir + "/.icon_list_cache.json"
|
||||||
|
|
||||||
NamespaceHomepageOverrides = ".homepage"
|
NamespaceHomepageOverrides = ".homepage"
|
||||||
NamespaceIconCache = ".icon_cache"
|
NamespaceIconCache = ".icon_cache"
|
||||||
|
@ -25,14 +25,12 @@ const (
|
||||||
|
|
||||||
ComposeFileName = "compose.yml"
|
ComposeFileName = "compose.yml"
|
||||||
ComposeExampleFileName = "compose.example.yml"
|
ComposeExampleFileName = "compose.example.yml"
|
||||||
|
|
||||||
DataDir = "data"
|
|
||||||
|
|
||||||
ErrorPagesBasePath = "error_pages"
|
ErrorPagesBasePath = "error_pages"
|
||||||
)
|
)
|
||||||
|
|
||||||
var RequiredDirectories = []string{
|
var RequiredDirectories = []string{
|
||||||
ConfigBasePath,
|
ConfigBasePath,
|
||||||
|
DataDir,
|
||||||
ErrorPagesBasePath,
|
ErrorPagesBasePath,
|
||||||
MiddlewareComposeBasePath,
|
MiddlewareComposeBasePath,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@ package common
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"log"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func decodeJWTKey(key string) []byte {
|
func decodeJWTKey(key string) []byte {
|
||||||
|
@ -13,7 +12,7 @@ func decodeJWTKey(key string) []byte {
|
||||||
}
|
}
|
||||||
bytes, err := base64.StdEncoding.DecodeString(key)
|
bytes, err := base64.StdEncoding.DecodeString(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Str("key", key).Err(err).Msg("failed to decode secret")
|
log.Fatalf("failed to decode secret: %s", err)
|
||||||
}
|
}
|
||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
@ -22,7 +21,7 @@ func RandomJWTKey() []byte {
|
||||||
key := make([]byte, 32)
|
key := make([]byte, 32)
|
||||||
_, err := rand.Read(key)
|
_, err := rand.Read(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to generate random jwt key")
|
log.Fatalf("failed to generate random jwt key: %s", err)
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ var (
|
||||||
MetricsDisableDisk = GetEnvBool("METRICS_DISABLE_DISK", false)
|
MetricsDisableDisk = GetEnvBool("METRICS_DISABLE_DISK", false)
|
||||||
MetricsDisableNetwork = GetEnvBool("METRICS_DISABLE_NETWORK", false)
|
MetricsDisableNetwork = GetEnvBool("METRICS_DISABLE_NETWORK", false)
|
||||||
MetricsDisableSensors = GetEnvBool("METRICS_DISABLE_SENSORS", false)
|
MetricsDisableSensors = GetEnvBool("METRICS_DISABLE_SENSORS", false)
|
||||||
|
|
||||||
|
ForceResolveCountry = GetEnvBool("FORCE_RESOLVE_COUNTRY", false)
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetEnv[T any](key string, defaultValue T, parser func(string) (T, error)) T {
|
func GetEnv[T any](key string, defaultValue T, parser func(string) (T, error)) T {
|
||||||
|
@ -76,14 +78,16 @@ func GetEnv[T any](key string, defaultValue T, parser func(string) (T, error)) T
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
log.Fatal().Err(err).Msgf("env %s: invalid %T value: %s", key, parsed, value)
|
log.Fatalf("env %s: invalid %T value: %s", key, parsed, value)
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnvString(key string, defaultValue string) string {
|
func stringstring(s string) (string, error) {
|
||||||
return GetEnv(key, defaultValue, func(s string) (string, error) {
|
|
||||||
return s, nil
|
return s, nil
|
||||||
})
|
}
|
||||||
|
|
||||||
|
func GetEnvString(key string, defaultValue string) string {
|
||||||
|
return GetEnv(key, defaultValue, stringstring)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnvBool(key string, defaultValue bool) bool {
|
func GetEnvBool(key string, defaultValue bool) bool {
|
||||||
|
@ -101,7 +105,7 @@ func GetAddrEnv(key, defaultValue, scheme string) (addr, host string, portInt in
|
||||||
}
|
}
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf("env %s: invalid address: %s", key, addr)
|
log.Fatalf("env %s: invalid address: %s", key, addr)
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
|
@ -109,7 +113,7 @@ func GetAddrEnv(key, defaultValue, scheme string) (addr, host string, portInt in
|
||||||
fullURL = fmt.Sprintf("%s://%s:%s", scheme, host, port)
|
fullURL = fmt.Sprintf("%s://%s:%s", scheme, host, port)
|
||||||
portInt, err = strconv.Atoi(port)
|
portInt, err = strconv.Atoi(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf("env %s: invalid port: %s", key, port)
|
log.Fatalf("env %s: invalid port: %s", key, port)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PE
|
||||||
|
|
||||||
var agentCfg agent.AgentConfig
|
var agentCfg agent.AgentConfig
|
||||||
agentCfg.Addr = host
|
agentCfg.Addr = host
|
||||||
err := agentCfg.StartWithCerts(cfg.Task(), ca.Cert, client.Cert, client.Key)
|
err := agentCfg.StartWithCerts(cfg.Task().Context(), ca.Cert, client.Cert, client.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, gperr.Wrap(err, "failed to start agent")
|
return 0, gperr.Wrap(err, "failed to start agent")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"github.com/yusing/go-proxy/internal/api"
|
"github.com/yusing/go-proxy/internal/api"
|
||||||
autocert "github.com/yusing/go-proxy/internal/autocert"
|
autocert "github.com/yusing/go-proxy/internal/autocert"
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
|
@ -16,12 +17,15 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/entrypoint"
|
"github.com/yusing/go-proxy/internal/entrypoint"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
|
"github.com/yusing/go-proxy/internal/maxmind"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/server"
|
"github.com/yusing/go-proxy/internal/net/gphttp/server"
|
||||||
"github.com/yusing/go-proxy/internal/notif"
|
"github.com/yusing/go-proxy/internal/notif"
|
||||||
|
"github.com/yusing/go-proxy/internal/proxmox"
|
||||||
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||||
|
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||||
"github.com/yusing/go-proxy/internal/watcher"
|
"github.com/yusing/go-proxy/internal/watcher"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/events"
|
"github.com/yusing/go-proxy/internal/watcher/events"
|
||||||
)
|
)
|
||||||
|
@ -114,7 +118,7 @@ func Reload() gperr.Error {
|
||||||
err := newCfg.load()
|
err := newCfg.load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newCfg.task.Finish(err)
|
newCfg.task.Finish(err)
|
||||||
return gperr.New("using last config").With(err)
|
return gperr.New(ansi.Warning("using last config")).With(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel all current subtasks -> wait
|
// cancel all current subtasks -> wait
|
||||||
|
@ -219,7 +223,7 @@ func (cfg *Config) load() gperr.Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
model := config.DefaultConfig()
|
model := config.DefaultConfig()
|
||||||
if err := utils.DeserializeYAML(data, model); err != nil {
|
if err := utils.UnmarshalValidateYAML(data, model); err != nil {
|
||||||
gperr.LogFatal(errMsg, err)
|
gperr.LogFatal(errMsg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,8 +231,10 @@ func (cfg *Config) load() gperr.Error {
|
||||||
errs := gperr.NewBuilder(errMsg)
|
errs := gperr.NewBuilder(errMsg)
|
||||||
errs.Add(cfg.entrypoint.SetMiddlewares(model.Entrypoint.Middlewares))
|
errs.Add(cfg.entrypoint.SetMiddlewares(model.Entrypoint.Middlewares))
|
||||||
errs.Add(cfg.entrypoint.SetAccessLogger(cfg.task, model.Entrypoint.AccessLog))
|
errs.Add(cfg.entrypoint.SetAccessLogger(cfg.task, model.Entrypoint.AccessLog))
|
||||||
|
errs.Add(cfg.initMaxMind(model.Providers.MaxMind))
|
||||||
cfg.initNotification(model.Providers.Notification)
|
cfg.initNotification(model.Providers.Notification)
|
||||||
errs.Add(cfg.initAutoCert(model.AutoCert))
|
errs.Add(cfg.initAutoCert(model.AutoCert))
|
||||||
|
errs.Add(cfg.initProxmox(model.Providers.Proxmox))
|
||||||
errs.Add(cfg.loadRouteProviders(&model.Providers))
|
errs.Add(cfg.loadRouteProviders(&model.Providers))
|
||||||
|
|
||||||
cfg.value = model
|
cfg.value = model
|
||||||
|
@ -242,12 +248,25 @@ func (cfg *Config) load() gperr.Error {
|
||||||
err := model.ACL.Start(cfg.task)
|
err := model.ACL.Start(cfg.task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs.Add(err)
|
errs.Add(err)
|
||||||
} else {
|
|
||||||
logging.Info().Msg("ACL started")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errs.HasError() {
|
||||||
|
notif.Notify(¬if.LogMessage{
|
||||||
|
Level: zerolog.ErrorLevel,
|
||||||
|
Title: "Config Reload Error",
|
||||||
|
Body: notif.ErrorBody{Error: errs.Error()},
|
||||||
|
})
|
||||||
return errs.Error()
|
return errs.Error()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) initMaxMind(maxmindCfg *maxmind.Config) gperr.Error {
|
||||||
|
if maxmindCfg != nil {
|
||||||
|
return maxmind.SetInstance(cfg.task, maxmindCfg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) initNotification(notifCfg []notif.NotificationConfig) {
|
func (cfg *Config) initNotification(notifCfg []notif.NotificationConfig) {
|
||||||
|
@ -278,6 +297,17 @@ func (cfg *Config) initAutoCert(autocertCfg *autocert.Config) gperr.Error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) initProxmox(proxmoxCfg []proxmox.Config) gperr.Error {
|
||||||
|
proxmox.Clients.Clear()
|
||||||
|
errs := gperr.NewBuilder()
|
||||||
|
for _, cfg := range proxmoxCfg {
|
||||||
|
if err := cfg.Init(); err != nil {
|
||||||
|
errs.Add(err.Subject(cfg.URL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errs.Error()
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *Config) errIfExists(p *proxy.Provider) gperr.Error {
|
func (cfg *Config) errIfExists(p *proxy.Provider) gperr.Error {
|
||||||
if _, ok := cfg.providers.Load(p.String()); ok {
|
if _, ok := cfg.providers.Load(p.String()); ok {
|
||||||
return gperr.Errorf("provider %s already exists", p.String())
|
return gperr.Errorf("provider %s already exists", p.String())
|
||||||
|
@ -296,8 +326,8 @@ func (cfg *Config) loadRouteProviders(providers *config.Providers) gperr.Error {
|
||||||
removeAllAgents()
|
removeAllAgents()
|
||||||
|
|
||||||
for _, agent := range providers.Agents {
|
for _, agent := range providers.Agents {
|
||||||
if err := agent.Start(cfg.task); err != nil {
|
if err := agent.Start(cfg.task.Context()); err != nil {
|
||||||
errs.Add(err.Subject(agent.String()))
|
errs.Add(gperr.PrependSubject(agent.String(), err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
addAgent(agent)
|
addAgent(agent)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
"github.com/yusing/go-proxy/internal/route"
|
"github.com/yusing/go-proxy/internal/route"
|
||||||
"github.com/yusing/go-proxy/internal/route/provider"
|
"github.com/yusing/go-proxy/internal/route/provider"
|
||||||
)
|
)
|
||||||
|
@ -23,10 +24,13 @@ func (cfg *Config) DumpRouteProviders() map[string]*provider.Provider {
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) RouteProviderList() []string {
|
func (cfg *Config) RouteProviderList() []config.RouteProviderListResponse {
|
||||||
var list []string
|
var list []config.RouteProviderListResponse
|
||||||
cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
|
cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
|
||||||
list = append(list, p.ShortName())
|
list = append(list, config.RouteProviderListResponse{
|
||||||
|
ShortName: p.ShortName(),
|
||||||
|
FullName: p.String(),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/autocert"
|
"github.com/yusing/go-proxy/internal/autocert"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging/accesslog"
|
"github.com/yusing/go-proxy/internal/logging/accesslog"
|
||||||
|
maxmind "github.com/yusing/go-proxy/internal/maxmind/types"
|
||||||
"github.com/yusing/go-proxy/internal/notif"
|
"github.com/yusing/go-proxy/internal/notif"
|
||||||
|
"github.com/yusing/go-proxy/internal/proxmox"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,6 +32,8 @@ type (
|
||||||
Docker map[string]string `json:"docker" yaml:"docker,omitempty" validate:"non_empty_docker_keys,dive,unix_addr|url"`
|
Docker map[string]string `json:"docker" yaml:"docker,omitempty" validate:"non_empty_docker_keys,dive,unix_addr|url"`
|
||||||
Agents []*agent.AgentConfig `json:"agents" yaml:"agents,omitempty"`
|
Agents []*agent.AgentConfig `json:"agents" yaml:"agents,omitempty"`
|
||||||
Notification []notif.NotificationConfig `json:"notification" yaml:"notification,omitempty"`
|
Notification []notif.NotificationConfig `json:"notification" yaml:"notification,omitempty"`
|
||||||
|
Proxmox []proxmox.Config `json:"proxmox" yaml:"proxmox,omitempty"`
|
||||||
|
MaxMind *maxmind.Config `json:"maxmind" yaml:"maxmind,omitempty"`
|
||||||
}
|
}
|
||||||
Entrypoint struct {
|
Entrypoint struct {
|
||||||
Middlewares []map[string]any `json:"middlewares"`
|
Middlewares []map[string]any `json:"middlewares"`
|
||||||
|
@ -38,12 +42,15 @@ type (
|
||||||
HomepageConfig struct {
|
HomepageConfig struct {
|
||||||
UseDefaultCategories bool `json:"use_default_categories"`
|
UseDefaultCategories bool `json:"use_default_categories"`
|
||||||
}
|
}
|
||||||
|
RouteProviderListResponse struct {
|
||||||
|
ShortName string `json:"short_name"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
}
|
||||||
ConfigInstance interface {
|
ConfigInstance interface {
|
||||||
Value() *Config
|
Value() *Config
|
||||||
Reload() gperr.Error
|
Reload() gperr.Error
|
||||||
Statistics() map[string]any
|
Statistics() map[string]any
|
||||||
RouteProviderList() []string
|
RouteProviderList() []RouteProviderListResponse
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool)
|
GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool)
|
||||||
VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error)
|
VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error)
|
||||||
|
@ -86,7 +93,7 @@ func HasInstance() bool {
|
||||||
|
|
||||||
func Validate(data []byte) gperr.Error {
|
func Validate(data []byte) gperr.Error {
|
||||||
var model Config
|
var model Config
|
||||||
return utils.DeserializeYAML(data, &model)
|
return utils.UnmarshalValidateYAML(data, &model)
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchDomainsRegex = regexp.MustCompile(`^[^\.]?([\w\d\-_]\.?)+[^\.]?$`)
|
var matchDomainsRegex = regexp.MustCompile(`^[^\.]?([\w\d\-_]\.?)+[^\.]?$`)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/yusing/go-proxy/internal/dnsproviders
|
module github.com/yusing/go-proxy/internal/dnsproviders
|
||||||
|
|
||||||
go 1.24.2
|
go 1.24.3
|
||||||
|
|
||||||
replace github.com/yusing/go-proxy => ../..
|
replace github.com/yusing/go-proxy => ../..
|
||||||
|
|
||||||
|
@ -12,10 +12,10 @@ require (
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/auth v0.16.1 // indirect
|
cloud.google.com/go/auth v0.16.1 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||||
|
@ -23,7 +23,7 @@ require (
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.106 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
|
||||||
|
@ -39,15 +39,15 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
|
||||||
github.com/aws/smithy-go v1.22.3 // indirect
|
github.com/aws/smithy-go v1.22.3 // indirect
|
||||||
github.com/baidubce/bce-sdk-go v0.9.224 // indirect
|
github.com/baidubce/bce-sdk-go v0.9.226 // indirect
|
||||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
github.com/boombuler/barcode v1.0.2 // indirect
|
github.com/boombuler/barcode v1.0.2 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/civo/civogo v0.3.98 // indirect
|
github.com/civo/civogo v0.5.0 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
||||||
github.com/exoscale/egoscale/v3 v3.1.14 // indirect
|
github.com/exoscale/egoscale/v3 v3.1.17 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
@ -62,7 +62,6 @@ require (
|
||||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/goccy/go-yaml v1.17.1 // indirect
|
github.com/goccy/go-yaml v1.17.1 // indirect
|
||||||
github.com/gofrs/flock v0.12.1 // indirect
|
github.com/gofrs/flock v0.12.1 // indirect
|
||||||
|
@ -72,15 +71,16 @@ require (
|
||||||
github.com/google/s2a-go v0.1.9 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||||
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||||
|
github.com/gotify/server/v2 v2.6.3 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 // indirect
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149 // indirect
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||||
github.com/infobloxopen/infoblox-go-client/v2 v2.9.0 // indirect
|
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||||
|
@ -89,12 +89,12 @@ require (
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/linode/linodego v1.49.0 // indirect
|
github.com/linode/linodego v1.50.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/miekg/dns v1.1.65 // indirect
|
github.com/miekg/dns v1.1.66 // indirect
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
@ -113,7 +113,7 @@ require (
|
||||||
github.com/nrdcg/porkbun v0.4.0 // indirect
|
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||||
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.89.2 // indirect
|
github.com/oracle/oci-go-sdk/v65 v65.91.0 // indirect
|
||||||
github.com/ovh/go-ovh v1.7.0 // indirect
|
github.com/ovh/go-ovh v1.7.0 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
@ -122,12 +122,12 @@ require (
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/pquerna/otp v1.4.0 // indirect
|
github.com/pquerna/otp v1.4.0 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
github.com/puzpuzpuz/xsync/v4 v4.1.0 // indirect
|
||||||
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
|
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
|
||||||
github.com/rs/zerolog v1.34.0 // indirect
|
github.com/rs/zerolog v1.34.0 // indirect
|
||||||
github.com/sacloud/api-client-go v0.2.10 // indirect
|
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||||
github.com/sacloud/go-http v0.1.9 // indirect
|
github.com/sacloud/go-http v0.1.9 // indirect
|
||||||
github.com/sacloud/iaas-api-go v1.14.0 // indirect
|
github.com/sacloud/iaas-api-go v1.15.0 // indirect
|
||||||
github.com/sacloud/packages-go v0.0.11 // indirect
|
github.com/sacloud/packages-go v0.0.11 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
|
||||||
|
@ -141,19 +141,19 @@ require (
|
||||||
github.com/sony/gobreaker v1.0.0 // indirect
|
github.com/sony/gobreaker v1.0.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.14.0 // indirect
|
github.com/spf13/afero v1.14.0 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/cast v1.8.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/spf13/viper v1.20.1 // indirect
|
github.com/spf13/viper v1.20.1 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||||
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.205 // indirect
|
github.com/volcengine/volc-sdk-golang v1.0.207 // indirect
|
||||||
github.com/vultr/govultr/v3 v3.19.1 // indirect
|
github.com/vultr/govultr/v3 v3.20.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.3 // indirect
|
go.mongodb.org/mongo-driver v1.17.3 // indirect
|
||||||
|
@ -165,28 +165,28 @@ require (
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/ratelimit v0.3.1 // indirect
|
go.uber.org/ratelimit v0.3.1 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.38.0 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.40.0 // indirect
|
||||||
golang.org/x/oauth2 v0.29.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.13.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/time v0.11.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
golang.org/x/tools v0.32.0 // indirect
|
golang.org/x/tools v0.33.0 // indirect
|
||||||
google.golang.org/api v0.230.0 // indirect
|
google.golang.org/api v0.233.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect
|
||||||
google.golang.org/grpc v1.72.0 // indirect
|
google.golang.org/grpc v1.72.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.14.2 // indirect
|
gopkg.in/ns1/ns1-go.v2 v2.14.3 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/api v0.33.0 // indirect
|
k8s.io/api v0.33.0 // indirect
|
||||||
k8s.io/apimachinery v0.33.0 // indirect
|
k8s.io/apimachinery v0.33.0 // indirect
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||||
|
|
|
@ -179,8 +179,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ
|
||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||||
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
||||||
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
||||||
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
|
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
|
||||||
|
@ -606,8 +606,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesn
|
||||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||||
|
@ -654,8 +654,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.106 h1:+YPfQheppCKOPJxhWDmStF1UMJrxnA1iiwBH12t6Fa4=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.106/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
|
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
|
||||||
|
@ -702,8 +702,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjK
|
||||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||||
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||||
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||||
github.com/baidubce/bce-sdk-go v0.9.224 h1:z2L8alGw/y3IUHjrLRyrxrgCvMssYTjgCd7OQdb4gt0=
|
github.com/baidubce/bce-sdk-go v0.9.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI=
|
||||||
github.com/baidubce/bce-sdk-go v0.9.224/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
@ -717,10 +717,6 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
|
||||||
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
|
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
|
||||||
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
|
||||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
|
||||||
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
||||||
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
|
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
|
||||||
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||||
|
@ -743,14 +739,12 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/civo/civogo v0.3.98 h1:FEbB5oxCcHeHUK3fJODxVoMQzhpLV9Jtb7bezANTY5c=
|
github.com/civo/civogo v0.5.0 h1:YDG38z+hmgaAhpDad1n/9sZrBtwRTeKVvMgdrS8Gwy4=
|
||||||
github.com/civo/civogo v0.3.98/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
|
github.com/civo/civogo v0.5.0/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
|
||||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
|
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
|
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
@ -807,8 +801,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||||
github.com/exoscale/egoscale/v3 v3.1.14 h1:ux1wOtx4561ZJM1sF2AFEjEY6HRj/RbtglKvZxh2iqg=
|
github.com/exoscale/egoscale/v3 v3.1.17 h1:+T6+GP/k3tFNsYIQzpF3Sou4ecH//FkERDsJze/OZ00=
|
||||||
github.com/exoscale/egoscale/v3 v3.1.14/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco=
|
github.com/exoscale/egoscale/v3 v3.1.17/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
@ -893,8 +887,6 @@ github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd
|
||||||
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
||||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=
|
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=
|
||||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
||||||
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.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
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.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
@ -1000,8 +992,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||||
|
@ -1030,8 +1022,8 @@ github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMd
|
||||||
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
||||||
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||||
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
||||||
|
@ -1047,6 +1039,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gotify/server/v2 v2.6.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I=
|
||||||
|
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
@ -1106,8 +1100,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
||||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 h1:ld5s5UeA9zgyFsZskVD2Tr6k6VnJWkvaLm5nqvfOEf4=
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149 h1:gDzo/eYE8/mwF5fi7v10pdBW2027PUvaHvRPA5aOPoM=
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
||||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
@ -1117,11 +1111,11 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
github.com/infobloxopen/infoblox-go-client/v2 v2.9.0 h1:wS8kTlQVeVbrepeY83s9X+XdSa6Qah5KO+tdW+zRQXU=
|
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 h1:AKsihjFT/t6Y0keEv3p59DACcOuh0inWXdUB0ZOzYH0=
|
||||||
github.com/infobloxopen/infoblox-go-client/v2 v2.9.0/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg=
|
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg=
|
||||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
||||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||||
|
@ -1163,8 +1157,6 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
||||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
|
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/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
@ -1189,8 +1181,8 @@ github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk=
|
||||||
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/linode/linodego v1.49.0 h1:MNd3qwvQzbXB5mCpvdCqlUIu1RPA9oC+50LyB9kK+GQ=
|
github.com/linode/linodego v1.50.0 h1:5y79VvvQnWb5JyPIjTwyUrU3ArHcs7XZQFdkPS/lNpw=
|
||||||
github.com/linode/linodego v1.49.0/go.mod h1:B+HAM3//4w1wOS0BwdaQBKwBxlfe6kYJ7bSC6jJ/xtc=
|
github.com/linode/linodego v1.50.0/go.mod h1:9S+REoPCtUNWCm63D1vjjxIJZfwEL2t2kTDnwt620FM=
|
||||||
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
||||||
|
@ -1236,8 +1228,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
|
github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
|
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
|
||||||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
||||||
|
@ -1330,8 +1322,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||||
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.89.2 h1:w0GwID9NlT+eg3InbAwkWsazDtxVLYQ8rJb4E33Yb14=
|
github.com/oracle/oci-go-sdk/v65 v65.91.0 h1:maO6AxKxVfszH0X4tbbtN21jOk03lCRR3IqiA8/FzZc=
|
||||||
github.com/oracle/oci-go-sdk/v65 v65.89.2/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
github.com/oracle/oci-go-sdk/v65 v65.91.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
||||||
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
|
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
|
||||||
github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
|
github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
@ -1401,12 +1393,12 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
github.com/puzpuzpuz/xsync/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
||||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||||
github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE=
|
github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE=
|
||||||
github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY=
|
github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
@ -1430,8 +1422,8 @@ github.com/sacloud/api-client-go v0.2.10 h1:+rv3jDohD+pkdYwOTBiB+jZsM0xK3AxadXRz
|
||||||
github.com/sacloud/api-client-go v0.2.10/go.mod h1:Jj3CTy2+O4bcMedVDXlbHuqqche85HEPuVXoQFhLaRc=
|
github.com/sacloud/api-client-go v0.2.10/go.mod h1:Jj3CTy2+O4bcMedVDXlbHuqqche85HEPuVXoQFhLaRc=
|
||||||
github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
|
github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
|
||||||
github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE=
|
github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE=
|
||||||
github.com/sacloud/iaas-api-go v1.14.0 h1:xjkFWqdo4ilTrKPNNYBNWR/CZ/kVRsJrdAHAad6J/AQ=
|
github.com/sacloud/iaas-api-go v1.15.0 h1:G88U69OOVUSjOZ2fEP5QWMg7r3VOVsfNkl5Mxl+/wMk=
|
||||||
github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFnpe69kyA4M3V0=
|
github.com/sacloud/iaas-api-go v1.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA=
|
||||||
github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo=
|
github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo=
|
||||||
github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
|
github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
|
||||||
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
|
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
|
||||||
|
@ -1484,8 +1476,8 @@ github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZ
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
@ -1527,8 +1519,8 @@ github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNG
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150 h1:r/cHvpMZ0oO5/HOuSsPdq3Dj1YX4pF0mhZS7G5gWKEs=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164 h1:qEzZCZf1sgvvrZ8ngws0gZlyW+sOdY0K9VXGm4AcvTE=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
|
@ -1537,8 +1529,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
|
||||||
github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550=
|
github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550=
|
||||||
github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s=
|
github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||||
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/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||||
|
@ -1548,10 +1538,10 @@ github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
|
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
|
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.205 h1:0ukVBX82JaF9N5H/D8j8jPjgJW52bQpAXabrg4WLJ88=
|
github.com/volcengine/volc-sdk-golang v1.0.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18=
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.205/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
|
github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
|
||||||
github.com/vultr/govultr/v3 v3.19.1 h1:31rOP5Tz40AOc8h6Ws4ryzqAniUBffgRhy9uMG/EFvs=
|
github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA=
|
||||||
github.com/vultr/govultr/v3 v3.19.1/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
|
github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
@ -1635,8 +1625,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||||
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
|
||||||
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
@ -1667,8 +1655,8 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -1806,8 +1794,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -1837,8 +1825,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
|
||||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -1855,8 +1843,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -1973,8 +1961,8 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
@ -1989,8 +1977,8 @@ golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -2010,8 +1998,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -2091,8 +2079,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -2167,8 +2155,8 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
|
||||||
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
|
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
|
||||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||||
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
|
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
|
||||||
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
|
google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI=
|
||||||
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
|
google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -2309,11 +2297,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
|
||||||
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||||
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
|
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
|
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -2356,8 +2345,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
|
||||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
@ -2398,8 +2387,8 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.14.2 h1:wz/toj9U20wBrmYxW4vTz7sZWED+JJVRjUBBJ7CKrzI=
|
gopkg.in/ns1/ns1-go.v2 v2.14.3 h1:Yn72GgB6AA9I4602AsLMtbC1ZKT5EUrKiG+IPS+Ovr0=
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.14.2/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
|
gopkg.in/ns1/ns1-go.v2 v2.14.3/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
@ -2433,8 +2422,8 @@ k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
|
||||||
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg=
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
config "github.com/yusing/go-proxy/internal/config/types"
|
config "github.com/yusing/go-proxy/internal/config/types"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
U "github.com/yusing/go-proxy/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
PortMapping = map[int]container.Port
|
PortMapping = map[int]container.Port
|
||||||
Container struct {
|
Container struct {
|
||||||
_ U.NoCopy
|
_ utils.NoCopy
|
||||||
|
|
||||||
DockerHost string `json:"docker_host"`
|
DockerHost string `json:"docker_host"`
|
||||||
Image *ContainerImage `json:"image"`
|
Image *ContainerImage `json:"image"`
|
||||||
|
@ -40,6 +42,7 @@ type (
|
||||||
Aliases []string `json:"aliases"`
|
Aliases []string `json:"aliases"`
|
||||||
IsExcluded bool `json:"is_excluded"`
|
IsExcluded bool `json:"is_excluded"`
|
||||||
IsExplicit bool `json:"is_explicit"`
|
IsExplicit bool `json:"is_explicit"`
|
||||||
|
IsHostNetworkMode bool `json:"is_host_network_mode"`
|
||||||
Running bool `json:"running"`
|
Running bool `json:"running"`
|
||||||
}
|
}
|
||||||
ContainerImage struct {
|
ContainerImage struct {
|
||||||
|
@ -79,6 +82,7 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
|
||||||
Aliases: helper.getAliases(),
|
Aliases: helper.getAliases(),
|
||||||
IsExcluded: isExcluded,
|
IsExcluded: isExcluded,
|
||||||
IsExplicit: isExplicit,
|
IsExplicit: isExplicit,
|
||||||
|
IsHostNetworkMode: c.HostConfig.NetworkMode == "host",
|
||||||
Running: c.Status == "running" || c.State == "running",
|
Running: c.Status == "running" || c.State == "running",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +104,33 @@ func (c *Container) IsBlacklisted() bool {
|
||||||
return c.Image.IsBlacklisted() || c.isDatabase()
|
return c.Image.IsBlacklisted() || c.isDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) UpdatePorts() error {
|
||||||
|
client, err := NewClient(c.DockerHost)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
inspect, err := client.ContainerInspect(context.Background(), c.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for port := range inspect.Config.ExposedPorts {
|
||||||
|
proto, portStr := nat.SplitProtoPort(string(port))
|
||||||
|
portInt, _ := nat.ParsePort(portStr)
|
||||||
|
if portInt == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.PublicPortMapping[portInt] = container.Port{
|
||||||
|
PublicPort: uint16(portInt),
|
||||||
|
PrivatePort: uint16(portInt),
|
||||||
|
Type: proto,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var databaseMPs = map[string]struct{}{
|
var databaseMPs = map[string]struct{}{
|
||||||
"/var/lib/postgresql/data": {},
|
"/var/lib/postgresql/data": {},
|
||||||
"/var/lib/mysql": {},
|
"/var/lib/mysql": {},
|
||||||
|
@ -126,11 +157,27 @@ func (c *Container) isDatabase() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) isLocal() bool {
|
||||||
|
if strings.HasPrefix(c.DockerHost, "unix://") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
url, err := url.Parse(c.DockerHost)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hostname := url.Hostname()
|
||||||
|
ip := net.ParseIP(hostname)
|
||||||
|
if ip != nil {
|
||||||
|
return ip.IsLoopback() || ip.IsUnspecified()
|
||||||
|
}
|
||||||
|
return hostname == "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Container) setPublicHostname() {
|
func (c *Container) setPublicHostname() {
|
||||||
if !c.Running {
|
if !c.Running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(c.DockerHost, "unix://") {
|
if c.isLocal() {
|
||||||
c.PublicHostname = "127.0.0.1"
|
c.PublicHostname = "127.0.0.1"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -144,19 +191,18 @@ func (c *Container) setPublicHostname() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) setPrivateHostname(helper containerHelper) {
|
func (c *Container) setPrivateHostname(helper containerHelper) {
|
||||||
if !strings.HasPrefix(c.DockerHost, "unix://") && c.Agent == nil {
|
if !c.isLocal() && c.Agent == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if helper.NetworkSettings == nil {
|
if helper.NetworkSettings == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, v := range helper.NetworkSettings.Networks {
|
for _, v := range helper.NetworkSettings.Networks {
|
||||||
if v.IPAddress == "" {
|
if v.IPAddress != "" {
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.PrivateHostname = v.IPAddress
|
c.PrivateHostname = v.IPAddress
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
|
func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
|
||||||
|
@ -178,7 +224,7 @@ func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
|
||||||
ContainerName: c.ContainerName,
|
ContainerName: c.ContainerName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := utils.Deserialize(cfg, idwCfg)
|
err := utils.MapUnmarshalValidate(cfg, idwCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gperr.LogWarn("invalid idlewatcher config", gperr.PrependSubject(c.ContainerName, err))
|
gperr.LogWarn("invalid idlewatcher config", gperr.PrependSubject(c.ContainerName, err))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -41,3 +41,38 @@ func TestContainerExplicit(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContainerHostNetworkMode(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
container *container.SummaryTrimmed
|
||||||
|
isHostNetworkMode bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "host network mode",
|
||||||
|
container: &container.SummaryTrimmed{
|
||||||
|
Names: []string{"test"},
|
||||||
|
State: "test",
|
||||||
|
HostConfig: struct {
|
||||||
|
NetworkMode string `json:",omitempty"`
|
||||||
|
}{
|
||||||
|
NetworkMode: "host",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isHostNetworkMode: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not host network mode",
|
||||||
|
container: &container.SummaryTrimmed{
|
||||||
|
Names: []string{"test"},
|
||||||
|
State: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := FromDocker(tt.container, "")
|
||||||
|
ExpectEqual(t, c.IsHostNetworkMode, tt.isHostNetworkMode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (ep *Entrypoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if ep.middleware != nil {
|
if ep.middleware != nil {
|
||||||
ep.middleware.ServeHTTP(mux.ServeHTTP, w, r)
|
ep.middleware.ServeHTTP(mux.ServeHTTP, w, routes.WithRouteContext(r, mux))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mux.ServeHTTP(w, r)
|
mux.ServeHTTP(w, r)
|
||||||
|
|
|
@ -36,8 +36,11 @@ func (err *baseError) Subjectf(format string, args ...any) Error {
|
||||||
return err.Subject(format)
|
return err.Subject(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err baseError) With(extra error) Error {
|
func (err *baseError) With(extra error) Error {
|
||||||
return &nestedError{&err, []error{extra}}
|
if extra == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &nestedError{&baseError{err.Err}, []error{extra}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err baseError) Withf(format string, args ...any) Error {
|
func (err baseError) Withf(format string, args ...any) Error {
|
||||||
|
@ -62,3 +65,11 @@ func (err *baseError) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(err.Error())
|
return json.Marshal(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err *baseError) Plain() []byte {
|
||||||
|
return Plain(err.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *baseError) Markdown() []byte {
|
||||||
|
return Markdown(err.Err)
|
||||||
|
}
|
||||||
|
|
|
@ -24,10 +24,6 @@ type Builder struct {
|
||||||
rwLock
|
rwLock
|
||||||
}
|
}
|
||||||
|
|
||||||
type multiline struct {
|
|
||||||
*Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuilder creates a new Builder.
|
// NewBuilder creates a new Builder.
|
||||||
//
|
//
|
||||||
// If about is not provided, the Builder will not have a subject
|
// If about is not provided, the Builder will not have a subject
|
||||||
|
@ -63,6 +59,9 @@ func (b *Builder) Error() Error {
|
||||||
if len(b.errs) == 0 {
|
if len(b.errs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if len(b.errs) == 1 && b.about == "" {
|
||||||
|
return wrap(b.errs[0])
|
||||||
|
}
|
||||||
return &nestedError{Err: New(b.about), Extras: b.errs}
|
return &nestedError{Err: New(b.about), Extras: b.errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,23 +87,6 @@ func (b *Builder) Add(err error) {
|
||||||
b.add(err)
|
b.add(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) add(err error) {
|
|
||||||
switch err := err.(type) {
|
|
||||||
case *baseError:
|
|
||||||
b.errs = append(b.errs, err.Err)
|
|
||||||
case *nestedError:
|
|
||||||
if err.Err == nil {
|
|
||||||
b.errs = append(b.errs, err.Extras...)
|
|
||||||
} else {
|
|
||||||
b.errs = append(b.errs, err)
|
|
||||||
}
|
|
||||||
case *MultilineError:
|
|
||||||
b.add(&err.nestedError)
|
|
||||||
default:
|
|
||||||
b.errs = append(b.errs, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) Adds(err string) {
|
func (b *Builder) Adds(err string) {
|
||||||
b.Lock()
|
b.Lock()
|
||||||
defer b.Unlock()
|
defer b.Unlock()
|
||||||
|
@ -160,3 +142,20 @@ func (b *Builder) ForEach(fn func(error)) {
|
||||||
fn(err)
|
fn(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) add(err error) {
|
||||||
|
switch err := err.(type) { //nolint:errorlint
|
||||||
|
case *baseError:
|
||||||
|
b.errs = append(b.errs, err.Err)
|
||||||
|
case *nestedError:
|
||||||
|
if err.Err == nil {
|
||||||
|
b.errs = append(b.errs, err.Extras...)
|
||||||
|
} else {
|
||||||
|
b.errs = append(b.errs, err)
|
||||||
|
}
|
||||||
|
case *MultilineError:
|
||||||
|
b.add(&err.nestedError)
|
||||||
|
default:
|
||||||
|
b.errs = append(b.errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ func TestBuilderNested(t *testing.T) {
|
||||||
• Inner: 1
|
• Inner: 1
|
||||||
• Inner: 2
|
• Inner: 2
|
||||||
• Action 2
|
• Action 2
|
||||||
• Inner: 3`
|
• Inner: 3
|
||||||
|
`
|
||||||
ExpectEqual(t, got, expected)
|
ExpectEqual(t, got, expected)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,16 @@ type Error interface {
|
||||||
Subject(subject string) Error
|
Subject(subject string) Error
|
||||||
// Subjectf is a wrapper for Subject(fmt.Sprintf(format, args...)).
|
// Subjectf is a wrapper for Subject(fmt.Sprintf(format, args...)).
|
||||||
Subjectf(format string, args ...any) Error
|
Subjectf(format string, args ...any) Error
|
||||||
|
PlainError
|
||||||
|
MarkdownError
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlainError interface {
|
||||||
|
Plain() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarkdownError interface {
|
||||||
|
Markdown() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// this makes JSON marshaling work,
|
// this makes JSON marshaling work,
|
||||||
|
|
|
@ -153,6 +153,7 @@ func TestErrorStringNested(t *testing.T) {
|
||||||
• 2
|
• 2
|
||||||
• action 3 > inner3: generic failure
|
• action 3 > inner3: generic failure
|
||||||
• 3
|
• 3
|
||||||
• 3`
|
• 3
|
||||||
|
`
|
||||||
expect.Equal(t, ansi.StripANSI(ne.Error()), want)
|
expect.Equal(t, ansi.StripANSI(ne.Error()), want)
|
||||||
}
|
}
|
||||||
|
|
43
internal/gperr/hint.go
Normal file
43
internal/gperr/hint.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package gperr
|
||||||
|
|
||||||
|
import "github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||||
|
|
||||||
|
type Hint struct {
|
||||||
|
Prefix string
|
||||||
|
Message string
|
||||||
|
Suffix string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ PlainError = (*Hint)(nil)
|
||||||
|
var _ MarkdownError = (*Hint)(nil)
|
||||||
|
|
||||||
|
func (h *Hint) Error() string {
|
||||||
|
return h.Prefix + ansi.Info(h.Message) + h.Suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hint) Plain() []byte {
|
||||||
|
return []byte(h.Prefix + h.Message + h.Suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hint) Markdown() []byte {
|
||||||
|
return []byte(h.Prefix + "**" + h.Message + "**" + h.Suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hint) MarshalText() ([]byte, error) {
|
||||||
|
return h.Plain(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hint) String() string {
|
||||||
|
return h.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoYouMean(s string) error {
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &Hint{
|
||||||
|
Prefix: "Do you mean ",
|
||||||
|
Message: s,
|
||||||
|
Suffix: "?",
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ func log(msg string, err error, level zerolog.Level, logger ...*zerolog.Logger)
|
||||||
} else {
|
} else {
|
||||||
l = logging.GetLogger()
|
l = logging.GetLogger()
|
||||||
}
|
}
|
||||||
l.WithLevel(level).Msg(New(highlight(msg)).With(err).Error())
|
l.WithLevel(level).Msg(New(highlightANSI(msg)).With(err).Error())
|
||||||
switch level {
|
switch level {
|
||||||
case zerolog.FatalLevel:
|
case zerolog.FatalLevel:
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -14,6 +14,9 @@ func Multiline() *MultilineError {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultilineError) add(err error) {
|
func (m *MultilineError) add(err error) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
m.Extras = append(m.Extras, err)
|
m.Extras = append(m.Extras, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ package gperr
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:recvcheck
|
//nolint:recvcheck
|
||||||
|
@ -13,9 +11,11 @@ type nestedError struct {
|
||||||
Extras []error `json:"extras"`
|
Extras []error `json:"extras"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var emptyError = errStr("")
|
||||||
|
|
||||||
func (err nestedError) Subject(subject string) Error {
|
func (err nestedError) Subject(subject string) Error {
|
||||||
if err.Err == nil {
|
if err.Err == nil {
|
||||||
err.Err = PrependSubject(subject, errStr(""))
|
err.Err = PrependSubject(subject, emptyError)
|
||||||
} else {
|
} else {
|
||||||
err.Err = PrependSubject(subject, err.Err)
|
err.Err = PrependSubject(subject, err.Err)
|
||||||
}
|
}
|
||||||
|
@ -67,48 +67,86 @@ func (err *nestedError) Is(other error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *nestedError) Error() string {
|
var nilError = newError("<nil>")
|
||||||
|
var bulletPrefix = []byte("• ")
|
||||||
|
var markdownBulletPrefix = []byte("- ")
|
||||||
|
var spaces = []byte(" ")
|
||||||
|
|
||||||
|
type appendLineFunc func(buf []byte, err error, level int) []byte
|
||||||
|
|
||||||
|
func (err *nestedError) fmtError(appendLine appendLineFunc) []byte {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return makeLine("<nil>", 0)
|
return appendLine(nil, nilError, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err.Err != nil {
|
if err.Err != nil {
|
||||||
lines := make([]string, 0, 1+len(err.Extras))
|
buf := appendLine(nil, err.Err, 0)
|
||||||
lines = append(lines, makeLine(err.Err.Error(), 0))
|
if len(err.Extras) > 0 {
|
||||||
lines = append(lines, makeLines(err.Extras, 1)...)
|
buf = append(buf, '\n')
|
||||||
return strutils.JoinLines(lines)
|
buf = appendLines(buf, err.Extras, 1, appendLine)
|
||||||
}
|
}
|
||||||
return strutils.JoinLines(makeLines(err.Extras, 0))
|
return buf
|
||||||
|
}
|
||||||
|
return appendLines(nil, err.Extras, 0, appendLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:inline
|
func (err *nestedError) Error() string {
|
||||||
func makeLine(err string, level int) string {
|
return string(err.fmtError(appendLineNormal))
|
||||||
const bulletPrefix = "• "
|
}
|
||||||
const spaces = " "
|
|
||||||
|
|
||||||
|
func (err *nestedError) Plain() []byte {
|
||||||
|
return err.fmtError(appendLinePlain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *nestedError) Markdown() []byte {
|
||||||
|
return err.fmtError(appendLineMd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLine(buf []byte, err error, level int, prefix []byte, format func(err error) []byte) []byte {
|
||||||
|
if err == nil {
|
||||||
|
return appendLine(buf, nilError, level, prefix, format)
|
||||||
|
}
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
return err
|
return append(buf, format(err)...)
|
||||||
}
|
}
|
||||||
return spaces[:2*level] + bulletPrefix + err
|
buf = append(buf, spaces[:2*level]...)
|
||||||
|
buf = append(buf, prefix...)
|
||||||
|
buf = append(buf, format(err)...)
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeLines(errs []error, level int) []string {
|
func appendLineNormal(buf []byte, err error, level int) []byte {
|
||||||
|
return appendLine(buf, err, level, bulletPrefix, Normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLinePlain(buf []byte, err error, level int) []byte {
|
||||||
|
return appendLine(buf, err, level, bulletPrefix, Plain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLineMd(buf []byte, err error, level int) []byte {
|
||||||
|
return appendLine(buf, err, level, markdownBulletPrefix, Markdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendLines(buf []byte, errs []error, level int, appendLine appendLineFunc) []byte {
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
return nil
|
return buf
|
||||||
}
|
}
|
||||||
lines := make([]string, 0, len(errs))
|
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
switch err := wrap(err).(type) {
|
switch err := wrap(err).(type) {
|
||||||
case *nestedError:
|
case *nestedError:
|
||||||
if err.Err != nil {
|
if err.Err != nil {
|
||||||
lines = append(lines, makeLine(err.Err.Error(), level))
|
buf = appendLine(buf, err.Err, level)
|
||||||
lines = append(lines, makeLines(err.Extras, level+1)...)
|
buf = append(buf, '\n')
|
||||||
|
buf = appendLines(buf, err.Extras, level+1, appendLine)
|
||||||
} else {
|
} else {
|
||||||
lines = append(lines, makeLines(err.Extras, level)...)
|
buf = appendLines(buf, err.Extras, level, appendLine)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
lines = append(lines, makeLine(err.Error(), level))
|
if err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf = appendLine(buf, err, level)
|
||||||
|
buf = append(buf, '\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lines
|
return buf
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package gperr
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||||
)
|
)
|
||||||
|
@ -19,10 +19,23 @@ type withSubject struct {
|
||||||
|
|
||||||
const subjectSep = " > "
|
const subjectSep = " > "
|
||||||
|
|
||||||
func highlight(subject string) string {
|
type highlightFunc func(subject string) string
|
||||||
|
|
||||||
|
var _ PlainError = (*withSubject)(nil)
|
||||||
|
var _ MarkdownError = (*withSubject)(nil)
|
||||||
|
|
||||||
|
func highlightANSI(subject string) string {
|
||||||
return ansi.HighlightRed + subject + ansi.Reset
|
return ansi.HighlightRed + subject + ansi.Reset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func highlightMarkdown(subject string) string {
|
||||||
|
return "**" + subject + "**"
|
||||||
|
}
|
||||||
|
|
||||||
|
func noHighlight(subject string) string {
|
||||||
|
return subject
|
||||||
|
}
|
||||||
|
|
||||||
func PrependSubject(subject string, err error) error {
|
func PrependSubject(subject string, err error) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -32,6 +45,11 @@ func PrependSubject(subject string, err error) error {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *withSubject:
|
case *withSubject:
|
||||||
return err.Prepend(subject)
|
return err.Prepend(subject)
|
||||||
|
case *wrappedError:
|
||||||
|
return &wrappedError{
|
||||||
|
Err: PrependSubject(subject, err.Err),
|
||||||
|
Message: err.Message,
|
||||||
|
}
|
||||||
case Error:
|
case Error:
|
||||||
return err.Subject(subject)
|
return err.Subject(subject)
|
||||||
}
|
}
|
||||||
|
@ -69,24 +87,42 @@ func (err *withSubject) Unwrap() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *withSubject) Error() string {
|
func (err *withSubject) Error() string {
|
||||||
|
return string(err.fmtError(highlightANSI))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *withSubject) Plain() []byte {
|
||||||
|
return err.fmtError(noHighlight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *withSubject) Markdown() []byte {
|
||||||
|
return err.fmtError(highlightMarkdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *withSubject) fmtError(highlight highlightFunc) []byte {
|
||||||
// subject is in reversed order
|
// subject is in reversed order
|
||||||
n := len(err.Subjects)
|
|
||||||
size := 0
|
size := 0
|
||||||
errStr := err.Err.Error()
|
errStr := err.Err.Error()
|
||||||
var sb strings.Builder
|
subjects := err.Subjects
|
||||||
for _, s := range err.Subjects {
|
if err.pendingSubject != "" {
|
||||||
|
subjects = append(subjects, err.pendingSubject)
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, s := range subjects {
|
||||||
size += len(s)
|
size += len(s)
|
||||||
}
|
}
|
||||||
sb.Grow(size + 2 + n*len(subjectSep) + len(errStr) + len(highlight("")))
|
n := len(subjects)
|
||||||
|
buf.Grow(size + 2 + n*len(subjectSep) + len(errStr) + len(highlight("")))
|
||||||
|
|
||||||
for i := n - 1; i > 0; i-- {
|
for i := n - 1; i > 0; i-- {
|
||||||
sb.WriteString(err.Subjects[i])
|
buf.WriteString(subjects[i])
|
||||||
sb.WriteString(subjectSep)
|
buf.WriteString(subjectSep)
|
||||||
}
|
}
|
||||||
sb.WriteString(highlight(err.Subjects[0]))
|
buf.WriteString(highlight(subjects[0]))
|
||||||
sb.WriteString(": ")
|
if errStr != "" {
|
||||||
sb.WriteString(errStr)
|
buf.WriteString(": ")
|
||||||
return sb.String()
|
buf.WriteString(errStr)
|
||||||
|
}
|
||||||
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
@ -100,6 +136,9 @@ func (err *withSubject) MarshalJSON() ([]byte, error) {
|
||||||
Subjects: subjects,
|
Subjects: subjects,
|
||||||
Err: err.Err,
|
Err: err.Err,
|
||||||
}
|
}
|
||||||
|
if err.pendingSubject != "" {
|
||||||
|
reversed.Subjects = append(reversed.Subjects, err.pendingSubject)
|
||||||
|
}
|
||||||
|
|
||||||
return json.Marshal(reversed)
|
return json.Marshal(reversed)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package gperr
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,13 +30,13 @@ func Wrap(err error, message ...string) Error {
|
||||||
//nolint:errorlint
|
//nolint:errorlint
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *baseError:
|
case *baseError:
|
||||||
err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
|
err.Err = &wrappedError{err.Err, message[0]}
|
||||||
return err
|
return err
|
||||||
case *nestedError:
|
case *nestedError:
|
||||||
err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
|
err.Err = &wrappedError{err.Err, message[0]}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return &baseError{fmt.Errorf("%s: %w", message[0], err)}
|
return &baseError{&wrappedError{err, message[0]}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unwrap(err error) Error {
|
func Unwrap(err error) Error {
|
||||||
|
@ -65,18 +63,6 @@ func wrap(err error) Error {
|
||||||
return &baseError{err}
|
return &baseError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsJSONMarshallable(err error) bool {
|
|
||||||
switch err := err.(type) {
|
|
||||||
case *nestedError, *withSubject:
|
|
||||||
return true
|
|
||||||
case *baseError:
|
|
||||||
return IsJSONMarshallable(err.Err)
|
|
||||||
default:
|
|
||||||
var v json.Marshaler
|
|
||||||
return errors.As(err, &v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Join(errors ...error) Error {
|
func Join(errors ...error) Error {
|
||||||
n := 0
|
n := 0
|
||||||
for _, err := range errors {
|
for _, err := range errors {
|
||||||
|
@ -98,8 +84,50 @@ func Join(errors ...error) Error {
|
||||||
return &nestedError{Extras: errs}
|
return &nestedError{Extras: errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func JoinLines(main error, errors ...string) Error {
|
||||||
|
errs := make([]error, len(errors))
|
||||||
|
for i, err := range errors {
|
||||||
|
if err == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errs[i] = newError(err)
|
||||||
|
}
|
||||||
|
return &nestedError{Err: main, Extras: errs}
|
||||||
|
}
|
||||||
|
|
||||||
func Collect[T any, Err error, Arg any, Func func(Arg) (T, Err)](eb *Builder, fn Func, arg Arg) T {
|
func Collect[T any, Err error, Arg any, Func func(Arg) (T, Err)](eb *Builder, fn Func, arg Arg) T {
|
||||||
result, err := fn(arg)
|
result, err := fn(arg)
|
||||||
eb.Add(err)
|
eb.Add(err)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Normal(err error) []byte {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []byte(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Plain(err error) []byte {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p, ok := err.(PlainError); ok {
|
||||||
|
return p.Plain()
|
||||||
|
}
|
||||||
|
return []byte(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Markdown(err error) []byte {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch err := err.(type) {
|
||||||
|
case MarkdownError:
|
||||||
|
return err.Markdown()
|
||||||
|
case interface{ Unwrap() []error }:
|
||||||
|
return appendLines(nil, err.Unwrap(), 0, appendLineMd)
|
||||||
|
default:
|
||||||
|
return []byte(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,63 +1,55 @@
|
||||||
package gperr
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testErr struct{}
|
type testErr struct{}
|
||||||
|
|
||||||
func (e *testErr) Error() string {
|
func (e testErr) Error() string {
|
||||||
return "test error"
|
return "test error"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *testErr) MarshalJSON() ([]byte, error) {
|
func (e testErr) Plain() []byte {
|
||||||
return nil, nil
|
return []byte("test error")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsJSONMarshallable(t *testing.T) {
|
func (e testErr) Markdown() []byte {
|
||||||
tests := []struct {
|
return []byte("**test error**")
|
||||||
name string
|
}
|
||||||
err error
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "testErr",
|
|
||||||
err: &testErr{},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "baseError",
|
|
||||||
err: &baseError{},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "baseError with json marshallable error",
|
|
||||||
err: &baseError{&testErr{}},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nestedError",
|
|
||||||
err: &nestedError{},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "withSubject",
|
|
||||||
err: &withSubject{},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "standard error",
|
|
||||||
err: errors.New("test error"),
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
type testMultiErr struct {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
errors []error
|
||||||
if got := IsJSONMarshallable(test.err); got != test.want {
|
}
|
||||||
t.Errorf("IsJSONMarshallable(%v) = %v, want %v", test.err, got, test.want)
|
|
||||||
|
func (e testMultiErr) Error() string {
|
||||||
|
return Join(e.errors...).Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e testMultiErr) Unwrap() []error {
|
||||||
|
return e.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatting(t *testing.T) {
|
||||||
|
err := testErr{}
|
||||||
|
plain := Plain(err)
|
||||||
|
if string(plain) != "test error" {
|
||||||
|
t.Errorf("expected test error, got %s", string(plain))
|
||||||
}
|
}
|
||||||
})
|
md := Markdown(err)
|
||||||
|
if string(md) != "**test error**" {
|
||||||
|
t.Errorf("expected test error, got %s", string(md))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiError(t *testing.T) {
|
||||||
|
err := testMultiErr{[]error{testErr{}, testErr{}}}
|
||||||
|
plain := Plain(err)
|
||||||
|
if string(plain) != "test error\ntest error\n" {
|
||||||
|
t.Errorf("expected test error, got %s", string(plain))
|
||||||
|
}
|
||||||
|
md := Markdown(err)
|
||||||
|
if string(md) != "**test error**\n**test error**\n" {
|
||||||
|
t.Errorf("expected test error, got %s", string(md))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
internal/gperr/wrapped.go
Normal file
34
internal/gperr/wrapped.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package gperr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedError struct {
|
||||||
|
Err error
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ PlainError = (*wrappedError)(nil)
|
||||||
|
var _ MarkdownError = (*wrappedError)(nil)
|
||||||
|
|
||||||
|
func (e *wrappedError) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", e.Message, e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *wrappedError) Plain() []byte {
|
||||||
|
return fmt.Appendf(nil, "%s: %s", e.Message, e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *wrappedError) Markdown() []byte {
|
||||||
|
return fmt.Appendf(nil, "**%s**: %s", e.Message, e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *wrappedError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *wrappedError) Is(target error) bool {
|
||||||
|
return errors.Is(e.Err, target)
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ func fetchIconAbsolute(ctx context.Context, url string) *FetchResult {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||||
return &FetchResult{StatusCode: http.StatusBadGateway, ErrMsg: "request timeout"}
|
return &FetchResult{StatusCode: http.StatusBadGateway, ErrMsg: "request timeout"}
|
||||||
|
@ -114,12 +114,18 @@ func fetchKnownIcon(ctx context.Context, url *IconURL) *FetchResult {
|
||||||
return fetchIconAbsolute(ctx, url.URL())
|
return fetchIconAbsolute(ctx, url.URL())
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchIcon(ctx context.Context, filetype, filename string) *FetchResult {
|
func fetchIcon(ctx context.Context, filename string) *FetchResult {
|
||||||
result := fetchKnownIcon(ctx, NewSelfhStIconURL(filename, filetype))
|
for _, fileType := range []string{"svg", "webp", "png"} {
|
||||||
|
result := fetchKnownIcon(ctx, NewSelfhStIconURL(filename, fileType))
|
||||||
if result.OK() {
|
if result.OK() {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return fetchKnownIcon(ctx, NewWalkXCodeIconURL(filename, filetype))
|
result = fetchKnownIcon(ctx, NewWalkXCodeIconURL(filename, fileType))
|
||||||
|
if result.OK() {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &FetchResult{StatusCode: http.StatusNotFound, ErrMsg: "no icon found"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindIcon(ctx context.Context, r route, uri string) *FetchResult {
|
func FindIcon(ctx context.Context, r route, uri string) *FetchResult {
|
||||||
|
@ -127,17 +133,18 @@ func FindIcon(ctx context.Context, r route, uri string) *FetchResult {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
result := fetchIcon(ctx, "png", sanitizeName(r.Reference()))
|
for _, ref := range r.References() {
|
||||||
if !result.OK() {
|
result := fetchIcon(ctx, sanitizeName(ref))
|
||||||
if r, ok := r.(httpRoute); ok {
|
|
||||||
// fallback to parse html
|
|
||||||
result = findIconSlow(ctx, r, uri, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if result.OK() {
|
if result.OK() {
|
||||||
storeIconCache(r.Key(), result)
|
storeIconCache(r.Key(), result)
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r, ok := r.(httpRoute); ok {
|
||||||
|
// fallback to parse html
|
||||||
|
return findIconSlow(ctx, r, uri, nil)
|
||||||
|
}
|
||||||
|
return &FetchResult{StatusCode: http.StatusNotFound, ErrMsg: "no icon found"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findIconSlow(ctx context.Context, r httpRoute, uri string, stack []string) *FetchResult {
|
func findIconSlow(ctx context.Context, r httpRoute, uri string, stack []string) *FetchResult {
|
||||||
|
@ -154,7 +161,7 @@ func findIconSlow(ctx context.Context, r httpRoute, uri string, stack []string)
|
||||||
ctx, cancel := context.WithTimeoutCause(ctx, faviconFetchTimeout, errors.New("favicon request timeout"))
|
ctx, cancel := context.WithTimeoutCause(ctx, faviconFetchTimeout, errors.New("favicon request timeout"))
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
newReq, err := http.NewRequestWithContext(ctx, "GET", r.TargetURL().String(), nil)
|
newReq, err := http.NewRequestWithContext(ctx, http.MethodGet, r.TargetURL().String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &FetchResult{StatusCode: http.StatusInternalServerError, ErrMsg: "cannot create request"}
|
return &FetchResult{StatusCode: http.StatusInternalServerError, ErrMsg: "cannot create request"}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ package homepage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"slices"
|
||||||
|
|
||||||
config "github.com/yusing/go-proxy/internal/config/types"
|
"github.com/yusing/go-proxy/internal/homepage/widgets"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,14 +19,15 @@ type (
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Description string `json:"description" aliases:"desc"`
|
Description string `json:"description" aliases:"desc"`
|
||||||
SortOrder int `json:"sort_order"`
|
SortOrder int `json:"sort_order"`
|
||||||
WidgetConfig map[string]any `json:"widget_config" aliases:"widget"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item struct {
|
Item struct {
|
||||||
*ItemConfig
|
*ItemConfig
|
||||||
|
WidgetConfig *widgets.Config `json:"widget_config,omitempty" aliases:"widget"`
|
||||||
|
|
||||||
Alias string
|
Alias string
|
||||||
Provider string
|
Provider string
|
||||||
|
OriginURL string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,23 +44,10 @@ func (cfg *ItemConfig) GetOverride(alias string) *ItemConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (item *Item) MarshalJSON() ([]byte, error) {
|
func (item *Item) MarshalJSON() ([]byte, error) {
|
||||||
var url *string
|
|
||||||
if !strings.ContainsRune(item.Alias, '.') {
|
|
||||||
godoxyCfg := config.GetInstance().Value()
|
|
||||||
// use first domain as base domain
|
|
||||||
domains := godoxyCfg.MatchDomains
|
|
||||||
if len(domains) > 0 {
|
|
||||||
url = new(string)
|
|
||||||
*url = item.Alias + domains[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
url = &item.Alias
|
|
||||||
}
|
|
||||||
return json.Marshal(map[string]any{
|
return json.Marshal(map[string]any{
|
||||||
"show": item.Show,
|
"show": item.Show,
|
||||||
"alias": item.Alias,
|
"alias": item.Alias,
|
||||||
"provider": item.Provider,
|
"provider": item.Provider,
|
||||||
"url": url,
|
|
||||||
"name": item.Name,
|
"name": item.Name,
|
||||||
"icon": item.Icon,
|
"icon": item.Icon,
|
||||||
"category": item.Category,
|
"category": item.Category,
|
||||||
|
@ -74,4 +62,13 @@ func (c Homepage) Add(item *Item) {
|
||||||
c[item.Category] = make(Category, 0)
|
c[item.Category] = make(Category, 0)
|
||||||
}
|
}
|
||||||
c[item.Category] = append(c[item.Category], item)
|
c[item.Category] = append(c[item.Category], item)
|
||||||
|
slices.SortStableFunc(c[item.Category], func(a, b *Item) int {
|
||||||
|
if a.SortOrder < b.SortOrder {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if a.SortOrder > b.SortOrder {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package homepage
|
package homepage_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/yusing/go-proxy/internal/homepage"
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ func TestOverrideItem(t *testing.T) {
|
||||||
Show: false,
|
Show: false,
|
||||||
Name: "Foo",
|
Name: "Foo",
|
||||||
Icon: &IconURL{
|
Icon: &IconURL{
|
||||||
Value: "/favicon.ico",
|
FullURL: strPtr("/favicon.ico"),
|
||||||
IconSource: IconSourceRelative,
|
IconSource: IconSourceRelative,
|
||||||
},
|
},
|
||||||
Category: "App",
|
Category: "App",
|
||||||
|
@ -24,7 +25,7 @@ func TestOverrideItem(t *testing.T) {
|
||||||
Name: "Bar",
|
Name: "Bar",
|
||||||
Category: "Test",
|
Category: "Test",
|
||||||
Icon: &IconURL{
|
Icon: &IconURL{
|
||||||
Value: "@walkxcode/example.png",
|
FullURL: strPtr("@walkxcode/example.png"),
|
||||||
IconSource: IconSourceWalkXCode,
|
IconSource: IconSourceWalkXCode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
|
"github.com/yusing/go-proxy/internal/jsonstore"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
|
@ -15,34 +16,24 @@ import (
|
||||||
|
|
||||||
type cacheEntry struct {
|
type cacheEntry struct {
|
||||||
Icon []byte `json:"icon"`
|
Icon []byte `json:"icon"`
|
||||||
ContentType string `json:"content_type"`
|
ContentType string `json:"content_type,omitempty"`
|
||||||
LastAccess atomic.Value[time.Time] `json:"last_access"`
|
LastAccess atomic.Value[time.Time] `json:"last_access"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache key can be absolute url or route name.
|
// cache key can be absolute url or route name.
|
||||||
var (
|
var (
|
||||||
iconCache = make(map[string]*cacheEntry)
|
iconCache = jsonstore.Store[*cacheEntry](common.NamespaceIconCache)
|
||||||
iconCacheMu sync.RWMutex
|
iconMu sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
iconCacheTTL = 3 * 24 * time.Hour
|
iconCacheTTL = 3 * 24 * time.Hour
|
||||||
cleanUpInterval = time.Minute
|
cleanUpInterval = time.Minute
|
||||||
maxCacheSize = 1024 * 1024 // 1MB
|
maxIconSize = 1024 * 1024 // 1MB
|
||||||
maxCacheEntries = 100
|
maxCacheEntries = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitIconCache() {
|
func init() {
|
||||||
iconCacheMu.Lock()
|
|
||||||
defer iconCacheMu.Unlock()
|
|
||||||
|
|
||||||
err := utils.LoadJSONIfExist(common.IconCachePath, &iconCache)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error().Err(err).Msg("failed to load icon cache")
|
|
||||||
} else if len(iconCache) > 0 {
|
|
||||||
logging.Info().Int("count", len(iconCache)).Msg("icon cache loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
cleanupTicker := time.NewTicker(cleanUpInterval)
|
cleanupTicker := time.NewTicker(cleanUpInterval)
|
||||||
defer cleanupTicker.Stop()
|
defer cleanupTicker.Stop()
|
||||||
|
@ -55,36 +46,21 @@ func InitIconCache() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
task.OnProgramExit("save_favicon_cache", func() {
|
|
||||||
iconCacheMu.Lock()
|
|
||||||
defer iconCacheMu.Unlock()
|
|
||||||
|
|
||||||
if len(iconCache) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := utils.SaveJSON(common.IconCachePath, &iconCache, 0o644); err != nil {
|
|
||||||
logging.Error().Err(err).Msg("failed to save icon cache")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pruneExpiredIconCache() {
|
func pruneExpiredIconCache() {
|
||||||
iconCacheMu.Lock()
|
|
||||||
defer iconCacheMu.Unlock()
|
|
||||||
|
|
||||||
nPruned := 0
|
nPruned := 0
|
||||||
for key, icon := range iconCache {
|
for key, icon := range iconCache.Range {
|
||||||
if icon.IsExpired() {
|
if icon.IsExpired() {
|
||||||
delete(iconCache, key)
|
iconCache.Delete(key)
|
||||||
nPruned++
|
nPruned++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(iconCache) > maxCacheEntries {
|
if iconCache.Size() > maxCacheEntries {
|
||||||
|
iconCache.Clear()
|
||||||
newIconCache := make(map[string]*cacheEntry, maxCacheEntries)
|
newIconCache := make(map[string]*cacheEntry, maxCacheEntries)
|
||||||
i := 0
|
i := 0
|
||||||
for key, icon := range iconCache {
|
for key, icon := range iconCache.Range {
|
||||||
if i == maxCacheEntries {
|
if i == maxCacheEntries {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -93,7 +69,9 @@ func pruneExpiredIconCache() {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iconCache = newIconCache
|
for key, icon := range newIconCache {
|
||||||
|
iconCache.Store(key, icon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if nPruned > 0 {
|
if nPruned > 0 {
|
||||||
logging.Info().Int("pruned", nPruned).Msg("pruned expired icon cache")
|
logging.Info().Int("pruned", nPruned).Msg("pruned expired icon cache")
|
||||||
|
@ -101,21 +79,18 @@ func pruneExpiredIconCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PruneRouteIconCache(route route) {
|
func PruneRouteIconCache(route route) {
|
||||||
iconCacheMu.Lock()
|
iconCache.Delete(route.Key())
|
||||||
defer iconCacheMu.Unlock()
|
|
||||||
delete(iconCache, route.Key())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIconCache(key string) *FetchResult {
|
func loadIconCache(key string) *FetchResult {
|
||||||
iconCacheMu.RLock()
|
iconMu.RLock()
|
||||||
defer iconCacheMu.RUnlock()
|
defer iconMu.RUnlock()
|
||||||
|
icon, ok := iconCache.Load(key)
|
||||||
icon, ok := iconCache[key]
|
|
||||||
if ok && len(icon.Icon) > 0 {
|
if ok && len(icon.Icon) > 0 {
|
||||||
logging.Debug().
|
logging.Debug().
|
||||||
Str("key", key).
|
Str("key", key).
|
||||||
Msg("icon found in cache")
|
Msg("icon found in cache")
|
||||||
icon.LastAccess.Store(time.Now())
|
icon.LastAccess.Store(utils.TimeNow())
|
||||||
return &FetchResult{Icon: icon.Icon, contentType: icon.ContentType}
|
return &FetchResult{Icon: icon.Icon, contentType: icon.ContentType}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -123,15 +98,17 @@ func loadIconCache(key string) *FetchResult {
|
||||||
|
|
||||||
func storeIconCache(key string, result *FetchResult) {
|
func storeIconCache(key string, result *FetchResult) {
|
||||||
icon := result.Icon
|
icon := result.Icon
|
||||||
if len(icon) > maxCacheSize {
|
if len(icon) > maxIconSize {
|
||||||
logging.Debug().Int("size", len(icon)).Msg("icon cache size exceeds max cache size")
|
logging.Debug().Int("size", len(icon)).Msg("icon cache size exceeds max cache size")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
iconCacheMu.Lock()
|
|
||||||
defer iconCacheMu.Unlock()
|
iconMu.Lock()
|
||||||
|
defer iconMu.Unlock()
|
||||||
|
|
||||||
entry := &cacheEntry{Icon: icon, ContentType: result.contentType}
|
entry := &cacheEntry{Icon: icon, ContentType: result.contentType}
|
||||||
entry.LastAccess.Store(time.Now())
|
entry.LastAccess.Store(time.Now())
|
||||||
iconCache[key] = entry
|
iconCache.Store(key, entry)
|
||||||
logging.Debug().Str("key", key).Int("size", len(icon)).Msg("stored icon cache")
|
logging.Debug().Str("key", key).Int("size", len(icon)).Msg("stored icon cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,12 +117,20 @@ func (e *cacheEntry) IsExpired() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *cacheEntry) UnmarshalJSON(data []byte) error {
|
func (e *cacheEntry) UnmarshalJSON(data []byte) error {
|
||||||
|
var tmp struct {
|
||||||
|
Icon []byte `json:"icon"`
|
||||||
|
ContentType string `json:"content_type,omitempty"`
|
||||||
|
LastAccess time.Time `json:"last_access"`
|
||||||
|
}
|
||||||
// check if data is json
|
// check if data is json
|
||||||
if json.Valid(data) {
|
if json.Valid(data) {
|
||||||
err := json.Unmarshal(data, &e)
|
err := json.Unmarshal(data, &tmp)
|
||||||
// return only if unmarshal is successful
|
// return only if unmarshal is successful
|
||||||
// otherwise fallback to base64
|
// otherwise fallback to base64
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
e.Icon = tmp.Icon
|
||||||
|
e.ContentType = tmp.ContentType
|
||||||
|
e.LastAccess.Store(tmp.LastAccess)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,68 +9,78 @@ import (
|
||||||
|
|
||||||
type (
|
type (
|
||||||
IconURL struct {
|
IconURL struct {
|
||||||
Value string `json:"value"`
|
FullURL *string `json:"value,omitempty"` // only for absolute/relative icons
|
||||||
FullValue string `json:"full_value"`
|
Extra *IconExtra `json:"extra,omitempty"` // only for walkxcode/selfhst icons
|
||||||
IconSource `json:"source"`
|
IconSource `json:"source"`
|
||||||
Extra *IconExtra `json:"extra"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IconExtra struct {
|
IconExtra struct {
|
||||||
|
Key IconKey `json:"key"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
FileType string `json:"file_type"`
|
FileType string `json:"file_type"`
|
||||||
Name string `json:"name"`
|
IsLight bool `json:"is_light"`
|
||||||
|
IsDark bool `json:"is_dark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
IconSource int
|
IconSource string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IconSourceAbsolute IconSource = iota
|
IconSourceAbsolute IconSource = "https://"
|
||||||
IconSourceRelative
|
IconSourceRelative IconSource = "@target"
|
||||||
IconSourceWalkXCode
|
IconSourceWalkXCode IconSource = "@walkxcode"
|
||||||
IconSourceSelfhSt
|
IconSourceSelfhSt IconSource = "@selfhst"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrInvalidIconURL = gperr.New("invalid icon url")
|
var ErrInvalidIconURL = gperr.New("invalid icon url")
|
||||||
|
|
||||||
func NewSelfhStIconURL(reference, format string) *IconURL {
|
func NewIconURL(source IconSource, refOrName, format string) *IconURL {
|
||||||
|
switch source {
|
||||||
|
case IconSourceWalkXCode, IconSourceSelfhSt:
|
||||||
|
default:
|
||||||
|
panic("invalid icon source")
|
||||||
|
}
|
||||||
|
isLight, isDark := false, false
|
||||||
|
if strings.HasSuffix(refOrName, "-light") {
|
||||||
|
isLight = true
|
||||||
|
refOrName = strings.TrimSuffix(refOrName, "-light")
|
||||||
|
} else if strings.HasSuffix(refOrName, "-dark") {
|
||||||
|
isDark = true
|
||||||
|
refOrName = strings.TrimSuffix(refOrName, "-dark")
|
||||||
|
}
|
||||||
return &IconURL{
|
return &IconURL{
|
||||||
Value: reference + "." + format,
|
IconSource: source,
|
||||||
FullValue: fmt.Sprintf("@selfhst/%s.%s", reference, format),
|
|
||||||
IconSource: IconSourceSelfhSt,
|
|
||||||
Extra: &IconExtra{
|
Extra: &IconExtra{
|
||||||
|
Key: NewIconKey(source, refOrName),
|
||||||
FileType: format,
|
FileType: format,
|
||||||
Name: reference,
|
Ref: refOrName,
|
||||||
|
IsLight: isLight,
|
||||||
|
IsDark: isDark,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSelfhStIconURL(refOrName, format string) *IconURL {
|
||||||
|
return NewIconURL(IconSourceSelfhSt, refOrName, format)
|
||||||
|
}
|
||||||
|
|
||||||
func NewWalkXCodeIconURL(name, format string) *IconURL {
|
func NewWalkXCodeIconURL(name, format string) *IconURL {
|
||||||
return &IconURL{
|
return NewIconURL(IconSourceWalkXCode, name, format)
|
||||||
Value: name + "." + format,
|
|
||||||
FullValue: fmt.Sprintf("@walkxcode/%s.%s", name, format),
|
|
||||||
IconSource: IconSourceWalkXCode,
|
|
||||||
Extra: &IconExtra{
|
|
||||||
FileType: format,
|
|
||||||
Name: name,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasIcon checks if the icon referenced by the IconURL exists in the cache based on its source.
|
// HasIcon checks if the icon referenced by the IconURL exists in the cache based on its source.
|
||||||
// Returns false if the icon does not exist for IconSourceSelfhSt or IconSourceWalkXCode,
|
// Returns false if the icon does not exist for IconSourceSelfhSt or IconSourceWalkXCode,
|
||||||
// otherwise returns true.
|
// otherwise returns true.
|
||||||
func (u *IconURL) HasIcon() bool {
|
func (u *IconURL) HasIcon() bool {
|
||||||
if u.IconSource == IconSourceSelfhSt {
|
return HasIcon(u)
|
||||||
return HasSelfhstIcon(u.Extra.Name, u.Extra.FileType)
|
|
||||||
}
|
|
||||||
if u.IconSource == IconSourceWalkXCode {
|
|
||||||
return HasWalkxCodeIcon(u.Extra.Name, u.Extra.FileType)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse implements strutils.Parser.
|
// Parse implements strutils.Parser.
|
||||||
func (u *IconURL) Parse(v string) error {
|
func (u *IconURL) Parse(v string) error {
|
||||||
|
return u.parse(v, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *IconURL) parse(v string, checkExists bool) error {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
return ErrInvalidIconURL
|
return ErrInvalidIconURL
|
||||||
}
|
}
|
||||||
|
@ -78,33 +88,25 @@ func (u *IconURL) Parse(v string) error {
|
||||||
if slashIndex == -1 {
|
if slashIndex == -1 {
|
||||||
return ErrInvalidIconURL
|
return ErrInvalidIconURL
|
||||||
}
|
}
|
||||||
u.FullValue = v
|
|
||||||
beforeSlash := v[:slashIndex]
|
beforeSlash := v[:slashIndex]
|
||||||
switch beforeSlash {
|
switch beforeSlash {
|
||||||
case "http:", "https:":
|
case "http:", "https:":
|
||||||
u.Value = v
|
u.FullURL = &v
|
||||||
u.IconSource = IconSourceAbsolute
|
u.IconSource = IconSourceAbsolute
|
||||||
case "@target", "": // @target/favicon.ico, /favicon.ico
|
case "@target", "": // @target/favicon.ico, /favicon.ico
|
||||||
u.Value = v[slashIndex:]
|
url := v[slashIndex:]
|
||||||
u.IconSource = IconSourceRelative
|
if url == "/" {
|
||||||
if u.Value == "/" {
|
|
||||||
return ErrInvalidIconURL.Withf("%s", "empty path")
|
return ErrInvalidIconURL.Withf("%s", "empty path")
|
||||||
}
|
}
|
||||||
case "png", "svg", "webp": // walkxcode Icons
|
u.FullURL = &url
|
||||||
u.Value = v
|
u.IconSource = IconSourceRelative
|
||||||
u.IconSource = IconSourceWalkXCode
|
|
||||||
u.Extra = &IconExtra{
|
|
||||||
FileType: beforeSlash,
|
|
||||||
Name: strings.TrimSuffix(v[slashIndex+1:], "."+beforeSlash),
|
|
||||||
}
|
|
||||||
case "@selfhst", "@walkxcode": // selfh.st / walkxcode Icons, @selfhst/<reference>.<format>
|
case "@selfhst", "@walkxcode": // selfh.st / walkxcode Icons, @selfhst/<reference>.<format>
|
||||||
u.Value = v[slashIndex+1:]
|
|
||||||
if beforeSlash == "@selfhst" {
|
if beforeSlash == "@selfhst" {
|
||||||
u.IconSource = IconSourceSelfhSt
|
u.IconSource = IconSourceSelfhSt
|
||||||
} else {
|
} else {
|
||||||
u.IconSource = IconSourceWalkXCode
|
u.IconSource = IconSourceWalkXCode
|
||||||
}
|
}
|
||||||
parts := strings.Split(u.Value, ".")
|
parts := strings.Split(v[slashIndex+1:], ".")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return ErrInvalidIconURL.Withf("expect @%s/<reference>.<format>, e.g. @%s/adguard-home.webp", beforeSlash, beforeSlash)
|
return ErrInvalidIconURL.Withf("expect @%s/<reference>.<format>, e.g. @%s/adguard-home.webp", beforeSlash, beforeSlash)
|
||||||
}
|
}
|
||||||
|
@ -117,40 +119,67 @@ func (u *IconURL) Parse(v string) error {
|
||||||
default:
|
default:
|
||||||
return ErrInvalidIconURL.Withf("%s", "invalid image format, expect svg/png/webp")
|
return ErrInvalidIconURL.Withf("%s", "invalid image format, expect svg/png/webp")
|
||||||
}
|
}
|
||||||
|
isLight, isDark := false, false
|
||||||
|
if strings.HasSuffix(reference, "-light") {
|
||||||
|
isLight = true
|
||||||
|
reference = strings.TrimSuffix(reference, "-light")
|
||||||
|
} else if strings.HasSuffix(reference, "-dark") {
|
||||||
|
isDark = true
|
||||||
|
reference = strings.TrimSuffix(reference, "-dark")
|
||||||
|
}
|
||||||
u.Extra = &IconExtra{
|
u.Extra = &IconExtra{
|
||||||
|
Key: NewIconKey(u.IconSource, reference),
|
||||||
FileType: format,
|
FileType: format,
|
||||||
Name: reference,
|
Ref: reference,
|
||||||
|
IsLight: isLight,
|
||||||
|
IsDark: isDark,
|
||||||
|
}
|
||||||
|
if checkExists && !u.HasIcon() {
|
||||||
|
return ErrInvalidIconURL.Withf("no such icon %s.%s from %s", reference, format, u.IconSource)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return ErrInvalidIconURL.Withf("%s", v)
|
return ErrInvalidIconURL.Subject(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Value == "" {
|
|
||||||
return ErrInvalidIconURL.Withf("%s", "empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !u.HasIcon() {
|
|
||||||
return ErrInvalidIconURL.Withf("no such icon %s from %s", u.Value, beforeSlash)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IconURL) URL() string {
|
func (u *IconURL) URL() string {
|
||||||
|
if u.FullURL != nil {
|
||||||
|
return *u.FullURL
|
||||||
|
}
|
||||||
|
if u.Extra == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
filename := u.Extra.Ref
|
||||||
|
if u.Extra.IsLight {
|
||||||
|
filename += "-light"
|
||||||
|
} else if u.Extra.IsDark {
|
||||||
|
filename += "-dark"
|
||||||
|
}
|
||||||
switch u.IconSource {
|
switch u.IconSource {
|
||||||
case IconSourceAbsolute:
|
|
||||||
return u.Value
|
|
||||||
case IconSourceRelative:
|
|
||||||
return "/" + u.Value
|
|
||||||
case IconSourceWalkXCode:
|
case IconSourceWalkXCode:
|
||||||
return fmt.Sprintf("https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/%s/%s.%s", u.Extra.FileType, u.Extra.Name, u.Extra.FileType)
|
return fmt.Sprintf("https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/%s/%s.%s", u.Extra.FileType, filename, u.Extra.FileType)
|
||||||
case IconSourceSelfhSt:
|
case IconSourceSelfhSt:
|
||||||
return fmt.Sprintf("https://cdn.jsdelivr.net/gh/selfhst/icons/%s/%s.%s", u.Extra.FileType, u.Extra.Name, u.Extra.FileType)
|
return fmt.Sprintf("https://cdn.jsdelivr.net/gh/selfhst/icons/%s/%s.%s", u.Extra.FileType, filename, u.Extra.FileType)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IconURL) String() string {
|
func (u *IconURL) String() string {
|
||||||
return u.FullValue
|
if u.FullURL != nil {
|
||||||
|
return *u.FullURL
|
||||||
|
}
|
||||||
|
if u.Extra == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var suffix string
|
||||||
|
if u.Extra.IsLight {
|
||||||
|
suffix = "-light"
|
||||||
|
} else if u.Extra.IsDark {
|
||||||
|
suffix = "-dark"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s%s.%s", u.IconSource, u.Extra.Ref, suffix, u.Extra.FileType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IconURL) MarshalText() ([]byte, error) {
|
func (u *IconURL) MarshalText() ([]byte, error) {
|
||||||
|
@ -159,5 +188,5 @@ func (u *IconURL) MarshalText() ([]byte, error) {
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
func (u *IconURL) UnmarshalText(data []byte) error {
|
func (u *IconURL) UnmarshalText(data []byte) error {
|
||||||
return u.Parse(string(data))
|
return u.parse(string(data), false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
package homepage
|
package homepage_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/yusing/go-proxy/internal/homepage"
|
||||||
expect "github.com/yusing/go-proxy/internal/utils/testing"
|
expect "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func strPtr(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
func TestIconURL(t *testing.T) {
|
func TestIconURL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -17,7 +22,7 @@ func TestIconURL(t *testing.T) {
|
||||||
name: "absolute",
|
name: "absolute",
|
||||||
input: "http://example.com/icon.png",
|
input: "http://example.com/icon.png",
|
||||||
wantValue: &IconURL{
|
wantValue: &IconURL{
|
||||||
Value: "http://example.com/icon.png",
|
FullURL: strPtr("http://example.com/icon.png"),
|
||||||
IconSource: IconSourceAbsolute,
|
IconSource: IconSourceAbsolute,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -25,7 +30,7 @@ func TestIconURL(t *testing.T) {
|
||||||
name: "relative",
|
name: "relative",
|
||||||
input: "@target/icon.png",
|
input: "@target/icon.png",
|
||||||
wantValue: &IconURL{
|
wantValue: &IconURL{
|
||||||
Value: "/icon.png",
|
FullURL: strPtr("/icon.png"),
|
||||||
IconSource: IconSourceRelative,
|
IconSource: IconSourceRelative,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -33,7 +38,7 @@ func TestIconURL(t *testing.T) {
|
||||||
name: "relative2",
|
name: "relative2",
|
||||||
input: "/icon.png",
|
input: "/icon.png",
|
||||||
wantValue: &IconURL{
|
wantValue: &IconURL{
|
||||||
Value: "/icon.png",
|
FullURL: strPtr("/icon.png"),
|
||||||
IconSource: IconSourceRelative,
|
IconSource: IconSourceRelative,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -49,25 +54,26 @@ func TestIconURL(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "walkxcode",
|
name: "walkxcode",
|
||||||
input: "png/adguard-home.png",
|
input: "@walkxcode/adguard-home.png",
|
||||||
wantValue: &IconURL{
|
wantValue: &IconURL{
|
||||||
Value: "png/adguard-home.png",
|
|
||||||
IconSource: IconSourceWalkXCode,
|
IconSource: IconSourceWalkXCode,
|
||||||
Extra: &IconExtra{
|
Extra: &IconExtra{
|
||||||
|
Key: NewIconKey(IconSourceWalkXCode, "adguard-home"),
|
||||||
FileType: "png",
|
FileType: "png",
|
||||||
Name: "adguard-home",
|
Ref: "adguard-home",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "walkxcode_alt",
|
name: "walkxcode_light",
|
||||||
input: "@walkxcode/adguard-home.png",
|
input: "@walkxcode/pfsense-light.png",
|
||||||
wantValue: &IconURL{
|
wantValue: &IconURL{
|
||||||
Value: "adguard-home.png",
|
|
||||||
IconSource: IconSourceWalkXCode,
|
IconSource: IconSourceWalkXCode,
|
||||||
Extra: &IconExtra{
|
Extra: &IconExtra{
|
||||||
|
Key: NewIconKey(IconSourceWalkXCode, "pfsense"),
|
||||||
FileType: "png",
|
FileType: "png",
|
||||||
Name: "adguard-home",
|
Ref: "pfsense",
|
||||||
|
IsLight: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -78,13 +84,39 @@ func TestIconURL(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "selfh.st_valid",
|
name: "selfh.st_valid",
|
||||||
input: "@selfhst/adguard-home.png",
|
input: "@selfhst/adguard-home.webp",
|
||||||
wantValue: &IconURL{
|
wantValue: &IconURL{
|
||||||
Value: "adguard-home.png",
|
|
||||||
IconSource: IconSourceSelfhSt,
|
IconSource: IconSourceSelfhSt,
|
||||||
Extra: &IconExtra{
|
Extra: &IconExtra{
|
||||||
|
Key: NewIconKey(IconSourceSelfhSt, "adguard-home"),
|
||||||
|
FileType: "webp",
|
||||||
|
Ref: "adguard-home",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "selfh.st_light",
|
||||||
|
input: "@selfhst/adguard-home-light.png",
|
||||||
|
wantValue: &IconURL{
|
||||||
|
IconSource: IconSourceSelfhSt,
|
||||||
|
Extra: &IconExtra{
|
||||||
|
Key: NewIconKey(IconSourceSelfhSt, "adguard-home"),
|
||||||
FileType: "png",
|
FileType: "png",
|
||||||
Name: "adguard-home",
|
Ref: "adguard-home",
|
||||||
|
IsLight: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "selfh.st_dark",
|
||||||
|
input: "@selfhst/adguard-home-dark.svg",
|
||||||
|
wantValue: &IconURL{
|
||||||
|
IconSource: IconSourceSelfhSt,
|
||||||
|
Extra: &IconExtra{
|
||||||
|
Key: NewIconKey(IconSourceSelfhSt, "adguard-home"),
|
||||||
|
FileType: "svg",
|
||||||
|
Ref: "adguard-home",
|
||||||
|
IsDark: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -116,7 +148,6 @@ func TestIconURL(t *testing.T) {
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
expect.ErrorIs(t, ErrInvalidIconURL, err)
|
expect.ErrorIs(t, ErrInvalidIconURL, err)
|
||||||
} else {
|
} else {
|
||||||
tc.wantValue.FullValue = tc.input
|
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Equal(t, u, tc.wantValue)
|
expect.Equal(t, u, tc.wantValue)
|
||||||
}
|
}
|
||||||
|
|
68
internal/homepage/integrations/qbittorrent/client.go
Normal file
68
internal/homepage/integrations/qbittorrent/client.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package qbittorrent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
|
"github.com/yusing/go-proxy/internal/homepage/widgets"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
URL string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Initialize(ctx context.Context, url string, cfg map[string]any) error {
|
||||||
|
c.URL = url
|
||||||
|
c.Username = cfg["username"].(string)
|
||||||
|
c.Password = cfg["password"].(string)
|
||||||
|
|
||||||
|
_, err := c.Version(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) doRequest(ctx context.Context, method, endpoint string, query url.Values, body io.Reader) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, c.URL+endpoint+query.Encode(), body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Username != "" && c.Password != "" {
|
||||||
|
req.SetBasicAuth(c.Username, c.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := widgets.HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, gperr.Errorf("%w: %d %s", widgets.ErrHTTPStatus, resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonRequest[T any](ctx context.Context, client *Client, endpoint string, query url.Values) (result T, err error) {
|
||||||
|
resp, err := client.doRequest(ctx, http.MethodGet, endpoint, query, nil)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue