diff --git a/api/system/system.go b/api/system/system.go new file mode 100644 index 0000000..04bb2bd --- /dev/null +++ b/api/system/system.go @@ -0,0 +1,15 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package system + +import ( + "context" + + "github.com/ayflying/p2p/api/system/v1" +) + +type ISystemV1 interface { + Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) +} diff --git a/api/system/v1/update.go b/api/system/v1/update.go new file mode 100644 index 0000000..9389238 --- /dev/null +++ b/api/system/v1/update.go @@ -0,0 +1,11 @@ +package v1 + +import "github.com/gogf/gf/v2/frame/g" + +type UpdateReq struct { + g.Meta `path:"/system/update" tags:"system" method:"get" sm:"更新服务端"` + Url string `json:"url" dc:"更新地址"` + Version string `json:"version" dc:"当前版本"` +} +type UpdateRes struct { +} diff --git a/hack/config.yaml b/hack/config.yaml index a3223a7..13f38da 100644 --- a/hack/config.yaml +++ b/hack/config.yaml @@ -7,7 +7,7 @@ gfcli: arch: "amd64" system: "windows,linux" mod: "none" - packSrc: "resource,manifest" + packSrc: "hack,resource,manifest" version: "v1.0.0" path: "./bin" # extra: -trimpath -ldflags="-s -w -H=windowsgui" diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index a9a2719..0a6a461 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -4,7 +4,9 @@ import ( "context" "time" + "github.com/ayflying/p2p/internal/consts" "github.com/ayflying/p2p/internal/controller/p2p" + "github.com/ayflying/p2p/internal/controller/system" "github.com/ayflying/p2p/internal/service" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" @@ -22,14 +24,16 @@ func init() { } var ( - s = g.Server() - Main = gcmd.Command{ Name: "main", Usage: "main", Brief: "start http server", Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { g.Log().Debug(ctx, "开始执行main") + Version, err := g.Cfg("hack").Get(ctx, "gfcli.build.version") + g.Log().Debugf(ctx, "当前启动的版本为:%v", Version) + + s := g.Server(consts.Name) parser, err = gcmd.Parse(g.MapStrBool{ "p,port": true, @@ -49,6 +53,7 @@ var ( group.Middleware(ghttp.MiddlewareHandlerResponse) group.Bind( p2p.NewV1(), + system.NewV1(), ) }) @@ -79,6 +84,9 @@ var ( }) + // 启动系统托盘 + service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico") + s.Run() return nil }, diff --git a/internal/cmd/dht.go b/internal/cmd/dht.go index 82e9b98..177aeba 100644 --- a/internal/cmd/dht.go +++ b/internal/cmd/dht.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/ayflying/p2p/internal/consts" "github.com/ayflying/p2p/internal/service" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gcmd" @@ -25,6 +26,7 @@ var ( // Func 为命令的执行函数,接收上下文和参数解析器 Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { g.Log().Debug(ctx, "开始执行dht") + s := g.Server(consts.Name) parser, err = gcmd.Parse(g.MapStrBool{ "p,port": true, diff --git a/internal/cmd/update.go b/internal/cmd/update.go index 2001f38..012b9ec 100644 --- a/internal/cmd/update.go +++ b/internal/cmd/update.go @@ -7,6 +7,7 @@ import ( "path" "runtime" + systemV1 "github.com/ayflying/p2p/api/system/v1" "github.com/ayflying/p2p/internal/service" "github.com/gogf/gf/v2/crypto/gsha1" "github.com/gogf/gf/v2/encoding/gcompress" @@ -45,8 +46,8 @@ var ( var filePath = path.Join(pathMain, version, platform, name) dirList, _ := gfile.ScanDir(path.Join(pathMain, version), "*", false) for _, v := range dirList { - updateFilename := gfile.Name(v) - updateFilePath := path.Join("server_update", name, version, updateFilename) + updatePlatform := gfile.Name(v) + updateFilePath := path.Join("server_update", name, version, updatePlatform) var obj bytes.Buffer g.Log().Debugf(ctx, "读取目录成功:%v", v) @@ -54,14 +55,14 @@ var ( g.Log().Debugf(ctx, "判断当前文件是否存在:%v", fileMian) if gfile.IsFile(fileMian) { // 写入文件哈希 - versionFile[updateFilePath+".gz"] = gsha1.MustEncryptFile(fileMian) + versionFile[updatePlatform] = gsha1.MustEncryptFile(fileMian) err = gcompress.GzipPathWriter(fileMian, &obj) service.S3().PutObject(ctx, &obj, updateFilePath+".gz") g.Log().Debugf(ctx, "成功上传文件到:%v", updateFilePath+".gz") } if gfile.IsFile(fileMian + ".exe") { // 写入文件哈希 - versionFile[updateFilePath+".gz"] = gsha1.MustEncryptFile(fileMian + ".exe") + versionFile[updatePlatform] = gsha1.MustEncryptFile(fileMian + ".exe") err = gcompress.GzipPathWriter(fileMian+".exe", &obj) service.S3().PutObject(ctx, &obj, updateFilePath+".gz") g.Log().Debugf(ctx, "成功上传文件到:%v", updateFilePath+".gz") @@ -69,13 +70,38 @@ var ( // 写入文件版本文件 fileByte := gjson.MustEncode(versionFile) - service.S3().PutObject(ctx, bytes.NewReader(fileByte), path.Join("server_update", name, version, "version.json")) + service.S3().PutObject(ctx, bytes.NewReader(fileByte), path.Join("server_update", name, "version.json")) if err != nil { g.Log().Error(ctx, err) } } - g.Log().Debugf(ctx, "当前获取到的地址为:%v", filePath) + + versionUrl := service.S3().GetCdnUrl(path.Join("server_update", name, "version.json")) + listVar := g.Cfg().MustGet(ctx, "p2p.list") + var p2pItem []struct { + Host string `json:"host"` + Port int `json:"port"` + SSL bool `json:"ssl"` + Ws string `json:"ws"` + } + listVar.Scan(&p2pItem) + for _, v := range p2pItem { + + url := "http" + if v.SSL == true { + url = "https" + } + url = fmt.Sprintf("%s://%s:%d/system/update", url, v.Host, v.Port) + + g.Log().Debugf(ctx, "开始上传到服务器:%v,file=%v", url, versionUrl) + _, err := g.Client().Get(ctx, url, systemV1.UpdateReq{ + Url: versionUrl, + }) + if err != nil { + g.Log().Error(ctx, err) + } + } return }} ) diff --git a/internal/controller/system/system.go b/internal/controller/system/system.go new file mode 100644 index 0000000..24bd259 --- /dev/null +++ b/internal/controller/system/system.go @@ -0,0 +1,5 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package system diff --git a/internal/controller/system/system_new.go b/internal/controller/system/system_new.go new file mode 100644 index 0000000..4237a85 --- /dev/null +++ b/internal/controller/system/system_new.go @@ -0,0 +1,15 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package system + +import ( + "github.com/ayflying/p2p/api/system" +) + +type ControllerV1 struct{} + +func NewV1() system.ISystemV1 { + return &ControllerV1{} +} diff --git a/internal/controller/system/system_v1_update.go b/internal/controller/system/system_v1_update.go new file mode 100644 index 0000000..6040eda --- /dev/null +++ b/internal/controller/system/system_v1_update.go @@ -0,0 +1,74 @@ +package system + +import ( + "context" + "log" + "os" + "os/exec" + "path/filepath" + "time" + + "github.com/ayflying/p2p/api/system/v1" + "github.com/gogf/gf/v2/crypto/gsha1" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcmd" +) + +func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) { + + getRunFile := gcmd.GetArg(0).String() + + fileSha, err := gsha1.EncryptFile(getRunFile) + g.Dump(fileSha) + g.Dump(getRunFile) + + go func() { + log.Println("5秒后开始重启...") + time.Sleep(5 * time.Second) + + if err = restartSelf(); err != nil { + log.Fatalf("重启失败:%v", err) + } + }() + + return +} + +// restartSelf 实现 Windows 平台下的程序自重启 +func restartSelf() error { + // 1. 获取当前程序的绝对路径 + exePath, err := os.Executable() + if err != nil { + return err + } + // 处理路径中的符号链接(确保路径正确) + exePath, err = filepath.EvalSymlinks(exePath) + if err != nil { + return err + } + + // 2. 获取命令行参数(os.Args[0] 是程序名,实际参数从 os.Args[1:] 开始) + args := os.Args[1:] + + // 3. 构建新进程命令(路径为当前程序,参数为原参数) + cmd := exec.Command(exePath, args...) + // 设置新进程的工作目录与当前进程一致 + cmd.Dir, err = os.Getwd() + if err != nil { + return err + } + + // 新进程的输出继承当前进程的标准输出(可选,根据需求调整) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + // 4. 启动新进程(非阻塞,Start() 后立即返回) + if err := cmd.Start(); err != nil { + return err + } + + // 5. 新进程启动成功后,退出当前进程 + os.Exit(0) + return nil // 理论上不会执行到这里 +} diff --git a/internal/logic/s3/s3.go b/internal/logic/s3/s3.go index 744e753..440d314 100644 --- a/internal/logic/s3/s3.go +++ b/internal/logic/s3/s3.go @@ -242,6 +242,12 @@ func (s *sS3) GetPath(url string) (filePath string) { return url[len(get+bucketName)+1:] } +// GetCdnUrl 通过文件名,获取直连地址 +func (s *sS3) GetCdnUrl(file string) string { + urlStr, _ := url.JoinPath(s.cfg.Url, file) + return urlStr +} + // CopyObject 在指定存储桶内复制文件 // bucketName 存储桶名称 // dstStr 目标文件路径 diff --git a/internal/service/s_3.go b/internal/service/s_3.go index fbe3bdd..778ad8c 100644 --- a/internal/service/s_3.go +++ b/internal/service/s_3.go @@ -41,6 +41,8 @@ type ( GetUrl(filePath string, defaultFile ...string) (url string) // GetPath 从文件访问 URL 中提取文件路径 GetPath(url string) (filePath string) + // GetCdnUrl 通过文件名,获取直连地址 + GetCdnUrl(file string) string // CopyObject 在指定存储桶内复制文件 // bucketName 存储桶名称 // dstStr 目标文件路径 diff --git a/main.go b/main.go index 1093202..500d25c 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,8 @@ package main import ( - "github.com/ayflying/p2p/internal/consts" _ "github.com/ayflying/p2p/internal/logic" _ "github.com/ayflying/p2p/internal/packed" - "github.com/ayflying/p2p/internal/service" "github.com/gogf/gf/v2/os/gfile" //步骤1:加载驱动 @@ -19,9 +17,6 @@ var ( ) func main() { - // 启动系统托盘 - service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico") - if ok := gfile.Exists("runtime"); !ok { gfile.Mkdir("runtime") }