diff --git a/build/update/hack/config.yaml b/build/update/hack/config.yaml new file mode 100644 index 0000000..3ed560e --- /dev/null +++ b/build/update/hack/config.yaml @@ -0,0 +1,25 @@ + +# CLI tool, only in development environment. +# https://goframe.org/docs/cli +gfcli: + build: + name: "update" + arch: "amd64" + system: "windows" + mod: "none" +# packSrc: "resource,manifest" + version: "v1.0.0" + # output: "./bin/" +# extra: -trimpath -ldflags="-s -w -H=windowsgui" + extra: -trimpath -ldflags="-s -w" +# cgo: true + dumpEnv: true + gen: + dao: + - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + descriptionTag: true + + docker: + build: "-a amd64 -s linux -p temp -ew" + tagPrefixes: + - my.image.pub/my-app \ No newline at end of file diff --git a/build/update/hack/hack-cli.mk b/build/update/hack/hack-cli.mk new file mode 100644 index 0000000..f4e2ad2 --- /dev/null +++ b/build/update/hack/hack-cli.mk @@ -0,0 +1,20 @@ + +# Install/Update to the latest CLI tool. +.PHONY: cli +cli: + @set -e; \ + wget -O gf \ + https://github.com/gogf/gf/releases/latest/download/gf_$(shell go env GOOS)_$(shell go env GOARCH) && \ + chmod +x gf && \ + ./gf install -y && \ + rm ./gf + + +# Check and install CLI tool. +.PHONY: cli.install +cli.install: + @set -e; \ + gf -v > /dev/null 2>&1 || if [[ "$?" -ne "0" ]]; then \ + echo "GoFame CLI is not installed, start proceeding auto installation..."; \ + make cli; \ + fi; \ No newline at end of file diff --git a/build/update/hack/hack.mk b/build/update/hack/hack.mk new file mode 100644 index 0000000..2f68179 --- /dev/null +++ b/build/update/hack/hack.mk @@ -0,0 +1,75 @@ +.DEFAULT_GOAL := build + +# Update GoFrame and its CLI to latest stable version. +.PHONY: up +up: cli.install + @gf up -a + +# Build binary using configuration from hack/config.yaml. +.PHONY: build +build: cli.install + @gf build -ew + +# Parse api and generate controller/sdk. +.PHONY: ctrl +ctrl: cli.install + @gf gen ctrl + +# Generate Go files for DAO/DO/Entity. +.PHONY: dao +dao: cli.install + @gf gen dao + +# Parse current project go files and generate enums go file. +.PHONY: enums +enums: cli.install + @gf gen enums + +# Generate Go files for Service. +.PHONY: service +service: cli.install + @gf gen service + + +# Build docker image. +.PHONY: image +image: cli.install + $(eval _TAG = $(shell git rev-parse --short HEAD)) +ifneq (, $(shell git status --porcelain 2>/dev/null)) + $(eval _TAG = $(_TAG).dirty) +endif + $(eval _TAG = $(if ${TAG}, ${TAG}, $(_TAG))) + $(eval _PUSH = $(if ${PUSH}, ${PUSH}, )) + @gf docker ${_PUSH} -tn $(DOCKER_NAME):${_TAG}; + + +# Build docker image and automatically push to docker repo. +.PHONY: image.push +image.push: cli.install + @make image PUSH=-p; + + +# Deploy image and yaml to current kubectl environment. +.PHONY: deploy +deploy: cli.install + $(eval _TAG = $(if ${TAG}, ${TAG}, develop)) + + @set -e; \ + mkdir -p $(ROOT_DIR)/temp/kustomize;\ + cd $(ROOT_DIR)/manifest/deploy/kustomize/overlays/${_ENV};\ + kustomize build > $(ROOT_DIR)/temp/kustomize.yaml;\ + kubectl apply -f $(ROOT_DIR)/temp/kustomize.yaml; \ + if [ $(DEPLOY_NAME) != "" ]; then \ + kubectl patch -n $(NAMESPACE) deployment/$(DEPLOY_NAME) -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"$(shell date +%s)\"}}}}}"; \ + fi; + + +# Parsing protobuf files and generating go files. +.PHONY: pb +pb: cli.install + @gf gen pb + +# Generate protobuf files for database tables. +.PHONY: pbentity +pbentity: cli.install + @gf gen pbentity \ No newline at end of file diff --git a/build/update/main.go b/build/update/main.go new file mode 100644 index 0000000..33c84ac --- /dev/null +++ b/build/update/main.go @@ -0,0 +1,183 @@ +package main + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "syscall" + "time" +) + +var ( + updateServer = "https://your-server.com/update" +) + +func main() { + if len(os.Args) < 3 { + fmt.Println("用法: updater <主程序路径> <当前版本> <更新服务器URL>") + os.Exit(1) + } + + mainExePath := os.Args[1] + currentVersion := os.Args[2] + updateServer = os.Args[3] + + fmt.Println("开始更新程序...") + + // 1. 等待主程序完全退出 + if err := waitForMainExit(mainExePath); err != nil { + fmt.Printf("等待主程序退出失败: %v\n", err) + pauseAndExit() + } + + // 2. 获取最新版本信息 + updateInfo, err := getLatestVersionInfo(currentVersion) + if err != nil { + fmt.Printf("获取更新信息失败: %v\n", err) + pauseAndExit() + } + + // 3. 下载新版本 + newExePath, err := downloadNewVersion(updateInfo.DownloadURL) + if err != nil { + fmt.Printf("下载更新失败: %v\n", err) + pauseAndExit() + } + + // 4. 替换主程序 + if err := replaceMainExe(mainExePath, newExePath); err != nil { + fmt.Printf("替换程序失败: %v\n", err) + pauseAndExit() + } + + // 5. 重启主程序 + if err := restartMainProgram(mainExePath); err != nil { + fmt.Printf("重启程序失败: %v\n", err) + pauseAndExit() + } + + fmt.Println("更新完成") +} + +// 等待主程序退出 +func waitForMainExit(mainPath string) error { + // 获取主程序文件名 + mainName := filepath.Base(mainPath) + + for { + // 检查是否还有同名进程在运行 + if !isProcessRunning(mainName) { + return nil + } + time.Sleep(300 * time.Millisecond) + } +} + +// 检查进程是否在运行 +func isProcessRunning(name string) bool { + // Windows下通过tasklist命令检查进程 + cmd := exec.Command("tasklist", "/FI", fmt.Sprintf("IMAGENAME eq %s", name)) + _, err := cmd.Output() + if err != nil { + return false + } + + return true +} + +// 获取最新版本信息 +func getLatestVersionInfo(currentVersion string) (UpdateInfo, error) { + var info UpdateInfo + resp, err := http.Get(fmt.Sprintf("%s/latest?current=%s", updateServer, currentVersion)) + if err != nil { + return info, err + } + defer resp.Body.Close() + + if err := json.NewDecoder(resp.Body).Decode(&info); err != nil { + return info, err + } + + return info, nil +} + +// 下载新版本 +func downloadNewVersion(url string) (string, error) { + tempFile := filepath.Join(os.TempDir(), "main_new.exe") + out, err := os.Create(tempFile) + if err != nil { + return "", err + } + defer out.Close() + + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + // 下载并计算哈希值 + hash := sha256.New() + writer := io.MultiWriter(out, hash) + + if _, err := io.Copy(writer, resp.Body); err != nil { + os.Remove(tempFile) + return "", err + } + + // 这里可以添加哈希校验逻辑 + + return tempFile, nil +} + +// 替换主程序文件 +func replaceMainExe(oldPath, newPath string) error { + // 备份旧版本 + backupPath := oldPath + ".bak" + if err := os.Remove(backupPath); err != nil && !os.IsNotExist(err) { + return err + } + + if err := os.Rename(oldPath, backupPath); err != nil { + return err + } + + // 移动新版本到目标路径 + if err := os.Rename(newPath, oldPath); err != nil { + // 替换失败,恢复备份 + os.Rename(backupPath, oldPath) + return err + } + + // 替换成功,删除备份 + os.Remove(backupPath) + return nil +} + +// 重启主程序 +func restartMainProgram(mainPath string) error { + cmd := exec.Command(mainPath) + // 在后台启动主程序 + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: false} + return cmd.Start() +} + +// 暂停并退出(给用户看错误信息) +func pauseAndExit() { + fmt.Println("按任意键退出...") + var input string + fmt.Scanln(&input) + os.Exit(1) +} + +// 更新信息结构体 +type UpdateInfo struct { + Version string `json:"version"` + DownloadURL string `json:"download_url"` + SHA256 string `json:"sha256"` +} diff --git a/go.mod b/go.mod index 58d359d..392f68a 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,14 @@ go 1.24.0 require ( github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994 + github.com/getlantern/systray v1.2.2 github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3 github.com/gogf/gf/v2 v2.9.3 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 - github.com/ipfs/go-ipns v0.3.1 github.com/libp2p/go-libp2p v0.43.0 github.com/libp2p/go-libp2p-kad-dht v0.35.1 github.com/multiformats/go-multiaddr v0.16.1 - github.com/multiformats/go-multihash v0.2.3 ) require ( @@ -31,10 +30,16 @@ require ( github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect + github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect + github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect + github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect + github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect + github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect - github.com/gogo/protobuf v1.3.2 // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect @@ -43,7 +48,6 @@ require ( github.com/ipfs/boxo v0.35.0 // indirect github.com/ipfs/go-cid v0.5.0 // indirect github.com/ipfs/go-datastore v0.9.0 // indirect - github.com/ipfs/go-ipfs-util v0.0.3 // indirect github.com/ipfs/go-log/v2 v2.8.1 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect @@ -78,12 +82,15 @@ require ( github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.9.2 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-multistream v0.6.1 // indirect github.com/multiformats/go-varint v0.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect github.com/olekukonko/errors v1.1.0 // indirect - github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/ll v0.1.1 // indirect github.com/olekukonko/tablewriter v1.0.9 // indirect + github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect @@ -104,7 +111,6 @@ require ( github.com/pion/transport/v3 v3.0.7 // indirect github.com/pion/turn/v4 v4.0.2 // indirect github.com/pion/webrtc/v4 v4.1.2 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -134,7 +140,7 @@ require ( golang.org/x/mod v0.28.0 // indirect golang.org/x/net v0.44.0 // indirect golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 // indirect + golang.org/x/sys v0.37.0 // indirect golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.12.0 // indirect diff --git a/go.sum b/go.sum index 5e7b3e2..5d1f67a 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,20 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= +github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= +github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So= +github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= +github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk= +github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc= +github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= +github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= +github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= +github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= +github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= +github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= +github.com/getlantern/systray v1.2.2 h1:dCEHtfmvkJG7HZ8lS/sLklTH4RKUcIsKrAD9sThoEBE= +github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgNMY5HvhHhcE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -73,14 +87,14 @@ 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-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3 h1:VTbeHq8XpBCWFIwBGmuBl+jP8AepULnpgNz8GPBKBRQ= github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3/go.mod h1:gcidgAYn4IWbx08QUThg7jw6bz3KklXI9/5zg8jnVHY= github.com/gogf/gf/v2 v2.9.3 h1:qjN4s55FfUzxZ1AE8vUHNDX3V0eIOUGXhF2DjRTVZQ4= github.com/gogf/gf/v2 v2.9.3/go.mod h1:w6rcfD13SmO7FKI80k9LSLiSMGqpMYp50Nfkrrc2sEE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -126,10 +140,6 @@ github.com/ipfs/go-datastore v0.9.0 h1:WocriPOayqalEsueHv6SdD4nPVl4rYMfYGLD4bqCZ github.com/ipfs/go-datastore v0.9.0/go.mod h1:uT77w/XEGrvJWwHgdrMr8bqCN6ZTW9gzmi+3uK+ouHg= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= -github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= -github.com/ipfs/go-ipns v0.3.1 h1:BZUCV4tzI/q/pFido7I81BPVCvWg+66/qPCuglxETew= -github.com/ipfs/go-ipns v0.3.1/go.mod h1:85Qm9tAQVUn7ELuHjl2qVF6OnMNJjrC6gmnh0+UGk/A= github.com/ipfs/go-log/v2 v2.8.1 h1:Y/X36z7ASoLJaYIJAL4xITXgwf7RVeqb1+/25aq/Xk0= github.com/ipfs/go-log/v2 v2.8.1/go.mod h1:NyhTBcZmh2Y55eWVjOeKf8M7e4pnJYM3yDZNxQBWEEY= github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc= @@ -145,7 +155,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= @@ -191,6 +200,8 @@ github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8S github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -247,13 +258,17 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +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.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= -github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= -github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/ll v0.1.1 h1:9Dfeed5/Mgaxb9lHRAftLK9pVfYETvHn+If6lywVhJc= +github.com/olekukonko/ll v0.1.1/go.mod h1:2dJo+hYZcJMLMbKwHEWvxCUbAOLc/CXWS9noET22Mdo= github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8= github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= @@ -299,8 +314,6 @@ github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7ITo github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= @@ -356,6 +369,7 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= @@ -368,6 +382,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -386,8 +401,6 @@ github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -github.com/yuin/goldmark v1.1.27/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= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -424,7 +437,6 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= @@ -440,8 +452,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/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.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.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= @@ -457,8 +467,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -479,8 +487,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ 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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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= @@ -493,19 +499,20 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8= golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -537,16 +544,12 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= 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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -567,6 +570,7 @@ google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/hack/config.yaml b/hack/config.yaml index e59ddeb..a3223a7 100644 --- a/hack/config.yaml +++ b/hack/config.yaml @@ -9,9 +9,10 @@ gfcli: mod: "none" packSrc: "resource,manifest" version: "v1.0.0" - # output: "./bin/" - # cgo: true + path: "./bin" +# extra: -trimpath -ldflags="-s -w -H=windowsgui" extra: -trimpath -ldflags="-s -w" +# cgo: true dumpEnv: true gen: dao: diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index 6f22ccc..210f470 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -2,7 +2,6 @@ package cmd import ( "context" - "net" "time" "github.com/ayflying/p2p/internal/controller/p2p" @@ -23,6 +22,8 @@ func init() { } var ( + s = g.Server() + Main = gcmd.Command{ Name: "main", Usage: "main", @@ -38,7 +39,6 @@ var ( ws := parser.GetOpt("ws", addr).String() //port := parser.GetOpt("port", 0).Int() - s := g.Server() s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(ghttp.MiddlewareHandlerResponse) group.Bind( @@ -64,12 +64,12 @@ var ( err = service.P2P().Start(ctx, ws) g.Log().Debugf(ctx, "当前监听端口:%v", s.GetListenedPort()) - addrs, _ := net.InterfaceAddrs() - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil { - g.Log().Infof(ctx, "访问地址:http://%v:%d", ipnet.IP.String(), s.GetListenedPort()) - } - } + //addrs, _ := net.InterfaceAddrs() + //for _, addr := range addrs { + // if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil { + // g.Log().Infof(ctx, "访问地址:http://%v:%d", ipnet.IP.String(), s.GetListenedPort()) + // } + //} }) diff --git a/internal/cmd/debug.go b/internal/cmd/debug.go index a447e51..990630b 100644 --- a/internal/cmd/debug.go +++ b/internal/cmd/debug.go @@ -3,6 +3,7 @@ package cmd import ( "context" + "github.com/ayflying/p2p/internal/service" "github.com/dop251/goja" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gcmd" @@ -50,11 +51,13 @@ var ( msg = res.Export() g.Dump(res.ToNumber()) case "p2p": - // host, err := service.P2P().Start(ctx) - // if err != nil { - // break - // } - // g.Dump(host.ID().String(), host.Addrs()) + // host, err := service.P2P().Start(ctx) + // if err != nil { + // break + // } + // g.Dump(host.ID().String(), host.Addrs()) + case "update": + service.OS().Update("v1.0.0", "http://127.0.0.1:8080") } g.Log().Debug(ctx, msg) return diff --git a/internal/cmd/p2p.go b/internal/cmd/p2p.go index f2f8df4..7baf030 100644 --- a/internal/cmd/p2p.go +++ b/internal/cmd/p2p.go @@ -84,9 +84,7 @@ var ( publicIp, err := service.P2P().GetIPv4PublicIP() err = service.P2P().StoreAddrToDHT(ctx, "ip", publicIp) - if err != nil { - return - } + case "dht2": h, _ := service.P2P().CreateLibp2pHost(ctx, 0) err := service.P2P().DHTStart(ctx, h) @@ -95,7 +93,7 @@ var ( } get, _ := service.P2P().FindAddrFromDHT(ctx, "ip") g.Dump(get) - + s.SetPort(0) default: // 显示帮助信息 g.Log().Info(ctx, p2pHelpDescription) diff --git a/internal/consts/consts.go b/internal/consts/consts.go index d709a2b..0ce9546 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -1 +1,6 @@ package consts + +const ( + // 应用名称 + Name = "P2P" +) diff --git a/internal/controller/p2p/p2p_v1_send.go b/internal/controller/p2p/p2p_v1_send.go index ed12951..40d99fa 100644 --- a/internal/controller/p2p/p2p_v1_send.go +++ b/internal/controller/p2p/p2p_v1_send.go @@ -8,6 +8,6 @@ import ( ) func (c *ControllerV1) Send(ctx context.Context, req *v1.SendReq) (res *v1.SendRes, err error) { - err = service.P2P().SendData(req.TargetID, []byte(req.Data)) + err = service.P2P().SendP2P(req.TargetID, "message", []byte(req.Data)) return } diff --git a/internal/logic/logic.go b/internal/logic/logic.go index 000c67c..d7e0bfa 100644 --- a/internal/logic/logic.go +++ b/internal/logic/logic.go @@ -5,5 +5,6 @@ package logic import ( + _ "github.com/ayflying/p2p/internal/logic/os" _ "github.com/ayflying/p2p/internal/logic/p2p" ) diff --git a/internal/logic/os/linux.go b/internal/logic/os/linux.go new file mode 100644 index 0000000..801f85e --- /dev/null +++ b/internal/logic/os/linux.go @@ -0,0 +1,11 @@ +//go:build !windows + +package os + +func (s *sOS) start() { + +} + +func (s *sOS) update(version, server string) { + +} diff --git a/internal/logic/os/os.go b/internal/logic/os/os.go new file mode 100644 index 0000000..3264406 --- /dev/null +++ b/internal/logic/os/os.go @@ -0,0 +1,45 @@ +package os + +import ( + "github.com/ayflying/p2p/internal/service" + "github.com/gogf/gf/v2/os/gcmd" +) + +type systrayType struct { + Icon string `json:"icon" dc:"图标"` + Title string `json:"title" dc:"标题"` + Tooltip string `json:"tooltip" dc:"提示"` +} + +type sOS struct { + systray *systrayType +} + +func New() *sOS { + return &sOS{ + systray: &systrayType{}, + } +} +func init() { + service.RegisterOS(New()) +} + +func (s *sOS) Load(title string, tooltip string, ico string) { + if title == "" { + title = gcmd.GetArg(0).String() + } + if tooltip == "" { + tooltip = gcmd.GetArg(0).String() + } + + s.systray = &systrayType{ + Icon: ico, + Title: title, + Tooltip: tooltip, + } + s.start() +} + +func (s *sOS) Update(version, server string) { + s.update(version, server) +} diff --git a/internal/logic/os/windows.go b/internal/logic/os/windows.go new file mode 100644 index 0000000..cbddeb2 --- /dev/null +++ b/internal/logic/os/windows.go @@ -0,0 +1,64 @@ +//go:build windows + +package os + +import ( + "os" + "os/exec" + "syscall" + + "github.com/getlantern/systray" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcmd" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gfile" +) + +func (s *sOS) start() { + + // 系统托盘初始化(设置图标、右键菜单) + go systray.Run(s.onSystrayReady, s.onSystrayExit) +} + +// 系统托盘初始化(设置图标、右键菜单) +func (s *sOS) onSystrayReady() { + + iconByte := gfile.GetBytes(s.systray.Icon) + systray.SetIcon(iconByte) + systray.SetTitle(s.systray.Title) + systray.SetTooltip(s.systray.Tooltip) + + mQuit := systray.AddMenuItem("退出", "退出应用") + systray.AddMenuItemCheckbox("隐藏窗口", "隐藏窗口", false) + // Sets the icon of a menu item. Only available on Mac and Windows. + //mQuit.SetIcon(iconByte) + go func() { + for { + select { + case <-mQuit.ClickedCh: + systray.Quit() + } + + } + }() + +} + +func (s *sOS) onSystrayExit() { + // clean up here + g.Log().Debugf(gctx.New(), "系统托盘退出") + defer os.Exit(0) + +} + +func (s *sOS) update(version, server string) { + updaterPath := gcmd.GetArg(0).String() + // 启动更新器,传递主程序路径和当前版本作为参数 + cmd := exec.Command(updaterPath, version, server) + // 将更新器与主程序的控制台分离 + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: false} // Windows特定设置 + + if err := cmd.Start(); err != nil { + return + } +} diff --git a/internal/logic/p2p/client.go b/internal/logic/p2p/client.go index eb76a6e..b8526c1 100644 --- a/internal/logic/p2p/client.go +++ b/internal/logic/p2p/client.go @@ -4,9 +4,11 @@ import ( "context" "encoding/json" "fmt" + "net" "strconv" "time" + "github.com/ayflying/p2p/internal/service" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" @@ -33,6 +35,41 @@ type Client struct { host host.Host wsConn *websocket.Conn // WebSocket连接 peers map[string]peer.ID // 存储已连接的节点 + //tcp map[string] +} + +type Message struct { + Type string `json:"type" dc:"消息类型"` + Port int `json:"port,omitempty" dc:"请求端口"` + Data []byte `json:"data" dc:"消息数据"` + From string `json:"from" dc:"发送方ID"` +} + +// SendP2P 发送格式化消息 +func (s *sP2P) SendP2P(targetID string, typ string, data []byte) (err error) { + message := &Message{ + Type: "message", + From: s.client.Id, + Data: data, + } + err = s.sendData(targetID, gjson.MustEncode(message)) + return +} + +func (s *sP2P) linkTcp(addr string) { + //conn, err := gtcp.Dial(addr) + + // 使用标准库 net.Dial 建立连接 + stdConn, err := net.Dial("tcp", addr) + if err != nil { + fmt.Printf("Dial error: %v\n", err) + return + } + defer stdConn.Close() + + //// 将标准库的 net.Conn 封装为 gtcp.Conn + //gtcpConn := gtcp.NewConn(stdConn) + //defer gtcpConn.Close() } func (s *sP2P) Start(ctx context.Context, wsStr string) (err error) { @@ -177,7 +214,7 @@ func (s *sP2P) handleStream(stream network.Stream) { defer stream.Close() peerID := stream.Conn().RemotePeer().String() - glog.Infof(ctx, "收到来自 %s 的连接", peerID) + //glog.Infof(ctx, "收到来自 %s 的连接", peerID) // 读取数据 buf := make([]byte, 1024) @@ -187,11 +224,15 @@ func (s *sP2P) handleStream(stream network.Stream) { return } - g.Log().Debugf(ctx, "收到来自 %s 的消息: %v> ", peerID, string(buf[:n])) + var msg = buf[:n] + // 解析消息 + var message *Message + err = gjson.DecodeTo(msg, &message) + g.Log().Debugf(ctx, "收到来自 %s 的消息: %v ", peerID, gjson.MustEncodeString(message)) } // 发送数据到目标节点 -func (s *sP2P) SendData(targetID string, data []byte) error { +func (s *sP2P) sendData(targetID string, data []byte) error { peerID, exists := s.client.peers[targetID] if !exists { return fmt.Errorf("未找到目标节点 %s 的连接", targetID) @@ -325,6 +366,8 @@ func (s *sP2P) receiveGatewayMessages() { continue } + g.Log().Infof(ctx, "准备开始打洞到目标节点:%v", msgData.TargetID) + addrs := make([]multiaddr.Multiaddr, len(msgData.Addrs)) for i, addrStr := range msgData.Addrs { addr, err := multiaddr.NewMultiaddr(addrStr) @@ -361,6 +404,21 @@ func (s *sP2P) receiveGatewayMessages() { } json.Unmarshal(msg.Data, &data) glog.Errorf(ctx, "网关错误: %s", data.Error) + case MsgUpdate: //更新节点信息 + var msgData struct { + Server string `json:"server"` + Version string `json:"version"` + } + //var msgData *dataType + json.Unmarshal(msg.Data, &msgData) + // 更新器路径(假设与主程序同目录) + //updaterPath := filepath.Join(filepath.Dir(selfPath), "updater.exe") + + g.Log().Infof(ctx, "更新节点信息: %v", data) + + // 调用不同系统的更新服务 + service.OS().Update(msgData.Version, msgData.Server) + } } } diff --git a/internal/logic/p2p/dht.go b/internal/logic/p2p/dht.go index 320cf62..92238b8 100644 --- a/internal/logic/p2p/dht.go +++ b/internal/logic/p2p/dht.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/gogf/gf/v2/frame/g" //"github.com/ipfs/boxo/ipns" dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p/core/host" @@ -73,7 +74,8 @@ func (s *sP2P) StoreAddrToDHT(ctx context.Context, key string, addr string) (err if err = s.dht.KadDHT.PutValue(ctx, key, value); err != nil { return fmt.Errorf("key=%s,存储地址到DHT失败: %v", key, err) } - fmt.Printf("成功存储地址到DHT,Key=%s, Value=%s\n", key, addr) + + g.Log().Info(ctx, "成功存储地址到DHT,Key=%s, Value=%s", key, addr) return } @@ -83,6 +85,7 @@ func (s *sP2P) FindAddrFromDHT(ctx context.Context, key string) (string, error) //key = s.generateStringDHTKey(key) //key = "/ipns/" + key key = fmt.Sprintf("%s/%s", ProtocolID, key) + g.Log().Debugf(ctx, "从DHT查找地址中...,Key=%s", key) value, err := s.dht.KadDHT.GetValue(ctx, key) if err != nil { return "", fmt.Errorf("从DHT查找地址失败: %v", err) diff --git a/internal/logic/p2p/p2p.go b/internal/logic/p2p/p2p.go index 2270726..40b911a 100644 --- a/internal/logic/p2p/p2p.go +++ b/internal/logic/p2p/p2p.go @@ -39,8 +39,9 @@ const ( MsgTypeDiscover = "discover" // 发现目标客户端 MsgTypeDiscoverAck = "discover_ack" // 发现响应 MsgTypeConnectionReq = "connection_req" // 连接请求通知 - MsgTypePunchRequest = "punch_request" - MsgTypeError = "error" // 错误消息 + MsgTypePunchRequest = "punch_request" // 打洞请求 + MsgTypeError = "error" // 错误消息 + MsgUpdate = "update" // 更新节点信息 ) // 注册请求数据 diff --git a/internal/service/os.go b/internal/service/os.go new file mode 100644 index 0000000..e3a5dea --- /dev/null +++ b/internal/service/os.go @@ -0,0 +1,28 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +type ( + IOS interface { + Load(title string, tooltip string, ico string) + Update(version string, server string) + } +) + +var ( + localOS IOS +) + +func OS() IOS { + if localOS == nil { + panic("implement not found for interface IOS, forgot register?") + } + return localOS +} + +func RegisterOS(i IOS) { + localOS = i +} diff --git a/internal/service/p_2_p.go b/internal/service/p_2_p.go index 7386251..df94d71 100644 --- a/internal/service/p_2_p.go +++ b/internal/service/p_2_p.go @@ -14,13 +14,13 @@ import ( type ( IP2P interface { + // SendP2P 发送格式化消息 + SendP2P(targetID string, typ string, data []byte) (err error) Start(ctx context.Context, wsStr string) (err error) // 创建libp2p主机 CreateLibp2pHost(ctx context.Context, port int) (host.Host, error) // 发现并连接目标节点 DiscoverAndConnect(targetID string) error - // 发送数据到目标节点 - SendData(targetID string, data []byte) error // 初始化无服务器DHT(作为节点加入DHT网络) DHTStart(ctx context.Context, h host.Host) (err error) // 存储数据到DHT(比如存储“目标节点ID-公网地址”的映射) diff --git a/main.go b/main.go index 1cd96b5..d353b1d 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,10 @@ package main import ( - _ "github.com/ayflying/p2p/internal/packed" - + "github.com/ayflying/p2p/internal/consts" _ "github.com/ayflying/p2p/internal/logic" + _ "github.com/ayflying/p2p/internal/packed" + "github.com/ayflying/p2p/internal/service" //步骤1:加载驱动 _ "github.com/gogf/gf/contrib/nosql/redis/v2" @@ -12,6 +13,13 @@ import ( "github.com/gogf/gf/v2/os/gctx" ) +var ( + ctx = gctx.GetInitCtx() +) + func main() { - cmd.Main.Run(gctx.GetInitCtx()) + // 启动系统托盘 + service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico") + + cmd.Main.Run(ctx) } diff --git a/manifest/images/favicon.ico b/manifest/images/favicon.ico new file mode 100644 index 0000000..6ee3a42 Binary files /dev/null and b/manifest/images/favicon.ico differ diff --git a/manifest/images/sonow.jpg b/manifest/images/sonow.jpg new file mode 100644 index 0000000..307c037 Binary files /dev/null and b/manifest/images/sonow.jpg differ