Initial commit
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UpdateHandler 系统更新处理器
|
||||
type UpdateHandler struct {
|
||||
httpClient *http.Client
|
||||
currentVersion string
|
||||
}
|
||||
|
||||
// NewUpdateHandler 创建系统更新处理器
|
||||
func NewUpdateHandler(currentVersion string) *UpdateHandler {
|
||||
return &UpdateHandler{
|
||||
httpClient: &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
currentVersion: currentVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// GitHubRelease GitHub 发布响应结构
|
||||
type GitHubRelease struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Name string `json:"name"`
|
||||
Body string `json:"body"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
HtmlURL string `json:"html_url"`
|
||||
Assets []struct {
|
||||
Name string `json:"name"`
|
||||
DownloadURL string `json:"browser_download_url"`
|
||||
Size int64 `json:"size"`
|
||||
} `json:"assets"`
|
||||
}
|
||||
|
||||
// CheckUpdateResponse 检查更新响应
|
||||
type CheckUpdateResponse struct {
|
||||
HasUpdate bool `json:"has_update"`
|
||||
LatestVersion string `json:"latest_version"`
|
||||
CurrentVersion string `json:"current_version"`
|
||||
ReleaseNotes string `json:"release_notes"`
|
||||
DownloadURL string `json:"download_url"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// CheckUpdate 检查更新
|
||||
// @Summary 检查系统更新
|
||||
// @Tags System
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} CheckUpdateResponse
|
||||
// @Router /api/v1/system/update/check [get]
|
||||
func (h *UpdateHandler) CheckUpdate() (*CheckUpdateResponse, error) {
|
||||
// Gitea Releases API
|
||||
repo := "zkcoi/meshray"
|
||||
url := fmt.Sprintf("https://git.zkcoi.com/api/v1/repos/%s/releases/latest", repo)
|
||||
|
||||
resp, err := h.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return &CheckUpdateResponse{
|
||||
HasUpdate: false,
|
||||
CurrentVersion: h.currentVersion,
|
||||
Error: "检查更新失败:" + err.Error(),
|
||||
}, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return &CheckUpdateResponse{
|
||||
HasUpdate: false,
|
||||
CurrentVersion: h.currentVersion,
|
||||
Error: "读取响应失败:" + err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var release GitHubRelease
|
||||
if err := json.Unmarshal(body, &release); err != nil {
|
||||
return &CheckUpdateResponse{
|
||||
HasUpdate: false,
|
||||
CurrentVersion: h.currentVersion,
|
||||
Error: "解析响应失败:" + err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 移除版本号前缀的 'v'
|
||||
latestVersion := release.TagName
|
||||
if len(latestVersion) > 0 && latestVersion[0] == 'v' {
|
||||
latestVersion = latestVersion[1:]
|
||||
}
|
||||
|
||||
currentVersion := h.currentVersion
|
||||
if len(currentVersion) > 0 && currentVersion[0] == 'v' {
|
||||
currentVersion = currentVersion[1:]
|
||||
}
|
||||
|
||||
// 比较版本号
|
||||
hasUpdate := compareVersions(latestVersion, currentVersion) > 0
|
||||
|
||||
downloadURL := release.HtmlURL
|
||||
if len(release.Assets) > 0 {
|
||||
// 优先选择 Windows 可执行文件
|
||||
for _, asset := range release.Assets {
|
||||
if asset.Name == "meshray.exe" {
|
||||
downloadURL = asset.DownloadURL
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &CheckUpdateResponse{
|
||||
HasUpdate: hasUpdate,
|
||||
LatestVersion: release.TagName,
|
||||
CurrentVersion: h.currentVersion,
|
||||
ReleaseNotes: release.Body,
|
||||
DownloadURL: downloadURL,
|
||||
PublishedAt: release.PublishedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// compareVersions 比较版本号
|
||||
// 返回:1 (v1 > v2), 0 (v1 == v2), -1 (v1 < v2)
|
||||
func compareVersions(v1, v2 string) int {
|
||||
if v1 == v2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 简单版本号比较(格式:major.minor.patch)
|
||||
parts1 := parseVersion(v1)
|
||||
parts2 := parseVersion(v2)
|
||||
|
||||
for i := 0; i < len(parts1) && i < len(parts2); i++ {
|
||||
if parts1[i] > parts2[i] {
|
||||
return 1
|
||||
} else if parts1[i] < parts2[i] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// 如果前面都相同,比较长度
|
||||
if len(parts1) > len(parts2) {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// parseVersion 解析版本号字符串为整数数组
|
||||
func parseVersion(version string) []int {
|
||||
var parts []int
|
||||
current := 0
|
||||
|
||||
for i, ch := range version {
|
||||
if ch == '.' {
|
||||
parts = append(parts, current)
|
||||
current = 0
|
||||
} else if ch >= '0' && ch <= '9' {
|
||||
current = current*10 + int(ch-'0')
|
||||
}
|
||||
|
||||
// 处理最后一个字符
|
||||
if i == len(version)-1 {
|
||||
parts = append(parts, current)
|
||||
}
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
Reference in New Issue
Block a user