bash_go_service/version/handler/handler.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
}