Initial commit

This commit is contained in:
2026-06-30 15:14:37 +08:00
commit 15dab96872
311 changed files with 95639 additions and 0 deletions
+173
View File
@@ -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
}