mirror of
https://github.com/yusing/godoxy.git
synced 2026-01-11 22:30:47 +01:00
Compare commits
250 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25ee8041da | ||
|
|
8687a57b6c | ||
|
|
3f4ed31e46 | ||
|
|
9930f3fa2e | ||
|
|
2157545e17 | ||
|
|
f721395ff0 | ||
|
|
0dc7c59af1 | ||
|
|
e3fe126a5c | ||
|
|
aa2575696d | ||
|
|
c1f9c2c957 | ||
|
|
c098fef615 | ||
|
|
9cdc985fb0 | ||
|
|
2034738422 | ||
|
|
55a42b81de | ||
|
|
48627753d6 | ||
|
|
09b514393d | ||
|
|
3b2ae5dbd6 | ||
|
|
fac3d67a51 | ||
|
|
cb642d7b32 | ||
|
|
9285977495 | ||
|
|
e00cd8a35b | ||
|
|
8ac459c038 | ||
|
|
1bcaf0dab5 | ||
|
|
a291a49a0e | ||
|
|
28fdf3d2f4 | ||
|
|
84b17baf46 | ||
|
|
06ddb178f8 | ||
|
|
61fa7d2665 | ||
|
|
615521ee1c | ||
|
|
bbe308e821 | ||
|
|
c156173757 | ||
|
|
b1aae1cacf | ||
|
|
f46552b477 | ||
|
|
efe1350ffd | ||
|
|
219eedf3c5 | ||
|
|
f6dcc8f118 | ||
|
|
4d6541c851 | ||
|
|
c9db350cbc | ||
|
|
56374d595a | ||
|
|
d81521f293 | ||
|
|
e9ac3cd1a9 | ||
|
|
d33ff2192a | ||
|
|
910ef639a4 | ||
|
|
3cbd70f73a | ||
|
|
83d70d3bb2 | ||
|
|
bbb1b8497f | ||
|
|
d57d76dc65 | ||
|
|
ef893974ea | ||
|
|
b90f2409ab | ||
|
|
36e9b0d416 | ||
|
|
306cb7a20e | ||
|
|
e3915210aa | ||
|
|
e8fb202ea9 | ||
|
|
082b2f5da2 | ||
|
|
e670acb4b8 | ||
|
|
77e486f4fe | ||
|
|
3ccaba3163 | ||
|
|
705923960c | ||
|
|
ca737c8979 | ||
|
|
b6b5d4dbd7 | ||
|
|
b2919fbaf6 | ||
|
|
722c40d103 | ||
|
|
860d9c71b6 | ||
|
|
e354d901c4 | ||
|
|
921a8fb935 | ||
|
|
975354cdc1 | ||
|
|
7d38bfd2d2 | ||
|
|
5506cafa26 | ||
|
|
9fd5bff81a | ||
|
|
38041ca5b8 | ||
|
|
61be88c1d3 | ||
|
|
cb4dcb962e | ||
|
|
1797a222cd | ||
|
|
098fb7e62d | ||
|
|
d4dfec8293 | ||
|
|
f29b69ff3b | ||
|
|
5e00e1c437 | ||
|
|
39c8cc2820 | ||
|
|
56232dbd0e | ||
|
|
baf774f927 | ||
|
|
a3c82209c6 | ||
|
|
386d946bd2 | ||
|
|
ee9bf31d30 | ||
|
|
2c87eebee3 | ||
|
|
5be784d567 | ||
|
|
a999c51bf8 | ||
|
|
7ca722b256 | ||
|
|
51295be463 | ||
|
|
51fc5f017a | ||
|
|
e4996733fc | ||
|
|
f76d86dfa2 | ||
|
|
8778f4ea73 | ||
|
|
6f75bb7593 | ||
|
|
964ba1eac1 | ||
|
|
6e7b571946 | ||
|
|
fc7a81faf5 | ||
|
|
488ad160e7 | ||
|
|
1ec2872f3d | ||
|
|
9c3346dd9d | ||
|
|
203faa8e7e | ||
|
|
fbc853fa6a | ||
|
|
3fefbdfded | ||
|
|
48be6def12 | ||
|
|
94d6b7a168 | ||
|
|
1ca4b4939e | ||
|
|
f8716d990e | ||
|
|
5a91db8d10 | ||
|
|
3e73be60a1 | ||
|
|
af9363209b | ||
|
|
ccc35b2a00 | ||
|
|
44536139c1 | ||
|
|
2b4c39a79e | ||
|
|
ddf78aacba | ||
|
|
f5a006ce81 | ||
|
|
290af4e311 | ||
|
|
feafdf05f2 | ||
|
|
b09bfd6c1e | ||
|
|
e13b18621d | ||
|
|
53f3397b7a | ||
|
|
19968834d2 | ||
|
|
d41c6f8d77 | ||
|
|
dcc5ab8952 | ||
|
|
cc8858332d | ||
|
|
82f02ea2bf | ||
|
|
046ff8a020 | ||
|
|
dc9ae32e8f | ||
|
|
5640d5d454 | ||
|
|
c66de99fcb | ||
|
|
eef994082c | ||
|
|
8c670ab92e | ||
|
|
d11ddb7c91 | ||
|
|
78aea4b4d2 | ||
|
|
80dd142861 | ||
|
|
92aa61e732 | ||
|
|
848f26aa86 | ||
|
|
81e500fcfc | ||
|
|
f417e0fa25 | ||
|
|
cb5a8e7b9d | ||
|
|
16cad11e89 | ||
|
|
2bfbdbf519 | ||
|
|
d5e9a7b3b6 | ||
|
|
7ea415078f | ||
|
|
e67704695b | ||
|
|
804c7eec60 | ||
|
|
ea8be56bf8 | ||
|
|
20c77edce5 | ||
|
|
4f2f0f58e2 | ||
|
|
ac8ad149b8 | ||
|
|
14ec80c883 | ||
|
|
5de5f854ce | ||
|
|
3d8994b42e | ||
|
|
66043e4a26 | ||
|
|
d1e403e16f | ||
|
|
e72e20af69 | ||
|
|
ad6201c27a | ||
|
|
c4c9e9300c | ||
|
|
b23c3f1c3b | ||
|
|
38c0419483 | ||
|
|
357ce38b18 | ||
|
|
ef34c3ffdd | ||
|
|
2e411373a2 | ||
|
|
3dedd66ad1 | ||
|
|
98f047d88a | ||
|
|
973a58e982 | ||
|
|
4b55d1c607 | ||
|
|
63eff4707c | ||
|
|
55a74c36b0 | ||
|
|
fbabb7b7fb | ||
|
|
7a1841e9a5 | ||
|
|
d82bfd0ebd | ||
|
|
1f41c035ea | ||
|
|
c2c9f42fb3 | ||
|
|
60cfff3435 | ||
|
|
c93a460043 | ||
|
|
9bf7a0beef | ||
|
|
c89c737ecd | ||
|
|
382fc61a9c | ||
|
|
b2de33e835 | ||
|
|
86644054e6 | ||
|
|
c2dcabe144 | ||
|
|
c59ddc1df6 | ||
|
|
f4db874fd6 | ||
|
|
f334f5c13c | ||
|
|
5acc4c3894 | ||
|
|
a8aa82f687 | ||
|
|
0f3a1ac6e6 | ||
|
|
9fceda6729 | ||
|
|
becb49e864 | ||
|
|
3aed41e078 | ||
|
|
8047067b2b | ||
|
|
c3fa7c66a7 | ||
|
|
cab68807ee | ||
|
|
d08be872a0 | ||
|
|
bb5f0cdf09 | ||
|
|
a150f1a628 | ||
|
|
584db2efce | ||
|
|
c27bc0e129 | ||
|
|
b46b464e65 | ||
|
|
52ec309f6b | ||
|
|
6051f75145 | ||
|
|
f4f104d206 | ||
|
|
448a2fbd6f | ||
|
|
74224c8e87 | ||
|
|
ae57edfcb0 | ||
|
|
fc23e262d7 | ||
|
|
11a3935e0c | ||
|
|
42e7adbf86 | ||
|
|
1e0c7a15d8 | ||
|
|
ba8edb160f | ||
|
|
4852efcf9c | ||
|
|
ef40793301 | ||
|
|
80862bcd2e | ||
|
|
45b16abd68 | ||
|
|
f411e17d80 | ||
|
|
024100aa8c | ||
|
|
9d508c5950 | ||
|
|
2ff5e5c0b6 | ||
|
|
2a05c6a630 | ||
|
|
6776f20332 | ||
|
|
5043ef778f | ||
|
|
22bcf1201b | ||
|
|
acecd827d6 | ||
|
|
b2713a4b83 | ||
|
|
e2aeef3a86 | ||
|
|
9545482a44 | ||
|
|
d406b940d9 | ||
|
|
dc1175ad69 | ||
|
|
1409a4e8b9 | ||
|
|
8ec9752656 | ||
|
|
a932688ca3 | ||
|
|
55c1c918ba | ||
|
|
14e243d245 | ||
|
|
f7149453d6 | ||
|
|
00d137d05c | ||
|
|
f9affba9fc | ||
|
|
6b3bf84148 | ||
|
|
62a667758d | ||
|
|
ddd27156fc | ||
|
|
af8e2d56b2 | ||
|
|
74a215b894 | ||
|
|
ccdc0046fd | ||
|
|
2f7fdc4c51 | ||
|
|
de1f4da126 | ||
|
|
a48ccb4423 | ||
|
|
193fd9a249 | ||
|
|
0bc4c4af77 | ||
|
|
5fa1417add | ||
|
|
b763c92645 | ||
|
|
09b14a47e9 | ||
|
|
83a69322fa |
@@ -63,9 +63,6 @@ GODOXY_METRICS_DISABLE_DISK=false
|
||||
GODOXY_METRICS_DISABLE_NETWORK=false
|
||||
GODOXY_METRICS_DISABLE_SENSORS=false
|
||||
|
||||
# Frontend listening port
|
||||
GODOXY_FRONTEND_PORT=3000
|
||||
|
||||
# Frontend aliases (subdomains / FQDNs, e.g. godoxy, godoxy.domain.com)
|
||||
GODOXY_FRONTEND_ALIASES=godoxy
|
||||
|
||||
|
||||
2
.github/workflows/agent-binary.yml
vendored
2
.github/workflows/agent-binary.yml
vendored
@@ -25,6 +25,8 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
1
.github/workflows/docker-image-prod.yml
vendored
1
.github/workflows/docker-image-prod.yml
vendored
@@ -10,7 +10,6 @@ jobs:
|
||||
uses: ./.github/workflows/docker-image.yml
|
||||
with:
|
||||
image_name: ${{ github.repository_owner }}/godoxy
|
||||
old_image_name: ${{ github.repository_owner }}/go-proxy
|
||||
tag: latest
|
||||
target: main
|
||||
build-prod-agent:
|
||||
|
||||
14
.github/workflows/docker-image.yml
vendored
14
.github/workflows/docker-image.yml
vendored
@@ -9,9 +9,6 @@ on:
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
old_image_name:
|
||||
required: false
|
||||
type: string
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
@@ -156,17 +153,6 @@ jobs:
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY }}/${{ inputs.image_name }}@sha256:%s ' *)
|
||||
|
||||
- name: Old image name
|
||||
if: inputs.old_image_name != ''
|
||||
run: |
|
||||
docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ inputs.old_image_name }}:${{ steps.meta.outputs.version }}\
|
||||
${{ env.REGISTRY }}/${{ inputs.image_name }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ inputs.image_name }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
- name: Inspect image (old)
|
||||
if: inputs.old_image_name != ''
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ inputs.old_image_name }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
28
.github/workflows/merge-main-into-compat.yml
vendored
Normal file
28
.github/workflows/merge-main-into-compat.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Merge Main Into Compat
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Configure git user
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
- name: Merge main into compat
|
||||
run: |
|
||||
git fetch origin compat
|
||||
git checkout compat
|
||||
git merge --no-edit origin/main
|
||||
- name: Push compat
|
||||
run: |
|
||||
git push origin compat
|
||||
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[submodule "internal/gopsutil"]
|
||||
path = internal/gopsutil
|
||||
url = https://github.com/godoxy-app/gopsutil.git
|
||||
[submodule "internal/go-oidc"]
|
||||
path = internal/go-oidc
|
||||
url = https://github.com/godoxy-app/go-oidc.git
|
||||
[submodule "goutils"]
|
||||
path = goutils
|
||||
url = https://github.com/yusing/goutils.git
|
||||
@@ -70,7 +70,6 @@ linters:
|
||||
govet:
|
||||
disable:
|
||||
- shadow
|
||||
- fieldalignment
|
||||
enable-all: true
|
||||
misspell:
|
||||
locale: US
|
||||
@@ -108,8 +107,7 @@ linters:
|
||||
- all
|
||||
- -SA1019
|
||||
dot-import-whitelist:
|
||||
- github.com/yusing/go-proxy/internal/utils/testing
|
||||
- github.com/yusing/go-proxy/internal/api/v1/utils
|
||||
- github.com/yusing/godoxy/internal/utils/testing
|
||||
tagalign:
|
||||
align: false
|
||||
sort: true
|
||||
|
||||
@@ -21,9 +21,9 @@ lint:
|
||||
- markdownlint
|
||||
- yamllint
|
||||
enabled:
|
||||
- checkov@3.2.467
|
||||
- golangci-lint2@2.4.0
|
||||
- hadolint@2.12.1-beta
|
||||
- checkov@3.2.471
|
||||
- golangci-lint2@2.5.0
|
||||
- hadolint@2.14.0
|
||||
- actionlint@1.7.7
|
||||
- git-diff-check
|
||||
- gofmt@1.20.4
|
||||
@@ -32,7 +32,7 @@ lint:
|
||||
- prettier@3.6.2
|
||||
- shellcheck@0.11.0
|
||||
- shfmt@3.6.0
|
||||
- trufflehog@3.90.5
|
||||
- trufflehog@3.90.8
|
||||
actions:
|
||||
disabled:
|
||||
- trunk-announce
|
||||
|
||||
4
.vscode/settings.example.json
vendored
4
.vscode/settings.example.json
vendored
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"yaml.schemas": {
|
||||
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/src/types/godoxy/config.schema.json": [
|
||||
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/types/godoxy/config.schema.json": [
|
||||
"config.example.yml",
|
||||
"config.yml"
|
||||
],
|
||||
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/src/types/godoxy/routes.schema.json": [
|
||||
"https://github.com/yusing/godoxy-webui/raw/refs/heads/main/types/godoxy/routes.schema.json": [
|
||||
"providers.example.yml"
|
||||
]
|
||||
}
|
||||
|
||||
15
Dockerfile
15
Dockerfile
@@ -1,5 +1,5 @@
|
||||
# Stage 1: deps
|
||||
FROM golang:1.25.1-alpine AS deps
|
||||
FROM golang:1.25.5-alpine AS deps
|
||||
HEALTHCHECK NONE
|
||||
|
||||
# package version does not matter
|
||||
@@ -7,13 +7,20 @@ HEALTHCHECK NONE
|
||||
RUN apk add --no-cache tzdata make libcap-setcap
|
||||
|
||||
ENV GOPATH=/root/go
|
||||
ENV GOCACHE=/root/.cache/go-build
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY goutils/go.mod goutils/go.sum ./goutils/
|
||||
COPY internal/go-oidc/go.mod internal/go-oidc/go.sum ./internal/go-oidc/
|
||||
COPY internal/gopsutil/go.mod internal/gopsutil/go.sum ./internal/gopsutil/
|
||||
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 && \
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/root/go/pkg/mod \
|
||||
sed -i '/^module github\.com\/yusing\/godoxy/!{/github\.com\/yusing\/godoxy/d}' go.mod && \
|
||||
sed -i '/^module github\.com\/yusing\/goutils/!{/github\.com\/yusing\/goutils/d}' go.mod && \
|
||||
go mod download -x
|
||||
|
||||
# Stage 2: builder
|
||||
@@ -28,6 +35,7 @@ COPY internal ./internal
|
||||
COPY pkg ./pkg
|
||||
COPY agent ./agent
|
||||
COPY socket-proxy ./socket-proxy
|
||||
COPY goutils ./goutils
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=${VERSION}
|
||||
@@ -35,9 +43,6 @@ ENV VERSION=${VERSION}
|
||||
ARG MAKE_ARGS
|
||||
ENV MAKE_ARGS=${MAKE_ARGS}
|
||||
|
||||
ENV GOCACHE=/root/.cache/go-build
|
||||
ENV GOPATH=/root/go
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/root/go/pkg/mod \
|
||||
make ${MAKE_ARGS} docker=1 build
|
||||
|
||||
11
Jenkinsfile
vendored
Normal file
11
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
node {
|
||||
stage('SCM') {
|
||||
checkout scm
|
||||
}
|
||||
stage('SonarQube Analysis') {
|
||||
def scannerHome = tool 'SonarScanner';
|
||||
withSonarQubeEnv() {
|
||||
sh "${scannerHome}/bin/sonar-scanner"
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Makefile
47
Makefile
@@ -3,10 +3,11 @@ export VERSION ?= $(shell git describe --tags --abbrev=0)
|
||||
export BUILD_DATE ?= $(shell date -u +'%Y%m%d-%H%M')
|
||||
export GOOS = linux
|
||||
|
||||
WEBUI_DIR ?= ../godoxy-frontend
|
||||
DOCS_DIR ?= ../godoxy-wiki
|
||||
WEBUI_DIR ?= ../godoxy-webui
|
||||
DOCS_DIR ?= ${WEBUI_DIR}/wiki
|
||||
|
||||
LDFLAGS = -X github.com/yusing/go-proxy/pkg.version=${VERSION} -checklinkname=0
|
||||
GO_TAGS = sonic
|
||||
LDFLAGS = -X github.com/yusing/goutils/version.version=${VERSION} -checklinkname=0
|
||||
|
||||
ifeq ($(agent), 1)
|
||||
NAME = godoxy-agent
|
||||
@@ -26,26 +27,28 @@ ifeq ($(trace), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(race), 1)
|
||||
debug = 1
|
||||
BUILD_FLAGS += -race
|
||||
endif
|
||||
|
||||
ifeq ($(debug), 1)
|
||||
CGO_ENABLED = 1
|
||||
GODOXY_DEBUG = 1
|
||||
BUILD_FLAGS += -gcflags=all='-N -l' -tags debug -asan
|
||||
else ifeq ($(pprof), 1)
|
||||
GO_TAGS += debug
|
||||
BUILD_FLAGS += -race
|
||||
else ifeq ($(debug), 1)
|
||||
CGO_ENABLED = 1
|
||||
GODOXY_DEBUG = 1
|
||||
GO_TAGS += debug
|
||||
BUILD_FLAGS += -asan # FIXME: -gcflags=all='-N -l'
|
||||
else ifeq ($(pprof), 1)
|
||||
CGO_ENABLED = 0
|
||||
GORACE = log_path=logs/pprof strip_path_prefix=$(shell pwd)/ halt_on_error=1
|
||||
BUILD_FLAGS += -tags pprof
|
||||
GO_TAGS += pprof
|
||||
VERSION := ${VERSION}-pprof
|
||||
else
|
||||
CGO_ENABLED = 0
|
||||
LDFLAGS += -s -w
|
||||
BUILD_FLAGS += -pgo=auto -tags production
|
||||
GO_TAGS += production
|
||||
BUILD_FLAGS += -pgo=auto
|
||||
endif
|
||||
|
||||
BUILD_FLAGS += -ldflags='$(LDFLAGS)'
|
||||
BUILD_FLAGS += -tags '$(GO_TAGS)' -ldflags='$(LDFLAGS)'
|
||||
BIN_PATH := $(shell pwd)/bin/${NAME}
|
||||
|
||||
export NAME
|
||||
@@ -72,7 +75,7 @@ endif
|
||||
.PHONY: debug
|
||||
|
||||
test:
|
||||
GODOXY_TEST=1 go test ./internal/...
|
||||
go test -v -race ./internal/...
|
||||
|
||||
docker-build-test:
|
||||
docker build -t godoxy .
|
||||
@@ -114,13 +117,13 @@ run:
|
||||
cd ${PWD} && [ -f .env ] && godotenv -f .env go run ${BUILD_FLAGS} ./cmd
|
||||
|
||||
dev:
|
||||
docker compose -f dev.compose.yml up -t 0 -d
|
||||
docker compose -f dev.compose.yml $(args)
|
||||
|
||||
dev-build: build
|
||||
docker compose -f dev.compose.yml up -t 0 -d --build
|
||||
docker compose -f dev.compose.yml up -t 0 -d app --force-recreate
|
||||
|
||||
dev-logs:
|
||||
docker compose -f dev.compose.yml logs -f app
|
||||
dev-run: build
|
||||
cd dev-data && ${BIN_PATH}
|
||||
|
||||
mtrace:
|
||||
${BIN_PATH} debug-ls-mtrace > mtrace.json
|
||||
@@ -144,15 +147,17 @@ push-github:
|
||||
git push origin $(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
gen-swagger:
|
||||
swag init --parseDependency --parseInternal -g handler.go -d internal/api -o internal/api/v1/docs
|
||||
swag init --parseDependency --parseInternal --parseFuncBody -g handler.go -d internal/api -o internal/api/v1/docs
|
||||
python3 scripts/fix-swagger-json.py
|
||||
# we don't need this
|
||||
rm internal/api/v1/docs/docs.go
|
||||
|
||||
gen-swagger-markdown: gen-swagger
|
||||
# brew tap go-swagger/go-swagger && brew install go-swagger
|
||||
swagger generate markdown -f internal/api/v1/docs/swagger.yaml --skip-validation --output ${DOCS_DIR}/src/API.md
|
||||
|
||||
gen-api-types: gen-swagger
|
||||
# --disable-throw-on-error
|
||||
pnpx swagger-typescript-api generate --sort-types --generate-union-enums --axios --add-readonly --route-types \
|
||||
--responses -o ${WEBUI_DIR}/lib -n api.ts -p internal/api/v1/docs/swagger.json
|
||||
bunx --bun swagger-typescript-api generate --sort-types --generate-union-enums --axios --add-readonly --route-types \
|
||||
--responses -o ${WEBUI_DIR}/lib -n api.ts -p internal/api/v1/docs/swagger.json
|
||||
bunx --bun prettier --config ${WEBUI_DIR}/.prettierrc --write ${WEBUI_DIR}/lib/api.ts
|
||||
16
README.md
16
README.md
@@ -33,6 +33,7 @@ Have questions? Ask [ChatGPT](https://chatgpt.com/g/g-6825390374b481919ad482f2e4
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Setup](#setup)
|
||||
- [How does GoDoxy work](#how-does-godoxy-work)
|
||||
- [Update / Uninstall system agent](#update--uninstall-system-agent)
|
||||
- [Screenshots](#screenshots)
|
||||
- [idlesleeper](#idlesleeper)
|
||||
- [Metrics and Logs](#metrics-and-logs)
|
||||
@@ -58,6 +59,7 @@ Have questions? Ask [ChatGPT](https://chatgpt.com/g/g-6825390374b481919ad482f2e4
|
||||
- Country **(Maxmind account required)**
|
||||
- Timezone **(Maxmind account required)**
|
||||
- **Access logging**
|
||||
- Periodic notification of access summaries for number of allowed and blocked connections
|
||||
- **Advanced Automation**
|
||||
- Automatic SSL certificate management with Let's Encrypt ([using DNS-01 Challenge](https://docs.godoxy.dev/DNS-01-Providers))
|
||||
- Auto-configuration for Docker containers
|
||||
@@ -128,6 +130,20 @@ Configure Wildcard DNS Record(s) to point to machine running `GoDoxy`, e.g.
|
||||
>
|
||||
> For example, with the label `proxy.aliases: qbt` you can access your app via `qbt.domain.com`.
|
||||
|
||||
## Update / Uninstall system agent
|
||||
|
||||
Update:
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -fsSL https://github.com/yusing/godoxy/raw/refs/heads/main/scripts/install-agent.sh)" -- update
|
||||
```
|
||||
|
||||
Uninstall:
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -fsSL https://github.com/yusing/godoxy/raw/refs/heads/main/scripts/install-agent.sh)" -- uninstall
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||
### idlesleeper
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
- [安裝](#安裝)
|
||||
- [手動安裝](#手動安裝)
|
||||
- [資料夾結構](#資料夾結構)
|
||||
- [更新 / 卸載系統代理 (System Agent)](#更新--卸載系統代理-system-agent)
|
||||
- [截圖](#截圖)
|
||||
- [閒置休眠](#閒置休眠)
|
||||
- [監控](#監控)
|
||||
@@ -57,6 +58,7 @@
|
||||
- 國家 **(需要 Maxmind 帳戶)**
|
||||
- 時區 **(需要 Maxmind 帳戶)**
|
||||
- **存取日誌記錄**
|
||||
- 定時發送摘要 (允許和拒絕的連線次數)
|
||||
- **自動化**
|
||||
- 使用 Let's Encrypt 自動管理 SSL 憑證 ([使用 DNS-01 驗證](https://docs.godoxy.dev/DNS-01-Providers))
|
||||
- Docker 容器自動配置
|
||||
@@ -144,6 +146,20 @@
|
||||
└── .env
|
||||
```
|
||||
|
||||
## 更新 / 卸載系統代理 (System Agent)
|
||||
|
||||
更新:
|
||||
|
||||
```bash
|
||||
sudo /bin/bash -c "$(curl -fsSL https://github.com/yusing/godoxy/raw/refs/heads/main/scripts/install-agent.sh)" -- update
|
||||
```
|
||||
|
||||
卸載:
|
||||
|
||||
```bash
|
||||
sudo /bin/bash -c "$(curl -fsSL https://github.com/yusing/godoxy/raw/refs/heads/main/scripts/install-agent.sh)" -- uninstall
|
||||
```
|
||||
|
||||
## 截圖
|
||||
|
||||
### 閒置休眠
|
||||
|
||||
@@ -5,16 +5,15 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||
"github.com/yusing/go-proxy/agent/pkg/server"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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/utils/strutils"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
socketproxy "github.com/yusing/go-proxy/socketproxy/pkg"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/env"
|
||||
"github.com/yusing/godoxy/agent/pkg/server"
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
socketproxy "github.com/yusing/godoxy/socketproxy/pkg"
|
||||
httpServer "github.com/yusing/goutils/server"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
"github.com/yusing/goutils/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -27,24 +26,24 @@ func main() {
|
||||
ca := &agent.PEMPair{}
|
||||
err := ca.Load(env.AgentCACert)
|
||||
if err != nil {
|
||||
gperr.LogFatal("init CA error", err)
|
||||
log.Fatal().Err(err).Msg("init CA error")
|
||||
}
|
||||
caCert, err := ca.ToTLSCert()
|
||||
if err != nil {
|
||||
gperr.LogFatal("init CA error", err)
|
||||
log.Fatal().Err(err).Msg("init CA error")
|
||||
}
|
||||
|
||||
srv := &agent.PEMPair{}
|
||||
srv.Load(env.AgentSSLCert)
|
||||
if err != nil {
|
||||
gperr.LogFatal("init SSL error", err)
|
||||
log.Fatal().Err(err).Msg("init SSL error")
|
||||
}
|
||||
srvCert, err := srv.ToTLSCert()
|
||||
if err != nil {
|
||||
gperr.LogFatal("init SSL error", err)
|
||||
log.Fatal().Err(err).Msg("init SSL error")
|
||||
}
|
||||
|
||||
log.Info().Msgf("GoDoxy Agent version %s", pkg.GetVersion())
|
||||
log.Info().Msgf("GoDoxy Agent version %s", version.Get())
|
||||
log.Info().Msgf("Agent name: %s", env.AgentName)
|
||||
log.Info().Msgf("Agent port: %d", env.AgentPort)
|
||||
log.Info().Msgf("Agent runtime: %s", env.Runtime)
|
||||
|
||||
124
agent/go.mod
124
agent/go.mod
@@ -1,113 +1,129 @@
|
||||
module github.com/yusing/go-proxy/agent
|
||||
module github.com/yusing/godoxy/agent
|
||||
|
||||
go 1.25.1
|
||||
go 1.25.5
|
||||
|
||||
replace github.com/yusing/go-proxy => ..
|
||||
|
||||
replace github.com/yusing/go-proxy/socketproxy => ../socket-proxy
|
||||
|
||||
replace github.com/yusing/go-proxy/internal/utils => ../internal/utils
|
||||
|
||||
replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250816043325-ee003f88b84d
|
||||
replace (
|
||||
github.com/shirou/gopsutil/v4 => ../internal/gopsutil
|
||||
github.com/yusing/godoxy => ../
|
||||
github.com/yusing/godoxy/socketproxy => ../socket-proxy
|
||||
github.com/yusing/goutils => ../goutils
|
||||
github.com/yusing/goutils/http/reverseproxy => ../goutils/http/reverseproxy
|
||||
github.com/yusing/goutils/http/websocket => ../goutils/http/websocket
|
||||
github.com/yusing/goutils/server => ../goutils/server
|
||||
)
|
||||
|
||||
exclude github.com/containerd/nerdctl/mod/tigron v0.0.0
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/bytedance/sonic v1.14.2
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/puzpuzpuz/xsync/v4 v4.1.0
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/yusing/go-proxy v0.17.6
|
||||
github.com/yusing/go-proxy/internal/utils v0.0.0
|
||||
github.com/yusing/go-proxy/socketproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/valyala/fasthttp v1.68.0
|
||||
github.com/yusing/godoxy v0.20.10
|
||||
github.com/yusing/godoxy/socketproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils v0.7.0
|
||||
github.com/yusing/goutils/http/reverseproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils/server v0.0.0-20250927113415-dd977d2edaeb
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.1 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/diskfs/go-diskfs v1.7.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/cli v28.4.0+incompatible // indirect
|
||||
github.com/docker/docker v28.4.0+incompatible // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/docker/cli v29.1.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-acme/lego/v4 v4.29.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/goccy/go-yaml v1.19.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gotify/server/v2 v2.7.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/gotify/server/v2 v2.7.3 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/luthermonson/go-proxmox v0.2.3 // indirect
|
||||
github.com/magefile/mage v1.15.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.68 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/moby/api v1.52.0 // indirect
|
||||
github.com/moby/moby/client v0.2.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.57.1 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/samber/slog-common v0.19.0 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.7.3 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.8 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vincent-petithory/dataurl v1.0.0 // indirect
|
||||
github.com/yusing/ds v0.1.0 // indirect
|
||||
github.com/yusing/ds v0.3.1 // indirect
|
||||
github.com/yusing/gointernals v0.1.16 // indirect
|
||||
github.com/yusing/goutils/http/websocket v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/arch v0.21.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.13.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.33.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
)
|
||||
|
||||
231
agent/go.sum
231
agent/go.sum
@@ -1,17 +1,21 @@
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
|
||||
github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
|
||||
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
|
||||
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
|
||||
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
||||
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
|
||||
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
|
||||
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
@@ -20,33 +24,43 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
|
||||
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
|
||||
github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
|
||||
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/docker/cli v29.1.2+incompatible h1:s4QI7drXpIo78OM+CwuthPsO5kCf8cpNsck5PsLVTH8=
|
||||
github.com/docker/cli v29.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-acme/lego/v4 v4.29.0 h1:vKMEtvoKb0gOO9rWO9zMBwE4CgI5A5CWDsK4QEeBqzo=
|
||||
github.com/go-acme/lego/v4 v4.29.0/go.mod h1:rnYyDj1NdDd9y1dHkVuUS97j7bfe9I61+oY9odKaHM8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@@ -61,15 +75,19 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
|
||||
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250816043325-ee003f88b84d h1:bNqtnmyhGDxpBSaFYIo7ferYRIc/QzlaGfIhh/JmMPk=
|
||||
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250816043325-ee003f88b84d/go.mod h1:7iQ/w4jyGYJCZ56dZLNztwM4atNxj5C2HNTBxhLvV8A=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
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/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
@@ -80,12 +98,18 @@ 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/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotify/server/v2 v2.7.2 h1:YRgYl/kB7Uh4OINd7gK60N3QBTY4YEeKTrBLhd67LC4=
|
||||
github.com/gotify/server/v2 v2.7.2/go.mod h1:KJH+8yhkAxArygPwaRfp9otHjt6tE0YSzdrsgPX/5EE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/gotify/server/v2 v2.7.3 h1:nro/ZnxdlZFvxFcw9LREGA8zdk6CK744azwhuhX/A4g=
|
||||
github.com/gotify/server/v2 v2.7.3/go.mod h1:VAtE1RIc/2j886PYs9WPQbMjqbFsoyQ0G8IdFtnAxU0=
|
||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU=
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -96,8 +120,12 @@ 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/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 h1:mFWunSatvkQQDhpdyuFAYwyAan3hzCuma+Pz8sqvOfg=
|
||||
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/luthermonson/go-proxmox v0.2.3 h1:NAjUJ5Jd1ynIK6UHMGd/VLGgNZWpGXhfL+DBmAVSEaA=
|
||||
github.com/luthermonson/go-proxmox v0.2.3/go.mod h1:oyFgg2WwTEIF0rP6ppjiixOHa5ebK1p8OaRiFhvICBQ=
|
||||
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
@@ -105,21 +133,19 @@ 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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
@@ -128,30 +154,35 @@ github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.1.0 h1:x9eHRl4QhZFIPJ17yl4KKW9xLyVWbb3/Yq4SXpjF71U=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.1.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0=
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89G9YI=
|
||||
github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M=
|
||||
github.com/samber/slog-zerolog/v2 v2.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.9.0 h1:6LkOabJmZdNLaUWkTC3IVVA+dq7b/V0FM6lz6/7+THI=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
@@ -159,37 +190,45 @@ github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8
|
||||
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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusing/ds v0.1.0 h1:aiZs7jPMN3MEChUsddMYjpZFHhhAmkxrwRyIUnGy5AU=
|
||||
github.com/yusing/ds v0.1.0/go.mod h1:KC785+mtt+Bau0LLR+slExDaUjeiqLT1k9Or6Rpryh4=
|
||||
github.com/yusing/ds v0.3.1 h1:mCqTgTQD8RhiBpcysvii5kZ7ZBmqcknVsFubNALGLbY=
|
||||
github.com/yusing/ds v0.3.1/go.mod h1:XhKV4l7cZwBbbl7lRzNC9zX27zvCM0frIwiuD40ULRk=
|
||||
github.com/yusing/gointernals v0.1.16 h1:GrhZZdxzA+jojLEqankctJrOuAYDb7kY1C93S1pVR34=
|
||||
github.com/yusing/gointernals v0.1.16/go.mod h1:B/0FVXt4WPmgzVy3ynzkqKi+BSGaJVmwCJBRXYapo34=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.0 h1:YpRtUFjvhSymycLS2T81lT6IGhcUP+LUPtv0iv1N8bM=
|
||||
go.opentelemetry.io/auto/sdk v1.2.0/go.mod h1:1deq2zL7rwjwC8mR7XgY2N+tlIl6pjmEUoLDENMEzwk=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
@@ -198,29 +237,27 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw=
|
||||
golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@@ -230,8 +267,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -239,15 +278,16 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -259,8 +299,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -279,28 +319,21 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -309,3 +342,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
|
||||
@@ -2,15 +2,16 @@ package agent
|
||||
|
||||
import (
|
||||
"iter"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
)
|
||||
|
||||
var agentPool = xsync.NewMap[string, *AgentConfig](xsync.WithPresize(10))
|
||||
|
||||
func init() {
|
||||
if common.IsTest {
|
||||
if strings.HasSuffix(os.Args[0], ".test") {
|
||||
agentPool.Store("test-agent", &AgentConfig{
|
||||
Addr: "test-agent",
|
||||
})
|
||||
@@ -63,5 +64,5 @@ func NumAgents() int {
|
||||
|
||||
func getAgentByAddr(addr string) (agent *AgentConfig, ok bool) {
|
||||
agent, ok = agentPool.Load(addr)
|
||||
return
|
||||
return agent, ok
|
||||
}
|
||||
|
||||
@@ -15,19 +15,21 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/yusing/godoxy/agent/pkg/certs"
|
||||
"github.com/yusing/goutils/version"
|
||||
)
|
||||
|
||||
type AgentConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Version version.Version `json:"version" swaggertype:"string"`
|
||||
Runtime ContainerRuntime `json:"runtime"`
|
||||
|
||||
httpClient *http.Client
|
||||
tlsConfig *tls.Config
|
||||
l zerolog.Logger
|
||||
httpClient *http.Client
|
||||
fasthttpClientHealthCheck *fasthttp.Client
|
||||
tlsConfig tls.Config
|
||||
l zerolog.Logger
|
||||
} // @name Agent
|
||||
|
||||
const (
|
||||
@@ -81,7 +83,7 @@ func (cfg *AgentConfig) Parse(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var serverVersion = pkg.GetVersion()
|
||||
var serverVersion = version.Get()
|
||||
|
||||
func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte) error {
|
||||
clientCert, err := tls.X509KeyPair(crt, key)
|
||||
@@ -96,7 +98,7 @@ func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte)
|
||||
return errors.New("invalid ca certificate")
|
||||
}
|
||||
|
||||
cfg.tlsConfig = &tls.Config{
|
||||
cfg.tlsConfig = tls.Config{
|
||||
Certificates: []tls.Certificate{clientCert},
|
||||
RootCAs: caCertPool,
|
||||
ServerName: CertsDNSName,
|
||||
@@ -104,34 +106,37 @@ func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte)
|
||||
|
||||
// create transport and http client
|
||||
cfg.httpClient = cfg.NewHTTPClient()
|
||||
applyNormalTransportConfig(cfg.httpClient)
|
||||
|
||||
cfg.fasthttpClientHealthCheck = cfg.NewFastHTTPHealthCheckClient()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// get agent name
|
||||
name, _, err := cfg.Fetch(ctx, EndpointName)
|
||||
name, _, err := cfg.fetchString(ctx, EndpointName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.Name = string(name)
|
||||
cfg.Name = name
|
||||
|
||||
cfg.l = log.With().Str("agent", cfg.Name).Logger()
|
||||
|
||||
// check agent version
|
||||
agentVersionBytes, _, err := cfg.Fetch(ctx, EndpointVersion)
|
||||
agentVersion, _, err := cfg.fetchString(ctx, EndpointVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check agent runtime
|
||||
runtimeBytes, status, err := cfg.Fetch(ctx, EndpointRuntime)
|
||||
runtime, status, err := cfg.fetchString(ctx, EndpointRuntime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch status {
|
||||
case http.StatusOK:
|
||||
switch string(runtimeBytes) {
|
||||
switch runtime {
|
||||
case "docker":
|
||||
cfg.Runtime = ContainerRuntimeDocker
|
||||
// case "nerdctl":
|
||||
@@ -139,20 +144,19 @@ func (cfg *AgentConfig) StartWithCerts(ctx context.Context, ca, crt, key []byte)
|
||||
case "podman":
|
||||
cfg.Runtime = ContainerRuntimePodman
|
||||
default:
|
||||
return fmt.Errorf("invalid agent runtime: %s", runtimeBytes)
|
||||
return fmt.Errorf("invalid agent runtime: %s", runtime)
|
||||
}
|
||||
case http.StatusNotFound:
|
||||
// backward compatibility, old agent does not have runtime endpoint
|
||||
cfg.Runtime = ContainerRuntimeDocker
|
||||
default:
|
||||
return fmt.Errorf("failed to get agent runtime: HTTP %d %s", status, runtimeBytes)
|
||||
return fmt.Errorf("failed to get agent runtime: HTTP %d %s", status, runtime)
|
||||
}
|
||||
|
||||
cfg.Version = string(agentVersionBytes)
|
||||
agentVersion := pkg.ParseVersion(cfg.Version)
|
||||
cfg.Version = version.Parse(agentVersion)
|
||||
|
||||
if serverVersion.IsNewerMajorThan(agentVersion) {
|
||||
log.Warn().Msgf("agent %s major version mismatch: server: %s, agent: %s", cfg.Name, serverVersion, agentVersion)
|
||||
if serverVersion.IsNewerThanMajor(cfg.Version) {
|
||||
log.Warn().Msgf("agent %s major version mismatch: server: %s, agent: %s", cfg.Name, serverVersion, cfg.Version)
|
||||
}
|
||||
|
||||
log.Info().Msgf("agent %q initialized", cfg.Name)
|
||||
@@ -184,6 +188,25 @@ func (cfg *AgentConfig) NewHTTPClient() *http.Client {
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) NewFastHTTPHealthCheckClient() *fasthttp.Client {
|
||||
return &fasthttp.Client{
|
||||
Dial: func(addr string) (net.Conn, error) {
|
||||
if addr != AgentHost+":443" {
|
||||
return nil, &net.AddrError{Err: "invalid address", Addr: addr}
|
||||
}
|
||||
return net.Dial("tcp", cfg.Addr)
|
||||
},
|
||||
TLSConfig: &cfg.tlsConfig,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 3 * time.Second,
|
||||
DisableHeaderNamesNormalizing: true,
|
||||
DisablePathNormalizing: true,
|
||||
NoDefaultUserAgentHeader: true,
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) Transport() *http.Transport {
|
||||
return &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
@@ -195,7 +218,7 @@ func (cfg *AgentConfig) Transport() *http.Transport {
|
||||
}
|
||||
return cfg.DialContext(ctx)
|
||||
},
|
||||
TLSClientConfig: cfg.tlsConfig,
|
||||
TLSClientConfig: &cfg.tlsConfig,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,3 +231,11 @@ func (cfg *AgentConfig) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
func (cfg *AgentConfig) String() string {
|
||||
return cfg.Name + "@" + cfg.Addr
|
||||
}
|
||||
|
||||
func applyNormalTransportConfig(client *http.Client) {
|
||||
transport := client.Transport.(*http.Transport)
|
||||
transport.MaxIdleConns = 100
|
||||
transport.MaxIdleConnsPerHost = 100
|
||||
transport.ReadBufferSize = 16384
|
||||
transport.WriteBufferSize = 16384
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@ package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/valyala/fasthttp"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
"github.com/yusing/goutils/http/reverseproxy"
|
||||
)
|
||||
|
||||
func (cfg *AgentConfig) Do(ctx context.Context, method, endpoint string, body io.Reader) (*http.Response, error) {
|
||||
@@ -30,14 +34,57 @@ func (cfg *AgentConfig) Forward(req *http.Request, endpoint string) (*http.Respo
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) Fetch(ctx context.Context, endpoint string) ([]byte, int, error) {
|
||||
type HealthCheckResponse struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Detail string `json:"detail"`
|
||||
Latency time.Duration `json:"latency"`
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) DoHealthCheck(timeout time.Duration, query string) (ret HealthCheckResponse, err error) {
|
||||
req := fasthttp.AcquireRequest()
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseResponse(resp)
|
||||
|
||||
req.SetRequestURI(APIBaseURL + EndpointHealth + "?" + query)
|
||||
req.Header.SetMethod(fasthttp.MethodGet)
|
||||
req.Header.Set("Accept-Encoding", "identity")
|
||||
req.SetConnectionClose()
|
||||
|
||||
start := time.Now()
|
||||
err = cfg.fasthttpClientHealthCheck.DoTimeout(req, resp, timeout)
|
||||
ret.Latency = time.Since(start)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if status := resp.StatusCode(); status != http.StatusOK {
|
||||
ret.Detail = fmt.Sprintf("HTTP %d %s", status, resp.Body())
|
||||
return ret, nil
|
||||
} else {
|
||||
err = sonic.Unmarshal(resp.Body(), &ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) fetchString(ctx context.Context, endpoint string) (string, int, error) {
|
||||
resp, err := cfg.Do(ctx, "GET", endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return "", 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
return data, resp.StatusCode, nil
|
||||
|
||||
data, release, err := httputils.ReadAllBody(resp)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
ret := string(data)
|
||||
release(data)
|
||||
return ret, resp.StatusCode, nil
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) Websocket(ctx context.Context, endpoint string) (*websocket.Conn, *http.Response, error) {
|
||||
@@ -56,7 +103,7 @@ func (cfg *AgentConfig) Websocket(ctx context.Context, endpoint string) (*websoc
|
||||
// It will create a new request with the same context, method, and body, but with the agent host and scheme, and the endpoint
|
||||
// If the request has a query, it will be added to the proxy request's URL
|
||||
func (cfg *AgentConfig) ReverseProxy(w http.ResponseWriter, req *http.Request, endpoint string) {
|
||||
rp := reverseproxy.NewReverseProxy("agent", nettypes.NewURL(AgentURL), cfg.Transport())
|
||||
rp := reverseproxy.NewReverseProxy("agent", AgentURL, cfg.Transport())
|
||||
req.URL.Host = AgentHost
|
||||
req.URL.Scheme = "https"
|
||||
req.URL.Path = endpoint
|
||||
|
||||
@@ -3,6 +3,8 @@ package agent
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
@@ -10,14 +12,11 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -244,5 +243,5 @@ func NewAgent() (ca, srv, client *PEMPair, err error) {
|
||||
}
|
||||
|
||||
client = toPEMPair(clientCertDER, clientKey)
|
||||
return
|
||||
return ca, srv, client, err
|
||||
}
|
||||
|
||||
73
agent/pkg/agentproxy/config.go
Normal file
73
agent/pkg/agentproxy/config.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package agentproxy
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
route "github.com/yusing/godoxy/internal/route/types"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Scheme string `json:"scheme,omitempty"`
|
||||
Host string `json:"host,omitempty"` // host or host:port
|
||||
|
||||
route.HTTPConfig
|
||||
}
|
||||
|
||||
func ConfigFromHeaders(h http.Header) (Config, error) {
|
||||
cfg, err := proxyConfigFromHeaders(h)
|
||||
if cfg.Host == "" || err != nil {
|
||||
cfg = proxyConfigFromHeadersLegacy(h)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func proxyConfigFromHeadersLegacy(h http.Header) (cfg Config) {
|
||||
cfg.Host = h.Get(HeaderXProxyHost)
|
||||
isHTTPS, _ := strconv.ParseBool(h.Get(HeaderXProxyHTTPS))
|
||||
cfg.NoTLSVerify, _ = strconv.ParseBool(h.Get(HeaderXProxySkipTLSVerify))
|
||||
responseHeaderTimeout, err := strconv.Atoi(h.Get(HeaderXProxyResponseHeaderTimeout))
|
||||
if err != nil {
|
||||
responseHeaderTimeout = 0
|
||||
}
|
||||
cfg.ResponseHeaderTimeout = time.Duration(responseHeaderTimeout) * time.Second
|
||||
|
||||
cfg.Scheme = "http"
|
||||
if isHTTPS {
|
||||
cfg.Scheme = "https"
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func proxyConfigFromHeaders(h http.Header) (cfg Config, err error) {
|
||||
cfg.Scheme = h.Get(HeaderXProxyScheme)
|
||||
cfg.Host = h.Get(HeaderXProxyHost)
|
||||
|
||||
cfgBase64 := h.Get(HeaderXProxyConfig)
|
||||
cfgJSON, err := base64.StdEncoding.DecodeString(cfgBase64)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
err = sonic.Unmarshal(cfgJSON, &cfg)
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func (cfg *Config) SetAgentProxyConfigHeadersLegacy(h http.Header) {
|
||||
h.Set(HeaderXProxyHost, cfg.Host)
|
||||
h.Set(HeaderXProxyHTTPS, strconv.FormatBool(cfg.Scheme == "https"))
|
||||
h.Set(HeaderXProxySkipTLSVerify, strconv.FormatBool(cfg.NoTLSVerify))
|
||||
h.Set(HeaderXProxyResponseHeaderTimeout, strconv.Itoa(int(cfg.ResponseHeaderTimeout.Round(time.Second).Seconds())))
|
||||
}
|
||||
|
||||
func (cfg *Config) SetAgentProxyConfigHeaders(h http.Header) {
|
||||
h.Set(HeaderXProxyHost, cfg.Host)
|
||||
h.Set(HeaderXProxyScheme, string(cfg.Scheme))
|
||||
cfgJSON, _ := sonic.Marshal(cfg.HTTPConfig)
|
||||
cfgBase64 := base64.StdEncoding.EncodeToString(cfgJSON)
|
||||
h.Set(HeaderXProxyConfig, cfgBase64)
|
||||
}
|
||||
@@ -1,27 +1,14 @@
|
||||
package agentproxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
const (
|
||||
HeaderXProxyScheme = "X-Proxy-Scheme"
|
||||
HeaderXProxyHost = "X-Proxy-Host"
|
||||
HeaderXProxyConfig = "X-Proxy-Config"
|
||||
)
|
||||
|
||||
// deprecated
|
||||
const (
|
||||
HeaderXProxyHost = "X-Proxy-Host"
|
||||
HeaderXProxyHTTPS = "X-Proxy-Https"
|
||||
HeaderXProxySkipTLSVerify = "X-Proxy-Skip-Tls-Verify"
|
||||
HeaderXProxyResponseHeaderTimeout = "X-Proxy-Response-Header-Timeout"
|
||||
)
|
||||
|
||||
type AgentProxyHeaders struct {
|
||||
Host string
|
||||
IsHTTPS bool
|
||||
SkipTLSVerify bool
|
||||
ResponseHeaderTimeout int
|
||||
}
|
||||
|
||||
func SetAgentProxyHeaders(r *http.Request, headers *AgentProxyHeaders) {
|
||||
r.Header.Set(HeaderXProxyHost, headers.Host)
|
||||
r.Header.Set(HeaderXProxyHTTPS, strconv.FormatBool(headers.IsHTTPS))
|
||||
r.Header.Set(HeaderXProxySkipTLSVerify, strconv.FormatBool(headers.SkipTLSVerify))
|
||||
r.Header.Set(HeaderXProxyResponseHeaderTimeout, strconv.Itoa(headers.ResponseHeaderTimeout))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
const AgentCertsBasePath = "certs"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
"github.com/yusing/godoxy/agent/pkg/certs"
|
||||
)
|
||||
|
||||
func TestZipCert(t *testing.T) {
|
||||
|
||||
18
agent/pkg/env/env.go
vendored
18
agent/pkg/env/env.go
vendored
@@ -3,8 +3,8 @@ package env
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/goutils/env"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -32,14 +32,14 @@ func init() {
|
||||
}
|
||||
|
||||
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)
|
||||
DockerSocket = env.GetEnvString("DOCKER_SOCKET", "/var/run/docker.sock")
|
||||
AgentName = env.GetEnvString("AGENT_NAME", DefaultAgentName())
|
||||
AgentPort = env.GetEnvInt("AGENT_PORT", 8890)
|
||||
AgentSkipClientCertCheck = env.GetEnvBool("AGENT_SKIP_CLIENT_CERT_CHECK", false)
|
||||
|
||||
AgentCACert = common.GetEnvString("AGENT_CA_CERT", "")
|
||||
AgentSSLCert = common.GetEnvString("AGENT_SSL_CERT", "")
|
||||
Runtime = agent.ContainerRuntime(common.GetEnvString("RUNTIME", "docker"))
|
||||
AgentCACert = env.GetEnvString("AGENT_CA_CERT", "")
|
||||
AgentSSLCert = env.GetEnvString("AGENT_SSL_CERT", "")
|
||||
Runtime = agent.ContainerRuntime(env.GetEnvString("RUNTIME", "docker"))
|
||||
|
||||
switch Runtime {
|
||||
case agent.ContainerRuntimeDocker, agent.ContainerRuntimePodman: //, agent.ContainerRuntimeNerdctl:
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
var defaultHealthConfig = types.DefaultHealthConfig()
|
||||
|
||||
func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
scheme := query.Get("scheme")
|
||||
@@ -22,8 +21,10 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var result *types.HealthCheckResult
|
||||
var err error
|
||||
var (
|
||||
result types.HealthCheckResult
|
||||
err error
|
||||
)
|
||||
switch scheme {
|
||||
case "fileserver":
|
||||
path := query.Get("path")
|
||||
@@ -32,7 +33,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
_, err := os.Stat(path)
|
||||
result = &types.HealthCheckResult{Healthy: err == nil}
|
||||
result = types.HealthCheckResult{Healthy: err == nil}
|
||||
if err != nil {
|
||||
result.Detail = err.Error()
|
||||
}
|
||||
@@ -47,7 +48,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
Path: path,
|
||||
}, defaultHealthConfig).CheckHealth()
|
||||
}, healthCheckConfigFromRequest(r)).CheckHealth()
|
||||
case "tcp", "udp":
|
||||
host := query.Get("host")
|
||||
if host == "" {
|
||||
@@ -66,7 +67,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
result, err = monitor.NewRawHealthMonitor(&url.URL{
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
}, defaultHealthConfig).CheckHealth()
|
||||
}, healthCheckConfigFromRequest(r)).CheckHealth()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -76,5 +77,15 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(result)
|
||||
sonic.ConfigDefault.NewEncoder(w).Encode(result)
|
||||
}
|
||||
|
||||
func healthCheckConfigFromRequest(r *http.Request) types.HealthCheckConfig {
|
||||
// we only need timeout and base context because it's one shot request
|
||||
return types.HealthCheckConfig{
|
||||
Timeout: types.HealthCheckTimeoutDefault,
|
||||
BaseContext: func() context.Context {
|
||||
return r.Context()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/handler"
|
||||
"github.com/yusing/go-proxy/internal/types"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/handler"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
)
|
||||
|
||||
func TestCheckHealthHTTP(t *testing.T) {
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
socketproxy "github.com/yusing/go-proxy/socketproxy/pkg"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/env"
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
socketproxy "github.com/yusing/godoxy/socketproxy/pkg"
|
||||
"github.com/yusing/goutils/version"
|
||||
)
|
||||
|
||||
type ServeMux struct{ *http.ServeMux }
|
||||
@@ -45,7 +45,7 @@ func NewAgentHandler() http.Handler {
|
||||
|
||||
mux.HandleFunc(agent.EndpointProxyHTTP+"/{path...}", ProxyHTTP)
|
||||
mux.HandleEndpoint("GET", agent.EndpointVersion, func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, pkg.GetVersion())
|
||||
fmt.Fprint(w, version.Get())
|
||||
})
|
||||
mux.HandleEndpoint("GET", agent.EndpointName, func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, env.AgentName)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/agentproxy"
|
||||
)
|
||||
|
||||
func NewTransport() *http.Transport {
|
||||
@@ -24,31 +23,24 @@ func NewTransport() *http.Transport {
|
||||
}
|
||||
|
||||
func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
host := r.Header.Get(agentproxy.HeaderXProxyHost)
|
||||
isHTTPS, _ := strconv.ParseBool(r.Header.Get(agentproxy.HeaderXProxyHTTPS))
|
||||
skipTLSVerify, _ := strconv.ParseBool(r.Header.Get(agentproxy.HeaderXProxySkipTLSVerify))
|
||||
responseHeaderTimeout, err := strconv.Atoi(r.Header.Get(agentproxy.HeaderXProxyResponseHeaderTimeout))
|
||||
cfg, err := agentproxy.ConfigFromHeaders(r.Header)
|
||||
if err != nil {
|
||||
responseHeaderTimeout = 0
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
http.Error(w, "missing required headers", http.StatusBadRequest)
|
||||
http.Error(w, fmt.Sprintf("failed to parse agent proxy config: %s", err.Error()), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
scheme := "http"
|
||||
if isHTTPS {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
transport := NewTransport()
|
||||
if skipTLSVerify {
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
if cfg.ResponseHeaderTimeout > 0 {
|
||||
transport.ResponseHeaderTimeout = cfg.ResponseHeaderTimeout
|
||||
}
|
||||
if cfg.DisableCompression {
|
||||
transport.DisableCompression = true
|
||||
}
|
||||
|
||||
if responseHeaderTimeout > 0 {
|
||||
transport.ResponseHeaderTimeout = time.Duration(responseHeaderTimeout) * time.Second
|
||||
transport.TLSClientConfig, err = cfg.BuildTLSConfig(r.URL)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to build TLS client config: %s", err.Error()), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
r.URL.Scheme = ""
|
||||
@@ -58,8 +50,8 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
rp := &httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {
|
||||
r.URL.Scheme = scheme
|
||||
r.URL.Host = host
|
||||
r.URL.Scheme = cfg.Scheme
|
||||
r.URL.Host = cfg.Host
|
||||
},
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||
"github.com/yusing/go-proxy/agent/pkg/handler"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/server"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/godoxy/agent/pkg/env"
|
||||
"github.com/yusing/godoxy/agent/pkg/handler"
|
||||
"github.com/yusing/goutils/server"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
@@ -39,5 +39,5 @@ func StartAgentServer(parent task.Parent, opt Options) {
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
server.Start(parent, agentServer, nil, &log.Logger)
|
||||
server.Start(parent.Subtask("agent-server", false), agentServer, server.WithLogger(&log.Logger))
|
||||
}
|
||||
|
||||
48
cmd/main.go
48
cmd/main.go
@@ -5,19 +5,22 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/config"
|
||||
"github.com/yusing/go-proxy/internal/dnsproviders"
|
||||
"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/memlogger"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
"github.com/yusing/godoxy/internal/api"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/config"
|
||||
"github.com/yusing/godoxy/internal/dnsproviders"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/logging"
|
||||
"github.com/yusing/godoxy/internal/logging/memlogger"
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/godoxy/internal/metrics/uptime"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/server"
|
||||
"github.com/yusing/goutils/task"
|
||||
"github.com/yusing/goutils/version"
|
||||
)
|
||||
|
||||
func parallel(fns ...func()) {
|
||||
@@ -32,7 +35,7 @@ func main() {
|
||||
initProfiling()
|
||||
|
||||
logging.InitLogger(os.Stderr, memlogger.GetMemLogger())
|
||||
log.Info().Msgf("GoDoxy version %s", pkg.GetVersion())
|
||||
log.Info().Msgf("GoDoxy version %s", version.Get())
|
||||
log.Trace().Msg("trace enabled")
|
||||
parallel(
|
||||
dnsproviders.InitProviders,
|
||||
@@ -50,26 +53,29 @@ func main() {
|
||||
prepareDirectory(dir)
|
||||
}
|
||||
|
||||
cfg, err := config.Load()
|
||||
err := config.Load()
|
||||
if err != nil {
|
||||
gperr.LogWarn("errors in config", err)
|
||||
}
|
||||
|
||||
cfg.Start(&config.StartServersOptions{
|
||||
Proxy: true,
|
||||
})
|
||||
config.StartProxyServers()
|
||||
|
||||
if err := auth.Initialize(); err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to initialize authentication")
|
||||
}
|
||||
rules.InitAuthHandler(auth.AuthOrProceed)
|
||||
|
||||
// API Handler needs to start after auth is initialized.
|
||||
cfg.StartServers(&config.StartServersOptions{
|
||||
API: true,
|
||||
server.StartServer(task.RootTask("api_server", false), server.Options{
|
||||
Name: "api",
|
||||
HTTPAddr: common.APIHTTPAddr,
|
||||
Handler: api.NewHandler(),
|
||||
})
|
||||
|
||||
uptime.Poller.Start()
|
||||
config.WatchChanges()
|
||||
|
||||
task.WaitExit(cfg.Value().TimeoutShutdown)
|
||||
task.WaitExit(config.Value().TimeoutShutdown)
|
||||
}
|
||||
|
||||
func prepareDirectory(dir string) {
|
||||
|
||||
@@ -10,16 +10,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
const mb = 1024 * 1024
|
||||
|
||||
func initProfiling() {
|
||||
debug.SetGCPercent(-1)
|
||||
debug.SetMemoryLimit(50 * mb)
|
||||
debug.SetMaxStack(4 * mb)
|
||||
|
||||
go func() {
|
||||
log.Info().Msgf("pprof server started at http://localhost:7777/debug/pprof/")
|
||||
log.Error().Err(http.ListenAndServe(":7777", nil)).Msg("pprof server failed")
|
||||
@@ -27,9 +21,14 @@ func initProfiling() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Second * 10)
|
||||
defer ticker.Stop()
|
||||
|
||||
var m runtime.MemStats
|
||||
var gcStats debug.GCStats
|
||||
|
||||
for range ticker.C {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
debug.ReadGCStats(&gcStats)
|
||||
|
||||
log.Info().Msgf("-----------------------------------------------------")
|
||||
log.Info().Msgf("Timestamp: %s", time.Now().Format(time.RFC3339))
|
||||
log.Info().Msgf(" Go Heap - In Use (Alloc/HeapAlloc): %s", strutils.FormatByteSize(m.Alloc))
|
||||
@@ -37,8 +36,12 @@ func initProfiling() {
|
||||
log.Info().Msgf(" Go Stacks - In Use (StackInuse): %s", strutils.FormatByteSize(m.StackInuse))
|
||||
log.Info().Msgf(" Go Runtime - Other Sys (MSpanInuse, MCacheInuse, BuckHashSys, GCSys, OtherSys): %s", strutils.FormatByteSize(m.MSpanInuse+m.MCacheInuse+m.BuckHashSys+m.GCSys+m.OtherSys))
|
||||
log.Info().Msgf(" Go Runtime - Total from OS (Sys): %s", strutils.FormatByteSize(m.Sys))
|
||||
log.Info().Msgf(" Go Runtime - Freed from OS (HeapReleased): %s", strutils.FormatByteSize(m.HeapReleased))
|
||||
log.Info().Msgf(" Number of Goroutines: %d", runtime.NumGoroutine())
|
||||
log.Info().Msgf(" Number of GCs: %d", m.NumGC)
|
||||
log.Info().Msgf(" Number of completed GC cycles: %d", m.NumGC)
|
||||
log.Info().Msgf(" Number of GCs: %d", gcStats.NumGC)
|
||||
log.Info().Msgf(" Total GC time: %s", gcStats.PauseTotal)
|
||||
log.Info().Msgf(" Last GC time: %s", gcStats.LastGC.Format(time.DateTime))
|
||||
log.Info().Msg("-----------------------------------------------------")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -22,26 +22,28 @@ services:
|
||||
- ${SOCKET_PROXY_LISTEN_ADDR:-127.0.0.1:2375}:2375
|
||||
frontend:
|
||||
image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}
|
||||
# lite variant
|
||||
# image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}-lite
|
||||
container_name: godoxy-frontend
|
||||
restart: unless-stopped
|
||||
network_mode: host # do not change this
|
||||
env_file: .env
|
||||
# comment out `user` for lite variant
|
||||
user: ${GODOXY_UID:-1000}:${GODOXY_GID:-1000}
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /app/.next/cache # next image caching
|
||||
|
||||
# for lite variant, do not change uid/gid
|
||||
# - /var/cache/nginx:uid=101,gid=101
|
||||
# - /run:uid=101,gid=101
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- all
|
||||
depends_on:
|
||||
- app
|
||||
environment:
|
||||
HOSTNAME: 127.0.0.1
|
||||
PORT: ${GODOXY_FRONTEND_PORT:-3000}
|
||||
labels:
|
||||
proxy.aliases: ${GODOXY_FRONTEND_ALIASES:-godoxy}
|
||||
proxy.#1.port: ${GODOXY_FRONTEND_PORT:-3000}
|
||||
# proxy.#1.middlewares.cidr_whitelist: |
|
||||
# status: 403
|
||||
# message: IP not allowed
|
||||
@@ -74,10 +76,9 @@ services:
|
||||
- ./error_pages:/app/error_pages:ro
|
||||
- ./data:/app/data
|
||||
|
||||
# To use autocert, certs will be stored in "./certs".
|
||||
# You can also use a docker volume to store it
|
||||
# This path stores certs obtained from autocert and agent TLS client certs
|
||||
- ./certs:/app/certs
|
||||
|
||||
# remove "./certs:/app/certs" and uncomment below to use existing certificate
|
||||
# mount existing certificate
|
||||
# - /path/to/certs/cert.crt:/app/certs/cert.crt
|
||||
# - /path/to/certs/priv.key:/app/certs/priv.key
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
# autocert:
|
||||
# provider: local
|
||||
# cert_path: /path/to/cert.crt # default: /app/certs/cert.crt
|
||||
# key_path: /path/to/priv.key # default: /app/certs/priv.key
|
||||
|
||||
# 2. cloudflare
|
||||
# autocert:
|
||||
@@ -17,6 +19,10 @@
|
||||
|
||||
# 3. other providers, see https://docs.godoxy.dev/DNS-01-Providers
|
||||
|
||||
# Access Control
|
||||
# When enabled, it will be applied globally at connection level,
|
||||
# all incoming connections (web, tcp and udp) will be checked against the ACL rules.
|
||||
|
||||
# acl:
|
||||
# default: allow # or deny (default: allow)
|
||||
# allow_local: true # or false (default: true)
|
||||
@@ -31,12 +37,21 @@
|
||||
# - country:US
|
||||
# - timezone:Asia/Shanghai
|
||||
# log: # warning: logging ACL can be slow based on the number of incoming connections and configured rules
|
||||
# buffer_size: 65536 # (default: 64KB)
|
||||
# path: /app/logs/acl.log # (default: none)
|
||||
# stdout: false # (default: false)
|
||||
# keep: last 10 # (default: none)
|
||||
# keep: 30 days # (default: 30 days)
|
||||
# log_allowed: false # (default: false)
|
||||
# notify:
|
||||
# interval: 1m # (default: 1m)
|
||||
# to: [gotify, discord] # names under providers.notification
|
||||
# include_allowed: false # (default: false)
|
||||
|
||||
entrypoint:
|
||||
# Proxy Protocol: https://www.haproxy.com/blog/use-the-proxy-protocol-to-preserve-a-clients-ip-address
|
||||
# When set to true, web entrypoint and all tcp routes will be wrapped with Proxy Protocol listener in order to preserve the client's IP address.
|
||||
# Note that HTTP/3 with proxy protocol is not supported yet.
|
||||
support_proxy_protocol: false
|
||||
|
||||
# Below define an example of middleware config
|
||||
# 1. set security headers
|
||||
# 2. block non local IP connections
|
||||
@@ -57,20 +72,27 @@ entrypoint:
|
||||
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
|
||||
access_log:
|
||||
format: combined
|
||||
path: /app/logs/entrypoint.log
|
||||
stdout: false # (default: false)
|
||||
keep: 30 days # (default: 30 days)
|
||||
|
||||
# customize behavior for non-existent routes, e.g. pass over to another proxy
|
||||
#
|
||||
# rules:
|
||||
# not_found:
|
||||
# - name: default
|
||||
# do: proxy http://other-proxy:8080
|
||||
|
||||
defaults:
|
||||
healthcheck:
|
||||
interval: 5s
|
||||
timeout: 15s
|
||||
retries: 3
|
||||
|
||||
providers:
|
||||
# include files are standalone yaml files under `config/` directory
|
||||
@@ -95,9 +117,13 @@ providers:
|
||||
# remote-1: tcp://10.0.2.1:2375
|
||||
# remote-2: ssh://root:1234@10.0.2.2
|
||||
|
||||
# notification providers (notify when service health changes)
|
||||
# notification providers
|
||||
#
|
||||
# notification:
|
||||
# - name: ntfy
|
||||
# provider: ntfy
|
||||
# url: https://ntfy.domain.tld
|
||||
# topic: godoxy
|
||||
# - name: gotify
|
||||
# provider: gotify
|
||||
# url: https://gotify.domain.tld
|
||||
@@ -106,6 +132,11 @@ providers:
|
||||
# provider: webhook
|
||||
# url: https://discord.com/api/webhooks/...
|
||||
# template: discord # this means use payload template from internal/notif/templates/discord.json
|
||||
# - name: pushover
|
||||
# provider: webhook
|
||||
# url: https://api.pushover.net/1/messages.json
|
||||
# mime_type: application/x-www-form-urlencoded
|
||||
# payload: '{"token": "your-app-token", "user": "your-user-key", "title": $title, "message": $message}'
|
||||
|
||||
# Proxmox providers (for idlesleep support for proxmox LXCs)
|
||||
#
|
||||
@@ -115,8 +146,8 @@ providers:
|
||||
# secret: aaaa-bbbb-cccc-dddd
|
||||
# no_tls_verify: true
|
||||
|
||||
# Check https://docs.godoxy.dev/Certificates-and-domain-matching
|
||||
# for explaination of `match_domains`
|
||||
# Match domains
|
||||
# See https://docs.godoxy.dev/Certificates-and-domain-matching
|
||||
#
|
||||
# match_domains:
|
||||
# - my.site
|
||||
|
||||
@@ -1,33 +1,7 @@
|
||||
# Stage 1: deps
|
||||
FROM golang:1.25.0-alpine AS deps
|
||||
HEALTHCHECK NONE
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# package version does not matter
|
||||
# trunk-ignore(hadolint/DL3018)
|
||||
RUN apk add --no-cache tzdata make libcap-setcap
|
||||
|
||||
# Stage 3: Final image
|
||||
FROM alpine:3.22
|
||||
|
||||
LABEL maintainer="yusing@6uo.me"
|
||||
LABEL proxy.exclude=1
|
||||
|
||||
# copy timezone data
|
||||
COPY --from=deps /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
|
||||
# copy certs
|
||||
COPY --from=deps /etc/ssl/certs /etc/ssl/certs
|
||||
|
||||
ARG TARGET
|
||||
ENV TARGET=${TARGET}
|
||||
|
||||
ENV DOCKER_HOST=unix:///var/run/docker.sock
|
||||
|
||||
# copy binary
|
||||
COPY bin/${TARGET} /app/run
|
||||
RUN apt-get update && apt-get install -y ca-certificates
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN chown -R 1000:1000 /app
|
||||
|
||||
CMD ["/app/run"]
|
||||
CMD ["/app/run"]
|
||||
|
||||
@@ -4,12 +4,11 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: dev.Dockerfile
|
||||
args:
|
||||
- TARGET=godoxy
|
||||
container_name: godoxy-proxy-dev
|
||||
restart: unless-stopped
|
||||
env_file: dev.env
|
||||
environment:
|
||||
DOCKER_HOST: unix:///var/run/docker.sock
|
||||
TZ: Asia/Hong_Kong
|
||||
API_ADDR: 127.0.0.1:8999
|
||||
API_USER: dev
|
||||
@@ -17,13 +16,14 @@ services:
|
||||
API_SKIP_ORIGIN_CHECK: true
|
||||
API_JWT_TTL: 24h
|
||||
DEBUG: true
|
||||
API_SECRET: 1234567891234567
|
||||
API_JWT_SECRET: 1234567891234567
|
||||
labels:
|
||||
proxy.exclude: true
|
||||
proxy.#1.healthcheck.disable: true
|
||||
ipc: host
|
||||
network_mode: host
|
||||
volumes:
|
||||
- ./bin/godoxy:/app/run:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./dev-data/config:/app/config
|
||||
- ./dev-data/certs:/app/certs
|
||||
@@ -31,6 +31,19 @@ services:
|
||||
- ./dev-data/data:/app/data
|
||||
- ./dev-data/logs:/app/logs
|
||||
- ~/certs/myCA.pem:/etc/ssl/certs/ca.crt:ro
|
||||
parca:
|
||||
image: ghcr.io/parca-dev/parca:v0.24.2
|
||||
container_name: godoxy-parca
|
||||
restart: unless-stopped
|
||||
command: [/parca, --config-path, /parca.yaml]
|
||||
network_mode: host
|
||||
# ports:
|
||||
# - 7070:7070
|
||||
configs:
|
||||
- source: parca
|
||||
target: /parca.yaml
|
||||
labels:
|
||||
proxy.#1.port: "7070"
|
||||
tinyauth:
|
||||
image: ghcr.io/steveiliop56/tinyauth:v3
|
||||
container_name: tinyauth
|
||||
@@ -41,3 +54,16 @@ services:
|
||||
- USERS=user:$$2a$$10$$UdLYoJ5lgPsC0RKqYH/jMua7zIn0g9kPqWmhYayJYLaZQ/FTmH2/u # user:password
|
||||
labels:
|
||||
proxy.tinyauth.port: "3000"
|
||||
configs:
|
||||
parca:
|
||||
content: |
|
||||
object_storage:
|
||||
bucket:
|
||||
type: "FILESYSTEM"
|
||||
config:
|
||||
directory: "./data"
|
||||
scrape_configs:
|
||||
- job_name: "parca"
|
||||
scrape_interval: "1s"
|
||||
static_configs:
|
||||
- targets: [ 'localhost:7777' ]
|
||||
|
||||
260
go.mod
260
go.mod
@@ -1,254 +1,182 @@
|
||||
module github.com/yusing/go-proxy
|
||||
module github.com/yusing/godoxy
|
||||
|
||||
go 1.25.1
|
||||
go 1.25.5
|
||||
|
||||
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/utils => ./internal/utils
|
||||
|
||||
replace github.com/coreos/go-oidc/v3 => github.com/godoxy-app/go-oidc/v3 v3.0.0-20250816044348-0630187cb14b
|
||||
|
||||
replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250816043325-ee003f88b84d
|
||||
replace (
|
||||
github.com/coreos/go-oidc/v3 => ./internal/go-oidc
|
||||
github.com/shirou/gopsutil/v4 => ./internal/gopsutil
|
||||
github.com/yusing/godoxy/agent => ./agent
|
||||
github.com/yusing/godoxy/internal/dnsproviders => ./internal/dnsproviders
|
||||
github.com/yusing/goutils => ./goutils
|
||||
github.com/yusing/goutils/http/reverseproxy => ./goutils/http/reverseproxy
|
||||
github.com/yusing/goutils/http/websocket => ./goutils/http/websocket
|
||||
github.com/yusing/goutils/server => ./goutils/server
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // parsing HTML for extract fav icon
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 // oidc authentication
|
||||
github.com/docker/docker v28.4.0+incompatible // docker daemon
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // parsing HTML for extract fav icon
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 // oidc authentication
|
||||
github.com/fsnotify/fsnotify v1.9.0 // file watcher
|
||||
github.com/go-acme/lego/v4 v4.26.0 // acme client
|
||||
github.com/go-playground/validator/v10 v10.27.0 // validator
|
||||
github.com/gin-gonic/gin v1.11.0 // api server
|
||||
github.com/go-acme/lego/v4 v4.29.0 // acme client
|
||||
github.com/go-playground/validator/v10 v10.28.0 // validator
|
||||
github.com/gobwas/glob v0.2.3 // glob matcher for route rules
|
||||
github.com/gorilla/websocket v1.5.3 // websocket for API and agent
|
||||
github.com/gotify/server/v2 v2.7.2 // reference the Message struct for json response
|
||||
github.com/gotify/server/v2 v2.7.3 // reference the Message struct for json response
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // fuzzy search for searching icons and filtering metrics
|
||||
github.com/puzpuzpuz/xsync/v4 v4.1.0 // lock free map for concurrent operations
|
||||
github.com/pires/go-proxyproto v0.8.1 // proxy protocol support
|
||||
github.com/puzpuzpuz/xsync/v4 v4.2.0 // lock free map for concurrent operations
|
||||
github.com/rs/zerolog v1.34.0 // logging
|
||||
github.com/shirou/gopsutil/v4 v4.25.8 // system info metrics
|
||||
github.com/vincent-petithory/dataurl v1.0.0 // data url for fav icon
|
||||
golang.org/x/crypto v0.42.0 // encrypting password with bcrypt
|
||||
golang.org/x/net v0.44.0 // HTTP header utilities
|
||||
golang.org/x/oauth2 v0.31.0 // oauth2 authentication
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/time v0.13.0 // time utilities
|
||||
golang.org/x/crypto v0.45.0 // encrypting password with bcrypt
|
||||
golang.org/x/net v0.47.0 // HTTP header utilities
|
||||
golang.org/x/oauth2 v0.33.0 // oauth2 authentication
|
||||
golang.org/x/sync v0.18.0
|
||||
golang.org/x/time v0.14.0 // time utilities
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/docker/cli v28.4.0+incompatible
|
||||
github.com/goccy/go-yaml v1.18.0 // yaml parsing for different config files
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/luthermonson/go-proxmox v0.2.3
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/samber/slog-zerolog/v2 v2.7.3
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/yusing/go-proxy/agent v0.0.0-20250913143824-493c0afdface
|
||||
github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-20250913143824-493c0afdface
|
||||
github.com/yusing/go-proxy/internal/utils v0.0.0
|
||||
github.com/bytedance/gopkg v0.1.3 // xxhash64 for fast hash
|
||||
github.com/bytedance/sonic v1.14.2 // fast json parsing
|
||||
github.com/docker/cli v29.1.2+incompatible // needs docker/cli/cli/connhelper connection helper for docker client
|
||||
github.com/goccy/go-yaml v1.19.0 // yaml parsing for different config files
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // jwt authentication
|
||||
github.com/luthermonson/go-proxmox v0.2.3 // proxmox API client
|
||||
github.com/moby/moby/api v1.52.0 // docker API
|
||||
github.com/moby/moby/client v0.2.1 // docker client
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // maxminddb for geoip database
|
||||
github.com/quic-go/quic-go v0.57.1 // http3 support
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 // system information
|
||||
github.com/spf13/afero v1.15.0 // afero for file system operations
|
||||
github.com/stretchr/testify v1.11.1 // testing framework
|
||||
github.com/valyala/fasthttp v1.68.0 // fast http for health check
|
||||
github.com/yusing/ds v0.3.1 // data structures and algorithms
|
||||
github.com/yusing/godoxy/agent v0.0.0-20251204100826-e3fe126a5c12
|
||||
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20251204100826-e3fe126a5c12
|
||||
github.com/yusing/gointernals v0.1.16
|
||||
github.com/yusing/goutils v0.7.0
|
||||
github.com/yusing/goutils/http/reverseproxy v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils/http/websocket v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/goutils/server v0.0.0-20250927113415-dd977d2edaeb
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.48.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.58.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect
|
||||
github.com/aws/smithy-go v1.23.0 // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/diskfs/go-diskfs v1.7.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/exoscale/egoscale/v3 v3.1.26 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gofrs/flock v0.13.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/linode/linodego v1.57.0 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 // indirect
|
||||
github.com/magefile/mage v1.15.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.68 // indirect
|
||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea // indirect
|
||||
github.com/nrdcg/desec v0.11.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.3.0 // indirect
|
||||
github.com/nrdcg/goacmedns v0.2.0 // indirect
|
||||
github.com/nrdcg/goinwx v0.11.0 // indirect
|
||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/ovh/go-ovh v1.9.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/peterhellberg/link v1.2.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/pquerna/otp v1.5.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
|
||||
github.com/sacloud/api-client-go v0.3.3 // indirect
|
||||
github.com/sacloud/go-http v0.1.9 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.17.1 // indirect
|
||||
github.com/sacloud/packages-go v0.0.11 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/samber/slog-common v0.19.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.34 // indirect
|
||||
github.com/selectel/domains-go v1.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.2.1 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/sony/gobreaker v1.0.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419 // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.219 // indirect
|
||||
github.com/vultr/govultr/v3 v3.23.0 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.1 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/api v0.249.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
google.golang.org/api v0.257.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.15.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/yusing/ds v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect
|
||||
github.com/aziontech/azionapi-go-sdk v0.142.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.1 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/dnsimple/dnsimple-go/v4 v4.0.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.17.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/namedotcom/go/v4 v4.0.2 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.100.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.100.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.25.1 // indirect
|
||||
github.com/onsi/gomega v1.38.1 // indirect
|
||||
github.com/linode/linodego v1.61.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.0 // indirect
|
||||
github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/selectel/go-selvpcclient/v4 v4.1.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.21.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vultr/govultr/v3 v3.25.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||
)
|
||||
|
||||
1
goutils
Submodule
1
goutils
Submodule
Submodule goutils added at c81cc80244
@@ -1,17 +1,22 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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/utils"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/maxmind"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -21,16 +26,42 @@ type Config struct {
|
||||
Deny Matchers `json:"deny"`
|
||||
Log *accesslog.ACLLoggerConfig `json:"log"`
|
||||
|
||||
Notify struct {
|
||||
To []string `json:"to"` // list of notification providers
|
||||
Interval time.Duration `json:"interval"` // interval between notifications
|
||||
IncludeAllowed *bool `json:"include_allowed"` // default: false
|
||||
} `json:"notify"`
|
||||
|
||||
config
|
||||
valErr gperr.Error
|
||||
}
|
||||
|
||||
const defaultNotifyInterval = 1 * time.Minute
|
||||
|
||||
type config struct {
|
||||
defaultAllow bool
|
||||
allowLocal bool
|
||||
ipCache *xsync.Map[string, *checkCache]
|
||||
logAllowed bool
|
||||
logger *accesslog.AccessLogger
|
||||
|
||||
// will be nil if Notify.To is empty
|
||||
// these are per IP, reset every Notify.Interval
|
||||
allowedCount map[string]uint32
|
||||
blockedCount map[string]uint32
|
||||
|
||||
// these are total, never reset
|
||||
totalAllowedCount uint64
|
||||
totalBlockedCount uint64
|
||||
|
||||
logAllowed bool
|
||||
// will be nil if Log is nil
|
||||
logger accesslog.AccessLogger
|
||||
|
||||
// will never tick if Notify.To is empty
|
||||
notifyTicker *time.Ticker
|
||||
notifyAllowed bool
|
||||
|
||||
// will be nil if both Log and Notify.To are empty
|
||||
logNotifyCh chan ipLog
|
||||
}
|
||||
|
||||
type checkCache struct {
|
||||
@@ -39,10 +70,18 @@ type checkCache struct {
|
||||
created time.Time
|
||||
}
|
||||
|
||||
type ipLog struct {
|
||||
info *maxmind.IPInfo
|
||||
allowed bool
|
||||
}
|
||||
|
||||
// could be nil
|
||||
var ActiveConfig atomic.Pointer[Config]
|
||||
|
||||
const cacheTTL = 1 * time.Minute
|
||||
|
||||
func (c *checkCache) Expired() bool {
|
||||
return c.created.Add(cacheTTL).Before(utils.TimeNow())
|
||||
return c.created.Add(cacheTTL).Before(time.Now())
|
||||
}
|
||||
|
||||
// TODO: add stats
|
||||
@@ -69,6 +108,10 @@ func (c *Config) Validate() gperr.Error {
|
||||
c.allowLocal = true
|
||||
}
|
||||
|
||||
if c.Notify.Interval < 0 {
|
||||
c.Notify.Interval = defaultNotifyInterval
|
||||
}
|
||||
|
||||
if c.Log != nil {
|
||||
c.logAllowed = c.Log.LogAllowed
|
||||
}
|
||||
@@ -79,6 +122,12 @@ func (c *Config) Validate() gperr.Error {
|
||||
}
|
||||
|
||||
c.ipCache = xsync.NewMap[string, *checkCache]()
|
||||
|
||||
if c.Notify.IncludeAllowed != nil {
|
||||
c.notifyAllowed = *c.Notify.IncludeAllowed
|
||||
} else {
|
||||
c.notifyAllowed = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -86,7 +135,7 @@ func (c *Config) Valid() bool {
|
||||
return c != nil && c.valErr == nil
|
||||
}
|
||||
|
||||
func (c *Config) Start(parent *task.Task) gperr.Error {
|
||||
func (c *Config) Start(parent task.Parent) gperr.Error {
|
||||
if c.Log != nil {
|
||||
logger, err := accesslog.NewAccessLogger(parent, c.Log)
|
||||
if err != nil {
|
||||
@@ -97,6 +146,23 @@ func (c *Config) Start(parent *task.Task) gperr.Error {
|
||||
if c.valErr != nil {
|
||||
return c.valErr
|
||||
}
|
||||
|
||||
if c.needLogOrNotify() {
|
||||
c.logNotifyCh = make(chan ipLog, 100)
|
||||
}
|
||||
|
||||
if c.needNotify() {
|
||||
c.allowedCount = make(map[string]uint32)
|
||||
c.blockedCount = make(map[string]uint32)
|
||||
c.notifyTicker = time.NewTicker(c.Notify.Interval)
|
||||
} else {
|
||||
c.notifyTicker = time.NewTicker(time.Duration(math.MaxInt64)) // never tick
|
||||
}
|
||||
|
||||
if c.needLogOrNotify() {
|
||||
go c.logNotifyLoop(parent)
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("default", c.Default).
|
||||
Bool("allow_local", c.allowLocal).
|
||||
@@ -113,16 +179,93 @@ func (c *Config) cacheRecord(info *maxmind.IPInfo, allow bool) {
|
||||
c.ipCache.Store(info.Str, &checkCache{
|
||||
IPInfo: info,
|
||||
allow: allow,
|
||||
created: utils.TimeNow(),
|
||||
created: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *config) log(info *maxmind.IPInfo, allowed bool) {
|
||||
if c.logger == nil {
|
||||
return
|
||||
func (c *Config) needLogOrNotify() bool {
|
||||
return c.needLog() || c.needNotify()
|
||||
}
|
||||
|
||||
func (c *Config) needLog() bool {
|
||||
return c.logger != nil
|
||||
}
|
||||
|
||||
func (c *Config) needNotify() bool {
|
||||
return len(c.Notify.To) > 0
|
||||
}
|
||||
|
||||
func (c *Config) getCachedCity(ip string) string {
|
||||
record, ok := c.ipCache.Load(ip)
|
||||
if ok {
|
||||
if record.City != nil {
|
||||
if record.City.Country.IsoCode != "" {
|
||||
return record.City.Country.IsoCode
|
||||
}
|
||||
return record.City.Location.TimeZone
|
||||
}
|
||||
}
|
||||
if !allowed || c.logAllowed {
|
||||
c.logger.LogACL(info, !allowed)
|
||||
return "unknown location"
|
||||
}
|
||||
|
||||
func (c *Config) logNotifyLoop(parent task.Parent) {
|
||||
defer c.notifyTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-parent.Context().Done():
|
||||
return
|
||||
case log := <-c.logNotifyCh:
|
||||
if c.logger != nil {
|
||||
if !log.allowed || c.logAllowed {
|
||||
c.logger.LogACL(log.info, !log.allowed)
|
||||
}
|
||||
}
|
||||
if c.needNotify() {
|
||||
if log.allowed {
|
||||
if c.notifyAllowed {
|
||||
c.allowedCount[log.info.Str]++
|
||||
c.totalAllowedCount++
|
||||
}
|
||||
} else {
|
||||
c.blockedCount[log.info.Str]++
|
||||
c.totalBlockedCount++
|
||||
}
|
||||
}
|
||||
case <-c.notifyTicker.C: // will never tick when notify is disabled
|
||||
total := len(c.allowedCount) + len(c.blockedCount)
|
||||
if total == 0 {
|
||||
continue
|
||||
}
|
||||
total++
|
||||
fieldsBody := make(notif.ListBody, total)
|
||||
i := 0
|
||||
fieldsBody[i] = fmt.Sprintf("Total: allowed %d, blocked %d", c.totalAllowedCount, c.totalBlockedCount)
|
||||
i++
|
||||
for ip, count := range c.allowedCount {
|
||||
fieldsBody[i] = fmt.Sprintf("%s (%s): allowed %d times", ip, c.getCachedCity(ip), count)
|
||||
i++
|
||||
}
|
||||
for ip, count := range c.blockedCount {
|
||||
fieldsBody[i] = fmt.Sprintf("%s (%s): blocked %d times", ip, c.getCachedCity(ip), count)
|
||||
i++
|
||||
}
|
||||
notif.Notify(¬if.LogMessage{
|
||||
Level: zerolog.InfoLevel,
|
||||
Title: "ACL Summary for last " + strutils.FormatDuration(c.Notify.Interval),
|
||||
Body: fieldsBody,
|
||||
To: c.Notify.To,
|
||||
})
|
||||
clear(c.allowedCount)
|
||||
clear(c.blockedCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log and notify if needed
|
||||
func (c *Config) logAndNotify(info *maxmind.IPInfo, allowed bool) {
|
||||
if c.logNotifyCh != nil {
|
||||
c.logNotifyCh <- ipLog{info: info, allowed: allowed}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,30 +280,30 @@ func (c *Config) IPAllowed(ip net.IP) bool {
|
||||
}
|
||||
|
||||
if c.allowLocal && ip.IsPrivate() {
|
||||
c.log(&maxmind.IPInfo{IP: ip, Str: ip.String()}, true)
|
||||
c.logAndNotify(&maxmind.IPInfo{IP: ip, Str: ip.String()}, true)
|
||||
return true
|
||||
}
|
||||
|
||||
ipStr := ip.String()
|
||||
record, ok := c.ipCache.Load(ipStr)
|
||||
if ok && !record.Expired() {
|
||||
c.log(record.IPInfo, record.allow)
|
||||
c.logAndNotify(record.IPInfo, record.allow)
|
||||
return record.allow
|
||||
}
|
||||
|
||||
ipAndStr := &maxmind.IPInfo{IP: ip, Str: ipStr}
|
||||
if c.Allow.Match(ipAndStr) {
|
||||
c.log(ipAndStr, true)
|
||||
c.logAndNotify(ipAndStr, true)
|
||||
c.cacheRecord(ipAndStr, true)
|
||||
return true
|
||||
}
|
||||
if c.Deny.Match(ipAndStr) {
|
||||
c.log(ipAndStr, false)
|
||||
c.logAndNotify(ipAndStr, false)
|
||||
c.cacheRecord(ipAndStr, false)
|
||||
return false
|
||||
}
|
||||
|
||||
c.log(ipAndStr, c.defaultAllow)
|
||||
c.logAndNotify(ipAndStr, c.defaultAllow)
|
||||
c.cacheRecord(ipAndStr, c.defaultAllow)
|
||||
return c.defaultAllow
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/maxmind"
|
||||
"github.com/yusing/godoxy/internal/maxmind"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type MatcherFunc func(*maxmind.IPInfo) bool
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
maxmind "github.com/yusing/go-proxy/internal/maxmind/types"
|
||||
"github.com/yusing/go-proxy/internal/serialization"
|
||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
)
|
||||
|
||||
func TestMatchers(t *testing.T) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
@@ -54,6 +55,21 @@ func (s *TCPListener) Accept() (net.Conn, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type tcpListener interface {
|
||||
SetDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
var _ tcpListener = (*net.TCPListener)(nil)
|
||||
|
||||
func (s *TCPListener) SetDeadline(t time.Time) error {
|
||||
switch lis := s.lis.(type) {
|
||||
case tcpListener:
|
||||
return lis.SetDeadline(t)
|
||||
default:
|
||||
return errors.New("not a TCPListener")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TCPListener) Close() error {
|
||||
return s.lis.Close()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
@@ -74,6 +75,31 @@ func (s *UDPListener) SetWriteDeadline(t time.Time) error {
|
||||
return s.lis.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type udpListener interface {
|
||||
SetReadBuffer(bytes int) error
|
||||
SetWriteBuffer(bytes int) error
|
||||
}
|
||||
|
||||
var _ udpListener = (*net.UDPConn)(nil)
|
||||
|
||||
func (s *UDPListener) SetReadBuffer(bytes int) error {
|
||||
switch lis := s.lis.(type) {
|
||||
case udpListener:
|
||||
return lis.SetReadBuffer(bytes)
|
||||
default:
|
||||
return errors.New("not a UDPConn")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UDPListener) SetWriteBuffer(bytes int) error {
|
||||
switch lis := s.lis.(type) {
|
||||
case udpListener:
|
||||
return lis.SetWriteBuffer(bytes)
|
||||
default:
|
||||
return errors.New("not a UDPConn")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UDPListener) Close() error {
|
||||
return s.lis.Close()
|
||||
}
|
||||
|
||||
@@ -2,25 +2,25 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
"reflect"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/rs/zerolog/log"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
apiV1 "github.com/yusing/go-proxy/internal/api/v1"
|
||||
agentApi "github.com/yusing/go-proxy/internal/api/v1/agent"
|
||||
authApi "github.com/yusing/go-proxy/internal/api/v1/auth"
|
||||
certApi "github.com/yusing/go-proxy/internal/api/v1/cert"
|
||||
dockerApi "github.com/yusing/go-proxy/internal/api/v1/docker"
|
||||
fileApi "github.com/yusing/go-proxy/internal/api/v1/file"
|
||||
homepageApi "github.com/yusing/go-proxy/internal/api/v1/homepage"
|
||||
metricsApi "github.com/yusing/go-proxy/internal/api/v1/metrics"
|
||||
routeApi "github.com/yusing/go-proxy/internal/api/v1/route"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
apiV1 "github.com/yusing/godoxy/internal/api/v1"
|
||||
agentApi "github.com/yusing/godoxy/internal/api/v1/agent"
|
||||
authApi "github.com/yusing/godoxy/internal/api/v1/auth"
|
||||
certApi "github.com/yusing/godoxy/internal/api/v1/cert"
|
||||
dockerApi "github.com/yusing/godoxy/internal/api/v1/docker"
|
||||
fileApi "github.com/yusing/godoxy/internal/api/v1/file"
|
||||
homepageApi "github.com/yusing/godoxy/internal/api/v1/homepage"
|
||||
metricsApi "github.com/yusing/godoxy/internal/api/v1/metrics"
|
||||
routeApi "github.com/yusing/godoxy/internal/api/v1/route"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
// @title GoDoxy API
|
||||
@@ -45,6 +45,9 @@ func NewHandler() *gin.Engine {
|
||||
r := gin.New()
|
||||
r.Use(ErrorHandler())
|
||||
r.Use(ErrorLoggingMiddleware())
|
||||
r.Use(NoCache())
|
||||
|
||||
log.Debug().Msg("gin codec json.API: " + reflect.TypeOf(json.API).Name())
|
||||
|
||||
r.GET("/api/v1/version", apiV1.Version)
|
||||
|
||||
@@ -69,7 +72,7 @@ func NewHandler() *gin.Engine {
|
||||
}
|
||||
{
|
||||
// enable cache for favicon
|
||||
v1.GET("/favicon", apiV1.FavIcon).Use(Cache(time.Hour * 24))
|
||||
v1.GET("/favicon", apiV1.FavIcon)
|
||||
v1.GET("/health", apiV1.Health)
|
||||
v1.GET("/icons", apiV1.Icons)
|
||||
v1.POST("/reload", apiV1.Reload)
|
||||
@@ -81,6 +84,7 @@ func NewHandler() *gin.Engine {
|
||||
route.GET("/:which", routeApi.Route)
|
||||
route.GET("/providers", routeApi.Providers)
|
||||
route.GET("/by_provider", routeApi.ByProvider)
|
||||
route.POST("/playground", routeApi.Playground)
|
||||
}
|
||||
|
||||
file := v1.Group("/file")
|
||||
@@ -139,15 +143,13 @@ func NewHandler() *gin.Engine {
|
||||
}
|
||||
}
|
||||
|
||||
// disable cache by default
|
||||
r.Use(NoCache())
|
||||
return r
|
||||
}
|
||||
|
||||
func NoCache() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// skip cache if Cache-Control header is set or if caching is explicitly enabled
|
||||
if !c.GetBool("cache_enabled") && c.Writer.Header().Get("Cache-Control") == "" {
|
||||
// skip cache if Cache-Control header is set
|
||||
if c.Writer.Header().Get("Cache-Control") == "" {
|
||||
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
c.Header("Pragma", "no-cache")
|
||||
c.Header("Expires", "0")
|
||||
@@ -156,20 +158,6 @@ func NoCache() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func Cache(duration time.Duration) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Signal to NoCache middleware that caching is intended
|
||||
c.Set("cache_enabled", true)
|
||||
// skip cache if Cache-Control header is set
|
||||
if c.Writer.Header().Get("Cache-Control") == "" {
|
||||
c.Header("Cache-Control", "public, max-age="+strconv.FormatFloat(duration.Seconds(), 'f', 0, 64)+", immutable")
|
||||
c.Header("Pragma", "public")
|
||||
c.Header("Expires", time.Now().Add(duration).Format(time.RFC1123))
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
err := auth.GetDefaultAuth().CheckToken(c.Request)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package apitypes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
Message string `json:"message"`
|
||||
Error string `json:"error,omitempty" extensions:"x-nullable"`
|
||||
} // @name ErrorResponse
|
||||
|
||||
type serverError struct {
|
||||
Message string
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error returns a generic error response
|
||||
func Error(message string, err ...error) ErrorResponse {
|
||||
if len(err) > 0 {
|
||||
var gpErr gperr.Error
|
||||
if errors.As(err[0], &gpErr) {
|
||||
return ErrorResponse{
|
||||
Message: message,
|
||||
Error: string(gpErr.Plain()),
|
||||
}
|
||||
}
|
||||
return ErrorResponse{
|
||||
Message: message,
|
||||
Error: err[0].Error(),
|
||||
}
|
||||
}
|
||||
return ErrorResponse{
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func InternalServerError(err error, message string) error {
|
||||
return serverError{
|
||||
Message: message,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (e serverError) Error() string {
|
||||
if e.Err != nil {
|
||||
return e.Message + ": " + e.Err.Error()
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e serverError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package apitypes
|
||||
|
||||
type QueryOptions struct {
|
||||
Limit int `binding:"required,min=1,max=20" form:"limit"`
|
||||
Offset int `binding:"omitempty,min=0" form:"offset"`
|
||||
OrderBy QueryOrder `binding:"omitempty,oneof=created_at updated_at" form:"order_by"`
|
||||
Order QueryOrderDirection `binding:"omitempty,oneof=asc desc" form:"order"`
|
||||
}
|
||||
|
||||
type QueryOrder string
|
||||
|
||||
const (
|
||||
QueryOrderCreatedAt QueryOrder = "created_at"
|
||||
QueryOrderUpdatedAt QueryOrder = "updated_at"
|
||||
)
|
||||
|
||||
type QueryOrderDirection string
|
||||
|
||||
const (
|
||||
QueryOrderDirectionAsc QueryOrderDirection = "asc"
|
||||
QueryOrderDirectionDesc QueryOrderDirection = "desc"
|
||||
)
|
||||
|
||||
type QueryResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package apitypes
|
||||
|
||||
type SuccessResponse struct {
|
||||
Message string `json:"message"`
|
||||
Details map[string]any `json:"details,omitempty" extensions:"x-nullable"`
|
||||
} // @name SuccessResponse
|
||||
|
||||
func Success(message string, extra ...map[string]any) SuccessResponse {
|
||||
if len(extra) > 0 {
|
||||
return SuccessResponse{
|
||||
Message: message,
|
||||
Details: extra[0],
|
||||
}
|
||||
}
|
||||
return SuccessResponse{
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
)
|
||||
|
||||
type PEMPairResponse struct {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type NewAgentRequest struct {
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
// @x-id "list"
|
||||
@@ -19,7 +21,6 @@ import (
|
||||
// @Produce json
|
||||
// @Success 200 {array} Agent
|
||||
// @Failure 403 {object} apitypes.ErrorResponse
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /agent/list [get]
|
||||
func List(c *gin.Context) {
|
||||
if httpheaders.IsWebsocket(c.Request.Header) {
|
||||
|
||||
@@ -6,10 +6,12 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
. "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/certs"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/route/provider"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type VerifyNewAgentRequest struct {
|
||||
@@ -35,44 +37,78 @@ type VerifyNewAgentRequest struct {
|
||||
func Verify(c *gin.Context) {
|
||||
var request VerifyNewAgentRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid request", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
}
|
||||
|
||||
filename, ok := certs.AgentCertsFilepath(request.Host)
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid host", nil))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid host", nil))
|
||||
return
|
||||
}
|
||||
|
||||
ca, err := fromEncryptedPEMPairResponse(request.CA)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid CA", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid CA", err))
|
||||
return
|
||||
}
|
||||
|
||||
client, err := fromEncryptedPEMPairResponse(request.Client)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid client", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid client", err))
|
||||
return
|
||||
}
|
||||
|
||||
nRoutesAdded, err := config.GetInstance().VerifyNewAgent(request.Host, ca, client, request.ContainerRuntime)
|
||||
nRoutesAdded, err := verifyNewAgent(request.Host, ca, client, request.ContainerRuntime)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid request", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
}
|
||||
|
||||
zip, err := certs.ZipCert(ca.Cert, client.Cert, client.Key)
|
||||
if err != nil {
|
||||
c.Error(InternalServerError(err, "failed to zip certs"))
|
||||
c.Error(apitypes.InternalServerError(err, "failed to zip certs"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filename, zip, 0o600); err != nil {
|
||||
c.Error(InternalServerError(err, "failed to write certs"))
|
||||
c.Error(apitypes.InternalServerError(err, "failed to write certs"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, Success(fmt.Sprintf("Added %d routes", nRoutesAdded)))
|
||||
c.JSON(http.StatusOK, apitypes.Success(fmt.Sprintf("Added %d routes", nRoutesAdded)))
|
||||
}
|
||||
|
||||
func verifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair, containerRuntime agent.ContainerRuntime) (int, gperr.Error) {
|
||||
cfgState := config.ActiveState.Load()
|
||||
for _, a := range cfgState.Value().Providers.Agents {
|
||||
if a.Addr == host {
|
||||
return 0, gperr.New("agent already exists")
|
||||
}
|
||||
}
|
||||
|
||||
var agentCfg agent.AgentConfig
|
||||
agentCfg.Addr = host
|
||||
agentCfg.Runtime = containerRuntime
|
||||
|
||||
err := agentCfg.StartWithCerts(cfgState.Context(), ca.Cert, client.Cert, client.Key)
|
||||
if err != nil {
|
||||
return 0, gperr.Wrap(err, "failed to start agent")
|
||||
}
|
||||
|
||||
provider := provider.NewAgentProvider(&agentCfg)
|
||||
if _, loaded := cfgState.LoadOrStoreProvider(provider.String(), provider); loaded {
|
||||
return 0, gperr.Errorf("provider %s already exists", provider.String())
|
||||
}
|
||||
|
||||
// agent must be added before loading routes
|
||||
agent.AddAgent(&agentCfg)
|
||||
err = provider.LoadRoutes()
|
||||
if err != nil {
|
||||
cfgState.DeleteProvider(provider.String())
|
||||
agent.RemoveAgent(&agentCfg)
|
||||
return 0, gperr.Wrap(err, "failed to load routes")
|
||||
}
|
||||
|
||||
return provider.NumRoutes(), nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
)
|
||||
|
||||
// @x-id "callback"
|
||||
|
||||
@@ -2,7 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
)
|
||||
|
||||
// @x-id "check"
|
||||
|
||||
@@ -2,7 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
)
|
||||
|
||||
// @x-id "login"
|
||||
|
||||
@@ -2,7 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
)
|
||||
|
||||
// @x-id "logout"
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/autocert"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type CertInfo struct {
|
||||
@@ -29,7 +29,7 @@ type CertInfo struct {
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /cert/info [get]
|
||||
func Info(c *gin.Context) {
|
||||
autocert := config.GetInstance().AutoCertProvider()
|
||||
autocert := autocert.ActiveProvider.Load()
|
||||
if autocert == nil {
|
||||
c.JSON(http.StatusNotFound, apitypes.Error("autocert is not enabled"))
|
||||
return
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/godoxy/internal/autocert"
|
||||
"github.com/yusing/godoxy/internal/logging/memlogger"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
)
|
||||
|
||||
// @x-id "renew"
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /cert/renew [get]
|
||||
func Renew(c *gin.Context) {
|
||||
autocert := config.GetInstance().AutoCertProvider()
|
||||
autocert := autocert.ActiveProvider.Load()
|
||||
if autocert == nil {
|
||||
c.JSON(http.StatusNotFound, apitypes.Error("autocert is not enabled"))
|
||||
return
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
// @x-id "container"
|
||||
@@ -34,30 +35,30 @@ func GetContainer(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
client, err := docker.NewClient(dockerHost)
|
||||
dockerClient, err := docker.NewClient(dockerHost)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to create docker client"))
|
||||
return
|
||||
}
|
||||
|
||||
defer client.Close()
|
||||
defer dockerClient.Close()
|
||||
|
||||
cont, err := client.ContainerInspect(c.Request.Context(), id)
|
||||
cont, err := dockerClient.ContainerInspect(c.Request.Context(), id, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to inspect container"))
|
||||
return
|
||||
}
|
||||
|
||||
var state ContainerState
|
||||
if cont.State != nil {
|
||||
state = cont.State.Status
|
||||
if cont.Container.State != nil {
|
||||
state = cont.Container.State.Status
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &Container{
|
||||
Server: dockerHost,
|
||||
Name: cont.Name,
|
||||
ID: cont.ID,
|
||||
Image: cont.Image,
|
||||
Name: cont.Container.Name,
|
||||
ID: cont.Container.ID,
|
||||
Image: cont.Container.Image,
|
||||
State: state,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type ContainerState = container.ContainerState // @name ContainerState
|
||||
@@ -37,12 +40,12 @@ func GetContainers(ctx context.Context, dockerClients DockerClients) ([]Containe
|
||||
errs := gperr.NewBuilder("failed to get containers")
|
||||
containers := make([]Container, 0)
|
||||
for server, dockerClient := range dockerClients {
|
||||
conts, err := dockerClient.ContainerList(ctx, container.ListOptions{All: true})
|
||||
conts, err := dockerClient.ContainerList(ctx, client.ContainerListOptions{All: true})
|
||||
if err != nil {
|
||||
errs.Add(err)
|
||||
continue
|
||||
}
|
||||
for _, cont := range conts {
|
||||
for _, cont := range conts.Items {
|
||||
containers = append(containers, Container{
|
||||
Server: server,
|
||||
Name: cont.Names[0],
|
||||
|
||||
@@ -4,10 +4,13 @@ import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
dockerSystem "github.com/docker/docker/api/types/system"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
dockerSystem "github.com/moby/moby/api/types/system"
|
||||
"github.com/moby/moby/client"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type containerStats struct {
|
||||
@@ -62,13 +65,13 @@ func GetDockerInfo(ctx context.Context, dockerClients DockerClients) ([]dockerIn
|
||||
|
||||
i := 0
|
||||
for name, dockerClient := range dockerClients {
|
||||
info, err := dockerClient.Info(ctx)
|
||||
info, err := dockerClient.Info(ctx, client.InfoOptions{})
|
||||
if err != nil {
|
||||
errs.Add(err)
|
||||
continue
|
||||
}
|
||||
info.Name = name
|
||||
dockerInfos[i] = toDockerInfo(info)
|
||||
info.Info.Name = name
|
||||
dockerInfos[i] = toDockerInfo(info.Info)
|
||||
i++
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/api/pkg/stdcopy"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type LogsQueryParams struct {
|
||||
@@ -70,7 +70,7 @@ func Logs(c *gin.Context) {
|
||||
}
|
||||
defer dockerClient.Close()
|
||||
|
||||
opts := container.LogsOptions{
|
||||
opts := client.ContainerLogsOptions{
|
||||
ShowStdout: queryParams.Stdout,
|
||||
ShowStderr: queryParams.Stderr,
|
||||
Since: queryParams.Since,
|
||||
|
||||
@@ -4,17 +4,23 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type RestartRequest struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
client.ContainerRestartOptions
|
||||
}
|
||||
|
||||
// @x-id "restart"
|
||||
// @BasePath /api/v1
|
||||
// @Summary Restart container
|
||||
// @Description Restart container by container id
|
||||
// @Tags docker
|
||||
// @Produce json
|
||||
// @Param request body StopRequest true "Request"
|
||||
// @Param request body RestartRequest true "Request"
|
||||
// @Success 200 {object} apitypes.SuccessResponse
|
||||
// @Failure 400 {object} apitypes.ErrorResponse "Invalid request"
|
||||
// @Failure 403 {object} apitypes.ErrorResponse
|
||||
@@ -22,7 +28,7 @@ import (
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /docker/restart [post]
|
||||
func Restart(c *gin.Context) {
|
||||
var req StopRequest
|
||||
var req RestartRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
@@ -42,7 +48,7 @@ func Restart(c *gin.Context) {
|
||||
|
||||
defer client.Close()
|
||||
|
||||
err = client.ContainerRestart(c.Request.Context(), req.ID, req.StopOptions)
|
||||
_, err = client.ContainerRestart(c.Request.Context(), req.ID, req.ContainerRestartOptions)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to restart container"))
|
||||
return
|
||||
|
||||
@@ -3,15 +3,15 @@ package dockerapi
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type StartRequest struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
container.StartOptions
|
||||
client.ContainerStartOptions
|
||||
}
|
||||
|
||||
// @x-id "start"
|
||||
@@ -48,7 +48,7 @@ func Start(c *gin.Context) {
|
||||
|
||||
defer client.Close()
|
||||
|
||||
err = client.ContainerStart(c.Request.Context(), req.ID, req.StartOptions)
|
||||
_, err = client.ContainerStart(c.Request.Context(), req.ID, req.ContainerStartOptions)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to start container"))
|
||||
return
|
||||
|
||||
@@ -3,15 +3,15 @@ package dockerapi
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type StopRequest struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
container.StopOptions
|
||||
client.ContainerStopOptions
|
||||
}
|
||||
|
||||
// @x-id "stop"
|
||||
@@ -48,7 +48,7 @@ func Stop(c *gin.Context) {
|
||||
|
||||
defer client.Close()
|
||||
|
||||
err = client.ContainerStop(c.Request.Context(), req.ID, req.StopOptions)
|
||||
_, err = client.ContainerStop(c.Request.Context(), req.ID, req.ContainerStopOptions)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to stop container"))
|
||||
return
|
||||
|
||||
@@ -6,13 +6,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -22,67 +20,6 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// getDockerClients returns a map of docker clients for the current config.
|
||||
//
|
||||
// Returns a map of docker clients by server name and an error if any.
|
||||
//
|
||||
// Even if there are errors, the map of docker clients might not be empty.
|
||||
func getDockerClients() (DockerClients, gperr.Error) {
|
||||
cfg := config.GetInstance()
|
||||
|
||||
dockerHosts := cfg.Value().Providers.Docker
|
||||
dockerClients := make(DockerClients)
|
||||
|
||||
connErrs := gperr.NewBuilder("failed to connect to docker")
|
||||
|
||||
for name, host := range dockerHosts {
|
||||
dockerClient, err := docker.NewClient(host)
|
||||
if err != nil {
|
||||
connErrs.Add(err)
|
||||
continue
|
||||
}
|
||||
dockerClients[name] = dockerClient
|
||||
}
|
||||
|
||||
for _, agent := range agent.ListAgents() {
|
||||
dockerClient, err := docker.NewClient(agent.FakeDockerHost())
|
||||
if err != nil {
|
||||
connErrs.Add(err)
|
||||
continue
|
||||
}
|
||||
dockerClients[agent.Name] = dockerClient
|
||||
}
|
||||
|
||||
return dockerClients, connErrs.Error()
|
||||
}
|
||||
|
||||
func getDockerClient(server string) (*docker.SharedClient, bool, error) {
|
||||
cfg := config.GetInstance()
|
||||
var host string
|
||||
for name, h := range cfg.Value().Providers.Docker {
|
||||
if name == server {
|
||||
host = h
|
||||
break
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
for _, agent := range agent.ListAgents() {
|
||||
if agent.Name == server {
|
||||
host = agent.FakeDockerHost()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
return nil, false, nil
|
||||
}
|
||||
dockerClient, err := docker.NewClient(host)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return dockerClient, true, nil
|
||||
}
|
||||
|
||||
// closeAllClients closes all docker clients after a delay.
|
||||
//
|
||||
// This is used to ensure that all docker clients are closed after the http handler returns.
|
||||
@@ -103,11 +40,7 @@ func handleResult[V any, T ResultType[V]](c *gin.Context, errs error, result T)
|
||||
}
|
||||
|
||||
func serveHTTP[V any, T ResultType[V]](c *gin.Context, getResult func(ctx context.Context, dockerClients DockerClients) (T, gperr.Error)) {
|
||||
dockerClients, err := getDockerClients()
|
||||
if err != nil {
|
||||
handleResult[V, T](c, err, nil)
|
||||
return
|
||||
}
|
||||
dockerClients := docker.Clients()
|
||||
defer closeAllClients(dockerClients)
|
||||
|
||||
if httpheaders.IsWebsocket(c.Request.Header) {
|
||||
|
||||
@@ -105,12 +105,6 @@
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-id": "list",
|
||||
@@ -624,7 +618,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dockerapi.StopRequest"
|
||||
"$ref": "#/definitions/dockerapi.RestartRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -2135,6 +2129,54 @@
|
||||
"operationId": "routes"
|
||||
}
|
||||
},
|
||||
"/route/playground": {
|
||||
"post": {
|
||||
"description": "Test rules against mock request/response",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"route"
|
||||
],
|
||||
"summary": "Rule Playground",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Playground request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/PlaygroundRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/PlaygroundResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-id": "playground",
|
||||
"operationId": "playground"
|
||||
}
|
||||
},
|
||||
"/route/providers": {
|
||||
"get": {
|
||||
"description": "List route providers",
|
||||
@@ -2726,6 +2768,83 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"FinalRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"query": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"FinalResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"statusCode": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"HTTPHeader": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2799,6 +2918,75 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"HealthInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"latency": {
|
||||
"description": "latency in microseconds",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"healthy",
|
||||
"unhealthy",
|
||||
"napping",
|
||||
"starting",
|
||||
"error",
|
||||
"unknown"
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"uptime": {
|
||||
"description": "uptime in milliseconds",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"HealthInfoWithoutDetail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"latency": {
|
||||
"description": "latency in microseconds",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"healthy",
|
||||
"unhealthy",
|
||||
"napping",
|
||||
"starting",
|
||||
"error",
|
||||
"unknown"
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"uptime": {
|
||||
"description": "uptime in milliseconds",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"HealthJSON": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2882,7 +3070,7 @@
|
||||
"HealthMap": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/routes.HealthInfo"
|
||||
"$ref": "#/definitions/HealthInfo"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -3327,6 +3515,16 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"sticky": {
|
||||
"type": "boolean",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"sticky_max_age": {
|
||||
"$ref": "#/definitions/time.Duration",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"weight": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
@@ -3494,6 +3692,113 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"MockCookie": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"MockRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"cookies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/MockCookie"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"query": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"remoteIP": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"MockResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"statusCode": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"NewAgentRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -3589,6 +3894,120 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ParsedRule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"do": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"isResponseRule": {
|
||||
"type": "boolean",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"on": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"validationError": {
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"PlaygroundRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"rules"
|
||||
],
|
||||
"properties": {
|
||||
"mockRequest": {
|
||||
"$ref": "#/definitions/MockRequest"
|
||||
},
|
||||
"mockResponse": {
|
||||
"$ref": "#/definitions/MockResponse"
|
||||
},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/routeApi.RawRule"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"PlaygroundResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"executionError": {
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"finalRequest": {
|
||||
"$ref": "#/definitions/FinalRequest",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"finalResponse": {
|
||||
"$ref": "#/definitions/FinalResponse",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"matchedRules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"parsedRules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ParsedRule"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"upstreamCalled": {
|
||||
"type": "boolean",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"Port": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"listening": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"proxy": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ProviderStats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3789,9 +4208,13 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"healthcheck": {
|
||||
"$ref": "#/definitions/HealthCheckConfig",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
"description": "null on load-balancer routes",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HealthCheckConfig"
|
||||
}
|
||||
],
|
||||
"x-nullable": true
|
||||
},
|
||||
"homepage": {
|
||||
"$ref": "#/definitions/HomepageItemConfig",
|
||||
@@ -3844,7 +4267,7 @@
|
||||
"x-nullable": true
|
||||
},
|
||||
"port": {
|
||||
"$ref": "#/definitions/github_com_yusing_go-proxy_internal_route_types.Port",
|
||||
"$ref": "#/definitions/Port",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -3868,9 +4291,12 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"rule_file": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "#/definitions/rules.Rule"
|
||||
},
|
||||
@@ -3878,7 +4304,47 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"scheme": {
|
||||
"$ref": "#/definitions/route.Scheme",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"http",
|
||||
"https",
|
||||
"tcp",
|
||||
"udp",
|
||||
"fileserver"
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_certificate": {
|
||||
"description": "Path to client certificate",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_certificate_key": {
|
||||
"description": "Path to client certificate key",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_protocols": {
|
||||
"description": "Allowed TLS protocols",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_server_name": {
|
||||
"description": "SSL/TLS proxy options (nginx-like)",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_trusted_certificate": {
|
||||
"description": "Path to trusted CA certificates",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
@@ -3975,7 +4441,7 @@
|
||||
"statuses": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/routes.HealthInfo"
|
||||
"$ref": "#/definitions/HealthInfoWithoutDetail"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -4464,12 +4930,16 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"container.Port": {
|
||||
"container.PortSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"IP": {
|
||||
"description": "Host IP address that the container's port is mapped to",
|
||||
"type": "string",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/netip.Addr"
|
||||
}
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -4486,7 +4956,7 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"Type": {
|
||||
"description": "type\nRequired: true",
|
||||
"description": "type\nRequired: true\nEnum: [\"tcp\",\"udp\",\"sctp\"]",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -4499,7 +4969,6 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"iops": {
|
||||
"description": "godoxy",
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -4522,7 +4991,6 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"read_speed": {
|
||||
"description": "godoxy",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -4538,7 +5006,6 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"write_speed": {
|
||||
"description": "godoxy",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
@@ -4566,7 +5033,7 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -4584,6 +5051,29 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"dockerapi.RestartRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"signal": {
|
||||
"description": "Signal (optional) is the signal to send to the container to (gracefully)\nstop it before forcibly terminating the container with SIGKILL after the\ntimeout expires. If no value is set, the default (SIGTERM) is used.",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "Timeout (optional) is the timeout (in seconds) to wait for the container\nto stop gracefully before forcibly terminating it with SIGKILL.\n\n- Use nil to use the default timeout (10 seconds).\n- Use '-1' to wait indefinitely.\n- Use '0' to not wait for the container to exit gracefully, and\n immediately proceeds to forcibly terminating the container.\n- Other positive values are used as timeout (in seconds).",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"dockerapi.StartRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -4617,7 +5107,7 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"signal": {
|
||||
"description": "Signal (optional) is the signal to send to the container to (gracefully)\nstop it before forcibly terminating the container with SIGKILL after the\ntimeout expires. If not value is set, the default (SIGTERM) is used.",
|
||||
"description": "Signal (optional) is the signal to send to the container to (gracefully)\nstop it before forcibly terminating the container with SIGKILL after the\ntimeout expires. If no value is set, the default (SIGTERM) is used.",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
@@ -4628,31 +5118,9 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"github_com_yusing_go-proxy_internal_route_types.Port": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"listening": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"proxy": {
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"homepage.FetchResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"errMsg": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -4739,15 +5207,9 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"free": {
|
||||
"description": "This is the kernel's notion of free memory; RAM chips whose bits nobody\ncares about the value of right now. For a human consumable number,\nAvailable is what you really want.",
|
||||
"type": "integer",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"total": {
|
||||
"description": "Total amount of RAM on this system",
|
||||
"type": "integer",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -4798,6 +5260,20 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"netip.Addr": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "ipv4"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "ipv6"
|
||||
}
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"route.Route": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4852,9 +5328,13 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"healthcheck": {
|
||||
"$ref": "#/definitions/HealthCheckConfig",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
"description": "null on load-balancer routes",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HealthCheckConfig"
|
||||
}
|
||||
],
|
||||
"x-nullable": true
|
||||
},
|
||||
"homepage": {
|
||||
"$ref": "#/definitions/HomepageItemConfig",
|
||||
@@ -4907,7 +5387,7 @@
|
||||
"x-nullable": true
|
||||
},
|
||||
"port": {
|
||||
"$ref": "#/definitions/github_com_yusing_go-proxy_internal_route_types.Port",
|
||||
"$ref": "#/definitions/Port",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -4931,9 +5411,12 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"rule_file": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "#/definitions/rules.Rule"
|
||||
},
|
||||
@@ -4941,7 +5424,47 @@
|
||||
"x-omitempty": false
|
||||
},
|
||||
"scheme": {
|
||||
"$ref": "#/definitions/route.Scheme",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"http",
|
||||
"https",
|
||||
"tcp",
|
||||
"udp",
|
||||
"fileserver"
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_certificate": {
|
||||
"description": "Path to client certificate",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_certificate_key": {
|
||||
"description": "Path to client certificate key",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_protocols": {
|
||||
"description": "Allowed TLS protocols",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_server_name": {
|
||||
"description": "SSL/TLS proxy options (nginx-like)",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"ssl_trusted_certificate": {
|
||||
"description": "Path to trusted CA certificates",
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
@@ -4949,22 +5472,25 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"route.Scheme": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"http",
|
||||
"https",
|
||||
"tcp",
|
||||
"udp",
|
||||
"fileserver"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"SchemeHTTP",
|
||||
"SchemeHTTPS",
|
||||
"SchemeTCP",
|
||||
"SchemeUDP",
|
||||
"SchemeFileServer"
|
||||
],
|
||||
"routeApi.RawRule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"do": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"on": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
@@ -4979,43 +5505,6 @@
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"routes.HealthInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"latency": {
|
||||
"description": "latency in microseconds",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"healthy",
|
||||
"unhealthy",
|
||||
"napping",
|
||||
"starting",
|
||||
"error",
|
||||
"unknown"
|
||||
],
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"uptime": {
|
||||
"description": "uptime in milliseconds",
|
||||
"type": "number",
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
}
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
},
|
||||
"rules.Rule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5100,7 +5589,7 @@
|
||||
"types.PortMapping": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/container.Port"
|
||||
"$ref": "#/definitions/container.PortSummary"
|
||||
},
|
||||
"x-nullable": false,
|
||||
"x-omitempty": false
|
||||
|
||||
@@ -217,6 +217,42 @@ definitions:
|
||||
- FileTypeConfig
|
||||
- FileTypeProvider
|
||||
- FileTypeMiddleware
|
||||
FinalRequest:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
host:
|
||||
type: string
|
||||
method:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
query:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
FinalResponse:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
statusCode:
|
||||
type: integer
|
||||
type: object
|
||||
HTTPHeader:
|
||||
properties:
|
||||
key:
|
||||
@@ -248,6 +284,44 @@ definitions:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
type: object
|
||||
HealthInfo:
|
||||
properties:
|
||||
detail:
|
||||
type: string
|
||||
latency:
|
||||
description: latency in microseconds
|
||||
type: number
|
||||
status:
|
||||
enum:
|
||||
- healthy
|
||||
- unhealthy
|
||||
- napping
|
||||
- starting
|
||||
- error
|
||||
- unknown
|
||||
type: string
|
||||
uptime:
|
||||
description: uptime in milliseconds
|
||||
type: number
|
||||
type: object
|
||||
HealthInfoWithoutDetail:
|
||||
properties:
|
||||
latency:
|
||||
description: latency in microseconds
|
||||
type: number
|
||||
status:
|
||||
enum:
|
||||
- healthy
|
||||
- unhealthy
|
||||
- napping
|
||||
- starting
|
||||
- error
|
||||
- unknown
|
||||
type: string
|
||||
uptime:
|
||||
description: uptime in milliseconds
|
||||
type: number
|
||||
type: object
|
||||
HealthJSON:
|
||||
properties:
|
||||
config:
|
||||
@@ -283,7 +357,7 @@ definitions:
|
||||
type: object
|
||||
HealthMap:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/routes.HealthInfo'
|
||||
$ref: '#/definitions/HealthInfo'
|
||||
type: object
|
||||
HomepageCategory:
|
||||
properties:
|
||||
@@ -481,6 +555,10 @@ definitions:
|
||||
options:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
sticky:
|
||||
type: boolean
|
||||
sticky_max_age:
|
||||
$ref: '#/definitions/time.Duration'
|
||||
weight:
|
||||
type: integer
|
||||
type: object
|
||||
@@ -564,6 +642,55 @@ definitions:
|
||||
- MetricsPeriod1h
|
||||
- MetricsPeriod1d
|
||||
- MetricsPeriod1mo
|
||||
MockCookie:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
type: object
|
||||
MockRequest:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
cookies:
|
||||
items:
|
||||
$ref: '#/definitions/MockCookie'
|
||||
type: array
|
||||
headers:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
host:
|
||||
type: string
|
||||
method:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
query:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
remoteIP:
|
||||
type: string
|
||||
type: object
|
||||
MockResponse:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
statusCode:
|
||||
type: integer
|
||||
type: object
|
||||
NewAgentRequest:
|
||||
properties:
|
||||
container_runtime:
|
||||
@@ -612,6 +739,56 @@ definitions:
|
||||
format: base64
|
||||
type: string
|
||||
type: object
|
||||
ParsedRule:
|
||||
properties:
|
||||
do:
|
||||
type: string
|
||||
isResponseRule:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
"on":
|
||||
type: string
|
||||
validationError: {}
|
||||
type: object
|
||||
PlaygroundRequest:
|
||||
properties:
|
||||
mockRequest:
|
||||
$ref: '#/definitions/MockRequest'
|
||||
mockResponse:
|
||||
$ref: '#/definitions/MockResponse'
|
||||
rules:
|
||||
items:
|
||||
$ref: '#/definitions/routeApi.RawRule'
|
||||
type: array
|
||||
required:
|
||||
- rules
|
||||
type: object
|
||||
PlaygroundResponse:
|
||||
properties:
|
||||
executionError: {}
|
||||
finalRequest:
|
||||
$ref: '#/definitions/FinalRequest'
|
||||
finalResponse:
|
||||
$ref: '#/definitions/FinalResponse'
|
||||
matchedRules:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
parsedRules:
|
||||
items:
|
||||
$ref: '#/definitions/ParsedRule'
|
||||
type: array
|
||||
upstreamCalled:
|
||||
type: boolean
|
||||
type: object
|
||||
Port:
|
||||
properties:
|
||||
listening:
|
||||
type: integer
|
||||
proxy:
|
||||
type: integer
|
||||
type: object
|
||||
ProviderStats:
|
||||
properties:
|
||||
reverse_proxies:
|
||||
@@ -708,7 +885,10 @@ definitions:
|
||||
- $ref: '#/definitions/HealthJSON'
|
||||
description: for swagger
|
||||
healthcheck:
|
||||
$ref: '#/definitions/HealthCheckConfig'
|
||||
allOf:
|
||||
- $ref: '#/definitions/HealthCheckConfig'
|
||||
description: null on load-balancer routes
|
||||
x-nullable: true
|
||||
homepage:
|
||||
$ref: '#/definitions/HomepageItemConfig'
|
||||
host:
|
||||
@@ -738,7 +918,7 @@ definitions:
|
||||
type: array
|
||||
x-nullable: true
|
||||
port:
|
||||
$ref: '#/definitions/github_com_yusing_go-proxy_internal_route_types.Port'
|
||||
$ref: '#/definitions/Port'
|
||||
provider:
|
||||
description: for backward compatibility
|
||||
type: string
|
||||
@@ -749,13 +929,38 @@ definitions:
|
||||
type: integer
|
||||
root:
|
||||
type: string
|
||||
rule_file:
|
||||
type: string
|
||||
x-nullable: true
|
||||
rules:
|
||||
items:
|
||||
$ref: '#/definitions/rules.Rule'
|
||||
type: array
|
||||
uniqueItems: true
|
||||
scheme:
|
||||
$ref: '#/definitions/route.Scheme'
|
||||
enum:
|
||||
- http
|
||||
- https
|
||||
- tcp
|
||||
- udp
|
||||
- fileserver
|
||||
type: string
|
||||
ssl_certificate:
|
||||
description: Path to client certificate
|
||||
type: string
|
||||
ssl_certificate_key:
|
||||
description: Path to client certificate key
|
||||
type: string
|
||||
ssl_protocols:
|
||||
description: Allowed TLS protocols
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
ssl_server_name:
|
||||
description: SSL/TLS proxy options (nginx-like)
|
||||
type: string
|
||||
ssl_trusted_certificate:
|
||||
description: Path to trusted CA certificates
|
||||
type: string
|
||||
type: object
|
||||
RouteProvider:
|
||||
properties:
|
||||
@@ -798,7 +1003,7 @@ definitions:
|
||||
properties:
|
||||
statuses:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/routes.HealthInfo'
|
||||
$ref: '#/definitions/HealthInfoWithoutDetail'
|
||||
type: object
|
||||
timestamp:
|
||||
type: integer
|
||||
@@ -1050,11 +1255,12 @@ definitions:
|
||||
- StateRemoving
|
||||
- StateExited
|
||||
- StateDead
|
||||
container.Port:
|
||||
container.PortSummary:
|
||||
properties:
|
||||
IP:
|
||||
allOf:
|
||||
- $ref: '#/definitions/netip.Addr'
|
||||
description: Host IP address that the container's port is mapped to
|
||||
type: string
|
||||
PrivatePort:
|
||||
description: |-
|
||||
Port on the container
|
||||
@@ -1067,12 +1273,12 @@ definitions:
|
||||
description: |-
|
||||
type
|
||||
Required: true
|
||||
Enum: ["tcp","udp","sctp"]
|
||||
type: string
|
||||
type: object
|
||||
disk.IOCountersStat:
|
||||
properties:
|
||||
iops:
|
||||
description: godoxy
|
||||
type: integer
|
||||
name:
|
||||
description: |-
|
||||
@@ -1096,14 +1302,12 @@ definitions:
|
||||
read_count:
|
||||
type: integer
|
||||
read_speed:
|
||||
description: godoxy
|
||||
type: number
|
||||
write_bytes:
|
||||
type: integer
|
||||
write_count:
|
||||
type: integer
|
||||
write_speed:
|
||||
description: godoxy
|
||||
type: number
|
||||
type: object
|
||||
disk.UsageStat:
|
||||
@@ -1115,12 +1319,36 @@ definitions:
|
||||
path:
|
||||
type: string
|
||||
total:
|
||||
type: integer
|
||||
type: number
|
||||
used:
|
||||
type: integer
|
||||
used_percent:
|
||||
type: number
|
||||
type: object
|
||||
dockerapi.RestartRequest:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
signal:
|
||||
description: |-
|
||||
Signal (optional) is the signal to send to the container to (gracefully)
|
||||
stop it before forcibly terminating the container with SIGKILL after the
|
||||
timeout expires. If no value is set, the default (SIGTERM) is used.
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
Timeout (optional) is the timeout (in seconds) to wait for the container
|
||||
to stop gracefully before forcibly terminating it with SIGKILL.
|
||||
|
||||
- Use nil to use the default timeout (10 seconds).
|
||||
- Use '-1' to wait indefinitely.
|
||||
- Use '0' to not wait for the container to exit gracefully, and
|
||||
immediately proceeds to forcibly terminating the container.
|
||||
- Other positive values are used as timeout (in seconds).
|
||||
type: integer
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
dockerapi.StartRequest:
|
||||
properties:
|
||||
checkpointDir:
|
||||
@@ -1140,7 +1368,7 @@ definitions:
|
||||
description: |-
|
||||
Signal (optional) is the signal to send to the container to (gracefully)
|
||||
stop it before forcibly terminating the container with SIGKILL after the
|
||||
timeout expires. If not value is set, the default (SIGTERM) is used.
|
||||
timeout expires. If no value is set, the default (SIGTERM) is used.
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
@@ -1156,17 +1384,8 @@ definitions:
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
github_com_yusing_go-proxy_internal_route_types.Port:
|
||||
properties:
|
||||
listening:
|
||||
type: integer
|
||||
proxy:
|
||||
type: integer
|
||||
type: object
|
||||
homepage.FetchResult:
|
||||
properties:
|
||||
errMsg:
|
||||
type: string
|
||||
icon:
|
||||
items:
|
||||
format: int32
|
||||
@@ -1212,15 +1431,9 @@ definitions:
|
||||
|
||||
This value is computed from the kernel specific values.
|
||||
type: integer
|
||||
free:
|
||||
description: |-
|
||||
This is the kernel's notion of free memory; RAM chips whose bits nobody
|
||||
cares about the value of right now. For a human consumable number,
|
||||
Available is what you really want.
|
||||
type: integer
|
||||
total:
|
||||
description: Total amount of RAM on this system
|
||||
type: integer
|
||||
type: number
|
||||
used:
|
||||
description: |-
|
||||
RAM used by programs
|
||||
@@ -1249,6 +1462,8 @@ definitions:
|
||||
description: godoxy
|
||||
type: number
|
||||
type: object
|
||||
netip.Addr:
|
||||
type: object
|
||||
route.Route:
|
||||
properties:
|
||||
access_log:
|
||||
@@ -1277,7 +1492,10 @@ definitions:
|
||||
- $ref: '#/definitions/HealthJSON'
|
||||
description: for swagger
|
||||
healthcheck:
|
||||
$ref: '#/definitions/HealthCheckConfig'
|
||||
allOf:
|
||||
- $ref: '#/definitions/HealthCheckConfig'
|
||||
description: null on load-balancer routes
|
||||
x-nullable: true
|
||||
homepage:
|
||||
$ref: '#/definitions/HomepageItemConfig'
|
||||
host:
|
||||
@@ -1307,7 +1525,7 @@ definitions:
|
||||
type: array
|
||||
x-nullable: true
|
||||
port:
|
||||
$ref: '#/definitions/github_com_yusing_go-proxy_internal_route_types.Port'
|
||||
$ref: '#/definitions/Port'
|
||||
provider:
|
||||
description: for backward compatibility
|
||||
type: string
|
||||
@@ -1318,54 +1536,54 @@ definitions:
|
||||
type: integer
|
||||
root:
|
||||
type: string
|
||||
rule_file:
|
||||
type: string
|
||||
x-nullable: true
|
||||
rules:
|
||||
items:
|
||||
$ref: '#/definitions/rules.Rule'
|
||||
type: array
|
||||
uniqueItems: true
|
||||
scheme:
|
||||
$ref: '#/definitions/route.Scheme'
|
||||
enum:
|
||||
- http
|
||||
- https
|
||||
- tcp
|
||||
- udp
|
||||
- fileserver
|
||||
type: string
|
||||
ssl_certificate:
|
||||
description: Path to client certificate
|
||||
type: string
|
||||
ssl_certificate_key:
|
||||
description: Path to client certificate key
|
||||
type: string
|
||||
ssl_protocols:
|
||||
description: Allowed TLS protocols
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
ssl_server_name:
|
||||
description: SSL/TLS proxy options (nginx-like)
|
||||
type: string
|
||||
ssl_trusted_certificate:
|
||||
description: Path to trusted CA certificates
|
||||
type: string
|
||||
type: object
|
||||
routeApi.RawRule:
|
||||
properties:
|
||||
do:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
"on":
|
||||
type: string
|
||||
type: object
|
||||
route.Scheme:
|
||||
enum:
|
||||
- http
|
||||
- https
|
||||
- tcp
|
||||
- udp
|
||||
- fileserver
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- SchemeHTTP
|
||||
- SchemeHTTPS
|
||||
- SchemeTCP
|
||||
- SchemeUDP
|
||||
- SchemeFileServer
|
||||
routeApi.RoutesByProvider:
|
||||
additionalProperties:
|
||||
items:
|
||||
$ref: '#/definitions/route.Route'
|
||||
type: array
|
||||
type: object
|
||||
routes.HealthInfo:
|
||||
properties:
|
||||
detail:
|
||||
type: string
|
||||
latency:
|
||||
description: latency in microseconds
|
||||
type: number
|
||||
status:
|
||||
enum:
|
||||
- healthy
|
||||
- unhealthy
|
||||
- napping
|
||||
- starting
|
||||
- error
|
||||
- unknown
|
||||
type: string
|
||||
uptime:
|
||||
description: uptime in milliseconds
|
||||
type: number
|
||||
type: object
|
||||
rules.Rule:
|
||||
properties:
|
||||
do:
|
||||
@@ -1412,7 +1630,7 @@ definitions:
|
||||
type: object
|
||||
types.PortMapping:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/container.Port'
|
||||
$ref: '#/definitions/container.PortSummary'
|
||||
type: object
|
||||
widgets.Config:
|
||||
properties:
|
||||
@@ -1494,10 +1712,6 @@ paths:
|
||||
description: Forbidden
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
summary: List agents
|
||||
tags:
|
||||
- agent
|
||||
@@ -1832,7 +2046,7 @@ paths:
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dockerapi.StopRequest'
|
||||
$ref: '#/definitions/dockerapi.RestartRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@@ -2878,6 +3092,37 @@ paths:
|
||||
- route
|
||||
- websocket
|
||||
x-id: routes
|
||||
/route/playground:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Test rules against mock request/response
|
||||
parameters:
|
||||
- description: Playground request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/PlaygroundRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/PlaygroundResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
"403":
|
||||
description: Forbidden
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
summary: Rule Playground
|
||||
tags:
|
||||
- route
|
||||
x-id: playground
|
||||
/route/providers:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
type GetFavIconRequest struct {
|
||||
@@ -44,9 +46,9 @@ func FavIcon(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid url", err))
|
||||
return
|
||||
}
|
||||
fetchResult := homepage.FetchFavIconFromURL(c.Request.Context(), &iconURL)
|
||||
if !fetchResult.OK() {
|
||||
c.JSON(fetchResult.StatusCode, apitypes.Error(fetchResult.ErrMsg))
|
||||
fetchResult, err := homepage.FetchFavIconFromURL(c.Request.Context(), &iconURL)
|
||||
if err != nil {
|
||||
homepage.GinFetchError(c, fetchResult.StatusCode, err)
|
||||
return
|
||||
}
|
||||
c.Data(fetchResult.StatusCode, fetchResult.ContentType(), fetchResult.Icon)
|
||||
@@ -54,38 +56,39 @@ func FavIcon(c *gin.Context) {
|
||||
}
|
||||
|
||||
// try with alias
|
||||
result := GetFavIconFromAlias(c.Request.Context(), request.Alias)
|
||||
if !result.OK() {
|
||||
c.JSON(result.StatusCode, apitypes.Error(result.ErrMsg))
|
||||
result, err := GetFavIconFromAlias(c.Request.Context(), request.Alias)
|
||||
if err != nil {
|
||||
homepage.GinFetchError(c, result.StatusCode, err)
|
||||
return
|
||||
}
|
||||
c.Data(result.StatusCode, result.ContentType(), result.Icon)
|
||||
}
|
||||
|
||||
func GetFavIconFromAlias(ctx context.Context, alias string) *homepage.FetchResult {
|
||||
//go:linkname GetFavIconFromAlias v1.GetFavIconFromAlias
|
||||
func GetFavIconFromAlias(ctx context.Context, alias string) (homepage.FetchResult, error) {
|
||||
// try with route.Icon
|
||||
r, ok := routes.HTTP.Get(alias)
|
||||
if !ok {
|
||||
return &homepage.FetchResult{
|
||||
StatusCode: http.StatusNotFound,
|
||||
ErrMsg: "route not found",
|
||||
}
|
||||
return homepage.FetchResultWithErrorf(http.StatusNotFound, "route not found")
|
||||
}
|
||||
|
||||
var result *homepage.FetchResult
|
||||
var (
|
||||
result homepage.FetchResult
|
||||
err error
|
||||
)
|
||||
hp := r.HomepageItem()
|
||||
if hp.Icon != nil {
|
||||
if hp.Icon.IconSource == homepage.IconSourceRelative {
|
||||
result = homepage.FindIcon(ctx, r, *hp.Icon.FullURL)
|
||||
result, err = homepage.FindIcon(ctx, r, *hp.Icon.FullURL)
|
||||
} else {
|
||||
result = homepage.FetchFavIconFromURL(ctx, hp.Icon)
|
||||
result, err = homepage.FetchFavIconFromURL(ctx, hp.Icon)
|
||||
}
|
||||
} else {
|
||||
// try extract from "link[rel=icon]"
|
||||
result = homepage.FindIcon(ctx, r, "/")
|
||||
result, err = homepage.FindIcon(ctx, r, "/")
|
||||
}
|
||||
if result.StatusCode == 0 {
|
||||
result.StatusCode = http.StatusOK
|
||||
}
|
||||
return result
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type FileType string // @name FileType
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
"github.com/yusing/goutils/fs"
|
||||
)
|
||||
|
||||
type ListFilesResponse struct {
|
||||
@@ -35,7 +35,7 @@ func List(c *gin.Context) {
|
||||
}
|
||||
|
||||
// config/
|
||||
files, err := utils.ListFiles(common.ConfigBasePath, 0, true)
|
||||
files, err := fs.ListFiles(common.ConfigBasePath, 0, true)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to list files"))
|
||||
return
|
||||
@@ -48,7 +48,7 @@ func List(c *gin.Context) {
|
||||
}
|
||||
|
||||
// config/middlewares/
|
||||
mids, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||
mids, err := fs.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to list files"))
|
||||
return
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type SetFileContentRequest GetFileContentRequest
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/provider"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route/provider"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type ValidateFileRequest struct {
|
||||
@@ -57,7 +57,7 @@ func validateFile(fileType FileType, content []byte) gperr.Error {
|
||||
return config.Validate(content)
|
||||
case FileTypeMiddleware:
|
||||
errs := gperr.NewBuilder("middleware errors")
|
||||
middleware.BuildMiddlewaresFromYAML("", content, errs)
|
||||
middleware.BuildMiddlewaresFromYAML("", content, &errs)
|
||||
return errs.Error()
|
||||
}
|
||||
return provider.Validate(content)
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type HealthMap = map[string]routes.HealthInfo // @name HealthMap
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
// @x-id "categories"
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type HomepageOverrideItemClickParams struct {
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
)
|
||||
|
||||
type HomepageItemsRequest struct {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type ListIconsRequest struct {
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/metrics/period"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/go-proxy/internal/utils/synk"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/metrics/period"
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
"github.com/yusing/goutils/synk"
|
||||
)
|
||||
|
||||
var (
|
||||
// for json marshaling (unknown size)
|
||||
allSystemInfoBytesPool = synk.GetBytesPoolWithUniqueMemory()
|
||||
// for storing http response body (known size)
|
||||
allSystemInfoFixedSizePool = synk.GetBytesPool()
|
||||
)
|
||||
var bytesPool = synk.GetUnsizedBytesPool()
|
||||
|
||||
type AllSystemInfoRequest struct {
|
||||
Period period.Filter `query:"period"`
|
||||
@@ -37,6 +32,7 @@ type AllSystemInfoRequest struct {
|
||||
|
||||
type bytesFromPool struct {
|
||||
json.RawMessage
|
||||
release func([]byte)
|
||||
}
|
||||
|
||||
// @x-id "all_system_info"
|
||||
@@ -182,38 +178,26 @@ func AllSystemInfo(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func getAgentSystemInfo(ctx context.Context, a *agent.AgentConfig, query string) (json.Marshaler, error) {
|
||||
func getAgentSystemInfo(ctx context.Context, a *agent.AgentConfig, query string) (bytesFromPool, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
path := agent.EndpointSystemInfo + "?" + query
|
||||
resp, err := a.Do(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return bytesFromPool{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// NOTE: buffer will be released by marshalSystemInfo once marshaling is done.
|
||||
if resp.ContentLength >= 0 {
|
||||
bytesBuf := allSystemInfoFixedSizePool.GetSized(int(resp.ContentLength))
|
||||
_, err = io.ReadFull(resp.Body, bytesBuf)
|
||||
if err != nil {
|
||||
// prevent pool leak on error.
|
||||
allSystemInfoFixedSizePool.Put(bytesBuf)
|
||||
return nil, err
|
||||
}
|
||||
return bytesFromPool{json.RawMessage(bytesBuf)}, nil
|
||||
}
|
||||
|
||||
// Fallback when content length is unknown (should not happen but just in case).
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
bytesBuf, release, err := httputils.ReadAllBody(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return bytesFromPool{}, err
|
||||
}
|
||||
return json.RawMessage(data), nil
|
||||
return bytesFromPool{json.RawMessage(bytesBuf), release}, nil
|
||||
}
|
||||
|
||||
func getAgentSystemInfoWithRetry(ctx context.Context, a *agent.AgentConfig, query string) (json.Marshaler, error) {
|
||||
func getAgentSystemInfoWithRetry(ctx context.Context, a *agent.AgentConfig, query string) (bytesFromPool, error) {
|
||||
const maxRetries = 3
|
||||
var lastErr error
|
||||
|
||||
@@ -223,7 +207,7 @@ func getAgentSystemInfoWithRetry(ctx context.Context, a *agent.AgentConfig, quer
|
||||
delay := max((1<<attempt)*time.Second, 5*time.Second)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
return bytesFromPool{}, ctx.Err()
|
||||
case <-time.After(delay):
|
||||
}
|
||||
}
|
||||
@@ -239,24 +223,23 @@ func getAgentSystemInfoWithRetry(ctx context.Context, a *agent.AgentConfig, quer
|
||||
|
||||
// Don't retry on context cancellation
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
return bytesFromPool{}, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return nil, lastErr
|
||||
return bytesFromPool{}, lastErr
|
||||
}
|
||||
|
||||
func marshalSystemInfo(ws *websocket.Manager, agentName string, systemInfo any) error {
|
||||
bytesBuf := allSystemInfoBytesPool.Get()
|
||||
defer allSystemInfoBytesPool.Put(bytesBuf)
|
||||
buf := bytesPool.GetBuffer()
|
||||
defer bytesPool.PutBuffer(buf)
|
||||
|
||||
// release the buffer retrieved from getAgentSystemInfo
|
||||
if bufFromPool, ok := systemInfo.(bytesFromPool); ok {
|
||||
defer allSystemInfoFixedSizePool.Put(bufFromPool.RawMessage)
|
||||
defer bufFromPool.release(bufFromPool.RawMessage)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(bytesBuf)
|
||||
err := json.NewEncoder(buf).Encode(map[string]any{
|
||||
err := sonic.ConfigDefault.NewEncoder(buf).Encode(map[string]any{
|
||||
agentName: systemInfo,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -6,11 +6,12 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
"github.com/yusing/go-proxy/internal/metrics/period"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
agentPkg "github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/metrics/period"
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/synk"
|
||||
)
|
||||
|
||||
type SystemInfoRequest struct {
|
||||
@@ -20,7 +21,7 @@ type SystemInfoRequest struct {
|
||||
Period period.Filter `query:"period"`
|
||||
} // @name SystemInfoRequest
|
||||
|
||||
type SystemInfoAggregate period.ResponseType[systeminfo.AggregatedJSON] // @name SystemInfoAggregate
|
||||
type SystemInfoAggregate period.ResponseType[systeminfo.Aggregated] // @name SystemInfoAggregate
|
||||
|
||||
// @x-id "system_info"
|
||||
// @BasePath /api/v1
|
||||
@@ -68,7 +69,16 @@ func SystemInfo(c *gin.Context) {
|
||||
|
||||
maps.Copy(c.Writer.Header(), resp.Header)
|
||||
c.Status(resp.StatusCode)
|
||||
io.Copy(c.Writer, resp.Body)
|
||||
|
||||
pool := synk.GetSizedBytesPool()
|
||||
buf := pool.GetSized(16384)
|
||||
_, err = io.CopyBuffer(c.Writer, resp.Body, buf)
|
||||
pool.Put(buf)
|
||||
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to copy response to client"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
agent.ReverseProxy(c.Writer, c.Request, agentPkg.EndpointSystemInfo)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package metrics
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/metrics/period"
|
||||
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
||||
"github.com/yusing/godoxy/internal/metrics/period"
|
||||
"github.com/yusing/godoxy/internal/metrics/uptime"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type UptimeRequest struct {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/config"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
// @x-id "reload"
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /reload [post]
|
||||
func Reload(c *gin.Context) {
|
||||
if err := config.GetInstance().Reload(); err != nil {
|
||||
if err := config.Reload(); err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to reload config"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/route"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type RoutesByProvider map[string][]route.Route
|
||||
|
||||
362
internal/api/v1/route/playground.go
Normal file
362
internal/api/v1/route/playground.go
Normal file
@@ -0,0 +1,362 @@
|
||||
package routeApi
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
)
|
||||
|
||||
type RawRule struct {
|
||||
Name string `json:"name"`
|
||||
On string `json:"on"`
|
||||
Do string `json:"do"`
|
||||
}
|
||||
|
||||
type PlaygroundRequest struct {
|
||||
Rules []RawRule `json:"rules" binding:"required"`
|
||||
MockRequest MockRequest `json:"mockRequest"`
|
||||
MockResponse MockResponse `json:"mockResponse"`
|
||||
} // @name PlaygroundRequest
|
||||
|
||||
type MockRequest struct {
|
||||
Method string `json:"method"`
|
||||
Path string `json:"path"`
|
||||
Host string `json:"host"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Query map[string][]string `json:"query"`
|
||||
Cookies []MockCookie `json:"cookies"`
|
||||
Body string `json:"body"`
|
||||
RemoteIP string `json:"remoteIP"`
|
||||
} // @name MockRequest
|
||||
|
||||
type MockCookie struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
} // @name MockCookie
|
||||
|
||||
type MockResponse struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Body string `json:"body"`
|
||||
} // @name MockResponse
|
||||
|
||||
type PlaygroundResponse struct {
|
||||
ParsedRules []ParsedRule `json:"parsedRules"`
|
||||
MatchedRules []string `json:"matchedRules"`
|
||||
FinalRequest FinalRequest `json:"finalRequest"`
|
||||
FinalResponse FinalResponse `json:"finalResponse"`
|
||||
ExecutionError gperr.Error `json:"executionError,omitempty"`
|
||||
UpstreamCalled bool `json:"upstreamCalled"`
|
||||
} // @name PlaygroundResponse
|
||||
|
||||
type ParsedRule struct {
|
||||
Name string `json:"name"`
|
||||
On string `json:"on"`
|
||||
Do string `json:"do"`
|
||||
ValidationError gperr.Error `json:"validationError,omitempty"`
|
||||
IsResponseRule bool `json:"isResponseRule"`
|
||||
} // @name ParsedRule
|
||||
|
||||
type FinalRequest struct {
|
||||
Method string `json:"method"`
|
||||
Path string `json:"path"`
|
||||
Host string `json:"host"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Query map[string][]string `json:"query"`
|
||||
Body string `json:"body"`
|
||||
} // @name FinalRequest
|
||||
|
||||
type FinalResponse struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Body string `json:"body"`
|
||||
} // @name FinalResponse
|
||||
|
||||
// @x-id "playground"
|
||||
// @BasePath /api/v1
|
||||
// @Summary Rule Playground
|
||||
// @Description Test rules against mock request/response
|
||||
// @Tags route
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body PlaygroundRequest true "Playground request"
|
||||
// @Success 200 {object} PlaygroundResponse
|
||||
// @Failure 400 {object} apitypes.ErrorResponse
|
||||
// @Failure 403 {object} apitypes.ErrorResponse
|
||||
// @Router /route/playground [post]
|
||||
func Playground(c *gin.Context) {
|
||||
var req PlaygroundRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Apply defaults
|
||||
if req.MockRequest.Method == "" {
|
||||
req.MockRequest.Method = "GET"
|
||||
}
|
||||
if req.MockRequest.Path == "" {
|
||||
req.MockRequest.Path = "/"
|
||||
}
|
||||
if req.MockRequest.Host == "" {
|
||||
req.MockRequest.Host = "localhost"
|
||||
}
|
||||
|
||||
// Parse rules
|
||||
parsedRules, rulesList, parseErr := parseRules(req.Rules)
|
||||
|
||||
// Create mock HTTP request
|
||||
mockReq := createMockRequest(req.MockRequest)
|
||||
|
||||
// Create mock HTTP response writer
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
// Set initial mock response if provided
|
||||
if req.MockResponse.StatusCode > 0 {
|
||||
recorder.Code = req.MockResponse.StatusCode
|
||||
}
|
||||
if req.MockResponse.Headers != nil {
|
||||
for k, values := range req.MockResponse.Headers {
|
||||
for _, v := range values {
|
||||
recorder.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if req.MockResponse.Body != "" {
|
||||
recorder.Body.WriteString(req.MockResponse.Body)
|
||||
}
|
||||
|
||||
// Execute rules
|
||||
matchedRules := []string{}
|
||||
upstreamCalled := false
|
||||
var executionError gperr.Error
|
||||
|
||||
// Variables to capture modified request state
|
||||
var finalReqMethod, finalReqPath, finalReqHost string
|
||||
var finalReqHeaders http.Header
|
||||
var finalReqQuery url.Values
|
||||
|
||||
if parseErr == nil && len(rulesList) > 0 {
|
||||
// Create upstream handler that records if it was called and captures request state
|
||||
upstreamHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
upstreamCalled = true
|
||||
// Capture the request state when upstream is called
|
||||
finalReqMethod = r.Method
|
||||
finalReqPath = r.URL.Path
|
||||
finalReqHost = r.Host
|
||||
finalReqHeaders = r.Header.Clone()
|
||||
finalReqQuery = r.URL.Query()
|
||||
|
||||
// Debug: also check RequestURI
|
||||
if r.URL.Path != r.URL.RawPath && r.URL.RawPath != "" {
|
||||
finalReqPath = r.URL.RawPath
|
||||
}
|
||||
|
||||
// If there's mock response body, write it during upstream call
|
||||
if req.MockResponse.Body != "" && w.Header().Get("Content-Type") == "" {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
}
|
||||
if req.MockResponse.StatusCode > 0 {
|
||||
w.WriteHeader(req.MockResponse.StatusCode)
|
||||
}
|
||||
if req.MockResponse.Body != "" {
|
||||
w.Write([]byte(req.MockResponse.Body))
|
||||
}
|
||||
}
|
||||
|
||||
// Build handler with rules
|
||||
handler := rulesList.BuildHandler(upstreamHandler)
|
||||
|
||||
// Execute the handler
|
||||
handlerWithRecover(recorder, mockReq, handler, &executionError)
|
||||
|
||||
// Track which rules matched
|
||||
// Since we can't easily instrument the rules, we'll check each rule manually
|
||||
matchedRules = checkMatchedRules(rulesList, recorder, mockReq)
|
||||
} else if parseErr != nil {
|
||||
executionError = parseErr
|
||||
}
|
||||
|
||||
// Build final request state
|
||||
// Use captured state if upstream was called, otherwise use current state
|
||||
var finalRequest FinalRequest
|
||||
if upstreamCalled {
|
||||
finalRequest = FinalRequest{
|
||||
Method: finalReqMethod,
|
||||
Path: finalReqPath,
|
||||
Host: finalReqHost,
|
||||
Headers: finalReqHeaders,
|
||||
Query: finalReqQuery,
|
||||
Body: req.MockRequest.Body,
|
||||
}
|
||||
} else {
|
||||
finalRequest = FinalRequest{
|
||||
Method: mockReq.Method,
|
||||
Path: mockReq.URL.Path,
|
||||
Host: mockReq.Host,
|
||||
Headers: mockReq.Header,
|
||||
Query: mockReq.URL.Query(),
|
||||
Body: req.MockRequest.Body,
|
||||
}
|
||||
}
|
||||
|
||||
// Build final response state
|
||||
finalResponse := FinalResponse{
|
||||
StatusCode: recorder.Code,
|
||||
Headers: recorder.Header(),
|
||||
Body: recorder.Body.String(),
|
||||
}
|
||||
|
||||
// Ensure status code defaults to 200 if not set
|
||||
if finalResponse.StatusCode == 0 {
|
||||
finalResponse.StatusCode = http.StatusOK
|
||||
}
|
||||
|
||||
// prevent null in response
|
||||
if parsedRules == nil {
|
||||
parsedRules = []ParsedRule{}
|
||||
}
|
||||
if matchedRules == nil {
|
||||
matchedRules = []string{}
|
||||
}
|
||||
|
||||
response := PlaygroundResponse{
|
||||
ParsedRules: parsedRules,
|
||||
MatchedRules: matchedRules,
|
||||
FinalRequest: finalRequest,
|
||||
FinalResponse: finalResponse,
|
||||
ExecutionError: executionError,
|
||||
UpstreamCalled: upstreamCalled,
|
||||
}
|
||||
|
||||
if common.IsTest {
|
||||
c.Set("response", response)
|
||||
}
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func handlerWithRecover(w http.ResponseWriter, r *http.Request, h http.HandlerFunc, outErr *gperr.Error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if outErr != nil {
|
||||
*outErr = gperr.Errorf("panic during rule execution: %v", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
h(w, r)
|
||||
}
|
||||
|
||||
func parseRules(rawRules []RawRule) ([]ParsedRule, rules.Rules, gperr.Error) {
|
||||
var parsedRules []ParsedRule
|
||||
var rulesList rules.Rules
|
||||
|
||||
// Parse each rule individually to capture per-rule errors
|
||||
for _, rawRule := range rawRules {
|
||||
var rule rules.Rule
|
||||
|
||||
// Extract fields
|
||||
name := rawRule.Name
|
||||
onStr := rawRule.On
|
||||
doStr := rawRule.Do
|
||||
|
||||
rule.Name = name
|
||||
|
||||
// Parse On
|
||||
var onErr error
|
||||
if onStr != "" {
|
||||
onErr = rule.On.Parse(onStr)
|
||||
}
|
||||
|
||||
// Parse Do
|
||||
var doErr error
|
||||
if doStr != "" {
|
||||
doErr = rule.Do.Parse(doStr)
|
||||
}
|
||||
|
||||
// Determine if valid
|
||||
isValid := onErr == nil && doErr == nil
|
||||
validationErr := gperr.Join(gperr.PrependSubject("on", onErr), gperr.PrependSubject("do", doErr))
|
||||
|
||||
parsedRules = append(parsedRules, ParsedRule{
|
||||
Name: name,
|
||||
On: onStr,
|
||||
Do: doStr,
|
||||
ValidationError: validationErr,
|
||||
IsResponseRule: rule.IsResponseRule(),
|
||||
})
|
||||
|
||||
// Only add valid rules to execution list
|
||||
if isValid {
|
||||
rulesList = append(rulesList, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return parsedRules, rulesList, nil
|
||||
}
|
||||
|
||||
func createMockRequest(mock MockRequest) *http.Request {
|
||||
// Create URL
|
||||
urlStr := mock.Path
|
||||
if len(mock.Query) > 0 {
|
||||
query := url.Values(mock.Query)
|
||||
urlStr = mock.Path + "?" + query.Encode()
|
||||
}
|
||||
|
||||
// Create request
|
||||
var body io.Reader
|
||||
if mock.Body != "" {
|
||||
body = strings.NewReader(mock.Body)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(mock.Method, urlStr, body)
|
||||
|
||||
// Set host
|
||||
req.Host = mock.Host
|
||||
|
||||
// Set headers
|
||||
req.Header = mock.Headers
|
||||
|
||||
// Set cookies
|
||||
if mock.Cookies != nil {
|
||||
for _, cookie := range mock.Cookies {
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: cookie.Name,
|
||||
Value: cookie.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Set remote address
|
||||
if mock.RemoteIP != "" {
|
||||
req.RemoteAddr = mock.RemoteIP + ":0"
|
||||
} else {
|
||||
req.RemoteAddr = "127.0.0.1:0"
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func checkMatchedRules(rulesList rules.Rules, w http.ResponseWriter, r *http.Request) []string {
|
||||
var matched []string
|
||||
|
||||
// Create a ResponseModifier to properly check rules
|
||||
rm := httputils.NewResponseModifier(w)
|
||||
|
||||
for _, rule := range rulesList {
|
||||
// Check if rule matches
|
||||
if rule.Check(rm, r) {
|
||||
matched = append(matched, rule.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return matched
|
||||
}
|
||||
229
internal/api/v1/route/playground_test.go
Normal file
229
internal/api/v1/route/playground_test.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package routeApi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestPlayground(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request PlaygroundRequest
|
||||
wantStatusCode int
|
||||
checkResponse func(t *testing.T, resp PlaygroundResponse)
|
||||
}{
|
||||
{
|
||||
name: "simple path matching rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "test rule",
|
||||
On: "path /api",
|
||||
Do: "pass",
|
||||
},
|
||||
},
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/api",
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if len(resp.ParsedRules) != 1 {
|
||||
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
|
||||
}
|
||||
if resp.ParsedRules[0].ValidationError != nil {
|
||||
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
|
||||
}
|
||||
if len(resp.MatchedRules) != 1 || resp.MatchedRules[0] != "test rule" {
|
||||
t.Errorf("expected matched rules to be ['test rule'], got %v", resp.MatchedRules)
|
||||
}
|
||||
if !resp.UpstreamCalled {
|
||||
t.Error("expected upstream to be called")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "header matching rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "check user agent",
|
||||
On: "header User-Agent Chrome",
|
||||
Do: "error 403 Forbidden",
|
||||
},
|
||||
},
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
Headers: map[string][]string{
|
||||
"User-Agent": {"Chrome"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if len(resp.ParsedRules) != 1 {
|
||||
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
|
||||
}
|
||||
if resp.ParsedRules[0].ValidationError != nil {
|
||||
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
|
||||
}
|
||||
if len(resp.MatchedRules) != 1 {
|
||||
t.Errorf("expected 1 matched rule, got %d", len(resp.MatchedRules))
|
||||
}
|
||||
if resp.FinalResponse.StatusCode != 403 {
|
||||
t.Errorf("expected status 403, got %d", resp.FinalResponse.StatusCode)
|
||||
}
|
||||
if resp.UpstreamCalled {
|
||||
t.Error("expected upstream not to be called")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid rule syntax",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "bad rule",
|
||||
On: "invalid_checker something",
|
||||
Do: "pass",
|
||||
},
|
||||
},
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if len(resp.ParsedRules) != 1 {
|
||||
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
|
||||
}
|
||||
if resp.ParsedRules[0].ValidationError == nil {
|
||||
t.Error("expected validation error to be set")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rewrite path rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "rewrite rule",
|
||||
On: "path glob(/api/*)",
|
||||
Do: "rewrite /api/ /v1/",
|
||||
},
|
||||
},
|
||||
MockRequest: MockRequest{
|
||||
Method: "GET",
|
||||
Path: "/api/users",
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if len(resp.ParsedRules) != 1 {
|
||||
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
|
||||
}
|
||||
if resp.ParsedRules[0].ValidationError != nil {
|
||||
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
|
||||
}
|
||||
if !resp.UpstreamCalled {
|
||||
t.Error("expected upstream to be called")
|
||||
}
|
||||
if resp.FinalRequest.Path != "/v1/users" {
|
||||
t.Errorf("expected path to be rewritten to /v1/users, got %s", resp.FinalRequest.Path)
|
||||
}
|
||||
// Note: matched rules tracking has limitations with fresh ResponseModifier
|
||||
// The important thing is that the rewrite actually worked
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "method matching rule",
|
||||
request: PlaygroundRequest{
|
||||
Rules: []RawRule{
|
||||
{
|
||||
Name: "block POST",
|
||||
On: "method POST",
|
||||
Do: `error "405" "Method Not Allowed"`,
|
||||
},
|
||||
},
|
||||
MockRequest: MockRequest{
|
||||
Method: "POST",
|
||||
Path: "/api",
|
||||
},
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
|
||||
if resp.ParsedRules[0].ValidationError != nil {
|
||||
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
|
||||
}
|
||||
if len(resp.MatchedRules) != 1 {
|
||||
t.Errorf("expected 1 matched rule, got %d", len(resp.MatchedRules))
|
||||
}
|
||||
if resp.FinalResponse.StatusCode != 405 {
|
||||
t.Errorf("expected status 405, got %d", resp.FinalResponse.StatusCode)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create request
|
||||
body, _ := json.Marshal(tt.request)
|
||||
req := httptest.NewRequest("POST", "/api/v1/route/playground", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Create response recorder
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Create gin context
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
// Call handler
|
||||
Playground(c)
|
||||
|
||||
// Check status code
|
||||
if w.Code != tt.wantStatusCode {
|
||||
t.Errorf("expected status code %d, got %d", tt.wantStatusCode, w.Code)
|
||||
}
|
||||
|
||||
respAny, ok := c.Get("response")
|
||||
if !ok {
|
||||
t.Fatalf("expected response to be set")
|
||||
}
|
||||
resp := respAny.(PlaygroundResponse)
|
||||
|
||||
// Run custom checks
|
||||
if tt.checkResponse != nil {
|
||||
tt.checkResponse(t, resp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaygroundInvalidRequest(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
req := httptest.NewRequest("POST", "/api/v1/route/playground", bytes.NewReader([]byte(`{}`)))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
Playground(c)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status code %d, got %d", http.StatusBadRequest, w.Code)
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
statequery "github.com/yusing/godoxy/internal/config/query"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
|
||||
_ "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
// @x-id "providers"
|
||||
@@ -17,17 +19,16 @@ import (
|
||||
// @Tags route,websocket
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} config.RouteProviderListResponse
|
||||
// @Success 200 {array} statequery.RouteProviderListResponse
|
||||
// @Failure 403 {object} apitypes.ErrorResponse
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /route/providers [get]
|
||||
func Providers(c *gin.Context) {
|
||||
cfg := config.GetInstance()
|
||||
if httpheaders.IsWebsocket(c.Request.Header) {
|
||||
websocket.PeriodicWrite(c, 5*time.Second, func() (any, error) {
|
||||
return config.GetInstance().RouteProviderList(), nil
|
||||
return statequery.RouteProviderList(), nil
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, cfg.RouteProviderList())
|
||||
c.JSON(http.StatusOK, statequery.RouteProviderList())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/go-proxy/internal/api/types"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
statequery "github.com/yusing/godoxy/internal/config/query"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type ListRouteRequest struct {
|
||||
@@ -40,7 +40,7 @@ func Route(c *gin.Context) {
|
||||
}
|
||||
|
||||
// also search for excluded routes
|
||||
route = config.GetInstance().SearchRoute(request.Which)
|
||||
route = statequery.SearchRoute(request.Which)
|
||||
if route != nil {
|
||||
c.JSON(http.StatusOK, route)
|
||||
return
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/route"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
)
|
||||
|
||||
type RouteType route.Route // @name Route
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/go-proxy/internal/types"
|
||||
statequery "github.com/yusing/godoxy/internal/config/query"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
)
|
||||
|
||||
type StatsResponse struct {
|
||||
@@ -35,10 +35,9 @@ type ProxyStats struct {
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /stats [get]
|
||||
func Stats(c *gin.Context) {
|
||||
cfg := config.GetInstance()
|
||||
getStats := func() (any, error) {
|
||||
return map[string]any{
|
||||
"proxies": cfg.Statistics(),
|
||||
"proxies": statequery.GetStatistics(),
|
||||
"uptime": int64(time.Since(startTime).Round(time.Second).Seconds()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
"github.com/yusing/goutils/version"
|
||||
)
|
||||
|
||||
// @x-id "version"
|
||||
@@ -17,5 +17,5 @@ import (
|
||||
// @Success 200 {string} string "version"
|
||||
// @Router /version [get]
|
||||
func Version(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, pkg.GetVersion().String())
|
||||
c.JSON(http.StatusOK, version.Get().String())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package auth
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
)
|
||||
|
||||
var defaultAuth Provider
|
||||
@@ -51,6 +51,10 @@ func ProceedNext(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if defaultAuth == nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
err := defaultAuth.CheckToken(r)
|
||||
if err != nil {
|
||||
defaultAuth.LoginHandler(w, r)
|
||||
@@ -58,3 +62,17 @@ func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func AuthOrProceed(w http.ResponseWriter, r *http.Request) (proceed bool) {
|
||||
if defaultAuth == nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return false
|
||||
}
|
||||
err := defaultAuth.CheckToken(r)
|
||||
if err != nil {
|
||||
defaultAuth.LoginHandler(w, r)
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/jsonstore"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/jsonstore"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@@ -151,7 +151,11 @@ func (auth *OIDCProvider) TryRefreshToken(ctx context.Context, sessionJWT string
|
||||
// verify the session cookie
|
||||
claims, valid, err := auth.parseSessionJWT(sessionJWT)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrInvalidSessionToken, err)
|
||||
var sessionID sessionID
|
||||
if claims != nil {
|
||||
sessionID = claims.SessionID
|
||||
}
|
||||
return nil, fmt.Errorf("session: %s - %w: %w", sessionID, ErrInvalidSessionToken, err)
|
||||
}
|
||||
if !valid {
|
||||
return nil, ErrInvalidSessionToken
|
||||
|
||||
@@ -10,14 +10,15 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/rs/zerolog/log"
|
||||
"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/utils"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
@@ -30,6 +31,8 @@ type (
|
||||
endSessionURL *url.URL
|
||||
allowedUsers []string
|
||||
allowedGroups []string
|
||||
|
||||
onUnknownPathHandler http.HandlerFunc
|
||||
}
|
||||
|
||||
IDTokenClaims struct {
|
||||
@@ -63,8 +66,9 @@ func (auth *OIDCProvider) getAppScopedCookieName(baseName string) string {
|
||||
|
||||
const (
|
||||
OIDCAuthInitPath = "/"
|
||||
OIDCPostAuthPath = "/auth/callback"
|
||||
OIDCLogoutPath = "/auth/logout"
|
||||
OIDCAuthBasePath = "/auth"
|
||||
OIDCPostAuthPath = OIDCAuthBasePath + "/callback"
|
||||
OIDCLogoutPath = OIDCAuthBasePath + "/logout"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -176,6 +180,10 @@ func (auth *OIDCProvider) SetScopes(scopes []string) {
|
||||
auth.oauthConfig.Scopes = scopes
|
||||
}
|
||||
|
||||
func (auth *OIDCProvider) SetOnUnknownPathHandler(handler http.HandlerFunc) {
|
||||
auth.onUnknownPathHandler = handler
|
||||
}
|
||||
|
||||
// optRedirectPostAuth returns an oauth2 option that sets the "redirect_uri"
|
||||
// parameter of the authorization URL to the post auth path of the current
|
||||
// request host.
|
||||
@@ -199,7 +207,7 @@ 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" {
|
||||
if r.TLS == nil && strings.EqualFold(r.Header.Get("X-Forwarded-Proto"), "https") {
|
||||
r.URL.Scheme = "https"
|
||||
http.Redirect(w, r, r.URL.String(), http.StatusFound)
|
||||
return
|
||||
@@ -212,6 +220,10 @@ func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
|
||||
case OIDCLogoutPath:
|
||||
auth.LogoutHandler(w, r)
|
||||
default:
|
||||
if auth.onUnknownPathHandler != nil {
|
||||
auth.onUnknownPathHandler(w, r)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, OIDCAuthInitPath, http.StatusFound)
|
||||
}
|
||||
}
|
||||
@@ -317,20 +329,23 @@ func (auth *OIDCProvider) PostAuthCallbackHandler(w http.ResponseWriter, r *http
|
||||
code := r.URL.Query().Get("code")
|
||||
oauth2Token, err := auth.oauthConfig.Exchange(r.Context(), code, optRedirectPostAuth(r))
|
||||
if err != nil {
|
||||
gphttp.ServerError(w, r, fmt.Errorf("failed to exchange token: %w", err))
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
httputils.LogError(r).Msg(fmt.Sprintf("failed to exchange token: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
idTokenJWT, idToken, err := auth.getIDToken(r.Context(), oauth2Token)
|
||||
if err != nil {
|
||||
gphttp.ServerError(w, r, err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
httputils.LogError(r).Msg(fmt.Sprintf("failed to get ID token: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if oauth2Token.RefreshToken != "" {
|
||||
claims, err := parseClaims(idToken)
|
||||
if err != nil {
|
||||
gphttp.ServerError(w, r, err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
httputils.LogError(r).Msg(fmt.Sprintf("failed to parse claims: %v", err))
|
||||
return
|
||||
}
|
||||
session := newSession(claims.Username, claims.Groups)
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
// setupMockOIDC configures mock OIDC provider for testing.
|
||||
@@ -35,7 +35,7 @@ func setupMockOIDC(t *testing.T) {
|
||||
},
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
},
|
||||
endSessionURL: Must(url.Parse("http://mock-provider/logout")),
|
||||
endSessionURL: expect.Must(url.Parse("http://mock-provider/logout")),
|
||||
oidcProvider: provider,
|
||||
oidcVerifier: provider.Verifier(&oidc.Config{
|
||||
ClientID: "test-client",
|
||||
@@ -75,7 +75,7 @@ func (j *provider) SignClaims(t *testing.T, claims jwt.Claims) string {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
token.Header["kid"] = keyID
|
||||
signed, err := token.SignedString(j.key)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
return signed
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func setupProvider(t *testing.T) *provider {
|
||||
|
||||
// Generate an RSA key pair for the test.
|
||||
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
|
||||
// Build the matching public JWK that will be served by the endpoint.
|
||||
jwk := buildRSAJWK(t, &privKey.PublicKey, keyID)
|
||||
@@ -227,12 +227,12 @@ func TestOIDCCallbackHandler(t *testing.T) {
|
||||
}
|
||||
|
||||
if tt.wantStatus == http.StatusTemporaryRedirect {
|
||||
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
ExpectEqual(t, setCookie.Name, CookieOauthToken)
|
||||
ExpectTrue(t, setCookie.Value != "")
|
||||
ExpectEqual(t, setCookie.Path, "/")
|
||||
ExpectEqual(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
ExpectEqual(t, setCookie.HttpOnly, true)
|
||||
setCookie := expect.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
expect.Equal(t, setCookie.Name, CookieOauthToken)
|
||||
expect.True(t, setCookie.Value != "")
|
||||
expect.Equal(t, setCookie.Path, "/")
|
||||
expect.Equal(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
expect.Equal(t, setCookie.HttpOnly, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func TestInitOIDC(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
ExpectNoError(t, json.NewEncoder(w).Encode(discoveryDocument(t, server)))
|
||||
expect.NoError(t, json.NewEncoder(w).Encode(discoveryDocument(t, server)))
|
||||
})
|
||||
server = httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
@@ -445,9 +445,9 @@ func TestCheckToken(t *testing.T) {
|
||||
// Call CheckToken and verify the result.
|
||||
err := auth.CheckToken(req)
|
||||
if tc.wantErr == nil {
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
} else {
|
||||
ExpectError(t, tc.wantErr, err)
|
||||
expect.ErrorIs(t, tc.wantErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"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/utils/strutils"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
httputils "github.com/yusing/goutils/http"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@@ -109,7 +109,7 @@ type UserPassAuthCallbackRequest struct {
|
||||
|
||||
func (auth *UserPassAuth) PostAuthCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var creds UserPassAuthCallbackRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&creds)
|
||||
err := sonic.ConfigDefault.NewDecoder(r.Body).Decode(&creds)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid request", http.StatusBadRequest)
|
||||
return
|
||||
@@ -121,7 +121,8 @@ func (auth *UserPassAuth) PostAuthCallbackHandler(w http.ResponseWriter, r *http
|
||||
}
|
||||
token, err := auth.NewToken()
|
||||
if err != nil {
|
||||
gphttp.ServerError(w, r, err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
httputils.LogError(r).Msg(fmt.Sprintf("failed to generate token: %v", err))
|
||||
return
|
||||
}
|
||||
SetTokenCookie(w, r, auth.TokenCookieName(), token, auth.tokenTTL)
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func newMockUserPassAuth() *UserPassAuth {
|
||||
return &UserPassAuth{
|
||||
username: "username",
|
||||
pwdHash: Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
|
||||
pwdHash: expect.Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
|
||||
secret: []byte("abcdefghijklmnopqrstuvwxyz"),
|
||||
tokenTTL: time.Hour,
|
||||
}
|
||||
@@ -25,17 +25,17 @@ func newMockUserPassAuth() *UserPassAuth {
|
||||
func TestUserPassValidateCredentials(t *testing.T) {
|
||||
auth := newMockUserPassAuth()
|
||||
err := auth.validatePassword("username", "password")
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
err = auth.validatePassword("username", "wrong-password")
|
||||
ExpectError(t, ErrInvalidPassword, err)
|
||||
expect.ErrorIs(t, ErrInvalidPassword, err)
|
||||
err = auth.validatePassword("wrong-username", "password")
|
||||
ExpectError(t, ErrInvalidUsername, err)
|
||||
expect.ErrorIs(t, ErrInvalidUsername, err)
|
||||
}
|
||||
|
||||
func TestUserPassCheckToken(t *testing.T) {
|
||||
auth := newMockUserPassAuth()
|
||||
token, err := auth.NewToken()
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
tests := []struct {
|
||||
token string
|
||||
wantErr bool
|
||||
@@ -60,9 +60,9 @@ func TestUserPassCheckToken(t *testing.T) {
|
||||
}
|
||||
err = auth.CheckToken(req)
|
||||
if tt.wantErr {
|
||||
ExpectTrue(t, err != nil)
|
||||
expect.True(t, err != nil)
|
||||
} else {
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,20 +96,20 @@ func TestUserPassLoginCallbackHandler(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := &http.Request{
|
||||
Host: "app.example.com",
|
||||
Body: io.NopCloser(bytes.NewReader(Must(json.Marshal(tt.creds)))),
|
||||
Body: io.NopCloser(bytes.NewReader(expect.Must(json.Marshal(tt.creds)))),
|
||||
}
|
||||
auth.PostAuthCallbackHandler(w, req)
|
||||
if tt.wantErr {
|
||||
ExpectEqual(t, w.Code, http.StatusUnauthorized)
|
||||
expect.Equal(t, w.Code, http.StatusBadRequest)
|
||||
} else {
|
||||
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
ExpectTrue(t, setCookie.Name == auth.TokenCookieName())
|
||||
ExpectTrue(t, setCookie.Value != "")
|
||||
ExpectEqual(t, setCookie.Domain, "example.com")
|
||||
ExpectEqual(t, setCookie.Path, "/")
|
||||
ExpectEqual(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
ExpectEqual(t, setCookie.HttpOnly, true)
|
||||
ExpectEqual(t, w.Code, http.StatusOK)
|
||||
setCookie := expect.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
expect.True(t, setCookie.Name == auth.TokenCookieName())
|
||||
expect.True(t, setCookie.Value != "")
|
||||
expect.Equal(t, setCookie.Domain, "example.com")
|
||||
expect.Equal(t, setCookie.Path, "/")
|
||||
expect.Equal(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
expect.Equal(t, setCookie.HttpOnly, true)
|
||||
expect.Equal(t, w.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -59,6 +59,17 @@ func cookieDomain(r *http.Request) string {
|
||||
return ".local"
|
||||
}
|
||||
|
||||
// if the host is an IP address, return an empty string
|
||||
{
|
||||
host, _, err := net.SplitHostPort(reqHost)
|
||||
if err != nil {
|
||||
host = reqHost
|
||||
}
|
||||
if net.ParseIP(host) != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
parts := strutils.SplitRune(reqHost, '.')
|
||||
if len(parts) < 2 {
|
||||
return ""
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user