feat: implement server self-upgrade functionality

- Added update service to fetch the latest server release from GitHub.
- Implemented server upgrade scheduling and execution logic.
- Created platform-specific restart mechanisms for Unix and Windows.
- Introduced API endpoints for fetching the latest release and triggering upgrades.
- Developed UI components for version upgrade modal, including loading and error states.
- Updated documentation to reflect new upgrade features and instructions.
- Refactored settings types to streamline interface definitions.
This commit is contained in:
ryan
2026-03-11 22:05:42 +08:00
parent c0ec718563
commit 6140ed1718
22 changed files with 3332 additions and 2392 deletions
+23 -52
View File
@@ -1,27 +1,12 @@
package controller
import (
"encoding/json"
"fmt"
"atsflare/service"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
const latestReleaseURL = "https://api.github.com/repos/Rain-kl/ATSFlare/releases/latest"
var updateHTTPClient = &http.Client{
Timeout: 10 * time.Second,
}
type latestReleaseResponse struct {
TagName string `json:"tag_name"`
Body string `json:"body"`
HTMLURL string `json:"html_url"`
PublishedAt string `json:"published_at"`
}
// GetLatestRelease godoc
// @Summary Get latest GitHub release
// @Tags Update
@@ -29,40 +14,11 @@ type latestReleaseResponse struct {
// @Success 200 {object} map[string]interface{}
// @Router /api/update/latest-release [get]
func GetLatestRelease(c *gin.Context) {
req, err := http.NewRequestWithContext(c.Request.Context(), http.MethodGet, latestReleaseURL, nil)
release, err := service.GetLatestServerRelease(c.Request.Context())
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "创建更新请求失败",
})
return
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("User-Agent", "ATSFlare-Server")
resp, err := updateHTTPClient.Do(req)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": fmt.Sprintf("获取最新版本失败: %v", err),
})
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": fmt.Sprintf("GitHub 返回异常状态: %s", resp.Status),
})
return
}
var release latestReleaseResponse
if err = json.NewDecoder(resp.Body).Decode(&release); err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "解析最新版本信息失败",
"message": err.Error(),
})
return
}
@@ -74,10 +30,25 @@ func GetLatestRelease(c *gin.Context) {
})
}
func UpdateHTTPClientForTest() *http.Client {
return updateHTTPClient
}
// UpgradeServer godoc
// @Summary Upgrade server binary from latest GitHub release
// @Tags Update
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /api/update/upgrade [post]
func UpgradeServer(c *gin.Context) {
release, err := service.ScheduleServerUpgrade()
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
func SetUpdateHTTPClientForTest(client *http.Client) {
updateHTTPClient = client
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "服务升级任务已启动,下载完成后将自动重启。",
"data": release,
})
}