feat: DH-HIS Web Giao Ban Go v1.0.1 (Favicon, Exe icon, System Tray, Gitea auto-update)
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
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"
|
||||
}
|
||||
repo := cfg.Updater.Repo
|
||||
if repo == "" {
|
||||
repo = "DUAN"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user