Compare commits

..

No commits in common. "main" and "v0.11.2-2" have entirely different histories.

288 changed files with 6611 additions and 7824 deletions

View file

@ -1,13 +1,6 @@
# 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=
@ -48,29 +41,14 @@ 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
# Frontend aliases (subdomains / FQDNs, e.g. godoxy, godoxy.domain.com) # Prometheus Metrics
GODOXY_FRONTEND_ALIASES=godoxy GODOXY_PROMETHEUS_ENABLED=true
# 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

View file

@ -15,10 +15,9 @@ 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
target: agent agent: true

View file

@ -12,10 +12,9 @@ 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
target: agent agent: true

View file

@ -1,23 +0,0 @@
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

View file

@ -12,20 +12,16 @@ on:
old_image_name: old_image_name:
required: false required: false
type: string type: string
target: agent:
required: true
type: string
dockerfile:
required: false required: false
type: string default: false
default: Dockerfile type: boolean
env: env:
REGISTRY: ghcr.io REGISTRY: ghcr.io
MAKE_ARGS: ${{ inputs.target }}=1 MAKE_ARGS: agent=${{ inputs.agent && '1' || '0' }}
DIGEST_PATH: /tmp/digests/${{ inputs.target }} DIGEST_PATH: /tmp/digests/${{ inputs.agent && 'agent' || 'main' }}
DIGEST_NAME_SUFFIX: ${{ inputs.target }} DIGEST_NAME_SUFFIX: ${{ inputs.agent && 'agent' || 'main' }}
DOCKERFILE: ${{ inputs.dockerfile }}
jobs: jobs:
build: build:
@ -80,14 +76,11 @@ 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 }} type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }}-${{ inputs.tag }}
type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }}
cache-to: | cache-to: |
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }}-${{ inputs.tag }},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 }}

View file

@ -1,151 +1,135 @@
version: "2" run:
timeout: 10m
linters-settings:
govet:
enable-all: true
disable:
- shadow
- fieldalignment
gocyclo:
min-complexity: 14
misspell:
locale: US
funlen:
lines: -1
statements: 120
forbidigo:
forbid:
- ^print(ln)?$
godox:
keywords:
- FIXME
tagalign:
align: false
sort: true
order:
- description
- json
- toml
- yaml
- yml
- label
- label-slice-as-struct
- file
- kv
- export
stylecheck:
dot-import-whitelist:
- github.com/yusing/go-proxy/internal/utils/testing # go tests only
- github.com/yusing/go-proxy/internal/api/v1/utils # api only
revive:
rules:
- name: struct-tag
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
disabled: true
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
disabled: true
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
disabled: true
- name: unreachable-code
- name: redefines-builtin-id
gomoddirectives:
replace-allow-list:
- github.com/abbot/go-http-auth
- github.com/gorilla/mux
- github.com/mailgun/minheap
- github.com/mailgun/multibuf
- github.com/jaguilar/vt100
- github.com/cucumber/godog
- github.com/http-wasm/http-wasm-host-go
testifylint:
disable:
- suite-dont-use-pkg
- require-error
- go-require
staticcheck:
checks:
- all
- -SA1019
errcheck:
exclude-functions:
- fmt.Fprintln
linters: linters:
default: all enable-all: true
disable: disable:
- bodyclose - execinquery # deprecated
- containedctx - gomnd # deprecated
- contextcheck - sqlclosecheck # not relevant (SQL)
- cyclop - rowserrcheck # not relevant (SQL)
- depguard - cyclop # duplicate of gocyclo
- dupl - depguard # Not relevant
- err113 - nakedret # Too strict
- exhaustive - lll # Not relevant
- exhaustruct - gocyclo # must be fixed
- forcetypeassert - gocognit # Too strict
- gochecknoglobals - nestif # Too many false-positive.
- prealloc # Too many false-positive.
- makezero # Not relevant
- dupl # Too strict
- gci # I don't care
- goconst # Too annoying
- gosec # Too strict
- gochecknoinits - gochecknoinits
- gocognit - gochecknoglobals
- goconst - wsl # Too strict
- gocyclo - nlreturn # Not relevant
- gomoddirectives - mnd # Too strict
- gosec - testpackage # Too strict
- gosmopolitan - tparallel # Not relevant
- ireturn - paralleltest # Not relevant
- lll - exhaustive # Not relevant
- maintidx - exhaustruct # Not relevant
- makezero - err113 # Too strict
- mnd - wrapcheck # Too strict
- nakedret - noctx # Too strict
- nestif - bodyclose # too many false-positive
- nilnil - forcetypeassert # Too strict
- nlreturn - tagliatelle # Too strict
- noctx - varnamelen # Not relevant
- nonamedreturns - nilnil # Not relevant
- paralleltest - ireturn # Not relevant
- prealloc - contextcheck # too many false-positive
- rowserrcheck - containedctx # too many false-positive
- sqlclosecheck - maintidx # kind of duplicate of gocyclo
- tagliatelle - nonamedreturns # Too strict
- testpackage - gosmopolitan # not relevant
- tparallel - exportloopref # Not relevant since go1.22
- varnamelen
- wrapcheck
- wsl
settings:
errcheck:
exclude-functions:
- fmt.Fprintln
forbidigo:
forbid:
- pattern: ^print(ln)?$
funlen:
lines: -1
statements: 120
gocyclo:
min-complexity: 14
godox:
keywords:
- FIXME
gomoddirectives:
replace-allow-list:
- github.com/abbot/go-http-auth
- github.com/gorilla/mux
- github.com/mailgun/minheap
- github.com/mailgun/multibuf
- github.com/jaguilar/vt100
- github.com/cucumber/godog
- github.com/http-wasm/http-wasm-host-go
govet:
disable:
- shadow
- fieldalignment
enable-all: true
misspell:
locale: US
revive:
rules:
- name: struct-tag
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
disabled: true
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
disabled: true
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
disabled: true
- name: unreachable-code
- name: redefines-builtin-id
staticcheck:
checks:
- all
- -SA1019
dot-import-whitelist:
- github.com/yusing/go-proxy/internal/utils/testing
- github.com/yusing/go-proxy/internal/api/v1/utils
tagalign:
align: false
sort: true
order:
- description
- json
- toml
- yaml
- yml
- label
- label-slice-as-struct
- file
- kv
- export
testifylint:
disable:
- suite-dont-use-pkg
- require-error
- go-require
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- gofumpt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View file

@ -2,37 +2,36 @@
# 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.15 version: 1.22.10
# 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.8 ref: v1.6.7
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.24.3 - go@1.23.2
# 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
- osv-scanner@2.0.2 - golangci-lint@1.64.5
- oxipng@9.1.5 - osv-scanner@1.9.2
- prettier@3.5.3 - oxipng@9.1.4
- prettier@3.5.1
- shellcheck@0.10.0 - shellcheck@0.10.0
- shfmt@3.6.0 - shfmt@3.6.0
- trufflehog@3.88.29 - trufflehog@3.88.9
actions: actions:
disabled: disabled:
- trunk-announce - trunk-announce

View file

@ -1,10 +1,10 @@
{ {
"yaml.schemas": { "yaml.schemas": {
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/src/types/godoxy/config.schema.json": [ "https://github.com/yusing/go-proxy/raw/main/schemas/config.schema.json": [
"config.example.yml", "config.example.yml",
"config.yml" "config.yml"
], ],
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/src/types/godoxy/routes.schema.json": [ "https://github.com/yusing/go-proxy/raw/main/schemas/routes.schema.json": [
"providers.example.yml" "providers.example.yml"
] ]
} }

View file

@ -1,33 +1,27 @@
# Stage 1: deps # Stage 1: deps
FROM golang:1.24.3-alpine AS deps FROM golang:1.24.2-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}
@ -37,10 +31,9 @@ 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 && \
RUN --mount=type=cache,target=/root/.cache/go-build \ mv bin /app/ && \
--mount=type=cache,target=/root/go/pkg/mod \ mkdir -p /app/error_pages /app/certs
make ${MAKE_ARGS} docker=1 build
# Stage 3: Final image # Stage 3: Final image
FROM scratch FROM scratch
@ -52,7 +45,10 @@ 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/run /app/run COPY --from=builder /app /app
# 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
View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2024 - present Yusing Copyright (c) 2024 [fullname]
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,27 +19,3 @@ 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.

View file

@ -1,4 +1,3 @@
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
@ -8,12 +7,11 @@ 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
@ -47,6 +45,7 @@ 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
@ -60,63 +59,30 @@ 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/...
docker-build-test: get:
docker build -t godoxy . for dir in ${PWD} ${PWD}/agent; do cd $$dir && go get -u ./... && go mod tidy; done
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 $(shell dirname ${BIN_PATH}) mkdir -p bin
cd ${PWD} && go build ${BUILD_FLAGS} -o ${BIN_PATH} ./cmd cd ${PWD} && go build ${BUILD_FLAGS} -o ${BIN_PATH} ${CMD_PATH}
${POST_BUILD}
# CAP_NET_BIND_SERVICE: permission for binding to :80 and :443
$(SETCAP_CMD) CAP_NET_BIND_SERVICE=+ep ${BIN_PATH}
run: run:
cd ${PWD} && [ -f .env ] && godotenv -f .env go run ${BUILD_FLAGS} ./cmd [ -f .env ] && godotenv -f .env go run ${BUILD_FLAGS} ${CMD_PATH}
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_PATH} debug-ls-mtrace > mtrace.json bin/godoxy 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 &&\
@ -131,7 +97,10 @@ 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 --include-lang=Go --not-match-f '_test.go$$' . cloc --not-match-f '_test.go$$' cmd internal pkg
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)

View file

@ -2,19 +2,17 @@
# GoDoxy # GoDoxy
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=yusing_godoxy)
![GitHub last commit](https://img.shields.io/github/last-commit/yusing/godoxy) ![GitHub last commit](https://img.shields.io/github/last-commit/yusing/godoxy)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=go-proxy) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=yusing_godoxy)
![Demo](https://img.shields.io/website?url=https%3A%2F%2Fdemo.godoxy.dev&label=Demo&link=https%3A%2F%2Fdemo.godoxy.dev) ![Demo](https://img.shields.io/website?url=https%3A%2F%2Fdemo.godoxy.dev&label=Demo&link=https%3A%2F%2Fdemo.godoxy.dev)
[![Discord](https://dcbadge.limes.pink/api/server/umReR62nRd?style=flat)](https://discord.gg/umReR62nRd) [![Discord](https://dcbadge.limes.pink/api/server/umReR62nRd?style=flat)](https://discord.gg/umReR62nRd)
A lightweight, simple, and performant reverse proxy with WebUI. A lightweight, simple, and [performant](https://github.com/yusing/godoxy/wiki/Benchmarks) reverse proxy with WebUI.
<h5> For full documentation, check out **[Wiki](https://github.com/yusing/godoxy/wiki)**
<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>
<h5>EN | <a href="README_CHT.md">中文</a></h5> **EN** | <a href="README_CHT.md">中文</a>
<img src="screenshots/webui.jpg" style="max-width: 650"> <img src="screenshots/webui.jpg" style="max-width: 650">
@ -47,8 +45,8 @@ A lightweight, simple, and performant reverse proxy with WebUI.
## Key Features ## Key Features
- **Simple** - **Simple**
- Effortless configuration with [simple labels](https://docs.godoxy.dev/Docker-labels-and-Route-Files) or WebUI - Effortless configuration with [simple labels](https://github.com/yusing/godoxy/wiki/Docker-labels-and-Route-Files) or WebUI
- [Simple multi-node setup](https://docs.godoxy.dev/Configurations#multi-docker-nodes-setup) - [Simple multi-node setup](https://github.com/yusing/godoxy/wiki/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
@ -56,7 +54,7 @@ A lightweight, simple, and performant reverse proxy with WebUI.
- 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://docs.godoxy.dev/DNS-01-Providers)) - 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))
- 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))_
@ -67,8 +65,8 @@ A lightweight, simple, and performant reverse proxy with WebUI.
- 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://docs.godoxy.dev/Middlewares) - [HTTP middlewares](https://github.com/yusing/go-proxy/wiki/Middlewares)
- [Custom error pages support](https://docs.godoxy.dev/Custom-Error-Pages) - [Custom error pages support](https://github.com/yusing/go-proxy/wiki/Middlewares#custom-error-pages)
- **Web UI** - **Web UI**
- App Dashboard - App Dashboard
- Config Editor - Config Editor
@ -101,13 +99,7 @@ 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. Start the docker compose service from generated `compose.yml`: 3. You may now do some extra configuration on WebUI `https://godoxy.yourdomain.com`
```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

View file

@ -8,13 +8,11 @@
![Demo](https://img.shields.io/website?url=https%3A%2F%2Fdemo.godoxy.dev&label=Demo&link=https%3A%2F%2Fdemo.godoxy.dev) ![Demo](https://img.shields.io/website?url=https%3A%2F%2Fdemo.godoxy.dev&label=Demo&link=https%3A%2F%2Fdemo.godoxy.dev)
[![Discord](https://dcbadge.limes.pink/api/server/umReR62nRd?style=flat)](https://discord.gg/umReR62nRd) [![Discord](https://dcbadge.limes.pink/api/server/umReR62nRd?style=flat)](https://discord.gg/umReR62nRd)
輕量、易用、 高效能,且帶有主頁和配置面板的反向代理 輕量、易用、 [高效能](https://github.com/yusing/godoxy/wiki/Benchmarks),且帶有主頁和配置面板的反向代理
<h5> 完整文檔請查閱 **[Wiki](https://github.com/yusing/godoxy/wiki)**(暫未有中文翻譯)
<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>
<h5><a href="README.md">EN</a> | 中文</h5> <a href="README.md">EN</a> | **中文**
<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">
@ -45,38 +43,20 @@
## 主要特點 ## 主要特點
- **簡單易用** - 容易使用
- 透過 Docker[標籤](https://docs.godoxy.dev/Docker-labels-and-Route-Files)或 WebUI 輕鬆設定 - 輕鬆配置
- [簡單的多節點設置](https://docs.godoxy.dev/Configurations#multi-docker-nodes-setup) - 簡單的多節點設置
- 詳細的錯誤訊息,便於故障排除 - 錯誤訊息清晰詳細,易於排除故障
- **存取控制 (ACL)**:連線/請求層級存取控制 - 自動 SSL 憑證管理(參見 [支援的 DNS-01 驗證提供商](https://github.com/yusing/godoxy/wiki/Supported-DNS%E2%80%9001-Providers)
- IP/CIDR - 自動配置 Docker 容器
- 國家 **(需要 Maxmind 帳戶)** - 容器狀態/配置文件變更時自動熱重載
- 時區 **(需要 Maxmind 帳戶)** - **閒置休眠**在閒置時停止容器有流量時喚醒_可選參見[截圖](#閒置休眠)_
- **存取日誌記錄** - OpenID Connect輕鬆實現單點登入
- **自動化** - HTTP(s) 反向代理和 TCP 和 UDP 埠轉發
- 使用 Let's Encrypt 自動管理 SSL 憑證 ([使用 DNS-01 驗證](https://docs.godoxy.dev/DNS-01-Providers)) - [HTTP 中介軟體](https://github.com/yusing/godoxy/wiki/Middlewares) 和 [自定義錯誤頁面](https://github.com/yusing/godoxy/wiki/Middlewares#custom-error-pages)
- 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)** 語言編寫
[🔼 回到頂部](#目錄) [🔼 回到頂部](#目錄)

View file

@ -1,19 +1,22 @@
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 {
@ -52,17 +55,6 @@ 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)

View file

@ -1,38 +1,33 @@
module github.com/yusing/go-proxy/agent module github.com/yusing/go-proxy/agent
go 1.24.3 go 1.24.2
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.0.0-00010101000000-000000000000 github.com/yusing/go-proxy v0.11.1
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/buger/goterm v1.0.4 // indirect github.com/bytedance/sonic v1.13.2 // 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.3 // indirect github.com/ebitengine/purego v0.8.2 // 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
@ -41,21 +36,18 @@ 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-20250501235452-c0086092b71a // indirect github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
github.com/gorilla/mux v1.8.1 // indirect github.com/gotify/server/v2 v2.6.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // 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.66 // indirect github.com/miekg/dns v1.1.65 // 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
@ -64,29 +56,33 @@ 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/v4 v4.1.0 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // 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.50.0 // indirect github.com/samber/lo v1.49.1 // 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.4 // indirect github.com/shirou/gopsutil/v4 v4.25.3 // 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.2 // indirect go.uber.org/mock v0.5.1 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/arch v0.16.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.40.0 // indirect golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View file

@ -8,12 +8,22 @@ 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=
@ -31,10 +41,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.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
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=
@ -58,42 +68,38 @@ 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/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
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-20250425105916-b2ad800de7a1 h1:fsSqE28vU0PRkq9FdekirRoDBeYJ+UaJ9dTErdXflWg= github.com/godoxy-app/docker v0.0.0-20250418000134-7af8fd7b079e h1:LEbMtJ6loEubxetD+Aw8+1x0rShor5iMoy9WuFQ8hN8=
github.com/godoxy-app/docker v0.0.0-20250425105916-b2ad800de7a1/go.mod h1:av6ggKWQz6SEkFyShjDEgVqiIB0RHvEQNIkPeqgJEeE= github.com/godoxy-app/docker v0.0.0-20250418000134-7af8fd7b079e/go.mod h1:3tMTnTkH7IN5smn7PX83XdmRnNj4Nw2/Pt8GgReqnKM=
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-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/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.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I= github.com/gotify/server/v2 v2.6.1 h1:Kf7v5fzBxzELzZa/jonWfwJMkqYqh1LBzBpCmt5QIAI=
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8= github.com/gotify/server/v2 v2.6.1/go.mod h1:Dk8HLyTVDqmXM8YEg6tjROBen6mxyHZFRggJFHTwZLc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
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/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 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/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=
@ -113,8 +119,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.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
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=
@ -137,12 +143,8 @@ 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=
@ -150,8 +152,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/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
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=
@ -159,26 +161,33 @@ 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.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
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/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
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=
@ -192,22 +201,24 @@ 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.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= 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/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
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.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
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.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
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.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
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=
@ -216,8 +227,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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
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=
@ -239,8 +250,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
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=
@ -250,18 +263,16 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.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=
@ -273,8 +284,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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.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=
@ -293,8 +304,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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
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=
@ -305,19 +316,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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
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-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= 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/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
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 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.72.0/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=
@ -328,3 +339,4 @@ 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=

View file

@ -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/godoxy/main/scripts/install-agent.sh)"` bash -c "$(curl -fsSL https://raw.githubusercontent.com/yusing/go-proxy/main/scripts/install-agent.sh)"`
installScriptTemplate = template.Must(template.New("install.sh").Parse(installScript)) installScriptTemplate = template.Must(template.New("install.sh").Parse(installScript))
) )

View file

@ -5,8 +5,6 @@ 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"
@ -16,7 +14,10 @@ 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"
) )
@ -26,7 +27,6 @@ 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,9 +80,7 @@ func (cfg *AgentConfig) Parse(addr string) error {
return nil return nil
} }
var serverVersion = pkg.GetVersion() func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte) error {
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
@ -92,7 +90,7 @@ func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte)
caCertPool := x509.NewCertPool() caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(ca) ok := caCertPool.AppendCertsFromPEM(ca)
if !ok { if !ok {
return errors.New("invalid ca certificate") return gperr.New("invalid ca certificate")
} }
cfg.tlsConfig = &tls.Config{ cfg.tlsConfig = &tls.Config{
@ -104,7 +102,7 @@ func (cfg *AgentConfig) StartWithCerts(ctx context.Context, 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(ctx, 5*time.Second) ctx, cancel := context.WithTimeout(parent.Context(), 5*time.Second)
defer cancel() defer cancel()
// get agent name // get agent name
@ -123,34 +121,33 @@ func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte)
return err return err
} }
cfg.version = string(agentVersionBytes) agentVersion := string(agentVersionBytes)
agentVersion := pkg.ParseVersion(cfg.version)
if serverVersion.IsNewerMajorThan(agentVersion) { if pkg.GetVersion().IsNewerMajorThan(pkg.ParseVersion(agentVersion)) {
logging.Warn().Msgf("agent %s major version mismatch: server: %s, agent: %s", cfg.name, serverVersion, agentVersion) logging.Warn().Msgf("agent %s major version mismatch: server: %s, agent: %s", cfg.name, pkg.GetVersion(), 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(ctx context.Context) error { func (cfg *AgentConfig) Start(parent task.Parent) gperr.Error {
filepath, ok := certs.AgentCertsFilepath(cfg.Addr) filepath, ok := certs.AgentCertsFilepath(cfg.Addr)
if !ok { if !ok {
return fmt.Errorf("invalid agent host: %s", cfg.Addr) return gperr.New("invalid agent host").Subject(cfg.Addr)
} }
certData, err := os.ReadFile(filepath) certData, err := os.ReadFile(filepath)
if err != nil { if err != nil {
return fmt.Errorf("failed to read agent certs: %w", err) return gperr.Wrap(err, "failed to read agent certs")
} }
ca, crt, key, err := certs.ExtractCert(certData) ca, crt, key, err := certs.ExtractCert(certData)
if err != nil { if err != nil {
return fmt.Errorf("failed to extract agent certs: %w", err) return gperr.Wrap(err, "failed to extract agent certs")
} }
return cfg.StartWithCerts(ctx, ca, crt, key) return gperr.Wrap(cfg.StartWithCerts(parent, ca, crt, key))
} }
func (cfg *AgentConfig) NewHTTPClient() *http.Client { func (cfg *AgentConfig) NewHTTPClient() *http.Client {
@ -174,10 +171,8 @@ 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 dialer.DialContext(ctx, "tcp", cfg.Addr) return gphttp.DefaultDialer.DialContext(ctx, "tcp", cfg.Addr)
} }
func (cfg *AgentConfig) Name() string { func (cfg *AgentConfig) Name() string {
@ -190,8 +185,7 @@ func (cfg *AgentConfig) String() string {
func (cfg *AgentConfig) MarshalJSON() ([]byte, error) { 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,
}) })
} }

View file

@ -2,6 +2,7 @@ package agent
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
@ -11,37 +12,20 @@ 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 *ecdsa.PrivateKey) *PEMPair { func toPEMPair(certDER []byte, key *rsa.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: "EC PRIVATE KEY", Bytes: marshaledKey}), Key: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}),
} }
} }
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)
} }
@ -79,23 +63,10 @@ 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: caSerialNumber, SerialNumber: big.NewInt(1),
Subject: pkix.Name{ Subject: pkix.Name{
Organization: []string{"GoDoxy"}, Organization: []string{"GoDoxy"},
CommonName: CertsDNSName, CommonName: CertsDNSName,
@ -105,12 +76,9 @@ 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 := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) caKey, err := rsa.GenerateKey(rand.Reader, KeySize)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -123,29 +91,20 @@ 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 := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) serverKey, err := rsa.GenerateKey(rand.Reader, KeySize)
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: serverSerialNumber, SerialNumber: big.NewInt(2),
Issuer: caTemplate.Subject, Issuer: caTemplate.Subject,
Subject: pkix.Name{ Subject: caTemplate.Subject,
Organization: caTemplate.Subject.Organization, DNSNames: []string{CertsDNSName},
OrganizationalUnit: []string{"Server"}, NotBefore: time.Now(),
CommonName: CertsDNSName, NotAfter: time.Now().AddDate(1000, 0, 0), // Add validity period
}, KeyUsage: x509.KeyUsageDigitalSignature,
DNSNames: []string{CertsDNSName}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1000, 0, 0), // Add validity period
KeyUsage: x509.KeyUsageDigitalSignature,
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)
@ -155,29 +114,20 @@ func NewAgent() (ca, srv, client *PEMPair, err error) {
srv = toPEMPair(srvCertDER, serverKey) srv = toPEMPair(srvCertDER, serverKey)
clientKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) clientKey, err := rsa.GenerateKey(rand.Reader, KeySize)
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: clientSerialNumber, SerialNumber: big.NewInt(3),
Issuer: caTemplate.Subject, Issuer: caTemplate.Subject,
Subject: pkix.Name{ Subject: caTemplate.Subject,
Organization: caTemplate.Subject.Organization, DNSNames: []string{CertsDNSName},
OrganizationalUnit: []string{"Client"}, NotBefore: time.Now(),
CommonName: CertsDNSName, NotAfter: time.Now().AddDate(1000, 0, 0),
}, KeyUsage: x509.KeyUsageDigitalSignature,
DNSNames: []string{CertsDNSName}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1000, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature,
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 {

View file

@ -8,59 +8,59 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/stretchr/testify/require" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
func TestNewAgent(t *testing.T) { func TestNewAgent(t *testing.T) {
ca, srv, client, err := NewAgent() ca, srv, client, err := NewAgent()
require.NoError(t, err) ExpectNoError(t, err)
require.NotNil(t, ca) ExpectTrue(t, ca != nil)
require.NotNil(t, srv) ExpectTrue(t, srv != nil)
require.NotNil(t, client) ExpectTrue(t, client != nil)
} }
func TestPEMPair(t *testing.T) { func TestPEMPair(t *testing.T) {
ca, srv, client, err := NewAgent() ca, srv, client, err := NewAgent()
require.NoError(t, err) ExpectNoError(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())
require.NoError(t, err) ExpectNoError(t, err)
require.Equal(t, p.Cert, pp.Cert) ExpectEqual(t, p.Cert, pp.Cert)
require.Equal(t, p.Key, pp.Key) ExpectEqual(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()
require.NoError(t, err) ExpectNoError(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()
require.NoError(t, err) ExpectNoError(t, err)
require.NotNil(t, cert) ExpectTrue(t, cert != nil)
}) })
} }
} }
func TestServerClient(t *testing.T) { func TestServerClient(t *testing.T) {
ca, srv, client, err := NewAgent() ca, srv, client, err := NewAgent()
require.NoError(t, err) ExpectNoError(t, err)
srvTLS, err := srv.ToTLSCert() srvTLS, err := srv.ToTLSCert()
require.NoError(t, err) ExpectNoError(t, err)
require.NotNil(t, srvTLS) ExpectTrue(t, srvTLS != nil)
clientTLS, err := client.ToTLSCert() clientTLS, err := client.ToTLSCert()
require.NoError(t, err) ExpectNoError(t, err)
require.NotNil(t, clientTLS) ExpectTrue(t, clientTLS != nil)
caPool := x509.NewCertPool() caPool := x509.NewCertPool()
require.True(t, caPool.AppendCertsFromPEM(ca.Cert)) ExpectTrue(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)
require.NoError(t, err) ExpectNoError(t, err)
require.Equal(t, resp.StatusCode, http.StatusOK) ExpectEqual(t, resp.StatusCode, http.StatusOK)
} }

View file

@ -9,36 +9,6 @@ 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

22
agent/pkg/env/env.go vendored
View file

@ -15,24 +15,10 @@ func DefaultAgentName() string {
} }
var ( var (
AgentName string AgentName = common.GetEnvString("AGENT_NAME", DefaultAgentName())
AgentPort int AgentPort = common.GetEnvInt("AGENT_PORT", 8890)
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())
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", "")
} )

View file

@ -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, "missing scheme", http.StatusBadRequest) http.Error(w, http.StatusText(http.StatusBadRequest), 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, "missing path", http.StatusBadRequest) http.Error(w, http.StatusText(http.StatusBadRequest), 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, "missing host", http.StatusBadRequest) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return return
} }
result, err = monitor.NewHTTPHealthMonitor(&url.URL{ result, err = monitor.NewHTTPHealthMonitor(&url.URL{
@ -51,17 +51,16 @@ 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, "missing host", http.StatusBadRequest) http.Error(w, http.StatusText(http.StatusBadRequest), 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 {
http.Error(w, "port and host with port cannot both be provided", http.StatusBadRequest)
return
}
if port != "" {
host = fmt.Sprintf("%s:%s", host, port) host = fmt.Sprintf("%s:%s", host, port)
} else {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
} }
result, err = monitor.NewRawHealthMonitor(&url.URL{ result, err = monitor.NewRawHealthMonitor(&url.URL{
Scheme: scheme, Scheme: scheme,
@ -74,7 +73,5 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
return return
} }
w.Header().Set("Content-Type", "application/json") gphttp.RespondJSON(w, r, result)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(result)
} }

View file

@ -0,0 +1,31 @@
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
}

View file

@ -1,57 +1,49 @@
package handler package handler
import ( import (
"context"
"fmt" "fmt"
"net" "io"
"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) HandleEndpoint(method, endpoint string, handler http.HandlerFunc) { func (mux ServeMux) HandleMethods(methods, endpoint string, handler http.HandlerFunc) {
mux.ServeMux.HandleFunc(method+" "+agent.APIEndpointBase+endpoint, handler) for _, m := range strutils.CommaSeperatedList(methods) {
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)
} }
var dialer = &net.Dialer{KeepAlive: 1 * time.Second} type NopWriteCloser struct {
io.Writer
func dialDockerSocket(ctx context.Context, _, _ string) (net.Conn, error) {
return dialer.DialContext(ctx, "unix", env.DockerSocket)
} }
func dockerSocketHandler() http.HandlerFunc { func (NopWriteCloser) Close() error {
rp := httputil.NewSingleHostReverseProxy(&url.URL{ return nil
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.HandleEndpoint("GET", agent.EndpointVersion, pkg.GetVersionHTTPHandler()) mux.HandleMethods("GET", agent.EndpointVersion, pkg.GetVersionHTTPHandler())
mux.HandleEndpoint("GET", agent.EndpointName, func(w http.ResponseWriter, r *http.Request) { mux.HandleMethods("GET", agent.EndpointName, func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, env.AgentName) fmt.Fprint(w, env.AgentName)
}) })
mux.HandleEndpoint("GET", agent.EndpointHealth, CheckHealth) mux.HandleMethods("GET", agent.EndpointHealth, CheckHealth)
mux.HandleEndpoint("GET", agent.EndpointSystemInfo, systeminfo.Poller.ServeHTTP) mux.HandleMethods("GET", agent.EndpointLogs, memlogger.HandlerFunc())
mux.ServeMux.HandleFunc("/", dockerSocketHandler()) mux.HandleMethods("GET", agent.EndpointSystemInfo, systeminfo.Poller.ServeHTTP)
mux.ServeMux.HandleFunc("/", DockerSocketHandler())
return mux return mux
} }

View file

@ -3,26 +3,18 @@ package handler
import ( import (
"crypto/tls" "crypto/tls"
"net/http" "net/http"
"net/http/httputil" "net/url"
"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))
@ -42,9 +34,11 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
scheme = "https" scheme = "https"
} }
transport := NewTransport() var transport *http.Transport
if skipTLSVerify { if skipTLSVerify {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} transport = gphttp.NewTransportWithTLSConfig(&tls.Config{InsecureSkipVerify: true})
} else {
transport = gphttp.NewTransport()
} }
if responseHeaderTimeout > 0 { if responseHeaderTimeout > 0 {
@ -55,13 +49,14 @@ 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
r.URL.Scheme = scheme
rp := &httputil.ReverseProxy{ logging.Debug().Msgf("proxy http request: %s %s", r.Method, r.URL.String())
Director: func(r *http.Request) {
r.URL.Scheme = scheme rp := reverseproxy.NewReverseProxy("agent", types.NewURL(&url.URL{
r.URL.Host = host Scheme: scheme,
}, Host: host,
Transport: transport, }), transport)
}
rp.ServeHTTP(w, r) rp.ServeHTTP(w, r)
} }

View file

@ -1,24 +1,30 @@
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 {
@ -33,29 +39,97 @@ func parallel(fns ...func()) {
func main() { func main() {
initProfiling() initProfiling()
dnsproviders.InitProviders()
args := pkg.GetArgs(common.MainServerCommandValidator{})
logging.InitLogger(os.Stderr, memlogger.GetMemLogger()) switch args.Command {
logging.Info().Msgf("GoDoxy version %s", pkg.GetVersion()) case common.CommandReload:
logging.Trace().Msg("trace enabled") if err := query.ReloadServer(); err != nil {
parallel( gperr.LogFatal("server reload error", err)
dnsproviders.InitProviders, }
homepage.InitIconListCache, rawLogger.Println("ok")
systeminfo.Poller.Start, return
middleware.LoadComposeFiles, 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 common.APIJWTSecret == nil { if args.Command == common.CommandStart {
logging.Warn().Msg("API_JWT_SECRET is not set, using random key") logging.InitLogger(os.Stderr, memlogger.GetMemLogger())
common.APIJWTSecret = common.RandomJWTKey() logging.Info().Msgf("GoDoxy version %s", pkg.GetVersion())
logging.Trace().Msg("trace enabled")
parallel(
internal.InitIconListCache,
systeminfo.Poller.Start,
)
if common.APIJWTSecret == nil {
logging.Warn().Msg("API_JWT_SECRET is not set, using random key")
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)
} }
cfg, err := config.Load() middleware.LoadComposeFiles()
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{
@ -82,3 +156,11 @@ 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"
}

View file

@ -1,46 +1,21 @@
--- ---
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:${TAG:-latest} image: ghcr.io/yusing/godoxy-frontend: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_FRONTEND_ALIASES:-godoxy} proxy.aliases: godoxy
proxy.#1.port: ${GODOXY_FRONTEND_PORT:-3000} proxy.godoxy.port: ${GODOXY_FRONTEND_PORT:-3000}
# proxy.#1.middlewares.cidr_whitelist: | # proxy.godoxy.middlewares.cidr_whitelist: |
# status: 403 # status: 403
# message: IP not allowed # message: IP not allowed
# allow: # allow:
@ -49,27 +24,16 @@ 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:${TAG:-latest} image: ghcr.io/yusing/godoxy: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:ro - ./error_pages:/app/error_pages
- ./data:/app/data - ./data:/app/data
# To use autocert, certs will be stored in "./certs". # To use autocert, certs will be stored in "./certs".

View file

@ -38,34 +38,19 @@
entrypoint: entrypoint:
# Below define an example of middleware config # Below define an example of middleware config
# 1. set security headers # 1. block non local IP connections
# 2. block non local IP connections # 2. redirect HTTP to HTTPS
# 3. redirect HTTP to HTTPS
# #
middlewares: # middlewares:
- use: CloudflareRealIP # - use: CIDRWhitelist
- use: ModifyResponse # allow:
set_headers: # - "127.0.0.1"
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD # - "10.0.0.0/8"
Access-Control-Allow-Headers: "*" # - "172.16.0.0/12"
Access-Control-Allow-Origin: "*" # - "192.168.0.0/16"
Access-Control-Max-Age: 180 # status: 403
Vary: "*" # message: "Forbidden"
X-XSS-Protection: 1; mode=block # - use: RedirectHTTP
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
# allow:
# - "127.0.0.1"
# - "10.0.0.0/8"
# - "172.16.0.0/12"
# - "192.168.0.0/16"
# status: 403
# message: "Forbidden"
# - use: RedirectHTTP
# below enables access log # below enables access log
access_log: access_log:

95
go.mod
View file

@ -1,17 +1,11 @@
module github.com/yusing/go-proxy module github.com/yusing/go-proxy
go 1.24.3 go 1.24.2
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
@ -21,21 +15,25 @@ 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.3 // reference the Message struct for json response github.com/gotify/server/v2 v2.6.1 // 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/v4 v4.1.0 // lock free map for concurrent operations github.com/puzpuzpuz/xsync/v3 v3.5.1 // 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.4 // system info metrics github.com/shirou/gopsutil/v4 v4.25.3 // 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.38.0 // encrypting password with bcrypt golang.org/x/crypto v0.37.0 // encrypting password with bcrypt
golang.org/x/net v0.40.0 // HTTP header utilities golang.org/x/net v0.39.0 // HTTP header utilities
golang.org/x/oauth2 v0.30.0 // oauth2 authentication golang.org/x/oauth2 v0.29.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 // yaml parsing for different config files github.com/goccy/go-yaml v1.17.1
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
@ -48,13 +46,21 @@ 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.7.0 // indirect cloud.google.com/go/compute/metadata v0.6.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.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.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
@ -80,22 +86,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.226 // indirect github.com/baidubce/bce-sdk-go v0.9.224 // 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.5.0 // indirect github.com/civo/civogo v0.3.99 // 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 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.3 // indirect github.com/ebitengine/purego v0.8.2 // indirect
github.com/exoscale/egoscale/v3 v3.1.17 // indirect github.com/exoscale/egoscale/v3 v3.1.15 // 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
@ -114,18 +120,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-20250501235452-c0086092b71a // indirect github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // 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.2 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // 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.149 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 // 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
@ -137,14 +143,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.50.0 // indirect github.com/linode/linodego v1.49.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.66 // indirect github.com/miekg/dns v1.1.65 // 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
@ -167,7 +173,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.91.0 // indirect github.com/oracle/oci-go-sdk/v65 v65.89.2 // 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
@ -182,10 +188,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.15.0 // indirect github.com/sacloud/iaas-api-go v1.14.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.50.0 // indirect github.com/samber/lo v1.49.1 // 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
@ -197,11 +203,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.8.0 // indirect github.com/spf13/cast v1.7.1 // 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.1164 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151 // 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
@ -209,8 +215,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.207 // indirect github.com/volcengine/volc-sdk-golang v1.0.206 // indirect
github.com/vultr/govultr/v3 v3.20.0 // indirect github.com/vultr/govultr/v3 v3.19.1 // 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
@ -218,32 +224,29 @@ 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.2 // indirect go.uber.org/mock v0.5.1 // 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.14.0 // indirect golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.25.0 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/api v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect
google.golang.org/grpc v1.72.1 // indirect google.golang.org/grpc v1.72.0 // 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.3 // indirect gopkg.in/ns1/ns1-go.v2 v2.14.2 // 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-20250502105355-0f33e8f1c979 // indirect k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // 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
View file

@ -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.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
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.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= 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/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= 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/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.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI= github.com/baidubce/bce-sdk-go v0.9.224 h1:z2L8alGw/y3IUHjrLRyrxrgCvMssYTjgCd7OQdb4gt0=
github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/baidubce/bce-sdk-go v0.9.224/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.5.0 h1:YDG38z+hmgaAhpDad1n/9sZrBtwRTeKVvMgdrS8Gwy4= github.com/civo/civogo v0.3.99 h1:ijv3ecaz9Ju82J72kbQwQvzlSxn9fLMlklu8C8pXGjI=
github.com/civo/civogo v0.5.0/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.3.99/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.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.2/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.17 h1:+T6+GP/k3tFNsYIQzpF3Sou4ecH//FkERDsJze/OZ00= github.com/exoscale/egoscale/v3 v3.1.15 h1:L2p9jWZhOeSBEwXCP12LPAoclBUv4LqzSN4RgNJtMdg=
github.com/exoscale/egoscale/v3 v3.1.17/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/exoscale/egoscale/v3 v3.1.15/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,8 +933,6 @@ 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=
@ -1035,8 +1033,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-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/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=
@ -1065,8 +1063,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.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
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=
@ -1084,17 +1082,16 @@ 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.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I= github.com/gotify/server/v2 v2.6.1 h1:Kf7v5fzBxzELzZa/jonWfwJMkqYqh1LBzBpCmt5QIAI=
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8= github.com/gotify/server/v2 v2.6.1/go.mod h1:Dk8HLyTVDqmXM8YEg6tjROBen6mxyHZFRggJFHTwZLc=
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.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
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=
@ -1150,8 +1147,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.149 h1:gDzo/eYE8/mwF5fi7v10pdBW2027PUvaHvRPA5aOPoM= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 h1:ld5s5UeA9zgyFsZskVD2Tr6k6VnJWkvaLm5nqvfOEf4=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146/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=
@ -1164,8 +1161,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.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
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=
@ -1235,8 +1232,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.50.0 h1:5y79VvvQnWb5JyPIjTwyUrU3ArHcs7XZQFdkPS/lNpw= github.com/linode/linodego v1.49.0 h1:MNd3qwvQzbXB5mCpvdCqlUIu1RPA9oC+50LyB9kK+GQ=
github.com/linode/linodego v1.50.0/go.mod h1:9S+REoPCtUNWCm63D1vjjxIJZfwEL2t2kTDnwt620FM= github.com/linode/linodego v1.49.0/go.mod h1:B+HAM3//4w1wOS0BwdaQBKwBxlfe6kYJ7bSC6jJ/xtc=
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=
@ -1290,8 +1287,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.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
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=
@ -1398,8 +1395,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.91.0 h1:maO6AxKxVfszH0X4tbbtN21jOk03lCRR3IqiA8/FzZc= github.com/oracle/oci-go-sdk/v65 v65.89.2 h1:w0GwID9NlT+eg3InbAwkWsazDtxVLYQ8rJb4E33Yb14=
github.com/oracle/oci-go-sdk/v65 v65.91.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.89.2/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=
@ -1480,16 +1477,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/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
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.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
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=
@ -1513,15 +1510,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.15.0 h1:G88U69OOVUSjOZ2fEP5QWMg7r3VOVsfNkl5Mxl+/wMk= github.com/sacloud/iaas-api-go v1.14.0 h1:xjkFWqdo4ilTrKPNNYBNWR/CZ/kVRsJrdAHAad6J/AQ=
github.com/sacloud/iaas-api-go v1.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA= github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFnpe69kyA4M3V0=
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.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
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=
@ -1533,6 +1530,8 @@ 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=
@ -1573,8 +1572,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.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.7.1/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=
@ -1616,8 +1615,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.1164 h1:qEzZCZf1sgvvrZ8ngws0gZlyW+sOdY0K9VXGm4AcvTE= 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/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151/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=
@ -1643,10 +1642,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.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18= github.com/volcengine/volc-sdk-golang v1.0.206 h1:7NG8FCpvu9wbx+Z4I/p3tcTS2zdBqTZtJXgydunGy6g=
github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= github.com/volcengine/volc-sdk-golang v1.0.206/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA= github.com/vultr/govultr/v3 v3.19.1 h1:31rOP5Tz40AOc8h6Ws4ryzqAniUBffgRhy9uMG/EFvs=
github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/vultr/govultr/v3 v3.19.1/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=
@ -1702,8 +1701,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.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= 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/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
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=
@ -1715,8 +1714,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.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
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=
@ -1726,8 +1725,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.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
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=
@ -1772,8 +1771,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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
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=
@ -1917,8 +1916,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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
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=
@ -1948,8 +1947,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.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
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=
@ -1970,8 +1969,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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.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=
@ -2093,8 +2092,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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.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=
@ -2112,8 +2111,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.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
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=
@ -2135,8 +2134,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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
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=
@ -2218,8 +2217,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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
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=
@ -2294,8 +2293,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.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
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=
@ -2436,12 +2435,10 @@ 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-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-20250425173222-7b384671a197 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= 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 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=
@ -2484,8 +2481,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.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.72.0/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=
@ -2526,8 +2523,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.3 h1:Yn72GgB6AA9I4602AsLMtbC1ZKT5EUrKiG+IPS+Ovr0= gopkg.in/ns1/ns1-go.v2 v2.14.2 h1:wz/toj9U20wBrmYxW4vTz7sZWED+JJVRjUBBJ7CKrzI=
gopkg.in/ns1/ns1-go.v2 v2.14.3/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/ns1/ns1-go.v2 v2.14.2/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=
@ -2563,8 +2560,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-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/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=

View file

@ -1,12 +1,15 @@
package maxmind package acl
import ( import (
"github.com/puzpuzpuz/xsync/v4" "github.com/puzpuzpuz/xsync/v3"
acl "github.com/yusing/go-proxy/internal/acl/types"
"go.uber.org/atomic"
) )
var cityCache = xsync.NewMap[string, *City]() var cityCache = xsync.NewMapOf[string, *acl.City]()
var numCachedLookup atomic.Uint64
func (cfg *MaxMind) lookupCity(ip *IPInfo) (*City, bool) { func (cfg *MaxMindConfig) lookupCity(ip *acl.IPInfo) (*acl.City, bool) {
if ip.City != nil { if ip.City != nil {
return ip.City, true return ip.City, true
} }
@ -17,14 +20,14 @@ func (cfg *MaxMind) lookupCity(ip *IPInfo) (*City, bool) {
city, ok := cityCache.Load(ip.Str) city, ok := cityCache.Load(ip.Str)
if ok { if ok {
ip.City = city numCachedLookup.Inc()
return city, true return city, true
} }
cfg.db.RLock() cfg.db.RLock()
defer cfg.db.RUnlock() defer cfg.db.RUnlock()
city = new(City) city = new(acl.City)
err := cfg.db.Lookup(ip.IP, city) err := cfg.db.Lookup(ip.IP, city)
if err != nil { if err != nil {
return nil, false return nil, false

View file

@ -2,14 +2,16 @@ package acl
import ( import (
"net" "net"
"sync"
"time" "time"
"github.com/puzpuzpuz/xsync/v4" "github.com/oschwald/maxminddb-golang"
"github.com/yusing/go-proxy/internal/common" "github.com/puzpuzpuz/xsync/v3"
"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"
) )
@ -17,24 +19,43 @@ 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 Matchers `json:"allow"` Allow []string `json:"allow"`
Deny Matchers `json:"deny"` Deny []string `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
ipCache *xsync.Map[string, *checkCache] allow []matcher
deny []matcher
ipCache *xsync.MapOf[string, *checkCache]
logAllowed bool logAllowed bool
logger *accesslog.AccessLogger logger *accesslog.AccessLogger
} }
type checkCache struct { type checkCache struct {
*maxmind.IPInfo *acl.IPInfo
allow bool allow bool
created time.Time created time.Time
} }
@ -42,7 +63,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).Before(utils.TimeNow()) return c.created.Add(cacheTTL).After(utils.TimeNow())
} }
//TODO: add stats //TODO: add stats
@ -52,6 +73,11 @@ 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:
@ -59,8 +85,7 @@ func (c *Config) Validate() gperr.Error {
case ACLDeny: case ACLDeny:
c.defaultAllow = false c.defaultAllow = false
default: default:
c.valErr = gperr.New("invalid default value").Subject(c.Default) return gperr.New("invalid default value").Subject(c.Default)
return c.valErr
} }
if c.AllowLocal != nil { if c.AllowLocal != nil {
@ -69,24 +94,55 @@ 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
} }
if !c.allowLocal && !c.defaultAllow && len(c.Allow) == 0 { errs := gperr.NewBuilder("syntax error")
c.valErr = gperr.New("allow_local is false and default is deny, but no allow rules are configured") c.allow = make([]matcher, 0, len(c.Allow))
return c.valErr c.deny = make([]matcher, 0, len(c.Deny))
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)
} }
c.ipCache = xsync.NewMap[string, *checkCache]() if errs.HasError() {
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 && c.valErr == nil return c != nil && (len(c.allow) > 0 || len(c.deny) > 0 || c.allowLocal)
} }
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 {
@ -94,22 +150,10 @@ 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 *maxmind.IPInfo, allow bool) { func (c *config) cacheRecord(info *acl.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,
@ -117,7 +161,7 @@ func (c *Config) cacheRecord(info *maxmind.IPInfo, allow bool) {
}) })
} }
func (c *config) log(info *maxmind.IPInfo, allowed bool) { func (c *config) log(info *acl.IPInfo, allowed bool) {
if c.logger == nil { if c.logger == nil {
return return
} }
@ -131,13 +175,14 @@ func (c *Config) IPAllowed(ip net.IP) bool {
return false return false
} }
// always allow loopback, not logged // always allow private and loopback
// 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(&maxmind.IPInfo{IP: ip, Str: ip.String()}, true) c.log(&acl.IPInfo{IP: ip, Str: ip.String()}, true)
return true return true
} }
@ -148,16 +193,20 @@ func (c *Config) IPAllowed(ip net.IP) bool {
return record.allow return record.allow
} }
ipAndStr := &maxmind.IPInfo{IP: ip, Str: ipStr} ipAndStr := &acl.IPInfo{IP: ip, Str: ipStr}
if c.Allow.Match(ipAndStr) { for _, m := range c.allow {
c.log(ipAndStr, true) if m(ipAndStr) {
c.cacheRecord(ipAndStr, true) c.log(ipAndStr, true)
return true c.cacheRecord(ipAndStr, true)
return true
}
} }
if c.Deny.Match(ipAndStr) { for _, m := range c.deny {
c.log(ipAndStr, false) if m(ipAndStr) {
c.cacheRecord(ipAndStr, false) c.log(ipAndStr, false)
return false c.cacheRecord(ipAndStr, false)
return false
}
} }
c.log(ipAndStr, c.defaultAllow) c.log(ipAndStr, c.defaultAllow)

View file

@ -4,17 +4,11 @@ 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 MatcherFunc func(*maxmind.IPInfo) bool type matcher func(*acl.IPInfo) bool
type Matcher struct {
match MatcherFunc
}
type Matchers []Matcher
const ( const (
MatcherTypeIP = "ip" MatcherTypeIP = "ip"
@ -23,9 +17,6 @@ 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",
@ -34,66 +25,62 @@ 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 (matcher *Matcher) Parse(s string) error { func (cfg *Config) parseMatcher(s string) (matcher, gperr.Error) {
parts := strings.Split(s, ":") parts := strings.Split(s, ":")
if len(parts) != 2 { if len(parts) != 2 {
return errSyntax return nil, 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 errInvalidIP return nil, errInvalidIP
} }
matcher.match = matchIP(ip) return matchIP(ip), nil
case MatcherTypeCIDR: case MatcherTypeCIDR:
_, net, err := net.ParseCIDR(parts[1]) _, net, err := net.ParseCIDR(parts[1])
if err != nil { if err != nil {
return errInvalidCIDR return nil, errInvalidCIDR
} }
matcher.match = matchCIDR(net) return matchCIDR(net), nil
case MatcherTypeTimeZone: case MatcherTypeTimeZone:
matcher.match = matchTimeZone(parts[1]) if cfg.MaxMind == nil {
case MatcherTypeCountry: return nil, errMaxMindNotConfigured
matcher.match = matchISOCode(parts[1])
default:
return errSyntax
}
return nil
}
func (matchers Matchers) Match(ip *maxmind.IPInfo) bool {
for _, m := range matchers {
if m.match(ip) {
return true
} }
return cfg.MaxMind.matchTimeZone(parts[1]), nil
case MatcherTypeCountry:
if cfg.MaxMind == nil {
return nil, errMaxMindNotConfigured
}
return cfg.MaxMind.matchISOCode(parts[1]), nil
default:
return nil, errSyntax
} }
return false
} }
func matchIP(ip net.IP) MatcherFunc { func matchIP(ip net.IP) matcher {
return func(ip2 *maxmind.IPInfo) bool { return func(ip2 *acl.IPInfo) bool {
return ip.Equal(ip2.IP) return ip.Equal(ip2.IP)
} }
} }
func matchCIDR(n *net.IPNet) MatcherFunc { func matchCIDR(n *net.IPNet) matcher {
return func(ip *maxmind.IPInfo) bool { return func(ip *acl.IPInfo) bool {
return n.Contains(ip.IP) return n.Contains(ip.IP)
} }
} }
func matchTimeZone(tz string) MatcherFunc { func (cfg *MaxMindConfig) matchTimeZone(tz string) matcher {
return func(ip *maxmind.IPInfo) bool { return func(ip *acl.IPInfo) bool {
city, ok := maxmind.LookupCity(ip) city, ok := cfg.lookupCity(ip)
if !ok { if !ok {
return false return false
} }
@ -101,9 +88,9 @@ func matchTimeZone(tz string) MatcherFunc {
} }
} }
func matchISOCode(iso string) MatcherFunc { func (cfg *MaxMindConfig) matchISOCode(iso string) matcher {
return func(ip *maxmind.IPInfo) bool { return func(ip *acl.IPInfo) bool {
city, ok := maxmind.LookupCity(ip) city, ok := cfg.lookupCity(ip)
if !ok { if !ok {
return false return false
} }

View file

@ -1,49 +0,0 @@
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)
}
}
}

281
internal/acl/maxmind.go Normal file
View file

@ -0,0 +1,281 @@
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
)

View file

@ -0,0 +1,213 @@
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)
}
}

View file

@ -1,9 +1,7 @@
package acl package acl
import ( import (
"io"
"net" "net"
"time"
) )
type TCPListener struct { type TCPListener struct {
@ -11,17 +9,6 @@ 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
@ -45,11 +32,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 noConn{}, nil return nil, nil
} }
if !s.acl.IPAllowed(addr.IP) { if !s.acl.IPAllowed(addr.IP) {
c.Close() c.Close()
return noConn{}, nil return nil, nil
} }
return c, nil return c, nil
} }

View file

@ -1,4 +1,4 @@
package maxmind package acl
type City struct { type City struct {
Location struct { Location struct {

View file

@ -1,4 +1,4 @@
package maxmind package acl
import "net" import "net"

View file

@ -10,12 +10,12 @@ type UDPListener struct {
lis net.PacketConn lis net.PacketConn
} }
func (c *Config) WrapUDP(lis net.PacketConn) net.PacketConn { func (cfg *Config) WrapUDP(lis net.PacketConn) net.PacketConn {
if c == nil { if cfg == nil {
return lis return lis
} }
return &UDPListener{ return &UDPListener{
acl: c, acl: cfg,
lis: lis, lis: lis,
} }
} }

View file

@ -12,7 +12,6 @@ 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"
@ -46,7 +45,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) {
gpwebsocket.SetWebsocketAllowedDomains(r.Header, matchDomains) httpheaders.SetWebsocketAllowedDomains(r.Header, matchDomains)
} }
origHandler(w, r) origHandler(w, r)
} }
@ -71,15 +70,9 @@ 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.ListRoutesHandler, true) mux.HandleFunc("GET", "/v1/list", v1.List, true)
mux.HandleFunc("GET", "/v1/list/routes", v1.ListRoutesHandler, true) mux.HandleFunc("GET", "/v1/list/{what}", v1.List, true)
mux.HandleFunc("GET", "/v1/list/route/{which}", v1.ListRouteHandler, true) mux.HandleFunc("GET", "/v1/list/{what}/{which}", v1.List, 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)

View file

@ -1,7 +1,6 @@
package v1 package v1
import ( import (
"fmt"
"io" "io"
"net/http" "net/http"
"os" "os"
@ -52,12 +51,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 = fmt.Errorf("invalid file type: %s", fileType) err = gphttp.ErrInvalidKey("type")
return return
} }
filename = r.PathValue("filename") filename = r.PathValue("filename")
if filename == "" { if filename == "" {
err = fmt.Errorf("missing filename") err = gphttp.ErrMissingKey("filename")
} }
return return
} }

View file

@ -0,0 +1,5 @@
package dockerapi
import "time"
const reqTimeout = 10 * time.Second

View file

@ -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](w, r, GetContainers) serveHTTP[Container, []Container](w, r, GetContainers)
} }
func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Container, gperr.Error) { func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Container, gperr.Error) {

View file

@ -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(server) dockerClient, found, err := getDockerClient(w, server)
if err != nil { if err != nil {
gphttp.BadRequest(w, err.Error()) gphttp.BadRequest(w, err.Error())
return return

View file

@ -56,7 +56,7 @@ func getDockerClients() (DockerClients, gperr.Error) {
return dockerClients, connErrs.Error() return dockerClients, connErrs.Error()
} }
func getDockerClient(server string) (*docker.SharedClient, bool, error) { func getDockerClient(w http.ResponseWriter, 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) //nolint json.NewEncoder(w).Encode(result)
} }
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](w, err, result) handleResult[V, T](w, err, result)
} }
} }

View file

@ -1,8 +1,10 @@
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"
@ -19,11 +21,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.MissingKey(w, "url or alias") gphttp.ClientError(w, gphttp.ErrMissingKey("url or alias"), http.StatusBadRequest)
return return
} }
if url != "" && alias != "" { if url != "" && alias != "" {
gphttp.BadRequest(w, "url and alias are mutually exclusive") gphttp.ClientError(w, gperr.New("url and alias are mutually exclusive"), http.StatusBadRequest)
return return
} }
@ -31,7 +33,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, req, err, http.StatusBadRequest) gphttp.ClientError(w, err, http.StatusBadRequest)
return return
} }
fetchResult := homepage.FetchFavIconFromURL(req.Context(), &iconURL) fetchResult := homepage.FetchFavIconFromURL(req.Context(), &iconURL)
@ -47,7 +49,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.ValueNotFound(w, "route", alias) gphttp.ClientError(w, errors.New("no such route"), http.StatusNotFound)
return return
} }
@ -55,7 +57,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.FullURL) result = homepage.FindIcon(req.Context(), r, hp.Icon.Value)
} else { } else {
result = homepage.FetchFavIconFromURL(req.Context(), hp.Icon) result = homepage.FetchFavIconFromURL(req.Context(), hp.Icon)
} }

View file

@ -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, r, err, http.StatusBadRequest) gphttp.ClientError(w, 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, &params); err != nil { if err := json.Unmarshal(data, &params); err != nil {
gphttp.ClientError(w, r, err, http.StatusBadRequest) gphttp.ClientError(w, err, http.StatusBadRequest)
return return
} }
overrides.OverrideItem(params.Which, &params.Value) overrides.OverrideItem(params.Which, &params.Value)
case HomepageOverrideItemsBatch: case HomepageOverrideItemsBatch:
var params HomepageOverrideItemsBatchParams var params HomepageOverrideItemsBatchParams
if err := json.Unmarshal(data, &params); err != nil { if err := json.Unmarshal(data, &params); err != nil {
gphttp.ClientError(w, r, err, http.StatusBadRequest) gphttp.ClientError(w, 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, &params); err != nil { if err := json.Unmarshal(data, &params); err != nil {
gphttp.ClientError(w, r, err, http.StatusBadRequest) gphttp.ClientError(w, 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, &params); err != nil { if err := json.Unmarshal(data, &params); err != nil {
gphttp.ClientError(w, r, err, http.StatusBadRequest) gphttp.ClientError(w, err, http.StatusBadRequest)
return return
} }
overrides.SetCategoryOrder(params.Which, params.Value) overrides.SetCategoryOrder(params.Which, params.Value)

128
internal/api/v1/list.go Normal file
View file

@ -0,0 +1,128 @@
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)
}

View file

@ -1,41 +0,0 @@
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)
}

View file

@ -1,13 +0,0 @@
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())
}

View file

@ -1,13 +0,0 @@
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")))
}

View file

@ -1,23 +0,0 @@
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)
}

View file

@ -1,19 +0,0 @@
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)
}
}

View file

@ -1,23 +0,0 @@
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())
}
}

View file

@ -1,25 +0,0 @@
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)
}

View file

@ -1,13 +0,0 @@
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())
}

View file

@ -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.MissingKey(w, "name") gphttp.ClientError(w, gphttp.ErrMissingKey("name"))
return return
} }
host := q.Get("host") host := q.Get("host")
if host == "" { if host == "" {
gphttp.MissingKey(w, "host") gphttp.ClientError(w, gphttp.ErrMissingKey("host"))
return return
} }
portStr := q.Get("port") portStr := q.Get("port")
if portStr == "" { if portStr == "" {
gphttp.MissingKey(w, "port") gphttp.ClientError(w, gphttp.ErrMissingKey("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.InvalidKey(w, "port") gphttp.ClientError(w, gphttp.ErrInvalidKey("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.KeyAlreadyExists(w, "agent", hostport) gphttp.ClientError(w, gphttp.ErrAlreadyExists("agent", hostport), http.StatusConflict)
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.MissingKey(w, "type") gphttp.ClientError(w, gphttp.ErrMissingKey("type"))
return return
default: default:
gphttp.InvalidKey(w, "type") gphttp.ClientError(w, gphttp.ErrInvalidKey("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, r, err) gphttp.ClientError(w, err, http.StatusBadRequest)
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, r, err) gphttp.ClientError(w, 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.InvalidKey(w, "host") gphttp.ClientError(w, gphttp.ErrInvalidKey("host"))
return return
} }

View file

@ -0,0 +1,64 @@
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)
}

View file

@ -38,35 +38,22 @@ 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) {
} if err := defaultAuth.CheckToken(r); err != nil {
return func(w http.ResponseWriter, r *http.Request) { gphttp.ClientError(w, err, http.StatusUnauthorized)
if err := defaultAuth.CheckToken(r); err != nil { } else {
gphttp.Unauthorized(w, err.Error()) next(w, r)
return }
} }
next(w, r)
}
}
func ProceedNext(w http.ResponseWriter, r *http.Request) {
next, ok := r.Context().Value(nextHandlerContextKey).(http.HandlerFunc)
if ok {
next(w, r)
} else {
w.WriteHeader(http.StatusOK)
} }
return next
} }
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) { func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
if err := defaultAuth.CheckToken(r); err != nil { if err := defaultAuth.CheckToken(r); err != nil {
defaultAuth.LoginHandler(w, r) http.Redirect(w, r, "/v1/auth/login", http.StatusFound)
} else { } else {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }

View file

@ -1,13 +1,11 @@
package auth package auth
import ( import (
"context"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"sync"
"time" "time"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@ -21,10 +19,6 @@ 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 {
@ -33,12 +27,6 @@ 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
@ -46,11 +34,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
sessionInvalidateDelay = 3 * time.Second refreshBefore = 30 * time.Second
) )
var ( var (
@ -62,7 +50,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")
} }
} }
@ -73,7 +61,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(hex.EncodeToString(b)) return sessionID(base64.StdEncoding.EncodeToString(b))
} }
func newSession(username string, groups []string) Session { func newSession(username string, groups []string) Session {
@ -84,26 +72,26 @@ func newSession(username string, groups []string) Session {
} }
} }
// getOAuthRefreshToken returns the refresh token for the given session. // getOnceOAuthRefreshToken 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),
@ -130,7 +118,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) {
@ -147,75 +135,51 @@ 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(ctx context.Context, sessionJWT string) (*RefreshResult, error) { func (auth *OIDCProvider) TryRefreshToken(w http.ResponseWriter, r *http.Request, sessionJWT string) 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 nil, fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrInvalidSessionToken, err) return fmt.Errorf("%w: %w", ErrInvalidSessionToken, err)
} }
if !valid { if !valid {
return nil, ErrInvalidSessionToken return ErrInvalidSessionToken
} }
// check if refresh is possible // check if refresh is possible
refreshToken, ok := getOAuthRefreshToken(&claims.Session) refreshToken, ok := getOnceOAuthRefreshToken(&claims.Session)
if !ok { if !ok {
return nil, errNoRefreshToken return errNoRefreshToken
} }
if !auth.checkAllowed(claims.Username, claims.Groups) { if !auth.checkAllowed(claims.Username, claims.Groups) {
return nil, ErrUserNotAllowed return 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(ctx, &oauth2.Token{ newToken, err := auth.oauthConfig.TokenSource(r.Context(), &oauth2.Token{
RefreshToken: refreshToken.RefreshToken, RefreshToken: refreshToken.RefreshToken,
}).Token() }).Token()
if err != nil { if err != nil {
refreshToken.err = fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrRefreshTokenFailure, err) return fmt.Errorf("%w: %w", ErrRefreshTokenFailure, err)
return nil, refreshToken.err
} }
idTokenJWT, idToken, err := auth.getIdToken(ctx, newToken) idTokenJWT, idToken, err := auth.getIdToken(r.Context(), newToken)
if err != nil { if err != nil {
refreshToken.err = fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrRefreshTokenFailure, err) return 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)
refreshToken.result = &RefreshResult{ // set new idToken and new sessionToken
newSession: Session{ auth.setIDTokenCookie(w, r, idTokenJWT, time.Until(idToken.Expiry))
SessionID: sessionID, auth.setSessionTokenCookie(w, r, Session{
Username: claims.Username, SessionID: sessionID,
Groups: claims.Groups, Username: claims.Username,
}, Groups: claims.Groups,
jwt: idTokenJWT, })
jwtExpiry: idToken.Expiry, return nil
}
return refreshToken.result, nil
} }

View file

@ -13,7 +13,6 @@ 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"
@ -48,12 +47,7 @@ const (
OIDCLogoutPath = "/auth/logout" OIDCLogoutPath = "/auth/logout"
) )
var ( var errMissingIDToken = errors.New("missing id_token field from oauth token")
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
@ -68,10 +62,7 @@ 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)
} }
@ -142,14 +133,6 @@ 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)
@ -165,25 +148,18 @@ 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 if err == nil {
result, err := auth.TryRefreshToken(r.Context(), sessionToken.Value) err = auth.TryRefreshToken(w, r, sessionToken.Value)
// redirect back to where they requested if err != nil {
// when token refresh is ok logging.Debug().Err(err).Msg("failed to refresh token")
if err == nil { auth.clearCookie(w, r)
auth.setIDTokenCookie(w, r, result.jwt, time.Until(result.jwtExpiry))
auth.setSessionTokenCookie(w, r, result.newSession)
ProceedNext(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)
} }
@ -194,7 +170,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, errors.New("missing username in ID token") return nil, fmt.Errorf("missing username in ID token")
} }
return &claim, nil return &claim, nil
} }
@ -308,12 +284,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.
@ -330,7 +306,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)
} }

View file

@ -1,6 +1,7 @@
package auth package auth
import ( import (
"context"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"encoding/base64" "encoding/base64"
@ -23,7 +24,7 @@ import (
func setupMockOIDC(t *testing.T) { func setupMockOIDC(t *testing.T) {
t.Helper() t.Helper()
provider := (&oidc.ProviderConfig{}).NewProvider(t.Context()) provider := (&oidc.ProviderConfig{}).NewProvider(context.TODO())
defaultAuth = &OIDCProvider{ defaultAuth = &OIDCProvider{
oauthConfig: &oauth2.Config{ oauthConfig: &oauth2.Config{
ClientID: "test-client", ClientID: "test-client",
@ -103,7 +104,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(t.Context(), ts.Client()) providerCtx := oidc.ClientContext(context.Background(), 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{

View file

@ -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)
} }

View file

@ -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.PostAuthCallbackHandler(w, req) auth.LoginHandler(w, req)
if tt.wantErr { if tt.wantErr {
ExpectEqual(t, w.Code, http.StatusUnauthorized) ExpectEqual(t, w.Code, http.StatusUnauthorized)
} else { } else {

View file

@ -10,21 +10,22 @@ 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
if IsFrontend(r) { switch r.Host {
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
@ -44,7 +45,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,
@ -57,7 +58,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: "",

View file

@ -13,6 +13,7 @@ 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 {
@ -69,7 +70,7 @@ func (cfg *Config) Validate() gperr.Error {
if !ok { if !ok {
b.Add(ErrUnknownProvider. b.Add(ErrUnknownProvider.
Subject(cfg.Provider). Subject(cfg.Provider).
With(gperr.DoYouMean(utils.NearestField(cfg.Provider, Providers)))) Withf(strutils.DoYouMean(utils.NearestField(cfg.Provider, Providers))))
} else { } else {
_, err := providerConstructor(cfg.Options) _, err := providerConstructor(cfg.Options)
if err != nil { if err != nil {

View file

@ -9,15 +9,14 @@ 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"
) )
@ -32,6 +31,8 @@ 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
@ -192,18 +193,8 @@ 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(&notif.LogMessage{
Level: zerolog.ErrorLevel,
Title: "SSL certificate renewal failed",
Body: notif.MessageBody(err.Error()),
})
continue continue
} }
notif.Notify(&notif.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()

View file

@ -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.MapUnmarshalValidate(opt, cfg)) require.NoError(t, utils.Deserialize(opt, cfg))
require.Equal(t, cfgExpected, cfg) require.Equal(t, cfg, cfgExpected)
} }

View file

@ -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.MapUnmarshalValidate(opt, &cfg) err := utils.Deserialize(opt, &cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -15,8 +15,8 @@ const (
ConfigExampleFileName = "config.example.yml" ConfigExampleFileName = "config.example.yml"
ConfigPath = ConfigBasePath + "/" + ConfigFileName ConfigPath = ConfigBasePath + "/" + ConfigFileName
DataDir = "data" IconListCachePath = ConfigBasePath + "/.icon_list_cache.json"
IconListCachePath = DataDir + "/.icon_list_cache.json" IconCachePath = ConfigBasePath + "/.icon_cache.json"
NamespaceHomepageOverrides = ".homepage" NamespaceHomepageOverrides = ".homepage"
NamespaceIconCache = ".icon_cache" NamespaceIconCache = ".icon_cache"
@ -25,12 +25,14 @@ const (
ComposeFileName = "compose.yml" ComposeFileName = "compose.yml"
ComposeExampleFileName = "compose.example.yml" ComposeExampleFileName = "compose.example.yml"
ErrorPagesBasePath = "error_pages"
DataDir = "data"
ErrorPagesBasePath = "error_pages"
) )
var RequiredDirectories = []string{ var RequiredDirectories = []string{
ConfigBasePath, ConfigBasePath,
DataDir,
ErrorPagesBasePath, ErrorPagesBasePath,
MiddlewareComposeBasePath, MiddlewareComposeBasePath,
} }

View file

@ -3,7 +3,8 @@ 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 {
@ -12,7 +13,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.Fatalf("failed to decode secret: %s", err) log.Fatal().Str("key", key).Err(err).Msg("failed to decode secret")
} }
return bytes return bytes
} }
@ -21,7 +22,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.Fatalf("failed to generate random jwt key: %s", err) log.Fatal().Err(err).Msg("failed to generate random jwt key")
} }
return key return key
} }

View file

@ -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,8 +58,6 @@ 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 {
@ -78,16 +76,14 @@ 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.Fatalf("env %s: invalid %T value: %s", key, parsed, value) log.Fatal().Err(err).Msgf("env %s: invalid %T value: %s", key, parsed, value)
return defaultValue return defaultValue
} }
func stringstring(s string) (string, error) {
return s, nil
}
func GetEnvString(key string, defaultValue string) string { func GetEnvString(key string, defaultValue string) string {
return GetEnv(key, defaultValue, stringstring) return GetEnv(key, defaultValue, func(s string) (string, error) {
return s, nil
})
} }
func GetEnvBool(key string, defaultValue bool) bool { func GetEnvBool(key string, defaultValue bool) bool {
@ -105,7 +101,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.Fatalf("env %s: invalid address: %s", key, addr) log.Fatal().Msgf("env %s: invalid address: %s", key, addr)
} }
if host == "" { if host == "" {
host = "localhost" host = "localhost"
@ -113,7 +109,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.Fatalf("env %s: invalid port: %s", key, port) log.Fatal().Msgf("env %s: invalid port: %s", key, port)
} }
return return
} }

View file

@ -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().Context(), ca.Cert, client.Cert, client.Key) err := agentCfg.StartWithCerts(cfg.Task(), 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")
} }

View file

@ -9,7 +9,6 @@ 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"
@ -17,15 +16,12 @@ 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"
) )
@ -118,7 +114,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(ansi.Warning("using last config")).With(err) return gperr.New("using last config").With(err)
} }
// cancel all current subtasks -> wait // cancel all current subtasks -> wait
@ -223,7 +219,7 @@ func (cfg *Config) load() gperr.Error {
} }
model := config.DefaultConfig() model := config.DefaultConfig()
if err := utils.UnmarshalValidateYAML(data, model); err != nil { if err := utils.DeserializeYAML(data, model); err != nil {
gperr.LogFatal(errMsg, err) gperr.LogFatal(errMsg, err)
} }
@ -231,10 +227,8 @@ 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
@ -248,25 +242,12 @@ 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() { return errs.Error()
notif.Notify(&notif.LogMessage{
Level: zerolog.ErrorLevel,
Title: "Config Reload Error",
Body: notif.ErrorBody{Error: 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) {
@ -297,17 +278,6 @@ 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())
@ -326,8 +296,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.Context()); err != nil { if err := agent.Start(cfg.task); err != nil {
errs.Add(gperr.PrependSubject(agent.String(), err)) errs.Add(err.Subject(agent.String()))
continue continue
} }
addAgent(agent) addAgent(agent)

View file

@ -1,7 +1,6 @@
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"
) )
@ -24,13 +23,10 @@ func (cfg *Config) DumpRouteProviders() map[string]*provider.Provider {
return entries return entries
} }
func (cfg *Config) RouteProviderList() []config.RouteProviderListResponse { func (cfg *Config) RouteProviderList() []string {
var list []config.RouteProviderListResponse var list []string
cfg.providers.RangeAll(func(_ string, p *provider.Provider) { cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
list = append(list, config.RouteProviderListResponse{ list = append(list, p.ShortName())
ShortName: p.ShortName(),
FullName: p.String(),
})
}) })
return list return list
} }

View file

@ -11,9 +11,7 @@ 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"
) )
@ -32,8 +30,6 @@ 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"`
@ -42,15 +38,12 @@ 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() []RouteProviderListResponse RouteProviderList() []string
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)
@ -93,7 +86,7 @@ func HasInstance() bool {
func Validate(data []byte) gperr.Error { func Validate(data []byte) gperr.Error {
var model Config var model Config
return utils.UnmarshalValidateYAML(data, &model) return utils.DeserializeYAML(data, &model)
} }
var matchDomainsRegex = regexp.MustCompile(`^[^\.]?([\w\d\-_]\.?)+[^\.]?$`) var matchDomainsRegex = regexp.MustCompile(`^[^\.]?([\w\d\-_]\.?)+[^\.]?$`)

View file

@ -1,6 +1,6 @@
module github.com/yusing/go-proxy/internal/dnsproviders module github.com/yusing/go-proxy/internal/dnsproviders
go 1.24.3 go 1.24.2
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.7.0 // indirect cloud.google.com/go/compute/metadata v0.6.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.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.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.107 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.63.106 // 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.226 // indirect github.com/baidubce/bce-sdk-go v0.9.224 // 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.5.0 // indirect github.com/civo/civogo v0.3.98 // 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.17 // indirect github.com/exoscale/egoscale/v3 v3.1.14 // 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,6 +62,7 @@ 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
@ -71,16 +72,15 @@ 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.2 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // 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.149 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 // 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.9.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.50.0 // indirect github.com/linode/linodego v1.49.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.66 // indirect github.com/miekg/dns v1.1.65 // 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.91.0 // indirect github.com/oracle/oci-go-sdk/v65 v65.89.2 // 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/v4 v4.1.0 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // 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.15.0 // indirect github.com/sacloud/iaas-api-go v1.14.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.8.0 // indirect github.com/spf13/cast v1.7.1 // 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.1164 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150 // 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.207 // indirect github.com/volcengine/volc-sdk-golang v1.0.205 // indirect
github.com/vultr/govultr/v3 v3.20.0 // indirect github.com/vultr/govultr/v3 v3.19.1 // 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.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.40.0 // indirect golang.org/x/net v0.39.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.32.0 // indirect
google.golang.org/api v0.233.0 // indirect google.golang.org/api v0.230.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/grpc v1.72.1 // indirect google.golang.org/grpc v1.72.0 // 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.3 // indirect gopkg.in/ns1/ns1-go.v2 v2.14.2 // 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-20250502105355-0f33e8f1c979 // indirect k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // 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

View file

@ -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.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
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.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= 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/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= 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/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.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU= github.com/aliyun/alibaba-cloud-sdk-go v1.63.106 h1:+YPfQheppCKOPJxhWDmStF1UMJrxnA1iiwBH12t6Fa4=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/alibaba-cloud-sdk-go v1.63.106/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.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI= github.com/baidubce/bce-sdk-go v0.9.224 h1:z2L8alGw/y3IUHjrLRyrxrgCvMssYTjgCd7OQdb4gt0=
github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/baidubce/bce-sdk-go v0.9.224/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,6 +717,10 @@ 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=
@ -739,12 +743,14 @@ 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.5.0 h1:YDG38z+hmgaAhpDad1n/9sZrBtwRTeKVvMgdrS8Gwy4= github.com/civo/civogo v0.3.98 h1:FEbB5oxCcHeHUK3fJODxVoMQzhpLV9Jtb7bezANTY5c=
github.com/civo/civogo v0.5.0/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.3.98/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=
@ -801,8 +807,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.17 h1:+T6+GP/k3tFNsYIQzpF3Sou4ecH//FkERDsJze/OZ00= github.com/exoscale/egoscale/v3 v3.1.14 h1:ux1wOtx4561ZJM1sF2AFEjEY6HRj/RbtglKvZxh2iqg=
github.com/exoscale/egoscale/v3 v3.1.17/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/exoscale/egoscale/v3 v3.1.14/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=
@ -887,6 +893,8 @@ 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=
@ -992,8 +1000,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-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/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=
@ -1022,8 +1030,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.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
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=
@ -1039,8 +1047,6 @@ 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=
@ -1100,8 +1106,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.149 h1:gDzo/eYE8/mwF5fi7v10pdBW2027PUvaHvRPA5aOPoM= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 h1:ld5s5UeA9zgyFsZskVD2Tr6k6VnJWkvaLm5nqvfOEf4=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.149/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146/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=
@ -1111,11 +1117,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.10.0 h1:AKsihjFT/t6Y0keEv3p59DACcOuh0inWXdUB0ZOzYH0= 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/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg= github.com/infobloxopen/infoblox-go-client/v2 v2.9.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.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
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=
@ -1157,6 +1163,8 @@ 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=
@ -1181,8 +1189,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.50.0 h1:5y79VvvQnWb5JyPIjTwyUrU3ArHcs7XZQFdkPS/lNpw= github.com/linode/linodego v1.49.0 h1:MNd3qwvQzbXB5mCpvdCqlUIu1RPA9oC+50LyB9kK+GQ=
github.com/linode/linodego v1.50.0/go.mod h1:9S+REoPCtUNWCm63D1vjjxIJZfwEL2t2kTDnwt620FM= github.com/linode/linodego v1.49.0/go.mod h1:B+HAM3//4w1wOS0BwdaQBKwBxlfe6kYJ7bSC6jJ/xtc=
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=
@ -1228,8 +1236,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.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
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=
@ -1322,8 +1330,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.91.0 h1:maO6AxKxVfszH0X4tbbtN21jOk03lCRR3IqiA8/FzZc= github.com/oracle/oci-go-sdk/v65 v65.89.2 h1:w0GwID9NlT+eg3InbAwkWsazDtxVLYQ8rJb4E33Yb14=
github.com/oracle/oci-go-sdk/v65 v65.91.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.89.2/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=
@ -1393,12 +1401,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/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
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.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
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=
@ -1422,8 +1430,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.15.0 h1:G88U69OOVUSjOZ2fEP5QWMg7r3VOVsfNkl5Mxl+/wMk= github.com/sacloud/iaas-api-go v1.14.0 h1:xjkFWqdo4ilTrKPNNYBNWR/CZ/kVRsJrdAHAad6J/AQ=
github.com/sacloud/iaas-api-go v1.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA= github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFnpe69kyA4M3V0=
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=
@ -1476,8 +1484,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.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.7.1/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=
@ -1519,8 +1527,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.1164 h1:qEzZCZf1sgvvrZ8ngws0gZlyW+sOdY0K9VXGm4AcvTE= 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/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150/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=
@ -1529,6 +1537,8 @@ 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=
@ -1538,10 +1548,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.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18= github.com/volcengine/volc-sdk-golang v1.0.205 h1:0ukVBX82JaF9N5H/D8j8jPjgJW52bQpAXabrg4WLJ88=
github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= github.com/volcengine/volc-sdk-golang v1.0.205/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA= github.com/vultr/govultr/v3 v3.19.1 h1:31rOP5Tz40AOc8h6Ws4ryzqAniUBffgRhy9uMG/EFvs=
github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/vultr/govultr/v3 v3.19.1/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=
@ -1625,6 +1635,8 @@ 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=
@ -1655,8 +1667,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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
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=
@ -1794,8 +1806,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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
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=
@ -1825,8 +1837,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.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
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=
@ -1843,8 +1855,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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.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=
@ -1961,8 +1973,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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.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=
@ -1977,8 +1989,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.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
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=
@ -1998,8 +2010,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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
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=
@ -2079,8 +2091,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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
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=
@ -2155,8 +2167,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.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
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=
@ -2297,12 +2309,11 @@ 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-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
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 h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= 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/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg= 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/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=
@ -2345,8 +2356,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.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.72.0/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=
@ -2387,8 +2398,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.3 h1:Yn72GgB6AA9I4602AsLMtbC1ZKT5EUrKiG+IPS+Ovr0= gopkg.in/ns1/ns1-go.v2 v2.14.2 h1:wz/toj9U20wBrmYxW4vTz7sZWED+JJVRjUBBJ7CKrzI=
gopkg.in/ns1/ns1-go.v2 v2.14.3/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/ns1/ns1-go.v2 v2.14.2/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=
@ -2422,8 +2433,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-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/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=

View file

@ -1,26 +1,24 @@
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 {
_ utils.NoCopy _ U.NoCopy
DockerHost string `json:"docker_host"` DockerHost string `json:"docker_host"`
Image *ContainerImage `json:"image"` Image *ContainerImage `json:"image"`
@ -39,11 +37,10 @@ type (
PublicHostname string `json:"public_hostname"` PublicHostname string `json:"public_hostname"`
PrivateHostname string `json:"private_hostname"` PrivateHostname string `json:"private_hostname"`
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 {
Author string `json:"author,omitempty"` Author string `json:"author,omitempty"`
@ -79,11 +76,10 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
PublicPortMapping: helper.getPublicPortMapping(), PublicPortMapping: helper.getPublicPortMapping(),
PrivatePortMapping: helper.getPrivatePortMapping(), PrivatePortMapping: helper.getPrivatePortMapping(),
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",
} }
if agent.IsDockerHostAgent(dockerHost) { if agent.IsDockerHostAgent(dockerHost) {
@ -104,33 +100,6 @@ 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": {},
@ -157,27 +126,11 @@ 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 c.isLocal() { if strings.HasPrefix(c.DockerHost, "unix://") {
c.PublicHostname = "127.0.0.1" c.PublicHostname = "127.0.0.1"
return return
} }
@ -191,17 +144,18 @@ func (c *Container) setPublicHostname() {
} }
func (c *Container) setPrivateHostname(helper containerHelper) { func (c *Container) setPrivateHostname(helper containerHelper) {
if !c.isLocal() && c.Agent == nil { if !strings.HasPrefix(c.DockerHost, "unix://") && 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 == "" {
c.PrivateHostname = v.IPAddress continue
return
} }
c.PrivateHostname = v.IPAddress
return
} }
} }
@ -224,7 +178,7 @@ func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
ContainerName: c.ContainerName, ContainerName: c.ContainerName,
}, },
} }
err := utils.MapUnmarshalValidate(cfg, idwCfg) err := utils.Deserialize(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 {

View file

@ -41,38 +41,3 @@ 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)
})
}
}

View file

@ -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, routes.WithRouteContext(r, mux)) ep.middleware.ServeHTTP(mux.ServeHTTP, w, r)
return return
} }
mux.ServeHTTP(w, r) mux.ServeHTTP(w, r)

View file

@ -36,11 +36,8 @@ 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 {
if extra == nil { return &nestedError{&err, []error{extra}}
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 {
@ -65,11 +62,3 @@ 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)
}

View file

@ -24,6 +24,10 @@ 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
@ -59,9 +63,6 @@ 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}
} }
@ -87,6 +88,23 @@ 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()
@ -142,20 +160,3 @@ 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)
}
}

View file

@ -50,7 +50,6 @@ 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)
} }

View file

@ -20,16 +20,6 @@ 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,

View file

@ -153,7 +153,6 @@ 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)
} }

View file

@ -1,43 +0,0 @@
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: "?",
}
}

View file

@ -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(highlightANSI(msg)).With(err).Error()) l.WithLevel(level).Msg(New(highlight(msg)).With(err).Error())
switch level { switch level {
case zerolog.FatalLevel: case zerolog.FatalLevel:
os.Exit(1) os.Exit(1)

View file

@ -14,9 +14,6 @@ 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)
} }

View file

@ -3,6 +3,8 @@ package gperr
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/yusing/go-proxy/internal/utils/strutils"
) )
//nolint:recvcheck //nolint:recvcheck
@ -11,11 +13,9 @@ 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, emptyError) err.Err = PrependSubject(subject, errStr(""))
} else { } else {
err.Err = PrependSubject(subject, err.Err) err.Err = PrependSubject(subject, err.Err)
} }
@ -67,86 +67,48 @@ func (err *nestedError) Is(other error) bool {
return false return false
} }
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 {
return appendLine(nil, nilError, 0)
}
if err.Err != nil {
buf := appendLine(nil, err.Err, 0)
if len(err.Extras) > 0 {
buf = append(buf, '\n')
buf = appendLines(buf, err.Extras, 1, appendLine)
}
return buf
}
return appendLines(nil, err.Extras, 0, appendLine)
}
func (err *nestedError) Error() string { func (err *nestedError) Error() string {
return string(err.fmtError(appendLineNormal))
}
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 { if err == nil {
return appendLine(buf, nilError, level, prefix, format) return makeLine("<nil>", 0)
} }
if err.Err != nil {
lines := make([]string, 0, 1+len(err.Extras))
lines = append(lines, makeLine(err.Err.Error(), 0))
lines = append(lines, makeLines(err.Extras, 1)...)
return strutils.JoinLines(lines)
}
return strutils.JoinLines(makeLines(err.Extras, 0))
}
//go:inline
func makeLine(err string, level int) string {
const bulletPrefix = "• "
const spaces = " "
if level == 0 { if level == 0 {
return append(buf, format(err)...) return err
} }
buf = append(buf, spaces[:2*level]...) return spaces[:2*level] + bulletPrefix + err
buf = append(buf, prefix...)
buf = append(buf, format(err)...)
return buf
} }
func appendLineNormal(buf []byte, err error, level int) []byte { func makeLines(errs []error, level int) []string {
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 buf return nil
} }
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 {
buf = appendLine(buf, err.Err, level) lines = append(lines, makeLine(err.Err.Error(), level))
buf = append(buf, '\n') lines = append(lines, makeLines(err.Extras, level+1)...)
buf = appendLines(buf, err.Extras, level+1, appendLine)
} else { } else {
buf = appendLines(buf, err.Extras, level, appendLine) lines = append(lines, makeLines(err.Extras, level)...)
} }
default: default:
if err == nil { lines = append(lines, makeLine(err.Error(), level))
continue
}
buf = appendLine(buf, err, level)
buf = append(buf, '\n')
} }
} }
return buf return lines
} }

View file

@ -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,23 +19,10 @@ type withSubject struct {
const subjectSep = " > " const subjectSep = " > "
type highlightFunc func(subject string) string func highlight(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
@ -45,11 +32,6 @@ 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)
} }
@ -87,42 +69,24 @@ 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()
subjects := err.Subjects var sb strings.Builder
if err.pendingSubject != "" { for _, s := range err.Subjects {
subjects = append(subjects, err.pendingSubject)
}
var buf bytes.Buffer
for _, s := range subjects {
size += len(s) size += len(s)
} }
n := len(subjects) sb.Grow(size + 2 + n*len(subjectSep) + len(errStr) + len(highlight("")))
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-- {
buf.WriteString(subjects[i]) sb.WriteString(err.Subjects[i])
buf.WriteString(subjectSep) sb.WriteString(subjectSep)
} }
buf.WriteString(highlight(subjects[0])) sb.WriteString(highlight(err.Subjects[0]))
if errStr != "" { sb.WriteString(": ")
buf.WriteString(": ") sb.WriteString(errStr)
buf.WriteString(errStr) return sb.String()
}
return buf.Bytes()
} }
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
@ -136,9 +100,6 @@ 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)
} }

View file

@ -1,6 +1,8 @@
package gperr package gperr
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
) )
@ -30,13 +32,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 = &wrappedError{err.Err, message[0]} err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
return err return err
case *nestedError: case *nestedError:
err.Err = &wrappedError{err.Err, message[0]} err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
return err return err
} }
return &baseError{&wrappedError{err, message[0]}} return &baseError{fmt.Errorf("%s: %w", message[0], err)}
} }
func Unwrap(err error) Error { func Unwrap(err error) Error {
@ -63,6 +65,18 @@ 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 {
@ -84,50 +98,8 @@ 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())
}
}

View file

@ -1,55 +1,63 @@
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) Plain() []byte { func (e *testErr) MarshalJSON() ([]byte, error) {
return []byte("test error") return nil, nil
} }
func (e testErr) Markdown() []byte { func TestIsJSONMarshallable(t *testing.T) {
return []byte("**test error**") tests := []struct {
} name string
err error
type testMultiErr struct { want bool
errors []error }{
} {
name: "testErr",
func (e testMultiErr) Error() string { err: &testErr{},
return Join(e.errors...).Error() want: true,
} },
{
func (e testMultiErr) Unwrap() []error { name: "baseError",
return e.errors err: &baseError{},
} want: false,
},
func TestFormatting(t *testing.T) { {
err := testErr{} name: "baseError with json marshallable error",
plain := Plain(err) err: &baseError{&testErr{}},
if string(plain) != "test error" { want: true,
t.Errorf("expected test error, got %s", string(plain)) },
{
name: "nestedError",
err: &nestedError{},
want: true,
},
{
name: "withSubject",
err: &withSubject{},
want: true,
},
{
name: "standard error",
err: errors.New("test error"),
want: false,
},
} }
md := Markdown(err)
if string(md) != "**test error**" { for _, test := range tests {
t.Errorf("expected test error, got %s", string(md)) t.Run(test.name, func(t *testing.T) {
} if got := IsJSONMarshallable(test.err); got != test.want {
} t.Errorf("IsJSONMarshallable(%v) = %v, want %v", test.err, got, test.want)
}
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))
} }
} }

View file

@ -1,34 +0,0 @@
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)
}

View file

@ -60,7 +60,7 @@ func fetchIconAbsolute(ctx context.Context, url string) *FetchResult {
return result return result
} }
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, "GET", 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,18 +114,12 @@ func fetchKnownIcon(ctx context.Context, url *IconURL) *FetchResult {
return fetchIconAbsolute(ctx, url.URL()) return fetchIconAbsolute(ctx, url.URL())
} }
func fetchIcon(ctx context.Context, filename string) *FetchResult { func fetchIcon(ctx context.Context, filetype, filename string) *FetchResult {
for _, fileType := range []string{"svg", "webp", "png"} { result := fetchKnownIcon(ctx, NewSelfhStIconURL(filename, filetype))
result := fetchKnownIcon(ctx, NewSelfhStIconURL(filename, fileType)) if result.OK() {
if result.OK() { return result
return result
}
result = fetchKnownIcon(ctx, NewWalkXCodeIconURL(filename, fileType))
if result.OK() {
return result
}
} }
return &FetchResult{StatusCode: http.StatusNotFound, ErrMsg: "no icon found"} return fetchKnownIcon(ctx, NewWalkXCodeIconURL(filename, filetype))
} }
func FindIcon(ctx context.Context, r route, uri string) *FetchResult { func FindIcon(ctx context.Context, r route, uri string) *FetchResult {
@ -133,18 +127,17 @@ func FindIcon(ctx context.Context, r route, uri string) *FetchResult {
return result return result
} }
for _, ref := range r.References() { result := fetchIcon(ctx, "png", sanitizeName(r.Reference()))
result := fetchIcon(ctx, sanitizeName(ref)) if !result.OK() {
if result.OK() { if r, ok := r.(httpRoute); ok {
storeIconCache(r.Key(), result) // fallback to parse html
return result result = findIconSlow(ctx, r, uri, nil)
} }
} }
if r, ok := r.(httpRoute); ok { if result.OK() {
// fallback to parse html storeIconCache(r.Key(), result)
return findIconSlow(ctx, r, uri, nil)
} }
return &FetchResult{StatusCode: http.StatusNotFound, ErrMsg: "no icon found"} return result
} }
func findIconSlow(ctx context.Context, r httpRoute, uri string, stack []string) *FetchResult { func findIconSlow(ctx context.Context, r httpRoute, uri string, stack []string) *FetchResult {
@ -161,7 +154,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, http.MethodGet, r.TargetURL().String(), nil) newReq, err := http.NewRequestWithContext(ctx, "GET", 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"}
} }

View file

@ -2,9 +2,9 @@ package homepage
import ( import (
"encoding/json" "encoding/json"
"slices" "strings"
"github.com/yusing/go-proxy/internal/homepage/widgets" config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/utils" "github.com/yusing/go-proxy/internal/utils"
) )
@ -13,21 +13,20 @@ type (
Category []*Item Category []*Item
ItemConfig struct { ItemConfig struct {
Show bool `json:"show"` Show bool `json:"show"`
Name string `json:"name"` // display name Name string `json:"name"` // display name
Icon *IconURL `json:"icon"` Icon *IconURL `json:"icon"`
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
} }
) )
@ -44,10 +43,23 @@ 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,
@ -62,13 +74,4 @@ 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
})
} }

View file

@ -1,9 +1,8 @@
package homepage_test package homepage
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"
) )
@ -14,7 +13,7 @@ func TestOverrideItem(t *testing.T) {
Show: false, Show: false,
Name: "Foo", Name: "Foo",
Icon: &IconURL{ Icon: &IconURL{
FullURL: strPtr("/favicon.ico"), Value: "/favicon.ico",
IconSource: IconSourceRelative, IconSource: IconSourceRelative,
}, },
Category: "App", Category: "App",
@ -25,7 +24,7 @@ func TestOverrideItem(t *testing.T) {
Name: "Bar", Name: "Bar",
Category: "Test", Category: "Test",
Icon: &IconURL{ Icon: &IconURL{
FullURL: strPtr("@walkxcode/example.png"), Value: "@walkxcode/example.png",
IconSource: IconSourceWalkXCode, IconSource: IconSourceWalkXCode,
}, },
} }

View file

@ -7,7 +7,6 @@ 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"
@ -16,24 +15,34 @@ import (
type cacheEntry struct { type cacheEntry struct {
Icon []byte `json:"icon"` Icon []byte `json:"icon"`
ContentType string `json:"content_type,omitempty"` ContentType string `json:"content_type"`
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 = jsonstore.Store[*cacheEntry](common.NamespaceIconCache) iconCache = make(map[string]*cacheEntry)
iconMu sync.RWMutex iconCacheMu sync.RWMutex
) )
const ( const (
iconCacheTTL = 3 * 24 * time.Hour iconCacheTTL = 3 * 24 * time.Hour
cleanUpInterval = time.Minute cleanUpInterval = time.Minute
maxIconSize = 1024 * 1024 // 1MB maxCacheSize = 1024 * 1024 // 1MB
maxCacheEntries = 100 maxCacheEntries = 100
) )
func init() { func InitIconCache() {
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()
@ -46,21 +55,36 @@ func init() {
} }
} }
}() }()
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.Range { for key, icon := range iconCache {
if icon.IsExpired() { if icon.IsExpired() {
iconCache.Delete(key) delete(iconCache, key)
nPruned++ nPruned++
} }
} }
if iconCache.Size() > maxCacheEntries { if len(iconCache) > maxCacheEntries {
iconCache.Clear()
newIconCache := make(map[string]*cacheEntry, maxCacheEntries) newIconCache := make(map[string]*cacheEntry, maxCacheEntries)
i := 0 i := 0
for key, icon := range iconCache.Range { for key, icon := range iconCache {
if i == maxCacheEntries { if i == maxCacheEntries {
break break
} }
@ -69,9 +93,7 @@ func pruneExpiredIconCache() {
i++ i++
} }
} }
for key, icon := range newIconCache { iconCache = 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")
@ -79,18 +101,21 @@ func pruneExpiredIconCache() {
} }
func PruneRouteIconCache(route route) { func PruneRouteIconCache(route route) {
iconCache.Delete(route.Key()) iconCacheMu.Lock()
defer iconCacheMu.Unlock()
delete(iconCache, route.Key())
} }
func loadIconCache(key string) *FetchResult { func loadIconCache(key string) *FetchResult {
iconMu.RLock() iconCacheMu.RLock()
defer iconMu.RUnlock() defer iconCacheMu.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(utils.TimeNow()) icon.LastAccess.Store(time.Now())
return &FetchResult{Icon: icon.Icon, contentType: icon.ContentType} return &FetchResult{Icon: icon.Icon, contentType: icon.ContentType}
} }
return nil return nil
@ -98,17 +123,15 @@ 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) > maxIconSize { if len(icon) > maxCacheSize {
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()
iconMu.Lock() defer iconCacheMu.Unlock()
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.Store(key, entry) iconCache[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")
} }
@ -117,20 +140,12 @@ 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, &tmp) err := json.Unmarshal(data, &e)
// 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
} }
} }

Some files were not shown because too many files have changed in this diff Show more