Files
web-giao-ban-go/updater/updater.go
T

191 lines
4.6 KiB
Go
Raw Normal View History

package updater
import (
"encoding/json"
"fmt"
"io"
"net/http"
"runtime"
"strings"
"time"
"github.com/minio/selfupdate"
"web-giao-ban-go/config"
"web-giao-ban-go/logger"
)
type GiteaAsset struct {
ID int64 `json:"id"`
Name string `json:"name"`
Size int64 `json:"size"`
DownloadURL string `json:"browser_download_url"`
}
type GiteaRelease struct {
TagName string `json:"tag_name"`
Name string `json:"name"`
Body string `json:"body"`
CreatedAt time.Time `json:"created_at"`
PublishedAt time.Time `json:"published_at"`
Assets []GiteaAsset `json:"assets"`
}
type CheckResult struct {
HasUpdate bool `json:"has_update"`
CurrentVersion string `json:"current_version"`
LatestVersion string `json:"latest_version"`
ReleaseTitle string `json:"release_title"`
ReleaseNotes string `json:"release_notes"`
AssetName string `json:"asset_name"`
AssetURL string `json:"asset_url"`
AssetSize int64 `json:"asset_size"`
}
func CheckUpdate() (*CheckResult, error) {
cfg := config.Get()
if !cfg.Updater.Enabled {
return &CheckResult{HasUpdate: false, CurrentVersion: cfg.Updater.CurrentVersion}, nil
}
baseURL := strings.TrimRight(cfg.Updater.GiteaURL, "/")
if baseURL == "" {
baseURL = "https://i.dh-his.com"
}
owner := cfg.Updater.Owner
if owner == "" || owner == "hdhiswork" {
owner = "ongtrieuhau"
}
repo := cfg.Updater.Repo
if repo == "" || repo == "DUAN" {
repo = "web-giao-ban"
}
apiURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/releases/latest", baseURL, owner, repo)
req, err := http.NewRequest("GET", apiURL, nil)
if err != nil {
return nil, err
}
token := cfg.DecryptedUpdaterToken
if token == "" {
token, _ = config.Decrypt(cfg.Updater.Token)
}
if token != "" {
req.Header.Set("Authorization", "token "+token)
}
req.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return &CheckResult{
HasUpdate: false,
CurrentVersion: cfg.Updater.CurrentVersion,
LatestVersion: cfg.Updater.CurrentVersion,
ReleaseNotes: "Chưa có bản phát hành nào trên Gitea",
}, nil
}
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("Gitea API trả về mã lỗi %d: %s", resp.StatusCode, string(bodyBytes))
}
var rel GiteaRelease
if err := json.NewDecoder(resp.Body).Decode(&rel); err != nil {
return nil, err
}
curVer := cfg.Updater.CurrentVersion
hasNew := isNewerVersion(rel.TagName, curVer)
// Pick asset according to OS
var matchedAsset *GiteaAsset
for _, a := range rel.Assets {
lowerName := strings.ToLower(a.Name)
if runtime.GOOS == "windows" {
if strings.HasSuffix(lowerName, ".exe") {
matchedAsset = &a
break
}
} else {
if strings.Contains(lowerName, "linux") || (!strings.HasSuffix(lowerName, ".exe") && !strings.HasSuffix(lowerName, ".zip")) {
matchedAsset = &a
break
}
}
}
res := &CheckResult{
HasUpdate: hasNew,
CurrentVersion: curVer,
LatestVersion: rel.TagName,
ReleaseTitle: rel.Name,
ReleaseNotes: rel.Body,
}
if matchedAsset != nil {
res.AssetName = matchedAsset.Name
res.AssetURL = matchedAsset.DownloadURL
res.AssetSize = matchedAsset.Size
}
return res, nil
}
func isNewerVersion(latest, current string) bool {
latest = strings.TrimPrefix(strings.TrimSpace(latest), "v")
current = strings.TrimPrefix(strings.TrimSpace(current), "v")
if latest == "" {
return false
}
if current == "" {
return true
}
return latest != current
}
func ApplyUpdate(downloadURL string) error {
cfg := config.Get()
logger.Info("Bắt đầu tải bản cập nhật từ: %s", downloadURL)
req, err := http.NewRequest("GET", downloadURL, nil)
if err != nil {
return err
}
token := cfg.DecryptedUpdaterToken
if token == "" {
token, _ = config.Decrypt(cfg.Updater.Token)
}
if token != "" {
req.Header.Set("Authorization", "token "+token)
}
client := &http.Client{Timeout: 5 * time.Minute}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("tải bản cập nhật thất bại với HTTP status %d", resp.StatusCode)
}
err = selfupdate.Apply(resp.Body, selfupdate.Options{})
if err != nil {
logger.Error("Self-update thất bại: %v", err)
return err
}
logger.Info("Đã cập nhật binary ứng dụng thành công! Vui lòng khởi động lại ứng dụng.")
return nil
}