mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-05-11 18:10:23 +08:00
功能: 系统更新检查(GitHub Release + Docker)
后端: - 新增 GET /api/system/update-check,从 GitHub Releases API 获取最新版本 - 自动比较当前版本与最新版本,匹配当前平台的下载链接 - 返回版本号、更新说明、下载链接、Docker 镜像信息 前端(系统设置页重构): - 新增"检查更新"按钮,点击后展示更新结果 - 有新版本时显示版本号、更新说明、下载按钮、Docker 更新命令 - 新增磁盘状态卡片(总空间/已用/可用/使用率) - 运行模式用彩色 Tag 区分(生产/开发)
This commit is contained in:
@@ -68,6 +68,7 @@ func NewRouter(deps RouterDependencies) *gin.Engine {
|
||||
system := api.Group("/system")
|
||||
system.Use(AuthMiddleware(deps.JWTManager))
|
||||
system.GET("/info", systemHandler.Info)
|
||||
system.GET("/update-check", systemHandler.CheckUpdate)
|
||||
|
||||
storageTargets := api.Group("/storage-targets")
|
||||
storageTargets.Use(AuthMiddleware(deps.JWTManager))
|
||||
|
||||
@@ -17,3 +17,17 @@ func NewSystemHandler(systemService *service.SystemService) *SystemHandler {
|
||||
func (h *SystemHandler) Info(c *gin.Context) {
|
||||
response.Success(c, h.systemService.GetInfo(c.Request.Context()))
|
||||
}
|
||||
|
||||
func (h *SystemHandler) CheckUpdate(c *gin.Context) {
|
||||
result, err := h.systemService.CheckUpdate(c.Request.Context())
|
||||
if err != nil {
|
||||
// 即使检查失败也返回当前版本信息
|
||||
response.Success(c, gin.H{
|
||||
"currentVersion": result.CurrentVersion,
|
||||
"hasUpdate": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -30,6 +35,82 @@ func NewSystemService(cfg config.Config, version string, startedAt time.Time) *S
|
||||
return &SystemService{cfg: cfg, version: version, startedAt: startedAt}
|
||||
}
|
||||
|
||||
// UpdateCheckResult 描述版本更新检查结果。
|
||||
type UpdateCheckResult struct {
|
||||
CurrentVersion string `json:"currentVersion"`
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
HasUpdate bool `json:"hasUpdate"`
|
||||
ReleaseURL string `json:"releaseUrl,omitempty"`
|
||||
ReleaseNotes string `json:"releaseNotes,omitempty"`
|
||||
PublishedAt string `json:"publishedAt,omitempty"`
|
||||
DownloadURL string `json:"downloadUrl,omitempty"`
|
||||
DockerImage string `json:"dockerImage,omitempty"`
|
||||
}
|
||||
|
||||
const githubRepoAPI = "https://api.github.com/repos/Awuqing/BackupX/releases/latest"
|
||||
|
||||
// CheckUpdate 从 GitHub Releases 检查是否有新版本。
|
||||
func (s *SystemService) CheckUpdate(ctx context.Context) (*UpdateCheckResult, error) {
|
||||
result := &UpdateCheckResult{
|
||||
CurrentVersion: s.version,
|
||||
DockerImage: "awuqing/backupx",
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, githubRepoAPI, nil)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||
req.Header.Set("User-Agent", "BackupX/"+s.version)
|
||||
|
||||
client := &http.Client{Timeout: 15 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("fetch latest release: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return result, fmt.Errorf("github api returned %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var release struct {
|
||||
TagName string `json:"tag_name"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Body string `json:"body"`
|
||||
Published string `json:"published_at"`
|
||||
Assets []struct {
|
||||
Name string `json:"name"`
|
||||
BrowserDownloadURL string `json:"browser_download_url"`
|
||||
} `json:"assets"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
return result, fmt.Errorf("decode release: %w", err)
|
||||
}
|
||||
|
||||
result.LatestVersion = release.TagName
|
||||
result.ReleaseURL = release.HTMLURL
|
||||
result.ReleaseNotes = release.Body
|
||||
result.PublishedAt = release.Published
|
||||
|
||||
// 比较版本号(去 v 前缀后字符串比较)
|
||||
current := strings.TrimPrefix(s.version, "v")
|
||||
latest := strings.TrimPrefix(release.TagName, "v")
|
||||
result.HasUpdate = latest > current && current != "dev"
|
||||
|
||||
// 匹配当前平台的下载链接
|
||||
goos := runtime.GOOS
|
||||
goarch := runtime.GOARCH
|
||||
suffix := fmt.Sprintf("%s-%s.tar.gz", goos, goarch)
|
||||
for _, asset := range release.Assets {
|
||||
if strings.HasSuffix(asset.Name, suffix) {
|
||||
result.DownloadURL = asset.BrowserDownloadURL
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SystemService) GetInfo(_ context.Context) *SystemInfo {
|
||||
now := time.Now().UTC()
|
||||
info := &SystemInfo{
|
||||
|
||||
Reference in New Issue
Block a user