From 90f06de298bee3fa11972ff363cfbe4e1fae2797 Mon Sep 17 00:00:00 2001 From: duorameng <2997944583@qq.com> Date: Thu, 25 Jun 2026 20:06:38 +0800 Subject: [PATCH] feat: add openconnect --- configs/config_child.ini | 21 + go.mod | 145 +---- go.sum | 598 +---------------- internal/bootstrap/bootstrap.go | 14 + internal/constant/constant.go | 18 +- internal/constant/interconnect.go | 22 + .../controllers/interconnect_controller.go | 526 +++++++++++++++ internal/controllers/monitor_controller.go | 142 +---- internal/controllers/settings_controller.go | 42 +- internal/controllers/task_controller.go | 82 +++ internal/controllers/terminal_controller.go | 13 +- internal/database/migrate.go | 1 + internal/executor/syscron.go | 69 ++ internal/middleware/auth.go | 42 ++ internal/middleware/travel_proxy.go | 133 ++++ internal/models/interconnect.go | 32 + internal/router/api_routes.go | 38 +- internal/router/events.go | 12 +- internal/router/register.go | 4 +- internal/router/router.go | 2 + internal/services/agent_ws_service.go | 96 ++- internal/services/interconnect_service.go | 85 +++ internal/services/monitor_service.go | 132 ++++ internal/tunnel/client.go | 254 ++++++++ internal/tunnel/conn.go | 87 +++ internal/tunnel/proxy.go | 109 ++++ internal/tunnel/reporter.go | 117 ++++ internal/tunnel/server.go | 96 +++ internal/tunnel/session.go | 91 +++ internal/tunnel/traffic.go | 48 ++ web/src/App.vue | 21 + web/src/api/index.ts | 36 +- web/src/api/interconnect.ts | 69 ++ web/src/components/InterconnectSyncDialog.vue | 102 +++ web/src/components/NodeSwitcher.vue | 118 ++++ web/src/composables/useSiteSettings.ts | 14 +- web/src/constants/index.ts | 5 + web/src/layouts/MainLayout.vue | 7 +- web/src/router/index.ts | 1 + web/src/views/editor/Editor.vue | 1 + web/src/views/environments/Environments.vue | 5 +- .../interconnect/components/ChildView.vue | 215 +++++++ .../interconnect/components/MasterList.vue | 603 ++++++++++++++++++ .../interconnect/components/MasterView.vue | 97 +++ .../interconnect/components/RoleSelector.vue | 43 ++ .../interconnect/components/SyncPanel.vue | 267 ++++++++ web/src/views/interconnect/index.vue | 111 ++++ web/src/views/monitor/Monitor.vue | 33 +- web/vite.config.ts | 4 +- 49 files changed, 3854 insertions(+), 969 deletions(-) create mode 100644 configs/config_child.ini create mode 100644 internal/constant/interconnect.go create mode 100644 internal/controllers/interconnect_controller.go create mode 100644 internal/executor/syscron.go create mode 100644 internal/middleware/travel_proxy.go create mode 100644 internal/models/interconnect.go create mode 100644 internal/services/interconnect_service.go create mode 100644 internal/services/monitor_service.go create mode 100644 internal/tunnel/client.go create mode 100644 internal/tunnel/conn.go create mode 100644 internal/tunnel/proxy.go create mode 100644 internal/tunnel/reporter.go create mode 100644 internal/tunnel/server.go create mode 100644 internal/tunnel/session.go create mode 100644 internal/tunnel/traffic.go create mode 100644 web/src/api/interconnect.ts create mode 100644 web/src/components/InterconnectSyncDialog.vue create mode 100644 web/src/components/NodeSwitcher.vue create mode 100644 web/src/views/interconnect/components/ChildView.vue create mode 100644 web/src/views/interconnect/components/MasterList.vue create mode 100644 web/src/views/interconnect/components/MasterView.vue create mode 100644 web/src/views/interconnect/components/RoleSelector.vue create mode 100644 web/src/views/interconnect/components/SyncPanel.vue create mode 100644 web/src/views/interconnect/index.vue diff --git a/configs/config_child.ini b/configs/config_child.ini new file mode 100644 index 0000000..54c30f4 --- /dev/null +++ b/configs/config_child.ini @@ -0,0 +1,21 @@ +[server] +port = 8055 +host = 0.0.0.0 +; url_prefix = /baihu +message_server = http://message-nest-demo-site.qwapi.eu.org/ +pprof_enabled=true + + +[database] +; type = sqlite +type = mysql +host = app.engigu.cn +port = 33066 +user = root +password = Gq19940507+****+ +dbname = baihu-test2 +table_prefix = baihu_ +; debug = true + +[security] +secret = baihu_secret_key_change_me diff --git a/go.mod b/go.mod index e1b31b3..8575613 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/gofrs/flock v0.13.0 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/gorilla/websocket v1.5.3 + github.com/hashicorp/yamux v0.1.2 github.com/robfig/cron/v3 v3.0.1 github.com/rs/xid v1.6.0 github.com/shirou/gopsutil/v3 v3.24.5 @@ -31,28 +32,8 @@ require ( ) require ( - cel.dev/expr v0.25.2 // indirect - cloud.google.com/go v0.123.0 // indirect - cloud.google.com/go/auth v0.20.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/iam v1.11.0 // indirect - cloud.google.com/go/monitoring v1.29.0 // indirect - cloud.google.com/go/storage v1.62.3 // indirect dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.2.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.0 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 // indirect - github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect - github.com/JohannesKaufmann/dom v0.3.1 // indirect github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2 // indirect github.com/air-verse/air v1.64.5 // indirect github.com/alecthomas/chroma/v2 v2.27.0 // indirect @@ -63,46 +44,8 @@ require ( github.com/alibabacloud-go/tea-utils/v2 v2.0.9 // indirect github.com/aliyun/credentials-go v1.4.12 // indirect github.com/andybalholm/brotli v1.2.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.42.0 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.25 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.24 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.28 // indirect - github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.2.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect - github.com/aws/aws-sdk-go-v2/service/cloudfront v1.65.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.29 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.104.0 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 // indirect - github.com/aws/smithy-go v1.27.2 // indirect - github.com/aymerick/douceur v0.2.0 // indirect - github.com/bep/clocks v0.5.0 // indirect - github.com/bep/debounce v1.2.1 // indirect - github.com/bep/gitmap v1.9.0 // indirect - github.com/bep/goat v0.5.0 // indirect github.com/bep/godartsass/v2 v2.5.0 // indirect github.com/bep/golibsass v1.2.0 // indirect - github.com/bep/golocales v0.2.0 // indirect - github.com/bep/goportabletext v0.2.0 // indirect - github.com/bep/helpers v0.12.0 // indirect - github.com/bep/imagemeta v0.17.2 // indirect - github.com/bep/lazycache v0.8.1 // indirect - github.com/bep/logg v0.4.0 // indirect - github.com/bep/mclib v1.20401.20400 // indirect - github.com/bep/overlayfs v0.11.0 // indirect - github.com/bep/simplecobra v0.7.0 // indirect - github.com/bep/textandbinarywriter v0.1.0 // indirect - github.com/bep/tmc v0.6.0 // indirect github.com/bits-and-blooms/bitset v1.24.5 // indirect github.com/bradfitz/gomemcache v0.0.0-20260422231931-4d751bb6e37c // indirect github.com/bytedance/gopkg v0.1.4 // indirect @@ -113,28 +56,18 @@ require ( github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudwego/base64x v0.1.7 // indirect - github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dlclark/regexp2 v1.12.0 // indirect github.com/dlclark/regexp2/v2 v2.2.2 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect github.com/evanw/esbuild v0.28.1 // indirect github.com/fatih/color v1.19.0 // indirect github.com/fatih/structs v1.1.0 // indirect - github.com/felixge/httpsnoop v1.1.0 // indirect - github.com/frankban/quicktest v1.14.6 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/getkin/kin-openapi v0.140.0 // indirect github.com/gin-contrib/sse v1.1.1 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.4 // 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-openapi/jsonpointer v0.23.1 // indirect github.com/go-openapi/swag/jsonname v0.26.1 // indirect @@ -143,92 +76,43 @@ require ( github.com/go-playground/validator/v10 v10.30.3 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sql-driver/mysql v1.10.0 // indirect - github.com/gobuffalo/flect v1.0.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-yaml v1.19.2 // indirect - github.com/gohugoio/gift v0.2.0 // indirect - github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6 // indirect - github.com/gohugoio/go-radix v1.2.0 // indirect github.com/gohugoio/hashstructure v0.6.0 // indirect - github.com/gohugoio/httpcache v0.8.0 // indirect github.com/gohugoio/hugo v0.163.3 // indirect - github.com/gohugoio/hugo-goldmark-extensions/extras v0.7.0 // indirect - github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.5.0 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/google/wire v0.7.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.16 // indirect - github.com/googleapis/gax-go/v2 v2.22.0 // indirect - github.com/gorilla/css v1.0.1 // indirect github.com/hairyhenderson/go-codeowners v0.7.1 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.10.0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/jdkato/prose v1.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/joho/godotenv v1.5.1 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect - github.com/kyokomi/emoji/v2 v2.2.13 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect - github.com/magefile/mage v1.17.2 // indirect - github.com/mailru/easyjson v0.9.2 // indirect - github.com/makeworld-the-better-one/dither/v2 v2.4.0 // indirect - github.com/marekm4/color-extractor v1.2.1 // indirect github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.22 // indirect github.com/mattn/go-runewidth v0.0.24 // indirect - github.com/microcosm-cc/bluemonday v1.0.27 // indirect - github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/muesli/smartcrop v0.3.0 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect - github.com/niklasfasching/go-org v1.9.1 // indirect - github.com/oasdiff/yaml v0.1.0 // indirect - github.com/oasdiff/yaml3 v0.0.13 // indirect - github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect github.com/olekukonko/errors v1.3.0 // indirect github.com/olekukonko/ll v0.1.8 // indirect - github.com/olekukonko/tablewriter v1.1.4 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.4.0 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // 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.6.0 // indirect github.com/quic-go/quic-go v0.60.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rogpeppe/go-internal v1.15.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/shoenig/go-m1cpu v0.2.1 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/cobra v1.10.2 // indirect - github.com/spf13/fsync v0.10.1 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect - github.com/tdewolff/minify/v2 v2.24.13 // indirect github.com/tdewolff/parse/v2 v2.8.13 // indirect - github.com/tdewolff/test v1.0.12 // indirect - github.com/tetratelabs/wazero v1.12.0 // indirect github.com/tidwall/gjson v1.19.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -237,47 +121,20 @@ require ( github.com/tklauser/numcpus v0.12.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect - github.com/woodsbury/decimal128 v1.4.0 // indirect - github.com/yuin/goldmark v1.8.2 // indirect - github.com/yuin/goldmark-emoji v1.0.6 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect - go.opentelemetry.io/otel v1.44.0 // indirect - go.opentelemetry.io/otel/metric v1.44.0 // indirect - go.opentelemetry.io/otel/sdk v1.44.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect - go.opentelemetry.io/otel/trace v1.44.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - gocloud.dev v0.46.0 // indirect golang.org/x/arch v0.28.0 // indirect golang.org/x/image v0.43.0 // indirect - golang.org/x/mod v0.37.0 // indirect - golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.21.0 // indirect golang.org/x/sys v0.46.0 // indirect - golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.46.0 // indirect - golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/api v0.285.0 // indirect - google.golang.org/genproto v0.0.0-20260618152121-87f3d3e198d3 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260618152121-87f3d3e198d3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260618152121-87f3d3e198d3 // indirect - google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - howett.net/plist v1.0.1 // indirect modernc.org/libc v1.73.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.52.0 // indirect - rsc.io/qr v0.2.0 // indirect - software.sslmate.com/src/go-pkcs12 v0.7.2 // indirect ) tool github.com/air-verse/air diff --git a/go.sum b/go.sum index 424659f..61d800c 100644 --- a/go.sum +++ b/go.sum @@ -1,99 +1,17 @@ -cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs= -cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= -cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= -cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= -cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= -cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.64.0 h1:7MmuzeAxlG5MOG5PQD2NLtyYR6bWjkvGljRu7pByoRU= -cloud.google.com/go/compute v1.64.0/go.mod h1:eHhcRZ6vf70fQCS3VEsiWSh+nQ+tLvSMb7mwLQskgN0= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v1.11.0 h1:KieQ9Pb+LLPak1O3Rv3GgCxhnmkYf7Xyh0P5HfF1jFM= -cloud.google.com/go/iam v1.11.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= -cloud.google.com/go/monitoring v1.29.0 h1:AHhDsFaSax1/4k+qlIDX/SDGe6hggnfXJ9dkgD9qBPY= -cloud.google.com/go/monitoring v1.29.0/go.mod h1:72NOVjJXHY/HBfoLT0+qlCZBT059+9VXLeAnL2PeeVM= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.62.3 h1:SZq1t23NCI+e96dH77Dg3PEfsNNEjqO8zE5AnD8gVD0= -cloud.google.com/go/storage v1.62.3/go.mod h1:cpYz/kRVZ+UQAF1uHeea10/9ewcRbxGoGNKsS9daSXA= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 h1:aokoqcHvaGjiM3VpjKDfMMnF/8epJ+Q1HLJ7CudztqE= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0/go.mod h1:/WYEx9pcM9Y+Dd/APJaNlSvVSvzl54rrMdZT5+Oi2LM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0 h1:CU4+EJeJi3TKYWEcYuSdWsjzw0nVsK/H0MSQOiPcymU= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0/go.mod h1:q0+UTSRvShwUCrR/s5HtyInYphN7Wvxb7snFM3u+SLA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.0 h1:irsmOWwkp0KCTTNS5e2hdFeIvSQClQo2No3IaNmL3Vw= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.0/go.mod h1:GWcBkQj3MqN7ozHKLaCCAuNLiXoIGv2RtanfAwSjY/Y= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest/to v0.4.1 h1:CxNHBqdzTr7rLtdrtb5CMjJcDut+WNGCVv7OmS5+lTc= -github.com/Azure/go-autorest/autorest/to v0.4.1/go.mod h1:EtaofgU4zmtvn1zT2ARsjRFdq9vXx0YWtmElwL+GZ9M= -github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 h1:RHK7bS+HQMslb1sZpAokUt+zTVmue0hKSs2C791hhzU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU= github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 h1:l7+6kwRMJNwdCvYdDl7Eax+wzEYHSnNY7zrrfbhDdTA= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 h1:jLdiS1vO+XJFyDSWRHBx56r4s/NNtcl5J6KyCcWUX/w= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0/go.mod h1:8lmpHY+1VRoteiOwyrQMDt1YGXOrFKCz+1wJW7n3ODY= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 h1:RoO5+d7uCmDqovLrHCr2/BuViUXvdcrNxyNM1pN9dDQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0/go.mod h1:YqwkQPrWSC7+byyc1VlKbWLBF5JsW5IoL6xUkemYSXk= -github.com/JohannesKaufmann/dom v0.2.0 h1:1bragmEb19K8lHAqgFgqCpiPCFEZMTXzOIEjuxkUfLQ= -github.com/JohannesKaufmann/dom v0.2.0/go.mod h1:57iSUl5RKric4bUkgos4zu6Xt5LMHUnw3TF1l5CbGZo= github.com/JohannesKaufmann/dom v0.3.1 h1:J16l9JAHWgkFPR3VIPbQ1gvS0cWab6laK1q7PFL3qh0= github.com/JohannesKaufmann/dom v0.3.1/go.mod h1:BZPkf8ZeYrBgABjwJn9iiKt8aiCtkxpHkevms+Yp2DE= -github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.0 h1:mklaPbT4f/EiDr1Q+zPrEt9lgKAkVrIBtWf33d9GpVA= -github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.0/go.mod h1:D56Cl9r8M5i3UwAchE+LlLc5hPN3kJtdZNVJn06lSHU= github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2 h1:XFJZFWESIWlUEHHjzBuv8RvrtCWnSGlimEX17ysSDb8= github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2/go.mod h1:BHWO8lJzttJLqwuV8Rb1B3OG2OSzLbssZDI1FRg2eAA= github.com/air-verse/air v1.64.5 h1:+gs/NgTzYYe+gGPyfHy3XxpJReQWC1pIsiKIg0LgNt4= github.com/air-verse/air v1.64.5/go.mod h1:OaJZSfZqf7wyjS2oP/CcEVyIt0JmZuPh5x1gdtklmmY= -github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= -github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs= github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8= github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA= @@ -154,50 +72,6 @@ github.com/aliyun/credentials-go v1.4.12 h1:7D8eXGotNwthZuUEgAMgBoqxmIHwfaPVwW+/ github.com/aliyun/credentials-go v1.4.12/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= -github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA= -github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 h1:p1BBrg/Hhp6uK7zpejeI8QFXHJeC/mynzi04Sl03k9g= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13/go.mod h1:8cIfkE9MDhkRZGpQ22aV6/lkYeYSozpz16Smrs5x4Ls= -github.com/aws/aws-sdk-go-v2/config v1.32.25 h1:ACCejvStYoilgwrfegSt5ZntCbPrk52qfwyNcnl3omM= -github.com/aws/aws-sdk-go-v2/config v1.32.25/go.mod h1:LJyU8sDRbXUxFn8xMJIGP+v9QYYwveNLI8a/giAOiAs= -github.com/aws/aws-sdk-go-v2/credentials v1.19.24 h1:2hQqYCV9yqyePQ9o6dCrZc/zO8U3TwPr9mIKlZnPu/I= -github.com/aws/aws-sdk-go-v2/credentials v1.19.24/go.mod h1:IDwpACtwqHLISdzfwUUNq4P9DsB/h5BLg4FwJPNfqFY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.28 h1:ez4y5o7sa0uaRI8BquYOXtZpioUPhbQEh7Igm88oV9U= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.28/go.mod h1:TpmZOrQA12XKEpVypgBGZSQBsm1WUTndCiSnbDsbvug= -github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.2.11 h1:ORrr68X2RQ4OBNI4aYJapjcmbaG5mst0Z7/tjUboZEA= -github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.2.11/go.mod h1:W4kVdkT0M/UDMNncotFG2X/HGX69yMdNn7S9WGMhUjk= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.65.2 h1:zG2KsuGIzgujekjJmRY4tEpqWVpbybdK/Ey+J8MlzTM= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.65.2/go.mod h1:rVFXjLSku4g0zfriDSkI1sdUGEJAjnmlPDqOnIFGZJ0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22 h1:V51LGlOq/1VsDsHUdoklAQi7rMmx4qQubvFYAlP2254= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22/go.mod h1:4Pzhyz8hJOm2bepgl+NjvRx8vlUFAIIvJnZ/MkcNPpU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.29 h1:hiME6pBzC7OTl9LMtlyTWBuEl1f4QBcUmFDKC7MLXtc= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.29/go.mod h1:G7RP+uhagpKtKhd1BM9N6JQqjCcGEU47K5lBVZQyRQw= -github.com/aws/aws-sdk-go-v2/service/s3 v1.104.0 h1:ta8csKy5vN91F3i5gGR85lFV0srBqySEji7Jroes6rE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.104.0/go.mod h1:77ZAgynvx1txMvDG8gGWoWkO1augYDxkp9JElWFgjQU= -github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 h1:3nXpRcFwRCW8n7HgO2QGy0Dc20eQNfBuUemGQhpF8m8= -github.com/aws/aws-sdk-go-v2/service/signin v1.2.0/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 h1:ey1XLTYXb9PcLt4535632o5kCGXNXEhNb620Dqwuylo= -github.com/aws/aws-sdk-go-v2/service/sso v1.31.3/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMbLMbSDF2YXsigqXngs6g= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4= -github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI= -github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc= -github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk= -github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= @@ -212,14 +86,10 @@ github.com/bep/godartsass/v2 v2.5.0 h1:tKRvwVdyjCIr48qgtLa4gHEdtRkPF8H1OeEhJAEv7 github.com/bep/godartsass/v2 v2.5.0/go.mod h1:rjsi1YSXAl/UbsGL85RLDEjRKdIKUlMQHr6ChUNYOFU= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= -github.com/bep/golocales v0.1.0 h1:rjWf1S4basIje+G+je5WMW8G+yzaoz4gEDFolrFVdvA= -github.com/bep/golocales v0.1.0/go.mod h1:Hl78nje8mNL3LzLeJvYN9NsIZgyFJGrGfvgO9r1+mwE= github.com/bep/golocales v0.2.0 h1:4H1H5UPw3ainpj5zykeEfiMRQngyaIC/t+I4Dvn+fvE= github.com/bep/golocales v0.2.0/go.mod h1:Hl78nje8mNL3LzLeJvYN9NsIZgyFJGrGfvgO9r1+mwE= github.com/bep/goportabletext v0.2.0 h1:CZ9f8jADBWqHwBymQiJJPCTSV/tHSA+PYzlUf86Yze0= github.com/bep/goportabletext v0.2.0/go.mod h1:xDeA5+qcgKzJq6Q6XjAiBKtxLD3Yn7f6XP4joD3J3qU= -github.com/bep/helpers v0.8.0 h1:plg2BFgA9AgIHF2XemyZdZLqixjzQk3uyyArV48FngQ= -github.com/bep/helpers v0.8.0/go.mod h1:PfE7MGdA8sSQ19nyDh4tYbs5rAlStlJaDI21f/fnNps= github.com/bep/helpers v0.12.0 h1:tD6V2DQW0B+FUynF2etR/106S/TO9akm+vA/Hk24GxY= github.com/bep/helpers v0.12.0/go.mod h1:PfE7MGdA8sSQ19nyDh4tYbs5rAlStlJaDI21f/fnNps= github.com/bep/imagemeta v0.17.2 h1:fDyXM1eAqCfBeqGLqS6UsN4OfuLM0cdu70KuLCehjOg= @@ -228,20 +98,12 @@ github.com/bep/lazycache v0.8.1 h1:ko6ASLjkPxyV5DMWoNNZ8B2M0weyjqXX8IZkjBoBtvg= github.com/bep/lazycache v0.8.1/go.mod h1:pbEiFsZoq7cLXvrTll0AHOPEurB1aGGxx4jKjOtlx9w= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= -github.com/bep/mclib v1.20401.20400 h1:silTOMNlNI7yHBb+HxEE0THIVFVWo/0I4SCH69FxtmU= -github.com/bep/mclib v1.20401.20400/go.mod h1:v5Hh3EIinPn7epigP28uf9JCkZlYzBS2vEOPe2wrHzM= -github.com/bep/overlayfs v0.10.0 h1:wS3eQ6bRsLX+4AAmwGjvoFSAQoeheamxofFiJ2SthSE= -github.com/bep/overlayfs v0.10.0/go.mod h1:ouu4nu6fFJaL0sPzNICzxYsBeWwrjiTdFZdK4lI3tro= github.com/bep/overlayfs v0.11.0 h1:aymHDGC0CHpvn0XvTfgpK6skCp16oMi+tdUF32l6pPs= github.com/bep/overlayfs v0.11.0/go.mod h1:L+ggdoKm+Y7Xb4a1osd+/LOPG4qsY62snqRqJH5Mspc= -github.com/bep/simplecobra v0.7.0 h1:kG8ZPwEc1o96hlIVGXcrrvwC8RornBqvMD3+pS0Z7y0= -github.com/bep/simplecobra v0.7.0/go.mod h1:PDXvBWH1ZMX05DRQ25ub/C6kKUuq+jROPgjbVz8wO1g= github.com/bep/textandbinarywriter v0.1.0 h1:KXmXsRN2Uhwhm1G3e/snM8+5SPQBJrCEpIosdIBR3po= github.com/bep/textandbinarywriter v0.1.0/go.mod h1:dAcHveajlWWU7PXhp6Dn4PIAYDg2H13Huif9xMS2w8w= github.com/bep/tmc v0.6.0 h1:5zWy4L+3gS+Kk8czzLC4g7ETaC3wkX9ZtTRdAdL8V4s= github.com/bep/tmc v0.6.0/go.mod h1:SNHxc3o2WSNMAYqJcAO0rxFY+pbhZzMwjIHe5xaAue0= -github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= -github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.24.5 h1:654xBVHc23gJMAgOTkPNoCVfiRxuIOAUnAZFtopqJ4w= github.com/bits-and-blooms/bitset v1.24.5/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= @@ -264,25 +126,13 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g= -github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= -github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos= -github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= -github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -291,51 +141,28 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= -github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2/v2 v2.2.2 h1:MYWvNYw8okuqNhwTYO587EZMiDruVa2vhV6fsGpfya0= github.com/dlclark/regexp2/v2 v2.2.2/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= -github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= -github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= -github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= -github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= -github.com/evanw/esbuild v0.28.0 h1:V96ghtc5p5JnNUQIUsc5H3kr+AcFcMqOJll2ZmJW6Lo= -github.com/evanw/esbuild v0.28.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/evanw/esbuild v0.28.1 h1:ds+yuRyUaZGx++GR56CrCeuXh8PVhVM4xq8v7PNELFc= github.com/evanw/esbuild v0.28.1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= -github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -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/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/getkin/kin-openapi v0.137.0 h1:Q3HhawNQV0GfvO2mIYMUBUSEFrDsVlzcYz4VydL9YEo= -github.com/getkin/kin-openapi v0.137.0/go.mod h1:vUYWaKyMqj7PfTybelXtLuLN9tReS12vxnzMRK+z2GY= github.com/getkin/kin-openapi v0.140.0 h1:JFn675aXRFjyiZKa/BFWploGldQlI0gobp4J5k0EZ2g= github.com/getkin/kin-openapi v0.140.0/go.mod h1:lISrB64F0CPcuDJ3LdtPTMJBY8VENjR9wJBdrcT6J3g= github.com/gin-contrib/pprof v1.5.4 h1:daxf2UNZw5IEx6WBdcpnEzgeDfkp083KdjUAFuMEkK4= @@ -348,26 +175,11 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/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= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= -github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= -github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE= github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -403,8 +215,6 @@ github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxU github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ= github.com/gohugoio/httpcache v0.8.0 h1:hNdsmGSELztetYCsPVgjA960zSa4dfEqqF/SficorCU= github.com/gohugoio/httpcache v0.8.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= -github.com/gohugoio/hugo v0.161.0 h1:RnVIwSeTVDbhXbSwa+5C8QFkQVa634IdmekvSjLVMmI= -github.com/gohugoio/hugo v0.161.0/go.mod h1:ZJStxHMZXnnhvCfOAy6FCLbWf90zTpH/cnvWAcmoyiE= github.com/gohugoio/hugo v0.163.3 h1:fkiOvLv8hYZt96OhJE9RDWw4IkQVASCIIndBmAiLHgM= github.com/gohugoio/hugo v0.163.3/go.mod h1:3FPGFgeqGYDTPRe1NJ10L1giY5ViUGeNlkVBDmVk46w= github.com/gohugoio/hugo-goldmark-extensions/extras v0.7.0 h1:I/n6v7VImJ3aISLnn73JAHXyjcQsMVvbguQPTk9Ehus= @@ -414,80 +224,32 @@ github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.5.0/go.mod h1:ob9PCH github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= -github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4= -github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18= -github.com/googleapis/enterprise-certificate-proxy v0.3.16 h1:F/VPrx0YPBdksZJQdCAp0WUsqnNmZpUZszzfYt0M5Dw= -github.com/googleapis/enterprise-certificate-proxy v0.3.16/go.mod h1:9Yb0eAkH/Xqhvv3zbeKf/+wMJqCeocWc6KIhDvEAuYE= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= -github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= @@ -496,19 +258,14 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/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/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= -github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= github.com/hairyhenderson/go-codeowners v0.7.1 h1:4yKuRk01b7DC+pnGMzsod4njH05EXsBjFlZcFmTn19g= github.com/hairyhenderson/go-codeowners v0.7.1/go.mod h1:oxxEFdmWj8geqXtG7tNO/rXF/hV7got2sSPdaIKpGVI= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -519,25 +276,18 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU= github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -545,32 +295,20 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U= github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= 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/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= -github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40= -github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA= -github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= -github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M= -github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE= github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc= github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mrXGUqIwn0= github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= @@ -584,37 +322,25 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc= github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0= github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oasdiff/yaml v0.0.9 h1:zQOvd2UKoozsSsAknnWoDJlSK4lC0mpmjfDsfqNwX48= -github.com/oasdiff/yaml v0.0.9/go.mod h1:8lvhgJG4xiKPj3HN5lDow4jZHPlx1i7dIwzkdAo6oAM= github.com/oasdiff/yaml v0.1.0 h1:0bqZjfKc/8S9urj4JuwepX41WX9EoA6ifhU3SV06cXg= github.com/oasdiff/yaml v0.1.0/go.mod h1:kOlRmMdL2X3vucLCEQO5u61SU22RysnfXvcttrZA1O0= -github.com/oasdiff/yaml3 v0.0.12 h1:75urAtPeDg2/iDEWwzNrLOWxI9N/dCh81nTTJtokt2M= -github.com/oasdiff/yaml3 v0.0.12/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/oasdiff/yaml3 v0.0.13 h1:06svmvOHOVBqF81+sY2EUScvUI/iS/vl2VIeUUxZQwg= github.com/oasdiff/yaml3 v0.0.13/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= -github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= -github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= github.com/olekukonko/errors v1.3.0 h1:teJvgLGUEqMzBUms+Dj3/3szNqCG/Jdw9iDbum8fR6U= github.com/olekukonko/errors v1.3.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= -github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA= -github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88= github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8= github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw= github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= @@ -634,20 +360,10 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2D github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= -github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.4.0 h1:Mwu0mAkUKbittDs3/ADDWXqMmq3EOK2VHiuCkV00Row= github.com/pelletier/go-toml/v2 v2.4.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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= @@ -664,16 +380,10 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -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/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc= github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= @@ -682,7 +392,6 @@ github.com/shoenig/go-m1cpu v0.2.1 h1:yqRB4fvOge2+FyRXFkXqsyMoqPazv14Yyy+iyccT2E github.com/shoenig/go-m1cpu v0.2.1/go.mod h1:KkDOw6m3ZJQAPHbrzkZki4hnx+pDRR1Lo+ldA56wD5w= github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= -github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc= github.com/silenceper/wechat/v2 v2.1.13 h1:4JCnQKFtw7t5DNTZPa/ITralevehQnl6BapjBp3W3Ns= github.com/silenceper/wechat/v2 v2.1.13/go.mod h1:7Iu3EhQYVtDUJAj+ZVRy8yom75ga7aDWv8RurLkVm0s= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -691,21 +400,11 @@ github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC4 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE= -github.com/spf13/fsync v0.10.1/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E= -github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -713,28 +412,20 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.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/tdewolff/minify/v2 v2.24.13 h1:xrcF7gKDnUszseEY9WX9mUlZII2v2Go/QAcAwRASw58= github.com/tdewolff/minify/v2 v2.24.13/go.mod h1:emvwoYeIl8bfAKqRU5ww95LX9Gpggpqv/naal9a8Yq0= -github.com/tdewolff/parse/v2 v2.8.12 h1:5BBjfaCv482v3nltlS0u6wH1xJaxjR6ofDrWttNvROg= -github.com/tdewolff/parse/v2 v2.8.12/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= github.com/tdewolff/parse/v2 v2.8.13 h1:si/8rLw5BZZTWCCiMm9A3f6x+RmqYfrkEeXCgpX5ick= github.com/tdewolff/parse/v2 v2.8.13/go.mod h1:XdsoSFThlVIRIajAuqz1evNY7bagZS8LBOPA3aVopwQ= -github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk= github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= -github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= -github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU= github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0= github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -757,16 +448,10 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= -github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= -github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= -github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= 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.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= @@ -779,32 +464,6 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8= go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -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/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw= -go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= -go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= -go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= -go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= -go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= @@ -815,21 +474,15 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -gocloud.dev v0.46.0 h1:niIuZwSjMtBx8K+ITB2s5kZullB13PGOS2ZoQPZxQ4Q= -gocloud.dev v0.46.0/go.mod h1:ACQe+2qO+hEO+pdcvvsM+RB63r8TyGD1W3ESCLFyzvM= golang.org/x/arch v0.28.0 h1:wVwVdqsTuUbJvhYVCspQYwZXHNYeLSoZnmHD+ggddpQ= golang.org/x/arch v0.28.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= @@ -841,84 +494,32 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= -golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY= golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.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.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -933,31 +534,15 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= @@ -969,45 +554,19 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1037,12 +596,9 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -1053,173 +609,39 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk= golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.285.0 h1:B7eHHoKGAX/LrPkQvhQqnGwjgWxofbdGwCTQvpm8FkM= -google.golang.org/api v0.285.0/go.mod h1:NlOlUIr8MPoIhT9Bb/oUnRuHbJOLwxb6JSYJM8Yz+jQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20260618152121-87f3d3e198d3 h1:QS9ra9U3VDDJzRX51Drz8usQSC4ACEcrDOdcoNafSnA= -google.golang.org/genproto v0.0.0-20260618152121-87f3d3e198d3/go.mod h1:V5M1lxGXNUICs0aOqAMsK6HtmLnCyuzY031uOQS9rJE= -google.golang.org/genproto/googleapis/api v0.0.0-20260618152121-87f3d3e198d3 h1:ctPmKL12ZsoKAlmPUsoW70zEDiYF+/H6aLieXxgAU0k= -google.golang.org/genproto/googleapis/api v0.0.0-20260618152121-87f3d3e198d3/go.mod h1:Z4WJ5pJOYWFWcHEQUelD5QaZDknIQkpIL/+fyJOT9+A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260618152121-87f3d3e198d3 h1:phvBWCAQMGN1945mp5fjCXP6jEF0+a0+4TjokS4sxNY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260618152121-87f3d3e198d3/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= @@ -1227,9 +649,7 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= @@ -1241,10 +661,8 @@ gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw= gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1261,14 +679,7 @@ gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXD gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= -howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= modernc.org/cc/v4 v4.28.4 h1:Hd/4Es+MBj+/7hSdZaisNyu6bv3V0Dp2MdllyfqaH+c= modernc.org/cc/v4 v4.28.4/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= modernc.org/ccgo/v4 v4.34.4 h1:OVnSOWQjVKOYkFxoHYB+qQmSHK5gqMqARM+K9DpR/Ws= @@ -1297,10 +708,5 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -software.sslmate.com/src/go-pkcs12 v0.7.2 h1:Rh9FoMaI5k7Oo6EOS+2/BnoZ+JFIS+XHjM0VGkSPXLM= -software.sslmate.com/src/go-pkcs12 v0.7.2/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index e6724ef..0a83789 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -10,9 +10,11 @@ import ( "github.com/engigu/baihu-panel/internal/constant" "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/executor" "github.com/engigu/baihu-panel/internal/logger" "github.com/engigu/baihu-panel/internal/router" "github.com/engigu/baihu-panel/internal/services" + "github.com/engigu/baihu-panel/internal/tunnel" "github.com/engigu/baihu-panel/internal/utils" "github.com/gin-gonic/gin" @@ -26,8 +28,19 @@ type App struct { func New() *App { app := InitBasic() app.initRouter() + + // 初始化完成后将路由引擎注入到隧道模块,以支持高性能的纯内存代理 + tunnel.SetLocalEngine(app.Router) + + // 初始化隧道后台服务 (读取配置决定角色并启动服务) + tunnel.Init() + + // 启动系统级后台定时任务调度器 + executor.InitSysCron() + // 初始化完成后回收一次内存 utils.FreeMemory() + return app } @@ -45,6 +58,7 @@ func InitBasic() *App { // 自动加载配置 (内部会自动处理 BH_CONFIG_PATH 环境变量与默认路径的优先级) app.initConfigWithPath("") app.initDatabase() + logger.Infof("[System] 低于1.0.11版本升级最新版本错误指引: https://github.com/engigu/baihu-panel/issues/64") globalApp = app }) diff --git a/internal/constant/constant.go b/internal/constant/constant.go index 7bd0c2b..695a4cb 100644 --- a/internal/constant/constant.go +++ b/internal/constant/constant.go @@ -14,20 +14,20 @@ const ( DefaultTaskTimeout = 30 // Settings Section 常量 - SectionSite = "site" - SectionSystem = "system" - SectionScheduler = "scheduler" - SectionSecurity = "security" - SectionNotify = "notify" + SectionSite = "site" + SectionSystem = "system" + SectionScheduler = "scheduler" + SectionSecurity = "security" + SectionNotify = "notify" // Site Settings Key 常量 KeyTitle = "title" KeySubtitle = "subtitle" KeyIcon = "icon" - KeyPageSize = "page_size" - KeyCookieDays = "cookie_days" - KeyOpenapiToken = "openapi_token" - KeyActiveWebUI = "active_webui" + KeyPageSize = "page_size" + KeyCookieDays = "cookie_days" + KeyOpenapiToken = "openapi_token" + KeyActiveWebUI = "active_webui" // Security Settings Key 常量 KeySecret = "secret" diff --git a/internal/constant/interconnect.go b/internal/constant/interconnect.go new file mode 100644 index 0000000..b5ce1b2 --- /dev/null +++ b/internal/constant/interconnect.go @@ -0,0 +1,22 @@ +package constant + +const ( + // CookieActiveInterconnectNodeID 穿越状态下标识目标子节点 ID 的 Cookie 键名 + CookieActiveInterconnectNodeID = "active_interconnect_node_id" + + // SectionInterconnect 互联设置分组 + SectionInterconnect = "interconnect" + + // 互联设置相关 Key + KeyInterconnectToken = "interconnect_token" + KeyInterconnectParentURL = "interconnect_parent_url" + KeyInterconnectParentToken = "interconnect_parent_token" + KeyInterconnectRole = "interconnect_role" + + // 互联角色 + InterconnectRoleMaster = "master" + InterconnectRoleChild = "child" + + // 互联系统事件 + EventInterconnectChildStatus = "interconnect_child_status" +) diff --git a/internal/controllers/interconnect_controller.go b/internal/controllers/interconnect_controller.go new file mode 100644 index 0000000..781858d --- /dev/null +++ b/internal/controllers/interconnect_controller.go @@ -0,0 +1,526 @@ +package controllers + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net" + "net/http" + "strings" + "time" + + "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/models" + "github.com/engigu/baihu-panel/internal/models/vo" + "github.com/engigu/baihu-panel/internal/services" + "github.com/engigu/baihu-panel/internal/tunnel" + "github.com/engigu/baihu-panel/internal/utils" + + "github.com/gin-gonic/gin" +) + +type InterconnectController struct { + interconnectService *services.InterconnectService + httpClient *http.Client +} + +func NewInterconnectController(interconnectService *services.InterconnectService) *InterconnectController { + return &InterconnectController{ + interconnectService: interconnectService, + httpClient: &http.Client{ + Timeout: 10 * time.Second, + }, + } +} + +// GetNodes 获取互联节点列表 +func (ic *InterconnectController) GetNodes(c *gin.Context) { + nodes, err := ic.interconnectService.GetNodes() + if err != nil { + utils.ServerError(c, "获取互联节点失败") + return + } + utils.Success(c, nodes) +} + +// CreateNode 创建互联节点 +func (ic *InterconnectController) CreateNode(c *gin.Context) { + var req struct { + Name string `json:"name" binding:"required"` + URL string `json:"url"` + Token string `json:"token" binding:"required"` + Remark string `json:"remark"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + node, err := ic.interconnectService.CreateNode(req.Name, req.URL, req.Token, req.Remark) + if err != nil { + utils.ServerError(c, "创建互联节点失败") + return + } + + utils.Success(c, node) +} + +// UpdateNode 更新互联节点 +func (ic *InterconnectController) UpdateNode(c *gin.Context) { + id := c.Param("id") + if id == "" { + utils.BadRequest(c, "无效的节点ID") + return + } + + var req struct { + Name string `json:"name" binding:"required"` + URL string `json:"url"` + Token string `json:"token" binding:"required"` + Remark string `json:"remark"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + node, err := ic.interconnectService.UpdateNode(id, req.Name, req.URL, req.Token, req.Remark) + if err != nil { + utils.ServerError(c, "更新互联节点失败") + return + } + + utils.Success(c, node) +} + +// DeleteNode 删除互联节点 +func (ic *InterconnectController) DeleteNode(c *gin.Context) { + id := c.Param("id") + if id == "" { + utils.BadRequest(c, "无效的节点ID") + return + } + + err := ic.interconnectService.DeleteNode(id) + if err != nil { + utils.ServerError(c, "删除互联节点失败") + return + } + + utils.Success(c, nil) +} + +// GetNodeStatus 获取单个子节点的状态 +func (ic *InterconnectController) GetNodeStatus(c *gin.Context) { + id := c.Param("id") + node, err := ic.interconnectService.GetNodeByID(id) + if err != nil { + utils.NotFound(c, "节点不存在") + return + } + + // 针对反向隧道节点状态检测的特判 + if strings.HasPrefix(node.URL, "tunnel://") { + sess := tunnel.GetSession(node.ID) + if sess == nil { + c.JSON(200, gin.H{"code": 500, "msg": "节点离线或反向隧道未建立", "data": nil}) + return + } + + // 使用当前 Yamux Session 的虚拟底层连接进行拨号 + transport := &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return sess.Session.Open() + }, + } + client := &http.Client{ + Transport: transport, + Timeout: 5 * time.Second, + } + + req, err := http.NewRequest("GET", "http://tunnel.local/api/v1/monitor", nil) + if err != nil { + utils.ServerError(c, "构建检测请求失败") + return + } + req.Header.Set("Authorization", "Bearer "+node.Token) + + resp, err := client.Do(req) + if err != nil { + c.JSON(200, gin.H{"code": 500, "msg": "与子节点逆向连接通讯失败", "data": nil}) + return + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode != 200 { + c.JSON(200, gin.H{"code": 500, "msg": "子节点检测异常", "data": string(body)}) + return + } + + var jsonResp map[string]interface{} + if err := json.Unmarshal(body, &jsonResp); err != nil { + utils.ServerError(c, "解析节点检测数据失败") + return + } + + if dataMap, ok := jsonResp["data"].(map[string]interface{}); ok { + dataMap["tunnel_connected"] = true + dataMap["tunnel_url"] = node.URL + if hostMap, ok := dataMap["host"].(map[string]interface{}); ok { + hostMap["tx_bytes"] = node.Metrics.TxBytes + hostMap["rx_bytes"] = node.Metrics.RxBytes + } + } + + utils.Success(c, jsonResp["data"]) + return + } + + apiURL := strings.TrimRight(node.URL, "/") + "/api/v1/monitor" + req, err := http.NewRequest("GET", apiURL, nil) + if err != nil { + utils.ServerError(c, "构建请求失败") + return + } + req.Header.Set("Authorization", "Bearer "+node.Token) + + resp, err := ic.httpClient.Do(req) + if err != nil { + c.JSON(200, gin.H{"code": 500, "msg": "节点离线或网络不可达", "data": nil}) + return + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode != 200 { + c.JSON(200, gin.H{"code": 500, "msg": "节点返回异常", "data": string(body)}) + return + } + + var jsonResp map[string]interface{} + if err := json.Unmarshal(body, &jsonResp); err != nil { + utils.ServerError(c, "解析节点响应失败") + return + } + + utils.Success(c, jsonResp["data"]) +} + +// SyncScript 将脚本同步到指定的节点列表 +func (ic *InterconnectController) SyncScript(c *gin.Context) { + var req struct { + NodeIDs []string `json:"node_ids" binding:"required"` + Filename string `json:"filename" binding:"required"` + Content string `json:"content" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + results := make([]map[string]interface{}, 0) + + for _, nodeID := range req.NodeIDs { + node, err := ic.interconnectService.GetNodeByID(nodeID) + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "节点不存在"}) + continue + } + + client, apiURL, err := ic.getClientAndURL(&node, "/api/v1/scripts/save") + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "反向隧道未连接"}) + continue + } + + payload := map[string]interface{}{ + "filename": req.Filename, + "content": req.Content, + } + payloadBytes, _ := json.Marshal(payload) + + httpReq, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes)) + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "构建请求失败"}) + continue + } + httpReq.Header.Set("Authorization", "Bearer "+node.Token) + httpReq.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(httpReq) + if err != nil || resp.StatusCode != 200 { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "同步请求失败或超时"}) + } else { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": true, "msg": "同步成功"}) + } + if resp != nil { + resp.Body.Close() + } + } + + utils.Success(c, results) +} + +// SyncEnv 将环境变量同步到指定的节点列表 +func (ic *InterconnectController) SyncEnv(c *gin.Context) { + var req struct { + NodeIDs []string `json:"node_ids" binding:"required"` + Envs []struct { + Name string `json:"name"` + Value string `json:"value"` + Remark string `json:"remark"` + } `json:"envs" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + results := make([]map[string]interface{}, 0) + + for _, nodeID := range req.NodeIDs { + node, err := ic.interconnectService.GetNodeByID(nodeID) + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "节点不存在"}) + continue + } + + client, apiURL, err := ic.getClientAndURL(&node, "/api/v1/env") + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "反向隧道未连接"}) + continue + } + + successCount := 0 + for _, env := range req.Envs { + payload := map[string]interface{}{ + "name": env.Name, + "value": env.Value, + "remark": env.Remark, + "type": "normal", + } + payloadBytes, _ := json.Marshal(payload) + + httpReq, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes)) + if err != nil { + continue + } + httpReq.Header.Set("Authorization", "Bearer "+node.Token) + httpReq.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(httpReq) + if err == nil && resp.StatusCode == 200 { + successCount++ + } + if resp != nil { + resp.Body.Close() + } + } + + results = append(results, map[string]interface{}{ + "node_id": nodeID, + "success": true, + "msg": "同步完成", + "count": successCount, + }) + } + + utils.Success(c, results) +} + +// SyncTask 将任务同步到指定的节点列表 +func (ic *InterconnectController) SyncTask(c *gin.Context) { + var req struct { + NodeIDs []string `json:"node_ids" binding:"required"` + Tasks []vo.TaskVO `json:"tasks" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + results := make([]map[string]interface{}, 0) + + for _, nodeID := range req.NodeIDs { + node, err := ic.interconnectService.GetNodeByID(nodeID) + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "节点不存在"}) + continue + } + + client, apiURL, err := ic.getClientAndURL(&node, "/api/v1/tasks/bulk_save") + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "反向隧道未连接"}) + continue + } + + payloadBytes, _ := json.Marshal(req.Tasks) + + httpReq, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes)) + if err != nil { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "构建请求失败"}) + continue + } + httpReq.Header.Set("Authorization", "Bearer "+node.Token) + httpReq.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(httpReq) + if err != nil || resp.StatusCode != 200 { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": false, "msg": "同步请求失败或超时"}) + } else { + results = append(results, map[string]interface{}{"node_id": nodeID, "success": true, "msg": "同步成功"}) + } + if resp != nil { + resp.Body.Close() + } + } + + utils.Success(c, results) +} + +// HandleTunnel 接受子节点 WebSocket 连接请求 +func (ic *InterconnectController) HandleTunnel(c *gin.Context) { + tunnel.HandleTunnel(c) +} + +// ProxyRequest 代理转发请求至目标节点 +func (ic *InterconnectController) ProxyRequest(c *gin.Context) { + nodeID := c.Param("node_id") + path := c.Param("path") + if nodeID == "" { + utils.BadRequest(c, "Node ID required") + return + } + + node, err := ic.interconnectService.GetNodeByID(nodeID) + if err != nil { + utils.NotFound(c, "Node not found") + return + } + + if strings.HasPrefix(node.URL, "tunnel://") { + // 走 WebSocket 逆向隧道 (基于 Yamux 流式多路复用) + err := tunnel.ProxyHTTP(nodeID, c, path) + if err != nil { + utils.ServerError(c, "Tunnel request failed: "+err.Error()) + } + return + } + + // 走普通 HTTP 直连 + // Construct the target URL + targetURL := strings.TrimRight(node.URL, "/") + path + if c.Request.URL.RawQuery != "" { + targetURL += "?" + c.Request.URL.RawQuery + } + + req, err := http.NewRequest(c.Request.Method, targetURL, c.Request.Body) + if err != nil { + utils.ServerError(c, "Failed to create proxy request") + return + } + + // Copy headers + req.Header = c.Request.Header.Clone() + + // If the node token exists, append it as Bearer Auth + if node.Token != "" { + req.Header.Set("Authorization", "Bearer "+node.Token) + } + + resp, err := ic.httpClient.Do(req) + if err != nil { + utils.ServerError(c, "Failed to connect to target node: "+err.Error()) + return + } + defer resp.Body.Close() + + for k, v := range resp.Header { + for _, vv := range v { + c.Writer.Header().Add(k, vv) + } + } + c.Status(resp.StatusCode) + io.Copy(c.Writer, resp.Body) +} + +// ReportMonitorData 接收子节点上报的监控数据 +func (ic *InterconnectController) ReportMonitorData(c *gin.Context) { + var req models.NodeMetrics + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.JSON(401, gin.H{"error": "missing authorization"}) + return + } + tokenStr := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer ")) + + node, err := ic.interconnectService.GetNodeByToken(tokenStr) + if err != nil { + c.JSON(401, gin.H{"error": "invalid token"}) + return + } + + err = ic.interconnectService.UpdateNodeMonitorData(node.ID, req) + if err != nil { + utils.ServerError(c, "更新节点数据失败") + return + } + utils.Success(c, gin.H{ + "tunnel_url": node.URL, + }) +} + +// GetChildStatus 获取本机作为子节点的连接状态 +func (ic *InterconnectController) GetChildStatus(c *gin.Context) { + settingsSvc := services.NewSettingsService() + parentURL := settingsSvc.Get(constant.SectionInterconnect, constant.KeyInterconnectParentURL) + parentToken := settingsSvc.Get(constant.SectionInterconnect, constant.KeyInterconnectParentToken) + + connected := tunnel.IsTunnelConnected() + tunnelURL := tunnel.GetLocalTunnelURL() + + utils.Success(c, gin.H{ + "parent_url": parentURL, + "parent_token": parentToken, + "connected": connected, + "tunnel_url": tunnelURL, + "tx_bytes": tunnel.GetTxBytes(), + "rx_bytes": tunnel.GetRxBytes(), + }) +} + +// getClientAndURL 辅助方法:根据节点类型决定走直连还是隧道,并返回对应的 Client 和完整 URL +func (ic *InterconnectController) getClientAndURL(node *models.InterconnectNode, path string) (*http.Client, string, error) { + if strings.HasPrefix(node.URL, "tunnel://") { + sess := tunnel.GetSession(node.ID) + if sess == nil { + return nil, "", net.ErrClosed + } + transport := &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return sess.Session.Open() + }, + } + client := &http.Client{ + Transport: transport, + Timeout: 10 * time.Second, + } + return client, "http://tunnel.local" + path, nil + } + + targetURL := strings.TrimRight(node.URL, "/") + path + return ic.httpClient, targetURL, nil +} diff --git a/internal/controllers/monitor_controller.go b/internal/controllers/monitor_controller.go index a7121a8..50b8448 100644 --- a/internal/controllers/monitor_controller.go +++ b/internal/controllers/monitor_controller.go @@ -1,34 +1,20 @@ package controllers import ( - "math/rand" "net/http" "runtime" - "sync" "time" - "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/services" "github.com/engigu/baihu-panel/internal/services/tasks" "github.com/engigu/baihu-panel/internal/utils" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/disk" - "github.com/shirou/gopsutil/v3/host" - "github.com/shirou/gopsutil/v3/mem" ) type MonitorController struct { executorService *tasks.ExecutorService - - // 缓存物理机状态 - hostMu sync.RWMutex - lastUpdate time.Time - cpuPercent float64 - vMem *mem.VirtualMemoryStat - diskUsage *disk.UsageStat - hostInfo *host.InfoStat } func NewMonitorController(executorService *tasks.ExecutorService) *MonitorController { @@ -49,118 +35,52 @@ func (mc *MonitorController) GetSystemMonitor(c *gin.Context) { utils.Success(c, data) } -// MonitorWS WebSocket 获取系统监控数据 -func (mc *MonitorController) MonitorWS(c *gin.Context) { - ws, err := monitorUpgrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - return - } - defer ws.Close() +// MonitorSSE Server-Sent Events 获取系统监控数据 +func (mc *MonitorController) MonitorSSE(c *gin.Context) { + // 设置 SSE 响应头 + c.Writer.Header().Set("Content-Type", "text/event-stream") + c.Writer.Header().Set("Cache-Control", "no-cache") + c.Writer.Header().Set("Connection", "keep-alive") + c.Writer.Header().Set("Transfer-Encoding", "chunked") // 初始发送一次数据 - if err := mc.sendMonitorData(ws); err != nil { + if err := mc.sendMonitorDataSSE(c); err != nil { return } - ticker := time.NewTicker(3 * time.Second) + ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: - if err := mc.sendMonitorData(ws); err != nil { + if err := mc.sendMonitorDataSSE(c); err != nil { return // 客户端断开连接或发送失败 } case <-c.Request.Context().Done(): - return + return // 连接已断开,立即退出 } } } -func (mc *MonitorController) sendMonitorData(ws *websocket.Conn) error { +func (mc *MonitorController) sendMonitorDataSSE(c *gin.Context) error { data := mc.getMonitorData() - return ws.WriteJSON(gin.H{ + // 使用 Gin 提供的 SSE 方法 + c.SSEvent("message", gin.H{ "code": 200, "data": data, "msg": "success", }) -} - -func (mc *MonitorController) updateHostMetrics() { - mc.hostMu.Lock() - defer mc.hostMu.Unlock() - - // 缓存 2 秒 - if time.Since(mc.lastUpdate) < 2*time.Second && mc.vMem != nil { - return - } - - if constant.DemoMode { - mc.updateDemoMetrics() - return - } - - cpuPercents, _ := cpu.Percent(0, false) - if len(cpuPercents) > 0 { - mc.cpuPercent = cpuPercents[0] - } - mc.vMem, _ = mem.VirtualMemory() - mc.diskUsage, _ = disk.Usage("/") - mc.hostInfo, _ = host.Info() - mc.lastUpdate = time.Now() -} - -func (mc *MonitorController) updateDemoMetrics() { - mc.cpuPercent = 10 + rand.Float64()*40 // 10% - 50% 的随机 CPU 波动 - - totalMem := uint64(8 * 1024 * 1024 * 1024) // 8GB 内存 - usedMem := uint64(float64(totalMem) * (0.3 + rand.Float64()*0.3)) // 30% - 60% 随机使用率 - mc.vMem = &mem.VirtualMemoryStat{ - Total: totalMem, - Used: usedMem, - UsedPercent: float64(usedMem) / float64(totalMem) * 100, - } - - totalDisk := uint64(500 * 1024 * 1024 * 1024) // 500GB 硬盘 - usedDisk := uint64(float64(totalDisk) * 0.45) // 固定 45% 使用率 - mc.diskUsage = &disk.UsageStat{ - Total: totalDisk, - Used: usedDisk, - UsedPercent: float64(usedDisk) / float64(totalDisk) * 100, - } - - mc.hostInfo = &host.InfoStat{ - Platform: "Demo Environment", - OS: "linux", - Uptime: uint64(time.Now().Unix() - 1700000000), // 生成一个较长且持续增加的运行时间 - } - mc.lastUpdate = time.Now() + c.Writer.Flush() + return nil } func (mc *MonitorController) getMonitorData() gin.H { - var m runtime.MemStats - runtime.ReadMemStats(&m) + rt := services.GetMonitorService().GetRuntimeMetrics() + m := rt.MemStats - // 更新并读取缓存的物理主机指标 - mc.updateHostMetrics() - - mc.hostMu.RLock() - cpuPercent := mc.cpuPercent - vMem := mc.vMem - diskUsage := mc.diskUsage - hostInfo := mc.hostInfo - mc.hostMu.RUnlock() - - // 提供默认值防空指针 - if vMem == nil { - vMem = &mem.VirtualMemoryStat{} - } - if diskUsage == nil { - diskUsage = &disk.UsageStat{} - } - if hostInfo == nil { - hostInfo = &host.InfoStat{} - } + // 调用统一的监控服务获取物理机指标 + metrics := services.GetMonitorService().GetHostMetrics() return gin.H{ "env": gin.H{ @@ -168,18 +88,18 @@ func (mc *MonitorController) getMonitorData() gin.H { "arch": runtime.GOARCH, "go_version": runtime.Version(), "num_cpu": runtime.NumCPU(), - "goroutines": runtime.NumGoroutine(), + "goroutines": rt.NumGoroutine, }, "host": gin.H{ - "cpu_percent": cpuPercent, - "mem_total": vMem.Total, - "mem_used": vMem.Used, - "mem_percent": vMem.UsedPercent, - "disk_total": diskUsage.Total, - "disk_used": diskUsage.Used, - "disk_percent": diskUsage.UsedPercent, - "uptime": hostInfo.Uptime, - "platform": hostInfo.Platform + " " + hostInfo.PlatformVersion, + "cpu_percent": metrics.CPUPercent, + "mem_total": metrics.VMem.Total, + "mem_used": metrics.VMem.Used, + "mem_percent": metrics.VMem.UsedPercent, + "disk_total": metrics.DiskUsage.Total, + "disk_used": metrics.DiskUsage.Used, + "disk_percent": metrics.DiskUsage.UsedPercent, + "uptime": metrics.HostInfo.Uptime, + "platform": metrics.HostInfo.Platform + " " + metrics.HostInfo.PlatformVersion, }, "mem": gin.H{ "alloc": m.Alloc, diff --git a/internal/controllers/settings_controller.go b/internal/controllers/settings_controller.go index ad41ab4..7f6c680 100644 --- a/internal/controllers/settings_controller.go +++ b/internal/controllers/settings_controller.go @@ -19,6 +19,7 @@ import ( "github.com/engigu/baihu-panel/internal/models/vo" "github.com/engigu/baihu-panel/internal/services" "github.com/engigu/baihu-panel/internal/services/tasks" + "github.com/engigu/baihu-panel/internal/tunnel" "github.com/engigu/baihu-panel/internal/utils" "github.com/gin-gonic/gin" @@ -134,6 +135,20 @@ func (sc *SettingsController) ChangePassword(c *gin.Context) { func (sc *SettingsController) GetSiteSettings(c *gin.Context) { settings := sc.settingsService.GetSection(constant.SectionSite) + // 纠正数据库中的空值,防止因配置冲突被意外置空 + if settings[constant.KeyTitle] == "" { + settings[constant.KeyTitle] = "白虎面板" + sc.settingsService.Set(constant.SectionSite, constant.KeyTitle, "白虎面板") + } + if settings[constant.KeySubtitle] == "" { + settings[constant.KeySubtitle] = "极致轻量、高性能的自动化任务调度平台" + sc.settingsService.Set(constant.SectionSite, constant.KeySubtitle, "极致轻量、高性能的自动化任务调度平台") + } + if settings[constant.KeyIcon] == "" { + settings[constant.KeyIcon] = constant.DefaultIcon + sc.settingsService.Set(constant.SectionSite, constant.KeyIcon, constant.DefaultIcon) + } + // 解析 JSON 格式的 OpenAPI Token if tokenJson, ok := settings[constant.KeyOpenapiToken]; ok && tokenJson != "" { var tokenConfig vo.TokenConfig @@ -164,11 +179,25 @@ func (sc *SettingsController) GetSiteSettings(c *gin.Context) { // GetPublicSiteSettings 获取公开的站点设置(无需认证) func (sc *SettingsController) GetPublicSiteSettings(c *gin.Context) { settings := sc.settingsService.GetSection(constant.SectionSite) + + title := settings[constant.KeyTitle] + if title == "" { + title = "白虎面板" + } + subtitle := settings[constant.KeySubtitle] + if subtitle == "" { + subtitle = "极致轻量、高性能的自动化任务调度平台" + } + icon := settings[constant.KeyIcon] + if icon == "" { + icon = constant.DefaultIcon + } + // 只返回公开信息 utils.Success(c, gin.H{ - constant.KeyTitle: settings[constant.KeyTitle], - constant.KeySubtitle: settings[constant.KeySubtitle], - constant.KeyIcon: settings[constant.KeyIcon], + constant.KeyTitle: title, + constant.KeySubtitle: subtitle, + constant.KeyIcon: icon, "demo_mode": constant.DemoMode, }) } @@ -530,6 +559,13 @@ func (sc *SettingsController) UpdateSectionSettings(c *gin.Context) { return } + // 当互联配置发生改变时,通知 tunnel 模块立刻应用新角色,启动或停止相关的后台协程 + if section == constant.SectionInterconnect { + if role, ok := values[constant.KeyInterconnectRole]; ok { + tunnel.ApplyRole(role) + } + } + utils.SuccessMsg(c, "保存成功") } diff --git a/internal/controllers/task_controller.go b/internal/controllers/task_controller.go index 49207cb..ad0ba0f 100644 --- a/internal/controllers/task_controller.go +++ b/internal/controllers/task_controller.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/database" "github.com/engigu/baihu-panel/internal/logger" "github.com/engigu/baihu-panel/internal/models" "github.com/engigu/baihu-panel/internal/models/vo" @@ -227,6 +228,87 @@ func (tc *TaskController) CreateTask(c *gin.Context) { utils.Success(c, vo.ToTaskVO(task)) } +// BulkSaveTask 批量保存/导入任务配置(用于主节点下发同步) +// @Summary 批量保存任务 +// @Description 批量导入任务配置,如果ID或同名存在则更新,不存在则创建 +// @Tags 任务管理 +// @Accept json +// @Produce json +// @Security BearerAuth +// @Router /tasks/bulk_save [post] +func (tc *TaskController) BulkSaveTask(c *gin.Context) { + var reqs []vo.TaskVO + + if err := c.ShouldBindJSON(&reqs); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + for _, req := range reqs { + param := tasks.TaskParam{ + Name: req.Name, + Remark: req.Remark, + Command: req.Command, + PreCommand: req.PreCommand, + PostCommand: req.PostCommand, + Tags: req.Tags, + Type: req.Type, + Config: req.Config, + Schedule: req.Schedule, + Timeout: req.Timeout, + WorkDir: req.WorkDir, + CleanConfig: req.CleanConfig, + Envs: req.Envs, + Languages: req.Languages, + AgentID: req.AgentID, + TriggerType: req.TriggerType, + RetryCount: req.RetryCount, + RetryInterval: req.RetryInterval, + RandomRange: req.RandomRange, + PinType: req.PinType, + Enabled: req.Enabled, + SourceID: "", // 不直接覆盖 + } + + var existingTask *models.Task + // 优先按 ID 匹配 + if req.ID != "" { + existingTask = tc.taskService.GetTaskByID(req.ID) + } + // 如果 ID 没找到,尝试按 Name 匹配 + if existingTask == nil { + var t models.Task + res := database.DB.Where("name = ?", req.Name).First(&t) + if res.Error == nil { + existingTask = &t + } + } + + var savedTask *models.Task + if existingTask != nil { + savedTask = tc.taskService.UpdateTask(existingTask.ID, ¶m) + } else { + savedTask = tc.taskService.CreateTask(¶m) + // 如果原始有 ID,强制覆盖更新 ID 保持强同步一致性 + if req.ID != "" && savedTask != nil { + database.DB.Model(savedTask).Update("id", req.ID) + savedTask.ID = req.ID + } + } + + // 如果是 Agent 任务,通知 Agent;否则添加到本地 cron + if savedTask != nil { + if savedTask.AgentID != nil && *savedTask.AgentID != "" { + tc.agentWSManager.BroadcastTasks(*savedTask.AgentID) + } else { + tc.executorService.AddCronTask(savedTask) + } + } + } + + utils.Success(c, nil) +} + // GetTasks 获取任务列表 // @Summary 获取任务列表 // @Description 分页获取任务列表,支持按名称、Agent ID、标签、类型筛选 diff --git a/internal/controllers/terminal_controller.go b/internal/controllers/terminal_controller.go index 6b68393..663a44e 100644 --- a/internal/controllers/terminal_controller.go +++ b/internal/controllers/terminal_controller.go @@ -224,6 +224,7 @@ func (tc *TerminalController) handlePtyMode(conn *websocket.Conn, userID string) close(pingDone) cmd.Process.Kill() cmd.Wait() + ptmx.Close() // Force close PTY to interrupt the blocking ptmx.Read() in the goroutine wg.Wait() } @@ -369,9 +370,19 @@ func (tc *TerminalController) handlePipeMode(conn *websocket.Conn, userID string } close(pingDone) - stdin.Close() cmd.Process.Kill() cmd.Wait() + + if stdinCloser, ok := stdin.(io.Closer); ok { + stdinCloser.Close() + } + if stdoutCloser, ok := stdout.(io.Closer); ok { + stdoutCloser.Close() + } + if stderrCloser, ok := stderr.(io.Closer); ok { + stderrCloser.Close() + } + wg.Wait() } diff --git a/internal/database/migrate.go b/internal/database/migrate.go index 4e4363e..f015773 100644 --- a/internal/database/migrate.go +++ b/internal/database/migrate.go @@ -29,6 +29,7 @@ var allModels = []interface{}{ &models.NotifyBinding{}, &models.DataRelation{}, &models.DataStorage{}, + &models.InterconnectNode{}, } func Migrate() error { diff --git a/internal/executor/syscron.go b/internal/executor/syscron.go new file mode 100644 index 0000000..daddb62 --- /dev/null +++ b/internal/executor/syscron.go @@ -0,0 +1,69 @@ +package executor + +import ( + "sync" + + "github.com/engigu/baihu-panel/internal/logger" + "github.com/engigu/baihu-panel/internal/systime" + "github.com/robfig/cron/v3" +) + +type SysCronManager struct { + cron *cron.Cron +} + +var ( + sysCronInstance *SysCronManager + sysCronOnce sync.Once +) + + +// InitSysCron 初始化系统的内部定时器 +func InitSysCron(){ + GetSysCron() +} + +// GetSysCron 获取内部系统定时器服务单例 +func GetSysCron() *SysCronManager { + sysCronOnce.Do(func() { + // 使用秒级精度,指定为东八区 + c := cron.New(cron.WithSeconds(), cron.WithLocation(systime.CST)) + c.Start() + sysCronInstance = &SysCronManager{ + cron: c, + } + logger.Infof("[SysCron] 内部系统定时管理器已启动") + }) + return sysCronInstance +} + +// AddJob 添加内部系统任务,spec为cron表达式(支持 @every 30s 这种快捷方式) +func (s *SysCronManager) AddJob(spec string, cmd func()) (cron.EntryID, error) { + id, err := s.cron.AddFunc(spec, cmd) + if err != nil { + logger.Errorf("[SysCron] 无法添加系统任务: %s, err: %v", spec, err) + return 0, err + } + return id, nil +} + +// AddJobWithRun 立即开启一个协程异步执行一次任务,随后将其加入到系统定时任务中 +func (s *SysCronManager) AddJobWithRun(spec string, cmd func()) (cron.EntryID, error) { + // 立即异步执行一次 + go func() { + defer func() { + if r := recover(); r != nil { + logger.Errorf("[SysCron] 立即执行任务时发生 panic: %v", r) + } + }() + cmd() + }() + + // 然后加入定时器 + return s.AddJob(spec, cmd) +} + +// RemoveJob 动态移除指定的系统定时任务 +func (s *SysCronManager) RemoveJob(id cron.EntryID) { + s.cron.Remove(id) +} diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index e9839fa..e1b1284 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -5,6 +5,7 @@ import ( "crypto/subtle" "encoding/json" "net/http" + "strings" "time" "github.com/engigu/baihu-panel/internal/constant" @@ -34,6 +35,47 @@ func AuthRequired() gin.HandlerFunc { } } + // 检查是否携带互联 Token(支持跨面板远程全接口调用) + authHeader := c.GetHeader("Authorization") + if authHeader != "" { + tokenStr := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer ")) + if tokenStr != "" { + settingsSvc := services.NewSettingsService() + interconnectToken := settingsSvc.Get(constant.SectionSite, constant.KeyInterconnectToken) + parentToken := settingsSvc.Get(constant.SectionInterconnect, constant.KeyInterconnectParentToken) + + isMatched := false + h1 := sha256.Sum256([]byte(tokenStr)) + + if interconnectToken != "" { + h2 := sha256.Sum256([]byte(interconnectToken)) + if subtle.ConstantTimeCompare(h1[:], h2[:]) == 1 { + isMatched = true + } + } + + if !isMatched && parentToken != "" { + h2 := sha256.Sum256([]byte(parentToken)) + if subtle.ConstantTimeCompare(h1[:], h2[:]) == 1 { + isMatched = true + } + } + + if isMatched { + // 模拟 Admin 角色 + var adminUser models.User + res := database.DB.Where("role = ?", constant.AdminRole).Limit(1).Find(&adminUser) + if res.Error == nil && res.RowsAffected > 0 { + c.Set("userID", adminUser.ID) + c.Set("username", adminUser.Username) + c.Set("role", adminUser.Role) + c.Next() + return + } + } + } + } + token, err := c.Cookie(constant.CookieName) if err != nil || token == "" { utils.Unauthorized(c, "请先登录") diff --git a/internal/middleware/travel_proxy.go b/internal/middleware/travel_proxy.go new file mode 100644 index 0000000..9ba98ba --- /dev/null +++ b/internal/middleware/travel_proxy.go @@ -0,0 +1,133 @@ +package middleware + +import ( + "context" + "io" + "net/http" + "strings" + "time" + + "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/models" + "github.com/engigu/baihu-panel/internal/services" + "github.com/engigu/baihu-panel/internal/tunnel" + "github.com/gin-gonic/gin" +) + +var travelProxyClient = &http.Client{ + Timeout: 10 * time.Second, +} + +func TravelProxyMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 1. 检查是否存在 active_interconnect_node_id Cookie + nodeID, err := c.Cookie(constant.CookieActiveInterconnectNodeID) + if err != nil || nodeID == "" { + c.Next() + return + } + + // 2. 白名单放行:反向隧道建立连接端点必须直达本机,不能二次代理 + path := c.Request.URL.Path + if strings.HasPrefix(path, "/api/v1/interconnect/tunnel") { + c.Next() + return + } + + // 3. 查询数据库中节点信息 + var node models.InterconnectNode + if err := database.DB.Where("id = ?", nodeID).First(&node).Error; err != nil { + // 节点不存在,说明 Cookie 无效,清除并放行 + c.SetCookie(constant.CookieActiveInterconnectNodeID, "", -1, "/", "", false, false) + c.Next() + return + } + + // 4. 准备代理的路径(若主节点配置了 URLPrefix,需要剥离) + cfg := services.GetConfig() + urlPrefix := strings.TrimSuffix(cfg.Server.URLPrefix, "/") + targetPath := path + if urlPrefix != "" { + targetPath = strings.TrimPrefix(targetPath, urlPrefix) + } + if !strings.HasPrefix(targetPath, "/") { + targetPath = "/" + targetPath + } + + // 5. 执行代理转发 + if strings.HasPrefix(node.URL, "tunnel://") { + // 走 WebSocket 逆向 Yamux 隧道 + err := tunnel.ProxyHTTP(nodeID, c, targetPath) + if err != nil { + // 如果是网页 HTML 导航请求,提供友好降级返回主节点 + if strings.Contains(c.GetHeader("Accept"), "text/html") { + c.SetCookie(constant.CookieActiveInterconnectNodeID, "", -1, "/", "", false, false) + c.Header("Content-Type", "text/html; charset=utf-8") + c.String(200, `

与子节点连接失败,正在自动返回主节点...

`) + c.Abort() + return + } + c.JSON(502, gin.H{"code": 502, "msg": "与子节点逆向隧道通信异常: " + err.Error()}) + c.Abort() + return + } + c.Abort() + return + } + + // 走普通 HTTP 直连代理 + targetURL := strings.TrimRight(node.URL, "/") + targetPath + if c.Request.URL.RawQuery != "" { + targetURL += "?" + c.Request.URL.RawQuery + } + + req, err := http.NewRequest(c.Request.Method, targetURL, c.Request.Body) + if err != nil { + c.JSON(500, gin.H{"code": 500, "msg": "Failed to create proxy request"}) + c.Abort() + return + } + + // 复制请求头 + req.Header = c.Request.Header.Clone() + + // 覆盖认证授权 Header 确保子节点鉴权通过 + if node.Token != "" { + req.Header.Set("Authorization", "Bearer "+node.Token) + } + // 移除 Cookie 头部防干扰 + req.Header.Del("Cookie") + req.Header.Set("X-Tunnel-Proxy", "true") + + resp, err := travelProxyClient.Do(req) + if err != nil { + // 如果是客户端自己主动取消了请求(例如连续刷新、关闭网页等),直接退出,不应视为子节点离线而执行退回主节点的操作 + if c.Request.Context().Err() == context.Canceled || strings.Contains(err.Error(), "context canceled") { + c.Abort() + return + } + if strings.Contains(c.GetHeader("Accept"), "text/html") { + c.SetCookie(constant.CookieActiveInterconnectNodeID, "", -1, "/", "", false, false) + c.Header("Content-Type", "text/html; charset=utf-8") + c.String(200, `

与子节点连接失败,正在自动返回主节点...

`) + c.Abort() + return + } + c.JSON(502, gin.H{"code": 502, "msg": "无法连接至目标子节点: " + err.Error()}) + c.Abort() + return + } + defer resp.Body.Close() + + // 复制响应头 + for k, v := range resp.Header { + for _, vv := range v { + c.Writer.Header().Add(k, vv) + } + } + c.Status(resp.StatusCode) + io.Copy(c.Writer, resp.Body) + c.Abort() + } +} diff --git a/internal/models/interconnect.go b/internal/models/interconnect.go new file mode 100644 index 0000000..2a5a823 --- /dev/null +++ b/internal/models/interconnect.go @@ -0,0 +1,32 @@ +package models + +import ( + "github.com/engigu/baihu-panel/internal/constant" +) + +// NodeMetrics 表示互联节点的性能指标,使用 JSON 存储 +type NodeMetrics struct { + CPUPercent float64 `json:"cpu_percent"` + MemPercent float64 `json:"mem_percent"` + DiskPercent float64 `json:"disk_percent"` + TxBytes uint64 `json:"tx_bytes,omitempty"` + RxBytes uint64 `json:"rx_bytes,omitempty"` +} + +// InterconnectNode represents a connected remote panel +type InterconnectNode struct { + ID string `json:"id" gorm:"primaryKey;size:20"` + Name string `json:"name" gorm:"size:255;not null"` + URL string `json:"url" gorm:"size:255;not null"` + Token string `json:"token" gorm:"size:255"` + Remark string `json:"remark" gorm:"size:500"` + CreatedAt LocalTime `json:"created_at"` + UpdatedAt LocalTime `json:"updated_at"` + Status string `json:"status" gorm:"size:50"` // online / offline + Metrics NodeMetrics `json:"metrics" gorm:"serializer:json"` + LastHeartbeatAt *LocalTime `json:"last_heartbeat_at"` +} + +func (InterconnectNode) TableName() string { + return constant.TablePrefix + "interconnect_nodes" +} diff --git a/internal/router/api_routes.go b/internal/router/api_routes.go index 784107b..acbb79e 100644 --- a/internal/router/api_routes.go +++ b/internal/router/api_routes.go @@ -1,9 +1,9 @@ package router import ( - // "github.com/engigu/baihu-panel/internal/controllers" - "github.com/engigu/baihu-panel/internal/middleware" + "runtime" + "github.com/engigu/baihu-panel/internal/middleware" "github.com/gin-gonic/gin" ) @@ -13,6 +13,12 @@ func initPublicAPIRoutes(api *gin.RouterGroup, c *Controllers) { ctx.JSON(200, gin.H{"message": "pong"}) }) + api.GET("/debug/goroutines", func(ctx *gin.Context) { + buf := make([]byte, 1024*1024) + n := runtime.Stack(buf, true) + ctx.Data(200, "text/plain; charset=utf-8", buf[:n]) + }) + // Authentication routes (无需认证) auth := api.Group("/auth") { @@ -24,6 +30,11 @@ func initPublicAPIRoutes(api *gin.RouterGroup, c *Controllers) { // 公开的站点设置(无需认证) api.GET("/settings/public", c.Settings.GetPublicSiteSettings) + // 隧道模式 (被控端反向连入,使用独立 Token 做 WebSocket 鉴权) + api.GET("/interconnect/tunnel", c.Interconnect.HandleTunnel) + // 子节点主动上报监控数据 (无中间件鉴权,内部鉴权) + api.POST("/interconnect/report", c.Interconnect.ReportMonitorData) + // 内部使用的 API(仅限本地调用,无需 Bearer 认证) internalAPI := api.Group("/internal") internalAPI.Use(middleware.LocalhostOnly()) @@ -66,6 +77,7 @@ func initAuthorizedAPIRoutes(api *gin.RouterGroup, c *Controllers) { registerSystemWSRoutes(adminOnly, c) registerWebUIRoutes(adminOnly, c) registerMonitorRoutes(adminOnly, c) + registerInterconnectRoutes(adminOnly, c) } } @@ -83,6 +95,7 @@ func registerTaskRoutes(g *gin.RouterGroup, c *Controllers) { tasks.POST("", c.Task.CreateTask) tasks.GET("", c.Task.GetTasks) tasks.GET("/:id", c.Task.GetTask) + tasks.POST("/bulk_save", c.Task.BulkSaveTask) tasks.PUT("/:id", c.Task.UpdateTask) tasks.DELETE("/:id", c.Task.DeleteTask) tasks.POST("/batch-delete", c.Task.BatchDeleteTasks) @@ -273,7 +286,7 @@ func registerMonitorRoutes(g *gin.RouterGroup, c *Controllers) { monitor := g.Group("/monitor") { monitor.GET("", c.Monitor.GetSystemMonitor) - monitor.GET("/ws", c.Monitor.MonitorWS) + monitor.GET("/sse", c.Monitor.MonitorSSE) } } @@ -298,3 +311,22 @@ func registerWebUIRoutes(g *gin.RouterGroup, c *Controllers) { webuiGroup.DELETE("/:name", c.WebUI.DeleteWebUI) } } + +func registerInterconnectRoutes(g *gin.RouterGroup, c *Controllers) { + interconnect := g.Group("/interconnect") + { + interconnect.GET("/nodes", c.Interconnect.GetNodes) + interconnect.POST("/nodes", c.Interconnect.CreateNode) + interconnect.PUT("/nodes/:id", c.Interconnect.UpdateNode) + interconnect.DELETE("/nodes/:id", c.Interconnect.DeleteNode) + interconnect.GET("/nodes/:id/status", c.Interconnect.GetNodeStatus) + interconnect.POST("/sync/script", c.Interconnect.SyncScript) + interconnect.POST("/sync/env", c.Interconnect.SyncEnv) + interconnect.POST("/sync/task", c.Interconnect.SyncTask) + + interconnect.GET("/child/status", c.Interconnect.GetChildStatus) + + // 代理模式 (面板穿越) + interconnect.Any("/proxy/:node_id/*path", c.Interconnect.ProxyRequest) + } +} diff --git a/internal/router/events.go b/internal/router/events.go index 1af6f9c..ac68406 100644 --- a/internal/router/events.go +++ b/internal/router/events.go @@ -2,13 +2,13 @@ package router import ( // "fmt" - "time" // "github.com/engigu/baihu-panel/internal/constant" "github.com/engigu/baihu-panel/internal/eventbus" // "github.com/engigu/baihu-panel/internal/logger" // "github.com/engigu/baihu-panel/internal/models" "github.com/engigu/baihu-panel/internal/services" + "github.com/engigu/baihu-panel/internal/executor" ) func setupEventHandlers(subscribers ...eventbus.Subscriber) { @@ -21,12 +21,8 @@ func setupEventHandlers(subscribers ...eventbus.Subscriber) { } func startAppLogCleanup(appLogSvc *services.AppLogService) { - // 初始化时执行一次清理 - appLogSvc.CleanUp() - - // 定期清理(每隔1小时执行一次巡检) - ticker := time.NewTicker(1 * time.Hour) - for range ticker.C { + // 注册到内部系统定时器(并立即执行第一次) + executor.GetSysCron().AddJobWithRun("@every 1h", func() { appLogSvc.CleanUp() - } + }) } diff --git a/internal/router/register.go b/internal/router/register.go index 8065f3d..f2a1c9f 100644 --- a/internal/router/register.go +++ b/internal/router/register.go @@ -29,6 +29,7 @@ func RegisterControllers() *Controllers { // 创建任务执行服务(需要依赖注入) notifyService := services.NewNotificationService() appLogService := services.NewAppLogService() + interconnectService := services.NewInterconnectService() // 清理 task 运行状态的任务可以直接由 executorService 承担或在此处通过 Database 直接清理 // 简单期间,我们使用一个新方法 tasks.CleanupRunningTasks() 或者让 executorService 启动时清理 @@ -42,7 +43,7 @@ func RegisterControllers() *Controllers { // 初始化所有关注系统总线的服务 setupEventHandlers(appLogService, notifyService, loginLogService, systemWSManager) - go startAppLogCleanup(appLogService) + startAppLogCleanup(appLogService) // 初始化并返回控制器 return &Controllers{ @@ -65,6 +66,7 @@ func RegisterControllers() *Controllers { SystemWS: controllers.NewSystemWSController(), WebUI: controllers.NewWebUIController(services.NewWebUIService(settingsService)), Monitor: controllers.NewMonitorController(executorService), + Interconnect: controllers.NewInterconnectController(interconnectService), } } diff --git a/internal/router/router.go b/internal/router/router.go index 0156d44..eaf562d 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -32,6 +32,7 @@ type Controllers struct { SystemWS *controllers.SystemWSController WebUI *controllers.WebUIController Monitor *controllers.MonitorController + Interconnect *controllers.InterconnectController } func Setup(c *Controllers) *gin.Engine { @@ -40,6 +41,7 @@ func Setup(c *Controllers) *gin.Engine { } router := gin.New() router.Use(middleware.GinLogger(), middleware.GinRecovery()) + router.Use(middleware.TravelProxyMiddleware()) // 获取 URL 前缀 cfg := services.GetConfig() diff --git a/internal/services/agent_ws_service.go b/internal/services/agent_ws_service.go index eb3e744..b7ac129 100644 --- a/internal/services/agent_ws_service.go +++ b/internal/services/agent_ws_service.go @@ -7,6 +7,7 @@ import ( "github.com/engigu/baihu-panel/internal/constant" "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/executor" "github.com/engigu/baihu-panel/internal/logger" "github.com/engigu/baihu-panel/internal/models" @@ -78,7 +79,11 @@ func GetAgentWSManager() *AgentWSManager { ipFailCount: make(map[string]int), remoteWaiters: make(map[string]chan *models.AgentTaskResult), } - go agentWSManager.cleanupLoop() + // 启动时,先将所有 "online" 状态的 Agent 重置为 "offline" + NewAgentService().ResetAllAgentsToOffline() + + // 将清理任务注册到系统内部 Cron,每 30 秒执行一次 + executor.GetSysCron().AddJob("@every 30s", agentWSManager.cleanupLoop) }) return agentWSManager } @@ -282,66 +287,51 @@ func (m *AgentWSManager) OnlineCount() int { return len(m.connections) } -// cleanupLoop 清理超时连接 +// cleanupLoop 清理超时连接 (由 SysCron 每 30 秒调用一次) func (m *AgentWSManager) cleanupLoop() { - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() + defer func() { + if r := recover(); r != nil { + logger.Errorf("[AgentWS] cleanupLoop panic: %v", r) + } + }() - // 启动时,先将所有 "online" 状态的 Agent 重置为 "offline" - // 因为 WebSocket 连接在应用启动时是空的,所有 Agent 客观上都是离线状态 - // 等它们重新连接上来后,会变为 "online" - NewAgentService().ResetAllAgentsToOffline() + m.mu.Lock() + defer m.mu.Unlock() + now := time.Now() - for range ticker.C { - func() { - defer func() { - if r := recover(); r != nil { - logger.Errorf("[AgentWS] cleanupLoop panic: %v", r) - } - }() - m.mu.Lock() - now := time.Now() - - // 清理超时连接 - for agentID, conn := range m.connections { - if now.Sub(conn.LastPing) > 2*time.Minute { - // 减少 IP 连接计数 - if conn.IP != "" { - if count, ok := m.ipConnections[conn.IP]; ok && count > 0 { - m.ipConnections[conn.IP] = count - 1 - } - } - conn.Close() - delete(m.connections, agentID) - // 更新数据库状态 - database.DB.Model(&models.Agent{}).Where("id = ?", agentID).Update("status", constant.AgentStatusOffline) - logger.Infof("[AgentWS] Agent #%s 心跳超时,已断开", agentID) + // 清理超时连接 + for agentID, conn := range m.connections { + if now.Sub(conn.LastPing) > 2*time.Minute { + // 减少 IP 连接计数 + if conn.IP != "" { + if count, ok := m.ipConnections[conn.IP]; ok && count > 0 { + m.ipConnections[conn.IP] = count - 1 } } + conn.Close() + delete(m.connections, agentID) + // 更新数据库状态 + database.DB.Model(&models.Agent{}).Where("id = ?", agentID).Update("status", constant.AgentStatusOffline) + logger.Infof("[AgentWS] Agent #%s 心跳超时,已断开", agentID) + } + } - // 定期清理数据库中的过期状态(处理服务重启或异常终止的情况) - // 有些 Agent 虽然没有连接,但数据库状态可能是 "online" - cutoff := now.Add(-2 * time.Minute) - database.DB.Model(&models.Agent{}). - Where("status = ? AND last_seen < ?", constant.AgentStatusOnline, cutoff). - Update("status", constant.AgentStatusOffline) + // 定期清理数据库中的过期状态(处理服务重启或异常终止的情况) + cutoff := now.Add(-2 * time.Minute) + database.DB.Model(&models.Agent{}). + Where("status = ? AND last_seen < ?", constant.AgentStatusOnline, cutoff). + Update("status", constant.AgentStatusOffline) - // 清理过期的限流记录(超过 10 分钟未活动) - - // 清理过期的限流记录(超过 10 分钟未活动) - for ip, lastAttempt := range m.ipLastAttempt { - if now.Sub(lastAttempt) > 10*time.Minute { - delete(m.ipLastAttempt, ip) - delete(m.ipFailCount, ip) - // 只清理没有活跃连接的 IP 计数 - if m.ipConnections[ip] == 0 { - delete(m.ipConnections, ip) - } - } + // 清理过期的限流记录(超过 10 分钟未活动) + for ip, lastAttempt := range m.ipLastAttempt { + if now.Sub(lastAttempt) > 10*time.Minute { + delete(m.ipLastAttempt, ip) + delete(m.ipFailCount, ip) + // 只清理没有活跃连接的 IP 计数 + if m.ipConnections[ip] == 0 { + delete(m.ipConnections, ip) } - - m.mu.Unlock() - }() + } } } diff --git a/internal/services/interconnect_service.go b/internal/services/interconnect_service.go new file mode 100644 index 0000000..bd3adec --- /dev/null +++ b/internal/services/interconnect_service.go @@ -0,0 +1,85 @@ +package services + +import ( + "strings" + + "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/models" + "github.com/engigu/baihu-panel/internal/utils" +) + +type InterconnectService struct{} + +func NewInterconnectService() *InterconnectService { + return &InterconnectService{} +} + +func (s *InterconnectService) GetNodes() ([]*models.InterconnectNode, error) { + var nodes []*models.InterconnectNode + err := database.DB.Find(&nodes).Error + return nodes, err +} + +func (s *InterconnectService) GetNodeByID(id string) (*models.InterconnectNode, error) { + var node models.InterconnectNode + err := database.DB.Where("id = ?", id).First(&node).Error + return &node, err +} + +func (s *InterconnectService) CreateNode(name, url, token, remark string) (*models.InterconnectNode, error) { + nodeID := utils.GenerateID() + if url == "" { + url = "tunnel://" + nodeID + } + node := &models.InterconnectNode{ + ID: nodeID, + Name: name, + URL: url, + Token: strings.ToLower(token), + Remark: remark, + CreatedAt: models.Now(), + UpdatedAt: models.Now(), + } + err := database.DB.Create(node).Error + return node, err +} + +func (s *InterconnectService) UpdateNode(id, name, url, token, remark string) (*models.InterconnectNode, error) { + node, err := s.GetNodeByID(id) + if err != nil { + return nil, err + } + node.Name = name + if url != "" { + node.URL = url + } + node.Token = token + node.Remark = remark + node.UpdatedAt = models.Now() + + err = database.DB.Save(node).Error + return node, err +} + +func (s *InterconnectService) DeleteNode(id string) error { + return database.DB.Where("id = ?", id).Delete(&models.InterconnectNode{}).Error +} + +func (s *InterconnectService) GetNodeByToken(token string) (*models.InterconnectNode, error) { + var node models.InterconnectNode + err := database.DB.Where("token = ?", token).First(&node).Error + return &node, err +} + +func (s *InterconnectService) UpdateNodeMonitorData(id string, metrics models.NodeMetrics) error { + now := models.Now() + return database.DB.Model(&models.InterconnectNode{}). + Where("id = ?", id). + Select("status", "metrics", "last_heartbeat_at", "updated_at"). + Updates(models.InterconnectNode{ + Status: "online", + Metrics: metrics, + LastHeartbeatAt: &now, + UpdatedAt: now, + }).Error +} diff --git a/internal/services/monitor_service.go b/internal/services/monitor_service.go new file mode 100644 index 0000000..7a008ff --- /dev/null +++ b/internal/services/monitor_service.go @@ -0,0 +1,132 @@ +package services + +import ( + "math/rand" + "runtime" + "sync" + "time" + + "github.com/engigu/baihu-panel/internal/constant" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/host" + "github.com/shirou/gopsutil/v3/mem" +) + +type HostMetrics struct { + CPUPercent float64 + VMem *mem.VirtualMemoryStat + DiskUsage *disk.UsageStat + HostInfo *host.InfoStat +} + +type MonitorService struct { + hostMu sync.RWMutex + lastUpdate time.Time + metrics HostMetrics +} + +var ( + monitorServiceInstance *MonitorService + monitorServiceOnce sync.Once +) + +// GetMonitorService 获取系统监控服务单例 +func GetMonitorService() *MonitorService { + monitorServiceOnce.Do(func() { + monitorServiceInstance = &MonitorService{} + }) + return monitorServiceInstance +} + +// GetHostMetrics 获取并返回物理机状态(带有缓存和演示模式伪装) +func (ms *MonitorService) GetHostMetrics() HostMetrics { + ms.hostMu.Lock() + defer ms.hostMu.Unlock() + + // 缓存 2 秒 + if time.Since(ms.lastUpdate) < 2*time.Second && ms.metrics.VMem != nil { + return ms.metrics + } + + if constant.DemoMode { + ms.updateDemoMetrics() + return ms.metrics + } + + cpuPercents, _ := cpu.Percent(0, false) + if len(cpuPercents) > 0 { + ms.metrics.CPUPercent = cpuPercents[0] + } + ms.metrics.VMem, _ = mem.VirtualMemory() + ms.metrics.DiskUsage, _ = disk.Usage("/") + ms.metrics.HostInfo, _ = host.Info() + ms.lastUpdate = time.Now() + + // 提供默认值防空指针 + if ms.metrics.VMem == nil { + ms.metrics.VMem = &mem.VirtualMemoryStat{} + } + if ms.metrics.DiskUsage == nil { + ms.metrics.DiskUsage = &disk.UsageStat{} + } + if ms.metrics.HostInfo == nil { + ms.metrics.HostInfo = &host.InfoStat{} + } + + return ms.metrics +} + +type RuntimeMetrics struct { + NumGoroutine int + MemStats runtime.MemStats +} + +var ( + runtimeMu sync.RWMutex + lastRuntime time.Time + cachedRuntime RuntimeMetrics +) + +// GetRuntimeMetrics 获取 Go 运行时指标(缓存 2 秒,防止高并发下频繁触发 STW) +func (ms *MonitorService) GetRuntimeMetrics() RuntimeMetrics { + runtimeMu.Lock() + defer runtimeMu.Unlock() + + if time.Since(lastRuntime) < 2*time.Second && cachedRuntime.NumGoroutine > 0 { + return cachedRuntime + } + + cachedRuntime.NumGoroutine = runtime.NumGoroutine() + runtime.ReadMemStats(&cachedRuntime.MemStats) + lastRuntime = time.Now() + + return cachedRuntime +} + +func (ms *MonitorService) updateDemoMetrics() { + ms.metrics.CPUPercent = 10 + rand.Float64()*40 // 10% - 50% 的随机 CPU 波动 + + totalMem := uint64(8 * 1024 * 1024 * 1024) // 8GB 内存 + usedMem := uint64(float64(totalMem) * (0.3 + rand.Float64()*0.3)) // 30% - 60% 随机使用率 + ms.metrics.VMem = &mem.VirtualMemoryStat{ + Total: totalMem, + Used: usedMem, + UsedPercent: float64(usedMem) / float64(totalMem) * 100, + } + + totalDisk := uint64(500 * 1024 * 1024 * 1024) // 500GB 硬盘 + usedDisk := uint64(float64(totalDisk) * 0.45) // 固定 45% 使用率 + ms.metrics.DiskUsage = &disk.UsageStat{ + Total: totalDisk, + Used: usedDisk, + UsedPercent: float64(usedDisk) / float64(totalDisk) * 100, + } + + ms.metrics.HostInfo = &host.InfoStat{ + Platform: "Demo Environment", + OS: "linux", + Uptime: uint64(time.Now().Unix() - 1700000000), // 生成一个较长且持续增加的运行时间 + } + ms.lastUpdate = time.Now() +} diff --git a/internal/tunnel/client.go b/internal/tunnel/client.go new file mode 100644 index 0000000..a43892d --- /dev/null +++ b/internal/tunnel/client.go @@ -0,0 +1,254 @@ +package tunnel + +import ( + "crypto/tls" + "fmt" + "math/rand" + "net/http" + "net/url" + "sync" + "time" + + "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/logger" + "github.com/engigu/baihu-panel/internal/services" + "github.com/gorilla/websocket" + "github.com/hashicorp/yamux" +) + +var ( + tunnelActive bool + clientConn *yamux.Session + clientMu sync.Mutex + stopCh chan struct{} + + // LocalEngine 保存全局的 Gin 引擎或 HTTP 处理器,用于接收主节点的隧道请求并进行纯内存函数路由 + LocalEngine http.Handler +) + +// SetLocalEngine 由 bootstrap 层注入 +func SetLocalEngine(engine http.Handler) { + LocalEngine = engine +} + +// StartClient 尝试连接到父节点面板(如果已配置) +func StartClient() { + clientMu.Lock() + if tunnelActive { + clientMu.Unlock() + return + } + tunnelActive = true + stopCh = make(chan struct{}) + clientMu.Unlock() + + go runClientLoop() +} + +// StopClient 停止后台的隧道客户端 +func StopClient() { + clientMu.Lock() + if !tunnelActive { + clientMu.Unlock() + return + } + tunnelActive = false + if stopCh != nil { + close(stopCh) + } + if clientConn != nil { + clientConn.Close() + } + clientMu.Unlock() +} + +// Init 从数据库加载配置并初始化对应的后台隧道服务 +func Init() { + siteConfig := services.NewSettingsService().GetSection(constant.SectionInterconnect) + role := siteConfig[constant.KeyInterconnectRole] + ApplyRole(role) +} + +// ApplyRole 根据互联角色动态启停背景协程服务 +func ApplyRole(role string) { + switch role { + case constant.InterconnectRoleMaster: + // 主控角色:无需后台轮询,等待子节点上报即可 + StopClient() + case constant.InterconnectRoleChild: + // 子节点:关闭可能存在的主控会话,启动连接守护 + CloseAllSessions() + StartClient() + default: + // 未开启或离线 + StopClient() + CloseAllSessions() + } +} + +// IsTunnelConnected 返回当前子节点隧道是否已连接到主节点 +func IsTunnelConnected() bool { + clientMu.Lock() + defer clientMu.Unlock() + if !tunnelActive || clientConn == nil { + return false + } + return !clientConn.IsClosed() +} + +func runClientLoop() { + settingsSvc := services.NewSettingsService() + + const ( + initialBackoff = 5 * time.Second + maxBackoff = 300 * time.Second + fatalBackoff = 60 * time.Second + ) + + backoff := initialBackoff + retryCount := 0 + var lastErrorMsg string + + for { + clientMu.Lock() + if !tunnelActive { + clientMu.Unlock() + return + } + clientMu.Unlock() + + siteConfig := settingsSvc.GetSection(constant.SectionInterconnect) + + role := siteConfig[constant.KeyInterconnectRole] + if role != constant.InterconnectRoleChild { + time.Sleep(10 * time.Second) + continue + } + + parentURL := siteConfig[constant.KeyInterconnectParentURL] + parentToken := siteConfig[constant.KeyInterconnectParentToken] + + if parentURL == "" || parentToken == "" { + time.Sleep(10 * time.Second) + continue + } + + u, err := url.Parse(parentURL) + if err != nil { + logger.Errorf("[Tunnel] 无效的主节点 URL: %v", err) + time.Sleep(10 * time.Second) + continue + } + + scheme := "ws" + if u.Scheme == "https" { + scheme = "wss" + } + + wsURL := fmt.Sprintf("%s://%s/api/v1/interconnect/tunnel", scheme, u.Host) + + header := http.Header{} + header.Set("Authorization", "Bearer "+parentToken) + + dialer := &websocket.Dialer{ + Proxy: http.ProxyFromEnvironment, + HandshakeTimeout: 45 * time.Second, + } + dialer.EnableCompression = true + if u.Scheme == "https" { + dialer.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + } + + conn, resp, err := dialer.Dial(wsURL, header) + if err != nil { + retryCount++ + errMsg := err.Error() + + // 特殊处理致命的认证或配置错误 (仅401作为致命错误,403使用指数退避) + isFatal := resp != nil && resp.StatusCode == 401 + if isFatal { + backoff = fatalBackoff + } + + // 先计算退避时间 (加上 0-8 秒随机抖动防止风暴) + jitter := time.Duration(rand.Intn(8000)) * time.Millisecond + sleepDuration := backoff + jitter + + // 日志记录 + if retryCount == 1 || errMsg != lastErrorMsg { + logger.Errorf("[Tunnel] 连接主节点隧道失败: %v, 下次重试将在 %v 后", err, sleepDuration.Round(time.Second)) + } else { + logger.Warnf("[Tunnel] 仍无法连接主节点,已尝试 %d 次, 下次重试将在 %v 后", retryCount, sleepDuration.Round(time.Second)) + } + + lastErrorMsg = errMsg + + time.Sleep(sleepDuration) + + // 指数级增加退避时间 (最大不超过 maxBackoff) + if !isFatal && backoff < maxBackoff { + backoff *= 2 + if backoff > maxBackoff { + backoff = maxBackoff + } + } + continue + } + + // 连接成功,重置退避状态 + retryCount = 0 + lastErrorMsg = "" + backoff = initialBackoff + + logger.Infof("[Tunnel] 已成功连接到主节点隧道: %s", wsURL) + + netConn := NetConn(conn) + trackedConn := &trafficCounterConn{Conn: netConn} + conf := yamux.DefaultConfig() + conf.EnableKeepAlive = true + + // 我们是子节点,接受来自主节点的请求。 + // 因此在这里,我们是 Yamux Server,负责监听传入的流。 + session, err := yamux.Server(trackedConn, conf) + if err != nil { + logger.Errorf("[Tunnel] 启动 Yamux Server 失败: %v", err) + trackedConn.Close() + time.Sleep(5 * time.Second) + continue + } + + // 如果已经存在上一个活跃的隧道连接,在重建前必须将其显式 Close(),以释放该会话在后台运行的 HTTP 监听与监听协程 + clientMu.Lock() + if clientConn != nil { + clientConn.Close() + } + clientConn = session + clientMu.Unlock() + + services.GetSystemWSManager().Broadcast(constant.EventInterconnectChildStatus, map[string]interface{}{ + "connected": true, + }) + + // 启动子节点主动上报服务 + StartReporter(parentURL, parentToken) + + // 启动本地 HTTP 代理服务 (阻塞直到会话关闭) + serveLocalProxy(session) + + // 断开时停止上报 + StopReporter() + + session.Close() + services.GetSystemWSManager().Broadcast(constant.EventInterconnectChildStatus, map[string]interface{}{ + "connected": false, + }) + logger.Infof("[Tunnel] 已从主节点隧道断开连接") + + // 断开后稍微缓冲一下再去重试 + time.Sleep(2 * time.Second) + } +} + + diff --git a/internal/tunnel/conn.go b/internal/tunnel/conn.go new file mode 100644 index 0000000..8c190c8 --- /dev/null +++ b/internal/tunnel/conn.go @@ -0,0 +1,87 @@ +package tunnel + +import ( + "io" + "net" + "time" + + "github.com/gorilla/websocket" +) + +// wsConn 包装 gorilla websocket 以实现 net.Conn 接口 +type wsConn struct { + conn *websocket.Conn + reader io.Reader +} + +// NetConn 将 WebSocket 连接转换为标准 net.Conn +func NetConn(conn *websocket.Conn) net.Conn { + return &wsConn{ + conn: conn, + } +} + +func (c *wsConn) Read(b []byte) (n int, err error) { + for { + if c.reader == nil { + _, r, err := c.conn.NextReader() + if err != nil { + return 0, err + } + c.reader = r + } + n, err = c.reader.Read(b) + if err == io.EOF { + c.reader = nil + if n > 0 { + return n, nil + } + continue + } + return n, err + } +} + +func (c *wsConn) Write(b []byte) (n int, err error) { + // Yamux 保证了对底层连接的序列化写入(不会出现并发写), + // 因此我们这里不需要加互斥锁 (Mutex)。 + w, err := c.conn.NextWriter(websocket.BinaryMessage) + if err != nil { + return 0, err + } + n, err = w.Write(b) + + // 必须关闭 writer 以触发 WebSocket 的消息帧封包发送 + closeErr := w.Close() + if err == nil { + err = closeErr + } + return n, err +} + +func (c *wsConn) Close() error { + return c.conn.Close() +} + +func (c *wsConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *wsConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *wsConn) SetDeadline(t time.Time) error { + if err := c.conn.SetReadDeadline(t); err != nil { + return err + } + return c.conn.SetWriteDeadline(t) +} + +func (c *wsConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *wsConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} diff --git a/internal/tunnel/proxy.go b/internal/tunnel/proxy.go new file mode 100644 index 0000000..62fbf93 --- /dev/null +++ b/internal/tunnel/proxy.go @@ -0,0 +1,109 @@ +package tunnel + +import ( + "errors" + "net/http" + "net/http/httputil" + "strings" + "time" + + "github.com/engigu/baihu-panel/internal/logger" + + "github.com/gin-gonic/gin" + "github.com/hashicorp/yamux" +) + +// ProxyHTTP 以完全透明的流式转发将 HTTP 请求代理到远程节点 (主节点调用) +func ProxyHTTP(nodeID string, c *gin.Context, targetPath string) error { + sess := GetSession(nodeID) + if sess == nil { + return errors.New("node is offline or tunnel not established") + } + + if sess.Token == "" { + return errors.New("node token not found in session cache") + } + + // 获取真实客户端 IP,传递给子节点用于审计日志等 + clientIP := c.ClientIP() + + director := func(req *http.Request) { + req.URL.Scheme = "http" + req.URL.Host = "tunnel.local" + req.URL.Path = targetPath + // gin 和 httputil 默认会保留并透传 Query 参数 + + req.Header.Set("Authorization", "Bearer "+sess.Token) + // 删除父节点的 Cookie 避免干扰子节点的会话验证 + req.Header.Del("Cookie") + + // 强制加上该 Header,防止出现节点相互代理的死循环 + req.Header.Set("X-Tunnel-Proxy", "true") + + // 传递真实的客户端 IP 给子节点 + if clientIP != "" { + req.Header.Set("X-Forwarded-For", clientIP) + } + } + + proxy := &httputil.ReverseProxy{ + Director: director, + Transport: sess.Transport, // 直接复用 AddSession 时创建的全局连接池,由原生库处理 Context 取消 + ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { + logger.Errorf("[Tunnel] 节点 %s 代理隧道请求发生错误: %v", nodeID, err) + w.WriteHeader(http.StatusBadGateway) + w.Write([]byte("Tunnel connection error: " + err.Error())) + }, + } + + proxy.ServeHTTP(c.Writer, c.Request) + return nil +} + +// serveLocalProxy 本地服务代理逻辑 (子节点调用) +func serveLocalProxy(session *yamux.Session) { + if LocalEngine == nil { + logger.Errorf("[Tunnel] 无法启动本地代理服务,因为 LocalEngine 尚未注入。") + return + } + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // 安全规则 1:防止互相调用的死循环 + if strings.HasPrefix(r.URL.Path, "/api/v1/interconnect/proxy/") { + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("Recursive proxy loops are not allowed via tunnel")) + return + } + + // 安全规则 2:严格限制只能访问本机的 /api/v1/ 接口 + if !strings.HasPrefix(r.URL.Path, "/api/v1/") { + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("Tunnel access is strictly restricted to /api/v1/ endpoints")) + return + } + + // 确保保留 X-Tunnel-Proxy 请求头,以防后续逻辑需要判定 + r.Header.Set("X-Tunnel-Proxy", "true") + + // 纯内存函数调用,直接扔给 Gin 引擎,零网络开销! + LocalEngine.ServeHTTP(w, r) + }) + + server := &http.Server{ + Handler: handler, + ReadTimeout: 15 * time.Second, + IdleTimeout: 15 * time.Second, + } + + // 监听会话关闭信号,主动关闭 http.Server 以彻底释放协程 + go func() { + <-session.CloseChan() + server.Close() + }() + + // yamux.Session 实现了标准的 net.Listener 接口! + err := server.Serve(session) + if err != nil && err != http.ErrServerClosed && err != yamux.ErrSessionShutdown { + logger.Errorf("[Tunnel] Yamux 代理服务意外停止: %v", err) + } +} diff --git a/internal/tunnel/reporter.go b/internal/tunnel/reporter.go new file mode 100644 index 0000000..beff10b --- /dev/null +++ b/internal/tunnel/reporter.go @@ -0,0 +1,117 @@ +package tunnel + +import ( + "bytes" + "encoding/json" + "net/http" + "strings" + "sync" + "time" + + "github.com/engigu/baihu-panel/internal/executor" + "github.com/engigu/baihu-panel/internal/logger" + "github.com/engigu/baihu-panel/internal/services" + "github.com/robfig/cron/v3" +) + +var ( + reporterCronID cron.EntryID + isReporting bool + localTunnelURL string + localTunnelURLMutex sync.RWMutex +) + +// GetLocalTunnelURL 返回当前子节点被分配的隧道地址 +func GetLocalTunnelURL() string { + localTunnelURLMutex.RLock() + defer localTunnelURLMutex.RUnlock() + return localTunnelURL +} + +// StartReporter 启动子节点上报守护进程 +func StartReporter(parentURL, token string) { + if isReporting { + return + } + isReporting = true + + // 修整 parentURL 确保正确指向 /api/v1/interconnect/report + // 如果用户填的是 http://192.168.1.100:8000,我们要加上路径 + baseURL := strings.TrimRight(parentURL, "/") + // 注意兼容如果用户带了 /api/v1 + if !strings.HasSuffix(baseURL, "/api/v1") && !strings.Contains(baseURL, "/api/v1") { + baseURL = baseURL + "/api/v1" + } + reportURL := baseURL + "/interconnect/report" + + // 使用全局 SysCron 并立即上报一次,随后每 45 秒循环一次 + id, err := executor.GetSysCron().AddJobWithRun("@every 45s", func() { + reportMonitorData(reportURL, token) + }) + + if err == nil { + reporterCronID = id + } else { + logger.Warnf("[Tunnel] 无法将上报任务加入 SysCron: %v", err) + } +} + +// StopReporter 停止子节点上报守护进程 +func StopReporter() { + if isReporting { + if reporterCronID != 0 { + executor.GetSysCron().RemoveJob(reporterCronID) + reporterCronID = 0 + } + isReporting = false + } +} + +func reportMonitorData(reportURL, token string) { + // 调用单例监控服务获取底层系统状态 + metrics := services.GetMonitorService().GetHostMetrics() + + payload := map[string]interface{}{ + "cpu_percent": metrics.CPUPercent, + "mem_percent": metrics.VMem.UsedPercent, + "disk_percent": metrics.DiskUsage.UsedPercent, + "tx_bytes": GetTxBytes(), + "rx_bytes": GetRxBytes(), + } + + data, _ := json.Marshal(payload) + + req, err := http.NewRequest("POST", reportURL, bytes.NewBuffer(data)) + if err != nil { + return + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Do(req) + if err != nil { + logger.Warnf("[Tunnel] 上报监控数据失败: %v", err) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + logger.Warnf("[Tunnel] 上报监控数据返回异常状态码: %d", resp.StatusCode) + return + } + + var res struct { + Data struct { + TunnelURL string `json:"tunnel_url"` + } `json:"data"` + } + if err := json.NewDecoder(resp.Body).Decode(&res); err == nil { + if res.Data.TunnelURL != "" { + localTunnelURLMutex.Lock() + localTunnelURL = res.Data.TunnelURL + localTunnelURLMutex.Unlock() + } + } +} diff --git a/internal/tunnel/server.go b/internal/tunnel/server.go new file mode 100644 index 0000000..e0a881f --- /dev/null +++ b/internal/tunnel/server.go @@ -0,0 +1,96 @@ +package tunnel + +import ( + "strings" + + "github.com/engigu/baihu-panel/internal/constant" + "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/logger" + "github.com/engigu/baihu-panel/internal/models" + "github.com/engigu/baihu-panel/internal/services" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "net/http" + "github.com/hashicorp/yamux" +) + +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + EnableCompression: true, // 启用协议级别的 Deflate 数据压缩 +} + +func HandleTunnel(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.JSON(401, gin.H{"error": "missing authorization"}) + return + } + + tokenStr := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer ")) + if tokenStr == "" { + c.JSON(401, gin.H{"error": "invalid token format"}) + return + } + + settingsSvc := services.NewSettingsService() + siteConfig := settingsSvc.GetSection(constant.SectionInterconnect) + role := siteConfig[constant.KeyInterconnectRole] + + if role != constant.InterconnectRoleMaster { + c.JSON(403, gin.H{"error": "interconnect master role not enabled on this server"}) + return + } + + var node models.InterconnectNode + if err := database.DB.Where("token = ?", tokenStr).First(&node).Error; err != nil { + c.JSON(401, gin.H{"error": "invalid interconnect token"}) + return + } + + nodeID := node.ID + + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + logger.Errorf("[Tunnel] 升级隧道连接失败 (NodeID: %s): %v", nodeID, err) + return + } + + // 将 WebSocket 包装为标准的 net.Conn + netConn := NetConn(conn) + + // 获取客户端真实 IP 和端口以更新连接地址,方便面板显示 + clientIP := c.ClientIP() + port := "unknown" + if parts := strings.Split(netConn.RemoteAddr().String(), ":"); len(parts) > 1 { + port = parts[len(parts)-1] + } + realURL := "tunnel://" + clientIP + ":" + port + + if node.URL != realURL { + database.DB.Model(&node).Update("url", realURL) + } + + // 我们是主节点(父节点),主动发起对子节点的请求。 + // 因此在 Yamux 协议中我们扮演 Client,而子节点扮演 Server。 + conf := yamux.DefaultConfig() + conf.EnableKeepAlive = true + + session, err := yamux.Client(netConn, conf) + if err != nil { + logger.Errorf("[Tunnel] 启动 Yamux Client 失败 (NodeID: %s): %v", nodeID, err) + netConn.Close() + return + } + + AddSession(nodeID, tokenStr, session) + logger.Infof("[Tunnel] 已建立来自子节点的隧道连接: %s (%s)", nodeID, node.Name) + + go func(s *yamux.Session) { + <-s.CloseChan() + logger.Infof("[Tunnel] 子节点隧道连接已断开 (NodeID: %s, IsClosed: %v)", nodeID, s.IsClosed()) + RemoveSession(nodeID, s) + }(session) +} diff --git a/internal/tunnel/session.go b/internal/tunnel/session.go new file mode 100644 index 0000000..f55437e --- /dev/null +++ b/internal/tunnel/session.go @@ -0,0 +1,91 @@ +package tunnel + +import ( + "context" + "net" + "net/http" + "sync" + + "github.com/engigu/baihu-panel/internal/database" + "github.com/engigu/baihu-panel/internal/models" + "github.com/hashicorp/yamux" +) + +// TunnelSession 表示一个基于 WebSocket 的 Yamux 活跃会话 +type TunnelSession struct { + NodeID string + Token string + Session *yamux.Session + Transport *http.Transport +} + +var ( + sessions = make(map[string]*TunnelSession) + sessionsMu sync.RWMutex +) + +func AddSession(nodeID string, token string, session *yamux.Session) *TunnelSession { + sessionsMu.Lock() + defer sessionsMu.Unlock() + + // 关闭已经存在的旧会话 + if old, exists := sessions[nodeID]; exists { + old.Session.Close() + old.Transport.CloseIdleConnections() + } + + tr := &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return session.Open() + }, + DisableKeepAlives: true, + } + + sess := &TunnelSession{ + NodeID: nodeID, + Token: token, + Session: session, + Transport: tr, + } + sessions[nodeID] = sess + return sess +} + +func RemoveSession(nodeID string, session *yamux.Session) { + sessionsMu.Lock() + defer sessionsMu.Unlock() + + if sess, exists := sessions[nodeID]; exists && sess.Session == session { + sess.Session.Close() + sess.Transport.CloseIdleConnections() + delete(sessions, nodeID) + + // 节点下线时实时更新数据库状态 + database.DB.Model(&models.InterconnectNode{}). + Where("id = ?", nodeID). + Update("status", "offline") + } +} + +// CloseAllSessions 强制关闭所有现存的子节点会话(用于主控角色被取消时) +func CloseAllSessions() { + sessionsMu.Lock() + defer sessionsMu.Unlock() + + for _, sess := range sessions { + if sess.Session != nil { + sess.Session.Close() + } + if sess.Transport != nil { + sess.Transport.CloseIdleConnections() + } + } + // 清空所有的会话 + sessions = make(map[string]*TunnelSession) +} + +func GetSession(nodeID string) *TunnelSession { + sessionsMu.RLock() + defer sessionsMu.RUnlock() + return sessions[nodeID] +} diff --git a/internal/tunnel/traffic.go b/internal/tunnel/traffic.go new file mode 100644 index 0000000..1c9bdfe --- /dev/null +++ b/internal/tunnel/traffic.go @@ -0,0 +1,48 @@ +package tunnel + +import ( + "net" + "sync/atomic" +) + +var ( + txBytes uint64 + rxBytes uint64 +) + +// trafficCounterConn 包装了原生的 net.Conn,用于统计底层收发的真实物理字节数 +type trafficCounterConn struct { + net.Conn +} + +func (c *trafficCounterConn) Read(b []byte) (n int, err error) { + n, err = c.Conn.Read(b) + if n > 0 { + atomic.AddUint64(&rxBytes, uint64(n)) + } + return +} + +func (c *trafficCounterConn) Write(b []byte) (n int, err error) { + n, err = c.Conn.Write(b) + if n > 0 { + atomic.AddUint64(&txBytes, uint64(n)) + } + return +} + +// GetTxBytes 返回隧道累计发送的字节数 +func GetTxBytes() uint64 { + return atomic.LoadUint64(&txBytes) +} + +// GetRxBytes 返回隧道累计接收的字节数 +func GetRxBytes() uint64 { + return atomic.LoadUint64(&rxBytes) +} + +// ResetTrafficBytes 重置流量统计计数器 +func ResetTrafficBytes() { + atomic.StoreUint64(&txBytes, 0) + atomic.StoreUint64(&rxBytes, 0) +} diff --git a/web/src/App.vue b/web/src/App.vue index cb9c02d..23aa376 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -2,6 +2,13 @@ import { onMounted } from 'vue' import { RouterView } from 'vue-router' import { Toaster } from '@/components/ui/sonner' +import { LogOut } from 'lucide-vue-next' +import { activeInterconnectNodeId, setActiveInterconnectNodeId } from '@/api' + +function exitTravel() { + setActiveInterconnectNodeId('') + window.location.href = '/' +} onMounted(() => { // 全局应用设备差异化垂直抗锯齿 @@ -19,4 +26,18 @@ onMounted(() => { diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 76e25da..459edec 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -65,16 +65,34 @@ export interface MonitorStats { }[] } } +export let activeInterconnectNodeId = localStorage.getItem('activeInterconnectNodeId') || '' -async function request(url: string, options?: RequestInit): Promise { - const res = await fetch(`${API_BASE_URL}${url}`, { - ...options, - credentials: 'include', // 携带 Cookie - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }) +export function setActiveInterconnectNodeId(id: string) { + activeInterconnectNodeId = id + localStorage.removeItem('site_settings_cache') + if (id) { + localStorage.setItem('activeInterconnectNodeId', id) + document.cookie = `active_interconnect_node_id=${id}; path=/; max-age=${7 * 24 * 3600}` + } else { + localStorage.removeItem('activeInterconnectNodeId') + document.cookie = `active_interconnect_node_id=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC` + } +} + +export async function request(url: string, options?: RequestInit): Promise { + let res: Response + try { + res = await fetch(`${API_BASE_URL}${url}`, { + ...options, + credentials: 'include', // 携带 Cookie + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }) + } catch (err: any) { + throw err + } const json: ApiResponse = await res.json() diff --git a/web/src/api/interconnect.ts b/web/src/api/interconnect.ts new file mode 100644 index 0000000..9754685 --- /dev/null +++ b/web/src/api/interconnect.ts @@ -0,0 +1,69 @@ +import { request } from '@/api' + +export interface InterconnectNode { + id: string + name: string + url: string + token: string + remark: string + created_at: string + updated_at: string + status?: string + metrics?: { + cpu_percent: number + mem_percent: number + disk_percent: number + } + last_heartbeat_at?: string +} + +export function getNodes() { + return request('/interconnect/nodes', { method: 'GET' }) +} + +export function createNode(data: Partial) { + return request('/interconnect/nodes', { + method: 'POST', + body: JSON.stringify(data) + }) +} + +export function updateNode(id: string, data: Partial) { + return request(`/interconnect/nodes/${id}`, { + method: 'PUT', + body: JSON.stringify(data) + }) +} + +export function deleteNode(id: string) { + return request(`/interconnect/nodes/${id}`, { method: 'DELETE' }) +} + +export function getNodeStatus(id: string) { + return request(`/interconnect/nodes/${id}/status`, { method: 'GET' }) +} + +export function syncScript(node_ids: string[], filename: string, content: string) { + return request('/interconnect/sync/script', { + method: 'POST', + body: JSON.stringify({ node_ids, filename, content }) + }) +} + +export function syncEnv(node_ids: string[], envs: any[]) { + return request('/interconnect/sync/env', { + method: 'POST', + body: JSON.stringify({ node_ids, envs }) + }) +} + +export function syncTask(node_ids: string[], tasks: any[]) { + return request('/interconnect/sync/task', { + method: 'POST', + body: JSON.stringify({ node_ids, tasks }) + }) +} + +export function getChildStatus() { + return request<{ parent_url: string; parent_token: string; connected: boolean }>('/interconnect/child/status', { method: 'GET' }) +} diff --git a/web/src/components/InterconnectSyncDialog.vue b/web/src/components/InterconnectSyncDialog.vue new file mode 100644 index 0000000..dcade12 --- /dev/null +++ b/web/src/components/InterconnectSyncDialog.vue @@ -0,0 +1,102 @@ + + + diff --git a/web/src/components/NodeSwitcher.vue b/web/src/components/NodeSwitcher.vue new file mode 100644 index 0000000..fada607 --- /dev/null +++ b/web/src/components/NodeSwitcher.vue @@ -0,0 +1,118 @@ + + + diff --git a/web/src/composables/useSiteSettings.ts b/web/src/composables/useSiteSettings.ts index 8f3b9f0..26f6671 100644 --- a/web/src/composables/useSiteSettings.ts +++ b/web/src/composables/useSiteSettings.ts @@ -34,7 +34,7 @@ const siteSettings = ref(cachedSettings || { cookie_days: '7' }) -// 立即应用缓存的设置 +// 立即应用缓存的设置,防止标题闪烁或空白 if (cachedSettings) { if (cachedSettings.title) { document.title = cachedSettings.title @@ -42,6 +42,8 @@ if (cachedSettings) { if (cachedSettings.icon) { updateFavicon(cachedSettings.icon) } +} else { + document.title = siteSettings.value.title } let loaded = false @@ -72,10 +74,16 @@ export function useSiteSettings() { siteSettings.value = res saveToCache(res) // 保存到缓存 document.title = res.title || '白虎面板' - if (res.icon) updateFavicon(res.icon) + if (res.icon) { + updateFavicon(res.icon) + } loaded = true - } catch { + } catch (error) { // 使用默认值或缓存值 + document.title = siteSettings.value.title || '白虎面板' + if (siteSettings.value.icon) { + updateFavicon(siteSettings.value.icon) + } } } diff --git a/web/src/constants/index.ts b/web/src/constants/index.ts index 97728fc..ff4b408 100644 --- a/web/src/constants/index.ts +++ b/web/src/constants/index.ts @@ -77,3 +77,8 @@ export const TASK_EVENTS = { export const LOG_EVENTS = { ADDED: 'app_log_added', } as const + +// 系统事件类型 (对应后端的 system_ws_service.go) +export const SYSTEM_EVENTS = { + INTERCONNECT_CHILD_STATUS: 'interconnect_child_status', +} as const diff --git a/web/src/layouts/MainLayout.vue b/web/src/layouts/MainLayout.vue index 5711668..653c159 100644 --- a/web/src/layouts/MainLayout.vue +++ b/web/src/layouts/MainLayout.vue @@ -2,10 +2,11 @@ import { ref, onMounted, computed } from 'vue' import { RouterLink, RouterView, useRoute } from 'vue-router' import { resetAuthCache } from '@/router' -import { LayoutDashboard, ListTodo, FileCode, Settings, LogOut, ScrollText, Terminal, Variable, KeyRound, Menu, X, Server, Globe, Bell, Activity } from 'lucide-vue-next' +import { LayoutDashboard, ListTodo, FileCode, Settings, LogOut, ScrollText, Terminal, Variable, KeyRound, Menu, X, Server, Globe, Bell, Activity, Network } from 'lucide-vue-next' import { Button } from '@/components/ui/button' import ThemeToggle from '@/components/ThemeToggle.vue' import SystemNotice from '@/components/SystemNotice.vue' +import NodeSwitcher from '@/components/NodeSwitcher.vue' import { api } from '@/api' import { useSiteSettings } from '@/composables/useSiteSettings' @@ -64,6 +65,7 @@ const navItems = [ { to: '/notify', icon: Bell, label: '消息推送', exact: true }, { to: '/logs', icon: KeyRound, label: '运行日志', exact: true }, { to: '/monitor', icon: Activity, label: '系统监控', exact: true }, + { to: '/interconnect', icon: Network, label: '互联管理', exact: true }, { to: '/settings', icon: Settings, label: '系统设置', exact: true }, ] @@ -164,7 +166,7 @@ onMounted(() => { -
+
{{ sentenceContent }} @@ -172,6 +174,7 @@ onMounted(() => {
+
diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 484f01b..cd771aa 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -55,6 +55,7 @@ const router = createRouter({ { path: 'terminal', name: 'terminal', component: () => import('@/views/terminal/Terminal.vue') }, { path: 'notify', name: 'notify', component: () => import('@/views/notify/Notify.vue') }, { path: 'monitor', name: 'monitor', component: () => import('@/views/monitor/Monitor.vue') }, + { path: 'interconnect', name: 'interconnect', component: () => import('@/views/interconnect/index.vue') }, { path: 'settings', name: 'settings', component: () => import('@/views/settings/Settings.vue') } ] }, diff --git a/web/src/views/editor/Editor.vue b/web/src/views/editor/Editor.vue index 243fcd6..905b36a 100644 --- a/web/src/views/editor/Editor.vue +++ b/web/src/views/editor/Editor.vue @@ -65,6 +65,7 @@ const showTerminalDialog = ref(false) const runCommand = ref('') const scriptsDir = ref('') + async function fetchInstalledLangs() { try { installedLangs.value = await api.mise.list() diff --git a/web/src/views/environments/Environments.vue b/web/src/views/environments/Environments.vue index 39e22fc..effb5a4 100644 --- a/web/src/views/environments/Environments.vue +++ b/web/src/views/environments/Environments.vue @@ -272,9 +272,6 @@ onMounted(() => { - @@ -381,7 +378,7 @@ onMounted(() => { -
+