145 lines
3.2 KiB
Go
145 lines
3.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"bash_go_service/shared/pkg/client"
|
|
"bash_go_service/shared/pkg/constants"
|
|
"bash_go_service/shared/pkg/logger"
|
|
"crypto/md5"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"time"
|
|
"version/api"
|
|
)
|
|
|
|
const (
|
|
updaterExeName = "updater-main"
|
|
)
|
|
|
|
func DoUpdate() {
|
|
logger.Debug("start check updating")
|
|
res, err := api.GetVersion()
|
|
if err != nil {
|
|
logger.Error("Failed to get version: %v", err)
|
|
return
|
|
}
|
|
logger.Debug("res: %v", res)
|
|
|
|
if !res.NeedUpdate {
|
|
logger.Info("No update needed.")
|
|
return
|
|
}
|
|
|
|
exePath, err := os.Executable()
|
|
if err != nil {
|
|
logger.Error("Failed to get executable path: %v", err)
|
|
return
|
|
}
|
|
exeDir := filepath.Dir(exePath)
|
|
exeBase := filepath.Base(exePath)
|
|
|
|
// 备份旧程序
|
|
backupName := fmt.Sprintf("%s.bak.%s", exeBase, time.Now().Format("20060102_150405"))
|
|
backupPath := filepath.Join(exeDir, backupName)
|
|
logger.Debug("backupPath: %v", backupPath)
|
|
err = copyFile(exePath, backupPath)
|
|
if err != nil {
|
|
logger.Error("Failed to backup current executable: %v", err)
|
|
return
|
|
}
|
|
logger.Info("Backup created: %s", backupPath)
|
|
|
|
// 下载新程序到临时路径
|
|
newExePath := filepath.Join(exeDir, exeBase+".new")
|
|
cli := client.NewClient()
|
|
query := map[string]string{
|
|
"version": res.Version,
|
|
}
|
|
err = cli.Download(constants.DownloadNewApi, query, newExePath)
|
|
if err != nil {
|
|
logger.Error("Download failed: %v", err)
|
|
_ = os.Rename(backupPath, exePath) // rollback
|
|
return
|
|
}
|
|
logger.Info("New executable downloaded to: %s", newExePath)
|
|
|
|
// 🔥 校验 MD5
|
|
if res.MD5 == "" {
|
|
logger.Error("MD5 is empty")
|
|
return
|
|
}
|
|
match, Merr := checkFileMD5(newExePath, res.MD5)
|
|
if Merr != nil {
|
|
logger.Error("Failed to check MD5: %v", Merr)
|
|
_ = os.Rename(backupPath, exePath)
|
|
return
|
|
}
|
|
if !match {
|
|
logger.Error("MD5 mismatch: expected %s", res.MD5)
|
|
_ = os.Rename(backupPath, exePath)
|
|
return
|
|
}
|
|
logger.Info("MD5 checksum verified.")
|
|
|
|
// 设置新文件执行权限
|
|
err = os.Chmod(newExePath, 0755)
|
|
if err != nil {
|
|
logger.Error("Failed to set executable permission: %v", err)
|
|
_ = os.Remove(newExePath)
|
|
_ = copyFile(backupPath, exePath)
|
|
return
|
|
}
|
|
logger.Info("Executable permission set for new file.")
|
|
|
|
// 启动 updater 进行替换
|
|
updaterPath := filepath.Join(exeDir, updaterExeName)
|
|
cmd := exec.Command(updaterPath, backupPath, newExePath, exePath)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
logger.Error("Failed to start updater: %v", err)
|
|
_ = os.Rename(backupPath, exePath)
|
|
return
|
|
}
|
|
logger.Info("Updater started, exiting current process.")
|
|
os.Exit(0)
|
|
}
|
|
|
|
// MD5 校验函数
|
|
func checkFileMD5(filePath, expectedMD5 string) (bool, error) {
|
|
f, err := os.Open(filePath)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer f.Close()
|
|
|
|
hash := md5.New()
|
|
if _, err := io.Copy(hash, f); err != nil {
|
|
return false, err
|
|
}
|
|
actualMD5 := fmt.Sprintf("%x", hash.Sum(nil))
|
|
return actualMD5 == expectedMD5, nil
|
|
}
|
|
|
|
// 复制文件函数
|
|
func copyFile(src, dst string) error {
|
|
sourceFile, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer sourceFile.Close()
|
|
|
|
destFile, err := os.Create(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer destFile.Close()
|
|
|
|
_, err = io.Copy(destFile, sourceFile)
|
|
return err
|
|
}
|