mirror of
https://github.com/ayflying/p2p.git
synced 2026-03-04 17:29:22 +00:00
增加分布式更新方法
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -18,4 +18,6 @@ temp.yaml
|
||||
bin
|
||||
**/config/config.yaml
|
||||
v1.0.0/
|
||||
manifest/config/local.yaml
|
||||
config/local.yaml
|
||||
main.exe~
|
||||
download/
|
||||
|
||||
@@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ayflying/p2p/internal/consts"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -47,7 +49,24 @@ var (
|
||||
})
|
||||
addr := g.Cfg().MustGet(ctx, "ws.address").String()
|
||||
ws := parser.GetOpt("ws", addr).String()
|
||||
//port := parser.GetOpt("port", 0).Int()
|
||||
if ws == "" {
|
||||
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)
|
||||
key := grand.Intn(len(p2pItem) - 1)
|
||||
wsData := p2pItem[key]
|
||||
ws = fmt.Sprintf("ws://%s:%d/ws", wsData.Host, wsData.Port)
|
||||
}
|
||||
|
||||
port := parser.GetOpt("port", 0).Int()
|
||||
if port > 0 {
|
||||
s.SetPort(port)
|
||||
}
|
||||
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
@@ -66,8 +85,6 @@ var (
|
||||
}
|
||||
})
|
||||
|
||||
//s.SetPort(port)
|
||||
|
||||
// 延迟启动
|
||||
gtimer.SetTimeout(ctx, time.Second*5, func(ctx context.Context) {
|
||||
g.Log().Debug(ctx, "开始执行客户端")
|
||||
@@ -75,12 +92,6 @@ var (
|
||||
err = service.P2P().Start(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())
|
||||
// }
|
||||
//}
|
||||
|
||||
})
|
||||
|
||||
|
||||
@@ -74,9 +74,6 @@ var (
|
||||
case "client":
|
||||
// 获取客户端模式所需的参数
|
||||
g.Log().Debug(ctx, "开始执行client")
|
||||
//addrs := []string{"/ip4/127.0.0.1/tcp/51888", "/ip4/192.168.50.173/tcp/51888"}
|
||||
//addr := "/ip4/192.168.50.173/tcp/51888/p2p/12D3KooWJKBB9bF9MjqgsFYUUsPBG249FDq7a3ZdaYc9iw8G78JQ"
|
||||
//addrs := "WyIvaXA0LzEyNy4wLjAuMS90Y3AvNTE4ODgiLCIvaXA0LzE5Mi4xNjguNTAuMTczL3RjcC81MTg4OCJd"
|
||||
wsStr := "ws://192.168.50.173:51888/ws"
|
||||
err = service.P2P().Start(wsStr)
|
||||
case "dht":
|
||||
|
||||
@@ -41,13 +41,14 @@ var (
|
||||
// 拼接操作系统和架构(格式:OS_ARCH)
|
||||
platform := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
var versionFile = make(map[string]string)
|
||||
rootDir := "server_update"
|
||||
|
||||
var versionFile = make(map[string]string)
|
||||
var filePath = path.Join(pathMain, version, platform, name)
|
||||
dirList, _ := gfile.ScanDir(path.Join(pathMain, version), "*", false)
|
||||
for _, v := range dirList {
|
||||
updatePlatform := gfile.Name(v)
|
||||
updateFilePath := path.Join("server_update", name, version, updatePlatform)
|
||||
updateFilePath := path.Join(rootDir, name, version, updatePlatform)
|
||||
|
||||
var obj bytes.Buffer
|
||||
g.Log().Debugf(ctx, "读取目录成功:%v", v)
|
||||
@@ -70,14 +71,14 @@ var (
|
||||
|
||||
// 写入文件版本文件
|
||||
fileByte := gjson.MustEncode(versionFile)
|
||||
service.S3().PutObject(ctx, bytes.NewReader(fileByte), path.Join("server_update", name, "version.json"))
|
||||
service.S3().PutObject(ctx, bytes.NewReader(fileByte), path.Join(rootDir, 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"))
|
||||
versionUrl := service.S3().GetCdnUrl(path.Join(rootDir, name))
|
||||
listVar := g.Cfg().MustGet(ctx, "p2p.list")
|
||||
var p2pItem []struct {
|
||||
Host string `json:"host"`
|
||||
@@ -96,7 +97,8 @@ var (
|
||||
|
||||
g.Log().Debugf(ctx, "开始上传到服务器:%v,file=%v", url, versionUrl)
|
||||
_, err := g.Client().Get(ctx, url, systemV1.UpdateReq{
|
||||
Url: versionUrl,
|
||||
Url: versionUrl,
|
||||
Version: version,
|
||||
})
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err)
|
||||
|
||||
@@ -2,73 +2,66 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"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/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
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)
|
||||
g.Log().Debugf(ctx, "当前文件哈希值:%v", fileSha)
|
||||
|
||||
go func() {
|
||||
log.Println("5秒后开始重启...")
|
||||
time.Sleep(5 * time.Second)
|
||||
versionUrl, _ := url.JoinPath(req.Url, "version.json")
|
||||
resp, err := g.Client().Get(ctx, versionUrl)
|
||||
var version map[string]string
|
||||
gjson.DecodeTo(resp.ReadAll(), &version)
|
||||
|
||||
if err = restartSelf(); err != nil {
|
||||
log.Fatalf("重启失败:%v", err)
|
||||
for k, _ := range version {
|
||||
//downloadUrl, _ := url.QueryUnescape(v)
|
||||
downloadUrl, _ := url.JoinPath(req.Url, req.Version, k+".gz")
|
||||
fileByte, err2 := g.Client().Get(ctx, downloadUrl)
|
||||
if err2 != nil {
|
||||
g.Log().Error(ctx, err2)
|
||||
continue
|
||||
}
|
||||
}()
|
||||
putFile := path.Join("download", gfile.Basename(downloadUrl))
|
||||
err2 = gfile.PutBytes(putFile, fileByte.ReadAll())
|
||||
if err2 != nil {
|
||||
g.Log().Error(ctx, err2)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
//更新文件
|
||||
err = service.System().Update(ctx)
|
||||
type DataType struct {
|
||||
File []byte `json:"file"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
var msgData = struct {
|
||||
Files []*DataType `json:"files"`
|
||||
}{}
|
||||
|
||||
msgData.Files = []*DataType{}
|
||||
|
||||
files, _ := gfile.ScanDir("download", ".*gz")
|
||||
|
||||
for _, v := range files {
|
||||
msgData.Files = append(msgData.Files, &DataType{
|
||||
File: gfile.GetBytes(v),
|
||||
Name: gfile.Basename(v),
|
||||
})
|
||||
}
|
||||
|
||||
service.P2P().SendAll("update", msgData)
|
||||
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 // 理论上不会执行到这里
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ import (
|
||||
_ "github.com/ayflying/p2p/internal/logic/os"
|
||||
_ "github.com/ayflying/p2p/internal/logic/p2p"
|
||||
_ "github.com/ayflying/p2p/internal/logic/s3"
|
||||
_ "github.com/ayflying/p2p/internal/logic/system"
|
||||
)
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"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"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
@@ -420,18 +421,23 @@ func (s *sP2P) receiveGatewayMessages(ctx context.Context) {
|
||||
glog.Errorf(ctx, "网关错误: %s", data.Error)
|
||||
case MsgUpdate: //更新节点信息
|
||||
var msgData struct {
|
||||
Server string `json:"server"`
|
||||
Version string `json:"version"`
|
||||
Files []struct {
|
||||
File []byte `json:"file"`
|
||||
Name string `json:"name"`
|
||||
} `json:"files"`
|
||||
}
|
||||
//var msgData *dataType
|
||||
json.Unmarshal(msg.Data, &msgData)
|
||||
for _, v := range msgData.Files {
|
||||
err = gfile.PutBytes(path.Join("download", v.Name), v.File)
|
||||
}
|
||||
// 更新器路径(假设与主程序同目录)
|
||||
//updaterPath := filepath.Join(filepath.Dir(selfPath), "updater.exe")
|
||||
|
||||
g.Log().Infof(ctx, "更新节点信息: %v", data)
|
||||
|
||||
// 调用不同系统的更新服务
|
||||
service.OS().Update(msgData.Version, msgData.Server)
|
||||
//// 调用不同系统的更新服务
|
||||
//service.OS().Update(msgData.Version, msgData.Server)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -66,14 +65,14 @@ func (s *sP2P) GatewayStart(ctx context.Context, group *ghttp.RouterGroup) (err
|
||||
_, data, _err := ws.ReadMessage()
|
||||
if _err != nil {
|
||||
//g.Log().Errorf(ctx, "读取消息失败: %v", err)
|
||||
//s.sendError(ws, err.Error())
|
||||
//s.SendError(ws, err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
var msg GatewayMessage
|
||||
var msg *GatewayMessage
|
||||
if err = json.Unmarshal(data, &msg); err != nil {
|
||||
//g.Log().Error(ctx, "消息格式错误")
|
||||
s.sendError(ws, "消息格式错误")
|
||||
s.SendError(ws, "消息格式错误")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -96,7 +95,7 @@ func (s *sP2P) GatewayStart(ctx context.Context, group *ghttp.RouterGroup) (err
|
||||
}
|
||||
|
||||
// 处理注册请求
|
||||
func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg GatewayMessage) {
|
||||
func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg *GatewayMessage) {
|
||||
if msg.From == "" {
|
||||
g.Log().Error(ctx, "客户端ID不能为空")
|
||||
return
|
||||
@@ -108,22 +107,22 @@ func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(msg.Data, &data); err != nil {
|
||||
s.sendError(conn, "注册数据格式错误")
|
||||
s.SendError(conn, "注册数据格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 追加公网ip
|
||||
publicIp, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
||||
ParseIP := net.ParseIP(publicIp)
|
||||
var ipType string
|
||||
if ParseIP.To4() != nil {
|
||||
ipType = "ip4"
|
||||
} else {
|
||||
ipType = "ip6"
|
||||
}
|
||||
port2 := 53533
|
||||
data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/tcp/%d", ipType, publicIp, port2))
|
||||
data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/udp/%d/quic-v1", ipType, publicIp, port2))
|
||||
//// 追加公网ip
|
||||
//publicIp, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
||||
//ParseIP := net.ParseIP(publicIp)
|
||||
//var ipType string
|
||||
//if ParseIP.To4() != nil {
|
||||
// ipType = "ip4"
|
||||
//} else {
|
||||
// ipType = "ip6"
|
||||
//}
|
||||
//port2 := 53533
|
||||
//data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/tcp/%d", ipType, publicIp, port2))
|
||||
//data.Addrs = append(data.Addrs, fmt.Sprintf("/%s/%s/udp/%d/quic-v1", ipType, publicIp, port2))
|
||||
|
||||
// 过滤回环地址
|
||||
data.Addrs = s.filterLoopbackAddrs(data.Addrs)
|
||||
@@ -144,12 +143,12 @@ func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
glog.Infof(ctx, "客户端 ip=%s,%s 注册成功,PeerID: %s", conn.RemoteAddr(), msg.From, data.PeerID)
|
||||
|
||||
// 发送注册成功响应
|
||||
err := s.sendMessage(conn, GatewayMessage{
|
||||
err := s.sendMessage(conn, &GatewayMessage{
|
||||
Type: MsgTypeRegisterAck,
|
||||
Data: json.RawMessage(`{"success": true, "message": "注册成功"}`),
|
||||
})
|
||||
if err != nil {
|
||||
s.sendError(conn, err.Error())
|
||||
s.SendError(conn, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,15 +177,41 @@ func (s *sP2P) cleanupClients(ctx context.Context) {
|
||||
}
|
||||
|
||||
// 发送错误消息
|
||||
func (s *sP2P) sendError(conn *websocket.Conn, errMsg string) {
|
||||
s.sendMessage(conn, GatewayMessage{
|
||||
func (s *sP2P) SendError(conn *websocket.Conn, errMsg string) {
|
||||
s.sendMessage(conn, &GatewayMessage{
|
||||
Type: "error",
|
||||
Data: json.RawMessage(fmt.Sprintf(`{"error": "%s"}`, errMsg)),
|
||||
})
|
||||
}
|
||||
|
||||
// SendAll 发送消息给所有客户端
|
||||
func (s *sP2P) SendAll(typ string, data any) (err error) {
|
||||
for _, client := range s.Clients {
|
||||
conn := client.Conn
|
||||
err = s.sendMessage(conn, &GatewayMessage{
|
||||
Type: MsgType(typ),
|
||||
Data: gjson.MustEncode(data),
|
||||
})
|
||||
if err != nil {
|
||||
g.Log().Error(gctx.New(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Send 发送消息给指定客户端
|
||||
func (s *sP2P) Send(conn *websocket.Conn, typ string, data any) (err error) {
|
||||
err = s.sendMessage(conn, &GatewayMessage{
|
||||
Type: MsgType(typ),
|
||||
Data: gjson.MustEncode(data),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
func (s *sP2P) sendMessage(conn *websocket.Conn, msg GatewayMessage) error {
|
||||
func (s *sP2P) sendMessage(conn *websocket.Conn, msg *GatewayMessage) error {
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
glog.Errorf(gctx.New(), "序列化消息失败: %v", err)
|
||||
@@ -196,9 +221,9 @@ func (s *sP2P) sendMessage(conn *websocket.Conn, msg GatewayMessage) error {
|
||||
}
|
||||
|
||||
// 处理发现请求
|
||||
func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg GatewayMessage) {
|
||||
func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg *GatewayMessage) {
|
||||
if msg.From == "" {
|
||||
s.sendError(conn, "消息缺少发送方ID(from)")
|
||||
s.SendError(conn, "消息缺少发送方ID(from)")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -207,12 +232,12 @@ func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(msg.Data, &data); err != nil {
|
||||
s.sendError(conn, "发现请求格式错误,需包含target_id")
|
||||
s.SendError(conn, "发现请求格式错误,需包含target_id")
|
||||
return
|
||||
}
|
||||
|
||||
if data.TargetID == "" {
|
||||
s.sendError(conn, "目标ID不能为空")
|
||||
s.SendError(conn, "目标ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -225,7 +250,7 @@ func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
s.lock.RUnlock()
|
||||
|
||||
//if !fromExists {
|
||||
// s.sendError(conn, "请先注册")
|
||||
// s.SendError(conn, "请先注册")
|
||||
// return
|
||||
//}
|
||||
|
||||
@@ -236,11 +261,10 @@ func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
|
||||
if targetClient == nil {
|
||||
// 目标不存在
|
||||
s.sendMessage(conn, GatewayMessage{
|
||||
s.sendMessage(conn, &GatewayMessage{
|
||||
Type: MsgTypeDiscoverAck,
|
||||
From: "gateway",
|
||||
To: msg.From,
|
||||
//Data: json.RawMessage(`{"found": false}`),
|
||||
Data: gjson.MustEncode(g.Map{
|
||||
"found": false,
|
||||
}),
|
||||
@@ -249,7 +273,7 @@ func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
}
|
||||
|
||||
// 向请求方发送目标信息
|
||||
s.sendMessage(conn, GatewayMessage{
|
||||
s.sendMessage(conn, &GatewayMessage{
|
||||
Type: MsgTypeDiscoverAck,
|
||||
From: "gateway", // 发送方是网关
|
||||
To: msg.From, // 接收方是原请求方
|
||||
@@ -262,7 +286,7 @@ func (s *sP2P) handleDiscover(ctx context.Context, conn *websocket.Conn, msg Gat
|
||||
})
|
||||
|
||||
// 向目标方发送打洞请求(协调时机)
|
||||
s.sendMessage(targetClient.Conn, GatewayMessage{
|
||||
s.sendMessage(targetClient.Conn, &GatewayMessage{
|
||||
Type: MsgTypePunchRequest,
|
||||
From: msg.From, // 发送方是原请求方
|
||||
To: data.TargetID, // 接收方是目标方
|
||||
|
||||
@@ -26,8 +26,8 @@ var (
|
||||
|
||||
// 常量定义
|
||||
const (
|
||||
ProtocolID string = "/ay"
|
||||
DefaultPort = 51888
|
||||
ProtocolID string = "/ay"
|
||||
//DefaultPort = 51888
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -58,7 +58,10 @@ func New(_name ...string) *sS3 {
|
||||
if len(_name) > 0 {
|
||||
name = _name[0]
|
||||
} else {
|
||||
getName, _ := g.Cfg("local").Get(gctx.New(), "s3.type")
|
||||
getName, err := g.Cfg("local").Get(gctx.New(), "s3.type")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
name = getName.String()
|
||||
}
|
||||
|
||||
|
||||
17
internal/logic/system/system.go
Normal file
17
internal/logic/system/system.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/ayflying/p2p/internal/service"
|
||||
)
|
||||
|
||||
type sSystem struct{}
|
||||
|
||||
func New() *sSystem {
|
||||
return &sSystem{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSystem(New())
|
||||
}
|
||||
|
||||
func (system *sSystem) Init() {}
|
||||
98
internal/logic/system/update.go
Normal file
98
internal/logic/system/update.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ayflying/p2p/internal/service"
|
||||
"github.com/gogf/gf/v2/encoding/gcompress"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
)
|
||||
|
||||
func (s *sSystem) Update(ctx context.Context) (err error) {
|
||||
//拼接操作系统和架构(格式:OS_ARCH)
|
||||
platform := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
runFile := gcmd.GetArg(0).String()
|
||||
oldFile, err := service.System().RenameRunningFile(runFile)
|
||||
g.Log().Debugf(ctx, "执行文件改名为%v", oldFile)
|
||||
gz := path.Join("download", platform+".gz")
|
||||
err = gcompress.UnGzipFile(gz, runFile)
|
||||
|
||||
go func() {
|
||||
log.Println("5秒后开始重启...")
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
if err = service.System().RestartSelf(); err != nil {
|
||||
log.Fatalf("重启失败:%v", err)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
// RestartSelf 实现 Windows 平台下的程序自重启
|
||||
func (s *sSystem) 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 // 理论上不会执行到这里
|
||||
}
|
||||
|
||||
// RenameRunningFile 重命名正在运行的程序文件(如 p2p.exe → p2p.exe~)
|
||||
func (s *sSystem) RenameRunningFile(exePath string) (string, error) {
|
||||
// 目标备份文件名(p2p.exe → p2p.exe~)
|
||||
backupPath := exePath + "~"
|
||||
|
||||
// 先删除已存在的备份文件(若有)
|
||||
if _, err := os.Stat(backupPath); err == nil {
|
||||
if err := os.Remove(backupPath); err != nil {
|
||||
return "", fmt.Errorf("删除旧备份文件失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 重命名正在运行的 exe 文件
|
||||
// 关键:Windows 允许对锁定的文件执行重命名操作
|
||||
if err := os.Rename(exePath, backupPath); err != nil {
|
||||
return "", fmt.Errorf("重命名运行中文件失败: %v", err)
|
||||
}
|
||||
return backupPath, nil
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
)
|
||||
|
||||
@@ -28,6 +29,12 @@ type (
|
||||
// FindFromDHT 从 DHT 查找数据(从网络节点获取)
|
||||
FindFromDHT(ctx context.Context, key string) (string, error)
|
||||
GatewayStart(ctx context.Context, group *ghttp.RouterGroup) (err error)
|
||||
// 发送错误消息
|
||||
SendError(conn *websocket.Conn, errMsg string)
|
||||
// SendAll 发送消息给所有客户端
|
||||
SendAll(typ string, data any) (err error)
|
||||
// Send 发送消息给指定客户端
|
||||
Send(conn *websocket.Conn, typ string, data any) (err error)
|
||||
// 只获取IPv4公网IP(过滤IPv6结果)
|
||||
GetIPv4PublicIP() (string, error)
|
||||
}
|
||||
|
||||
36
internal/service/system.go
Normal file
36
internal/service/system.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// ================================================================================
|
||||
// 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
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
ISystem interface {
|
||||
Init()
|
||||
Update(ctx context.Context) (err error)
|
||||
// RestartSelf 实现 Windows 平台下的程序自重启
|
||||
RestartSelf() error
|
||||
// RenameRunningFile 重命名正在运行的程序文件(如 p2p.exe → p2p.exe~)
|
||||
RenameRunningFile(exePath string) (string, error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
localSystem ISystem
|
||||
)
|
||||
|
||||
func System() ISystem {
|
||||
if localSystem == nil {
|
||||
panic("implement not found for interface ISystem, forgot register?")
|
||||
}
|
||||
return localSystem
|
||||
}
|
||||
|
||||
func RegisterSystem(i ISystem) {
|
||||
localSystem = i
|
||||
}
|
||||
36
main.go
36
main.go
@@ -1,9 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "github.com/ayflying/p2p/internal/logic"
|
||||
_ "github.com/ayflying/p2p/internal/packed"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
//步骤1:加载驱动
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
@@ -17,9 +23,39 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.Log().Infof(ctx, "启动文件最后修改时间:%v", gtime.New(gfile.MTime(gcmd.GetArg(0).String())).String())
|
||||
g.Dump("v1.0.0.2")
|
||||
|
||||
if ok := gfile.Exists("runtime"); !ok {
|
||||
gfile.Mkdir("runtime")
|
||||
}
|
||||
|
||||
//daili()
|
||||
|
||||
cmd.Main.Run(ctx)
|
||||
}
|
||||
|
||||
func daili() {
|
||||
|
||||
// 读取HTTP代理环境变量(小写/大写通常都兼容,部分系统可能用大写)
|
||||
httpProxy := os.Getenv("http_proxy")
|
||||
if httpProxy == "" {
|
||||
httpProxy = os.Getenv("HTTP_PROXY")
|
||||
}
|
||||
|
||||
// 读取HTTPS代理环境变量
|
||||
httpsProxy := os.Getenv("https_proxy")
|
||||
if httpsProxy == "" {
|
||||
httpsProxy = os.Getenv("HTTPS_PROXY")
|
||||
}
|
||||
|
||||
// 读取无代理列表(不使用代理的域名/IP)
|
||||
noProxy := os.Getenv("no_proxy")
|
||||
if noProxy == "" {
|
||||
noProxy = os.Getenv("NO_PROXY")
|
||||
}
|
||||
|
||||
fmt.Printf("HTTP 代理: %s\n", httpProxy)
|
||||
fmt.Printf("HTTPS 代理: %s\n", httpsProxy)
|
||||
fmt.Printf("无代理列表: %s\n", noProxy)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user