feat: 完成升级功能

This commit is contained in:
ssongliu 2023-01-30 21:05:20 +08:00 committed by ssongliu
parent 2652e7089b
commit 905999cdf0
22 changed files with 493 additions and 77 deletions

View File

@ -45,4 +45,5 @@ var (
logService = service.ServiceGroupApp.LogService logService = service.ServiceGroupApp.LogService
snapshotService = service.ServiceGroupApp.SnapshotService snapshotService = service.ServiceGroupApp.SnapshotService
upgradeService = service.ServiceGroupApp.UpgradeService
) )

View File

@ -25,10 +25,45 @@ func (b *BaseApi) GetUpgradeInfo(c *gin.Context) {
} }
info := dto.UpgradeInfo{ info := dto.UpgradeInfo{
NewVersion: string(*stats.Name), NewVersion: string(*stats.Name),
Tag: string(*stats.TagName),
ReleaseNote: string(*stats.Body), ReleaseNote: string(*stats.Body),
CreatedAt: github.Timestamp(*stats.CreatedAt).Format("2006-01-02 15:04:05"), CreatedAt: github.Timestamp(*stats.CreatedAt).Format("2006-01-02 15:04:05"),
PublishedAt: github.Timestamp(*stats.PublishedAt).Format("2006-01-02 15:04:05"),
} }
helper.SuccessWithData(c, info) helper.SuccessWithData(c, info)
} }
// @Tags System Setting
// @Summary Load upgrade info
// @Description 从 OSS 加载系统更新信息
// @Success 200 {object} dto.UpgradeInfo
// @Security ApiKeyAuth
// @Router /settings/upgrade [get]
func (b *BaseApi) GetUpgradeInfoByOSS(c *gin.Context) {
info, err := upgradeService.SearchUpgrade()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, info)
}
// @Tags System Setting
// @Summary Upgrade
// @Description 系统更新
// @Accept json
// @Param request body dto.Upgrade true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/upgrade [post]
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade service => [version]"}
func (b *BaseApi) Upgrade(c *gin.Context) {
var req dto.Upgrade
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := upgradeService.Upgrade(req.Version); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

View File

@ -76,8 +76,9 @@ type SnapshotInfo struct {
type UpgradeInfo struct { type UpgradeInfo struct {
NewVersion string `json:"newVersion"` NewVersion string `json:"newVersion"`
Tag string `json:"tag"`
ReleaseNote string `json:"releaseNote"` ReleaseNote string `json:"releaseNote"`
CreatedAt string `json:"createdAt"` CreatedAt string `json:"createdAt"`
PublishedAt string `json:"publishedAt"` }
type Upgrade struct {
Version string `json:"version"`
} }

View File

@ -4,8 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr"
"io/ioutil" "io/ioutil"
"math" "math"
"net/http" "net/http"
@ -16,6 +14,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
@ -490,7 +491,7 @@ func handleErr(install model.AppInstall, err error, out string) error {
} }
func getAppFromOss() error { func getAppFromOss() error {
res, err := http.Get(global.CONF.System.AppOss) res, err := http.Get(global.CONF.System.AppOss + "/apps/apps.json")
if err != nil { if err != nil {
return err return err
} }

View File

@ -38,6 +38,7 @@ type ServiceGroup struct {
LogService LogService
SnapshotService SnapshotService
UpgradeService
} }
var ServiceGroupApp = new(ServiceGroup) var ServiceGroupApp = new(ServiceGroup)

View File

@ -95,7 +95,6 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
_ = snapshotRepo.Create(&snap) _ = snapshotRepo.Create(&snap)
go func() { go func() {
defer func() { defer func() {
global.LOG.Info("zhengque zoudao le zheli")
_ = os.RemoveAll(rootDir) _ = os.RemoveAll(rootDir)
}() }()
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
@ -157,10 +156,8 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
return return
} }
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess}) _ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
_ = os.RemoveAll(rootDir)
_ = os.RemoveAll(fmt.Sprintf("%s/system/1panel_snapshot_%s.tar.gz", localDir, timeNow)) _ = os.RemoveAll(fmt.Sprintf("%s/system/1panel_snapshot_%s.tar.gz", localDir, timeNow))
updateSnapshotStatus(snap.ID, constant.StatusSuccess, "")
global.LOG.Infof("upload snapshot to %s success", backup.Type) global.LOG.Infof("upload snapshot to %s success", backup.Type)
}() }()
return nil return nil
@ -313,17 +310,11 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
} }
isReTry = false isReTry = false
} }
fmt.Println(000)
_ = os.RemoveAll(rootDir) _ = os.RemoveAll(rootDir)
fmt.Println(111)
global.LOG.Info("recover successful") global.LOG.Info("recover successful")
fmt.Println(222)
_, _ = cmd.Exec("systemctl daemon-reload") _, _ = cmd.Exec("systemctl daemon-reload")
fmt.Println(333)
_, _ = cmd.Exec("systemctl restart 1panel.service") _, _ = cmd.Exec("systemctl restart 1panel.service")
fmt.Println(444)
updateRecoverStatus(snap.ID, "", constant.StatusSuccess, "") updateRecoverStatus(snap.ID, "", constant.StatusSuccess, "")
fmt.Println(555)
}() }()
return nil return nil
} }
@ -435,17 +426,11 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
return nil return nil
} }
fmt.Println(000)
_ = os.RemoveAll(rootDir) _ = os.RemoveAll(rootDir)
fmt.Println(111)
global.LOG.Info("rollback successful") global.LOG.Info("rollback successful")
fmt.Println(222)
_, _ = cmd.Exec("systemctl daemon-reload") _, _ = cmd.Exec("systemctl daemon-reload")
fmt.Println(333)
_, _ = cmd.Exec("systemctl restart 1panel.service") _, _ = cmd.Exec("systemctl restart 1panel.service")
fmt.Println(444)
updateRollbackStatus(snap.ID, constant.StatusSuccess, "") updateRollbackStatus(snap.ID, constant.StatusSuccess, "")
fmt.Println(555)
return nil return nil
} }

View File

@ -4,16 +4,26 @@ import (
"context" "context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings"
"testing" "testing"
"time"
"github.com/1Panel-dev/1Panel/backend/init/log"
"github.com/1Panel-dev/1Panel/backend/init/viper"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/google/go-github/github" "github.com/google/go-github/github"
) )
func TestDi(t *testing.T) { func TestDi(t *testing.T) {
docker := "var/lib/docker" viper.Init()
fmt.Println(docker[strings.LastIndex(docker, "/"):]) log.Init()
fmt.Println(docker[:strings.LastIndex(docker, "/")]) ti := time.Now().Format("20060102150405")
oss := "https://1panel.oss-cn-hangzhou.aliyuncs.com"
tmpPath := fmt.Sprintf("/opt/1Panel/data/tmp/%s_upgrade.tar.gz", ti)
fileOp := files.NewFileOp()
downloadPath := fmt.Sprintf("%s/releases/v1.0.1/v1.0.1.tar.gz", oss)
if err := fileOp.DownloadFile(downloadPath, tmpPath); err != nil {
fmt.Println(err)
}
} }
func TestGit(t *testing.T) { func TestGit(t *testing.T) {

View File

@ -0,0 +1,176 @@
package service
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
type latestVersion struct {
Version string `json:"version"`
UpdateTime string `json:"update_time"`
}
type UpgradeService struct{}
type IUpgradeService interface {
Upgrade(version string) error
SearchUpgrade() (*dto.UpgradeInfo, error)
}
func NewIUpgradeService() IUpgradeService {
return &UpgradeService{}
}
func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
res, err := http.Get(global.CONF.System.AppOss + "/releases/latest.json")
if err != nil {
return nil, err
}
resByte, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
var latest latestVersion
if err := json.Unmarshal(resByte, &latest); err != nil {
return nil, err
}
setting, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
if err != nil {
return nil, err
}
if latest.Version != setting.Value {
notes, err := http.Get(global.CONF.System.AppOss + fmt.Sprintf("/releases/%s/release_notes.md", latest.Version))
if err != nil {
return nil, err
}
noteBytes, err := ioutil.ReadAll(notes.Body)
if err != nil {
return nil, err
}
return &dto.UpgradeInfo{
NewVersion: latest.Version,
CreatedAt: latest.UpdateTime,
ReleaseNote: string(noteBytes),
}, nil
}
return nil, nil
}
func (u *UpgradeService) Upgrade(version string) error {
global.LOG.Info("start to upgrade now...")
fileOp := files.NewFileOp()
timeStr := time.Now().Format("20060102150405")
filePath := fmt.Sprintf("%s/%s.tar.gz", constant.TmpDir, timeStr)
rootDir := constant.TmpDir + "/" + timeStr
originalDir := fmt.Sprintf("%s/%s/original", constant.TmpDir, timeStr)
downloadPath := fmt.Sprintf("%s/releases/%s/%s.tar.gz", global.CONF.System.AppOss, version, version)
setting, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
if err != nil {
return err
}
u.changeStatus(constant.StatusWaiting, nil)
go func() {
if err := os.MkdirAll(originalDir, os.ModePerm); err != nil {
u.changeStatus(setting.Value, err)
return
}
if err := fileOp.DownloadFile(downloadPath, filePath); err != nil {
u.changeStatus(setting.Value, fmt.Errorf("download file failed, err: %v", err))
return
}
global.LOG.Info("download file from oss successful!")
defer func() {
_ = os.Remove(filePath)
}()
if err := fileOp.Decompress(filePath, rootDir, files.TarGz); err != nil {
u.changeStatus(setting.Value, fmt.Errorf("decompress file failed, err: %v", err))
return
}
if err := u.handleBackup(fileOp, originalDir); err != nil {
u.changeStatus(setting.Value, fmt.Errorf("handle backup original file failed, err: %v", err))
return
}
global.LOG.Info("backup original data successful, now start to upgrade!")
if err := cpBinary(rootDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
u.handleRollback(fileOp, originalDir, 1)
u.changeStatus(setting.Value, fmt.Errorf("upgrade 1panel failed, err: %v", err))
return
}
if err := cpBinary(rootDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
u.handleRollback(fileOp, originalDir, 2)
u.changeStatus(setting.Value, fmt.Errorf("upgrade 1pctl failed, err: %v", err))
return
}
if err := cpBinary(rootDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
u.handleRollback(fileOp, originalDir, 3)
u.changeStatus(setting.Value, fmt.Errorf("upgrade 1panel.service failed, err: %v", err))
return
}
global.LOG.Info("upgrade successful!")
u.changeStatus(version, nil)
_, _ = cmd.Exec("systemctl restart 1panel.service")
}()
return nil
}
func (u *UpgradeService) handleBackup(fileOp files.FileOp, originalDir string) error {
if err := fileOp.Copy("/usr/local/bin/1panel", originalDir); err != nil {
return err
}
if err := fileOp.Copy("/usr/local/bin/1pctl", originalDir); err != nil {
return err
}
if err := fileOp.Copy("/etc/systemd/system/1panel.service", originalDir); err != nil {
return err
}
dbPath := global.CONF.System.DbPath + "/" + global.CONF.System.DbFile
if err := fileOp.Copy(dbPath, originalDir); err != nil {
return err
}
return nil
}
func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string, errStep int) {
dbPath := global.CONF.System.DbPath + "/1Panel.db"
if err := cpBinary(originalDir+"/1Panel.db", dbPath); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
if err := cpBinary(originalDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
global.LOG.Errorf("rollback 1pctl failed, err: %v", err)
}
if errStep == 1 {
return
}
if err := cpBinary(originalDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
if errStep == 2 {
return
}
if err := cpBinary(originalDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
}
func (u *UpgradeService) changeStatus(status string, err error) {
if err != nil {
global.LOG.Error(err.Error())
}
if err := settingRepo.Update("SystemVersion", status); err != nil {
global.LOG.Errorf("update system version failed, err: %v", err)
}
}

View File

@ -1,11 +1,12 @@
package viper package viper
import ( import (
"bytes"
"fmt" "fmt"
"path"
"github.com/1Panel-dev/1Panel/backend/configs" "github.com/1Panel-dev/1Panel/backend/configs"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/cmd/server/conf"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -13,15 +14,12 @@ import (
func Init() { func Init() {
baseDir := "/opt" baseDir := "/opt"
v := viper.NewWithOptions() v := viper.NewWithOptions()
v.SetConfigName("app")
v.SetConfigType("yaml") v.SetConfigType("yaml")
v.AddConfigPath(path.Dir(baseDir + "/1Panel/conf/app.yaml")) reader := bytes.NewReader(conf.AppYaml)
if err := v.ReadInConfig(); err != nil { if err := v.ReadConfig(reader); err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err)) panic(fmt.Errorf("Fatal error config file: %s \n", err))
} }
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) { v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("config file changed:", e.Name)
if err := v.Unmarshal(&global.CONF); err != nil { if err := v.Unmarshal(&global.CONF); err != nil {
panic(err) panic(err)
} }

View File

@ -29,7 +29,9 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot) settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot)
settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot) settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot)
settingRouter.POST("/snapshot/rollback", baseApi.RollbackSnapshot) settingRouter.POST("/snapshot/rollback", baseApi.RollbackSnapshot)
settingRouter.POST("/upgrade", baseApi.Upgrade)
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo) settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
settingRouter.GET("/upgrade/byoss", baseApi.GetUpgradeInfoByOSS)
settingRouter.GET("/basedir", baseApi.LoadBaseDir) settingRouter.GET("/basedir", baseApi.LoadBaseDir)
} }
} }

View File

@ -26,8 +26,8 @@ import (
) )
func Start() { func Start() {
app.Init()
viper.Init() viper.Init()
app.Init()
log.Init() log.Init()
db.Init() db.Init()
migration.Init() migration.Init()

View File

@ -4,10 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/mholt/archiver/v4"
"github.com/pkg/errors"
"github.com/spf13/afero"
"io" "io"
"io/fs" "io/fs"
"net/http" "net/http"
@ -17,6 +13,11 @@ import (
"strconv" "strconv"
"sync" "sync"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/mholt/archiver/v4"
"github.com/pkg/errors"
"github.com/spf13/afero"
) )
type FileOp struct { type FileOp struct {
@ -170,16 +171,18 @@ func (f FileOp) DownloadFileWithProcess(url, dst, key string) error {
func (f FileOp) DownloadFile(url, dst string) error { func (f FileOp) DownloadFile(url, dst string) error {
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
global.LOG.Errorf("get download file [%s] error, err %s", dst, err.Error()) return fmt.Errorf("get download file [%s] error, err %s", dst, err.Error())
} }
defer resp.Body.Close()
out, err := os.Create(dst) out, err := os.Create(dst)
if err != nil { if err != nil {
global.LOG.Errorf("create download file [%s] error, err %s", dst, err.Error()) return fmt.Errorf("create download file [%s] error, err %s", dst, err.Error())
} }
defer out.Close()
if _, err = io.Copy(out, resp.Body); err != nil { if _, err = io.Copy(out, resp.Body); err != nil {
global.LOG.Errorf("save download file [%s] error, err %s", dst, err.Error()) return fmt.Errorf("save download file [%s] error, err %s", dst, err.Error())
} }
out.Close() out.Close()
resp.Body.Close() resp.Body.Close()

View File

@ -1,7 +1,7 @@
system: system:
port: 9999 port: 9999
db_file: 1Panel.db db_file: 1Panel.db
app_oss: "https://1panel.oss-cn-hangzhou.aliyuncs.com/apps/list.json" app_oss: "https://1panel.oss-cn-hangzhou.aliyuncs.com"
log: log:
level: debug level: debug

6
cmd/server/conf/conf.go Normal file
View File

@ -0,0 +1,6 @@
package conf
import _ "embed"
//go:embed app.yaml
var AppYaml []byte

View File

@ -5976,6 +5976,28 @@ var doc = `{
} }
} }
}, },
"/settings/basedir": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取安装根目录",
"tags": [
"System Setting"
],
"summary": "Load local backup dir",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/settings/expired/handle": { "/settings/expired/handle": {
"post": { "post": {
"security": [ "security": [
@ -6519,7 +6541,7 @@ var doc = `{
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "加载系统更新信息", "description": "从 OSS 加载系统更新信息",
"tags": [ "tags": [
"System Setting" "System Setting"
], ],
@ -6532,6 +6554,46 @@ var doc = `{
} }
} }
} }
},
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "系统更新",
"consumes": [
"application/json"
],
"tags": [
"System Setting"
],
"summary": "Upgrade",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.Upgrade"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"version"
],
"formatEN": "upgrade service =\u003e [version]",
"formatZH": "更新系统 =\u003e [version]",
"paramKeys": []
}
} }
}, },
"/websites": { "/websites": {
@ -10318,6 +10380,14 @@ var doc = `{
} }
} }
}, },
"dto.Upgrade": {
"type": "object",
"properties": {
"version": {
"type": "string"
}
}
},
"dto.UpgradeInfo": { "dto.UpgradeInfo": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -10327,14 +10397,8 @@ var doc = `{
"newVersion": { "newVersion": {
"type": "string" "type": "string"
}, },
"publishedAt": {
"type": "string"
},
"releaseNote": { "releaseNote": {
"type": "string" "type": "string"
},
"tag": {
"type": "string"
} }
} }
}, },

View File

@ -5962,6 +5962,28 @@
} }
} }
}, },
"/settings/basedir": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取安装根目录",
"tags": [
"System Setting"
],
"summary": "Load local backup dir",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/settings/expired/handle": { "/settings/expired/handle": {
"post": { "post": {
"security": [ "security": [
@ -6505,7 +6527,7 @@
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "加载系统更新信息", "description": "从 OSS 加载系统更新信息",
"tags": [ "tags": [
"System Setting" "System Setting"
], ],
@ -6518,6 +6540,46 @@
} }
} }
} }
},
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "系统更新",
"consumes": [
"application/json"
],
"tags": [
"System Setting"
],
"summary": "Upgrade",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.Upgrade"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"version"
],
"formatEN": "upgrade service =\u003e [version]",
"formatZH": "更新系统 =\u003e [version]",
"paramKeys": []
}
} }
}, },
"/websites": { "/websites": {
@ -10304,6 +10366,14 @@
} }
} }
}, },
"dto.Upgrade": {
"type": "object",
"properties": {
"version": {
"type": "string"
}
}
},
"dto.UpgradeInfo": { "dto.UpgradeInfo": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -10313,14 +10383,8 @@
"newVersion": { "newVersion": {
"type": "string" "type": "string"
}, },
"publishedAt": {
"type": "string"
},
"releaseNote": { "releaseNote": {
"type": "string" "type": "string"
},
"tag": {
"type": "string"
} }
} }
}, },

View File

@ -1334,18 +1334,19 @@ definitions:
required: required:
- id - id
type: object type: object
dto.Upgrade:
properties:
version:
type: string
type: object
dto.UpgradeInfo: dto.UpgradeInfo:
properties: properties:
createdAt: createdAt:
type: string type: string
newVersion: newVersion:
type: string type: string
publishedAt:
type: string
releaseNote: releaseNote:
type: string type: string
tag:
type: string
type: object type: object
dto.UploadRecover: dto.UploadRecover:
properties: properties:
@ -6367,6 +6368,19 @@ paths:
formatEN: Update nginx conf [domain] formatEN: Update nginx conf [domain]
formatZH: 更新 nginx 配置 [domain] formatZH: 更新 nginx 配置 [domain]
paramKeys: [] paramKeys: []
/settings/basedir:
get:
description: 获取安装根目录
responses:
"200":
description: OK
schema:
type: string
security:
- ApiKeyAuth: []
summary: Load local backup dir
tags:
- System Setting
/settings/expired/handle: /settings/expired/handle:
post: post:
consumes: consumes:
@ -6713,7 +6727,7 @@ paths:
paramKeys: [] paramKeys: []
/settings/upgrade: /settings/upgrade:
get: get:
description: 加载系统更新信息 description: 从 OSS 加载系统更新信息
responses: responses:
"200": "200":
description: OK description: OK
@ -6724,6 +6738,32 @@ paths:
summary: Load upgrade info summary: Load upgrade info
tags: tags:
- System Setting - System Setting
post:
consumes:
- application/json
description: 系统更新
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.Upgrade'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Upgrade
tags:
- System Setting
x-panel-log:
BeforeFuntions: []
bodyKeys:
- version
formatEN: upgrade service => [version]
formatZH: 更新系统 => [version]
paramKeys: []
/websites: /websites:
post: post:
consumes: consumes:

View File

@ -77,9 +77,7 @@ export namespace Setting {
} }
export interface UpgradeInfo { export interface UpgradeInfo {
newVersion: string; newVersion: string;
tag: string;
releaseNote: string; releaseNote: string;
createdAt: string; createdAt: string;
publishedAt: string;
} }
} }

View File

@ -67,3 +67,9 @@ export const searchSnapshotPage = (param: ReqPage) => {
export const loadUpgradeInfo = () => { export const loadUpgradeInfo = () => {
return http.get<Setting.UpgradeInfo>(`/settings/upgrade`); return http.get<Setting.UpgradeInfo>(`/settings/upgrade`);
}; };
export const loadUpgradeInfoByOSS = () => {
return http.get<Setting.UpgradeInfo>(`/settings/upgrade/byoss`);
};
export const upgrade = (version: string) => {
return http.post(`/settings/upgrade`, { version: version });
};

View File

@ -762,9 +762,11 @@ export default {
'This recovery is about to be rolled back, which will replace all the files recovered this time. In the process, docker and 1panel services may need to be restarted. Do you want to continue?', 'This recovery is about to be rolled back, which will replace all the files recovered this time. In the process, docker and 1panel services may need to be restarted. Do you want to continue?',
upgrade: 'Upgrade', upgrade: 'Upgrade',
upgrading: 'Please wait while the upgrade is underway...',
upgradeHelper: 'The upgrade requires a service restart. Do you want to continue?',
noUpgrade: 'This is the latest version. You do not need to update it',
newVersion: 'NewVersion', newVersion: 'NewVersion',
upgradeCheck: 'Check for updates', upgradeCheck: 'Check for updates',
tag: 'Tag',
upgradeNotes: 'Release note', upgradeNotes: 'Release note',
upgradeNow: 'Upgrade now', upgradeNow: 'Upgrade now',

View File

@ -756,9 +756,11 @@ export default {
'即将回滚本次恢复回滚将替换所有本次恢复的文件过程中可能需要重启 docker 以及 1panel 服务是否继续', '即将回滚本次恢复回滚将替换所有本次恢复的文件过程中可能需要重启 docker 以及 1panel 服务是否继续',
upgrade: '升级', upgrade: '升级',
upgrading: '正在升级中请稍候...',
upgradeHelper: '升级操作需要重启服务是否继续',
noUpgrade: '当前已是最新版本无需更新',
newVersion: '新版本', newVersion: '新版本',
upgradeCheck: '检查更新', upgradeCheck: '检查更新',
tag: '标签',
upgradeNotes: '更新内容', upgradeNotes: '更新内容',
upgradeNow: '立即更新', upgradeNow: '立即更新',

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div v-loading="loading">
<Submenu activeName="about" /> <Submenu activeName="about" />
<el-card style="margin-top: 20px"> <el-card style="margin-top: 20px">
<LayoutContent :header="$t('setting.about')"> <LayoutContent :header="$t('setting.about')">
@ -10,9 +10,10 @@
<h3>{{ $t('setting.description') }}</h3> <h3>{{ $t('setting.description') }}</h3>
<h3> <h3>
{{ version }} {{ version }}
<el-button type="primary" link @click="onLoadUpgradeInfo"> <el-button v-if="version !== 'Waiting'" type="primary" link @click="onLoadUpgradeInfo">
{{ $t('setting.upgradeCheck') }} {{ $t('setting.upgradeCheck') }}
</el-button> </el-button>
<el-tag v-else round style="margin-left: 10px">{{ $t('setting.upgrading') }}</el-tag>
</h3> </h3>
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<el-link @click="toGithub"> <el-link @click="toGithub">
@ -40,20 +41,14 @@
<el-form-item :label="$t('setting.newVersion')"> <el-form-item :label="$t('setting.newVersion')">
<el-tag>{{ upgradeInfo.newVersion }}</el-tag> <el-tag>{{ upgradeInfo.newVersion }}</el-tag>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.tag')">
<el-tag>{{ upgradeInfo.tag }}</el-tag>
</el-form-item>
<el-form-item :label="$t('setting.upgradeNotes')">
<MdEditor style="height: 450px" v-model="upgradeInfo.releaseNote" previewOnly />
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')"> <el-form-item :label="$t('commons.table.createdAt')">
<el-tag>{{ upgradeInfo.createdAt }}</el-tag> <el-tag>{{ upgradeInfo.createdAt }}</el-tag>
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.table.publishedAt')"> <el-form-item :label="$t('setting.upgradeNotes')">
<el-tag>{{ upgradeInfo.publishedAt }}</el-tag> <MdEditor style="height: calc(100vh - 260px)" v-model="upgradeInfo.releaseNote" previewOnly />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary">{{ $t('setting.upgradeNow') }}</el-button> <el-button type="primary" @click="onUpgrade">{{ $t('setting.upgradeNow') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-drawer> </el-drawer>
@ -62,17 +57,20 @@
<script lang="ts" setup> <script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue'; import LayoutContent from '@/layout/layout-content.vue';
import { getSettingInfo, loadUpgradeInfo } from '@/api/modules/setting'; import { getSettingInfo, loadUpgradeInfoByOSS, upgrade } from '@/api/modules/setting';
import Submenu from '@/views/setting/index.vue'; import Submenu from '@/views/setting/index.vue';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import MdEditor from 'md-editor-v3'; import MdEditor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css'; import 'md-editor-v3/lib/style.css';
import { ElMessage, ElMessageBox } from 'element-plus';
import i18n from '@/lang';
const version = ref(); const version = ref();
const upgradeInfo = ref(); const upgradeInfo = ref();
const drawerShow = ref(); const drawerShow = ref();
const refresh = ref(); const refresh = ref();
const loading = ref();
const search = async () => { const search = async () => {
const res = await getSettingInfo(); const res = await getSettingInfo();
version.value = res.data.systemVersion; version.value = res.data.systemVersion;
@ -92,10 +90,33 @@ const toGithubStar = () => {
}; };
const onLoadUpgradeInfo = async () => { const onLoadUpgradeInfo = async () => {
const res = await loadUpgradeInfo(); const res = await loadUpgradeInfoByOSS();
if (!res.data) {
ElMessage.success(i18n.global.t('setting.noUpgrade'));
return;
}
upgradeInfo.value = res.data; upgradeInfo.value = res.data;
drawerShow.value = true; drawerShow.value = true;
}; };
const onUpgrade = async () => {
ElMessageBox.confirm(i18n.global.t('setting.upgradeHelper', i18n.global.t('setting.upgrade')), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(() => {
loading.value = true;
upgrade(upgradeInfo.value.newVersion)
.then(() => {
loading.value = false;
drawerShow.value = false;
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
loading.value = false;
});
});
};
onMounted(() => { onMounted(() => {
search(); search();