mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 16:29:17 +08:00
feat: 完成快照同步功能
This commit is contained in:
parent
71349d2a63
commit
205d406761
@ -210,3 +210,30 @@ func (b *BaseApi) ListBackup(c *gin.Context) {
|
|||||||
|
|
||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Backup Account
|
||||||
|
// @Summary List files from backup accounts
|
||||||
|
// @Description 获取备份账号内文件列表
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.BackupSearchFile true "request"
|
||||||
|
// @Success 200 {anrry} string
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /backups/search/files [post]
|
||||||
|
func (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {
|
||||||
|
var req dto.BackupSearchFile
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := backupService.ListFiles(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
@ -40,13 +40,13 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
|
|||||||
// @Summary Update mysql database description
|
// @Summary Update mysql database description
|
||||||
// @Description 更新 mysql 数据库库描述信息
|
// @Description 更新 mysql 数据库库描述信息
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.MysqlDescription true "request"
|
// @Param request body dto.UpdateDescription true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /databases/description/update [post]
|
// @Router /databases/description/update [post]
|
||||||
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
|
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
|
||||||
func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
|
||||||
var req dto.MysqlDescription
|
var req dto.UpdateDescription
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Create system backup
|
// @Summary Create system snapshot
|
||||||
// @Description 创建系统快照
|
// @Description 创建系统快照
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.SnapshotCreate true "request"
|
// @Param request body dto.SnapshotCreate true "request"
|
||||||
@ -34,6 +34,58 @@ func (b *BaseApi) CreateSnapshot(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Import system snapshot
|
||||||
|
// @Description 导入已有快照
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SnapshotImport true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /settings/snapshot/import [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["from", "names"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"从 [from] 同步系统快照 [names]","formatEN":"Sync system snapshots [names] from [from]"}
|
||||||
|
func (b *BaseApi) ImportSnapshot(c *gin.Context) {
|
||||||
|
var req dto.SnapshotImport
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := snapshotService.SnapshotImport(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags System Setting
|
||||||
|
// @Summary Update snapshot description
|
||||||
|
// @Description 更新快照描述信息
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.UpdateDescription true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /settings/snapshot/description/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"}
|
||||||
|
func (b *BaseApi) UpdateSnapDescription(c *gin.Context) {
|
||||||
|
var req dto.UpdateDescription
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := snapshotService.UpdateDescription(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags System Setting
|
// @Tags System Setting
|
||||||
// @Summary Page system snapshot
|
// @Summary Page system snapshot
|
||||||
// @Description 获取系统快照列表分页
|
// @Description 获取系统快照列表分页
|
||||||
|
@ -26,6 +26,10 @@ type BackupSearch struct {
|
|||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BackupSearchFile struct {
|
||||||
|
Type string `json:"type" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type RecordSearch struct {
|
type RecordSearch struct {
|
||||||
PageInfo
|
PageInfo
|
||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type" validate:"required"`
|
||||||
|
@ -10,6 +10,11 @@ type PageInfo struct {
|
|||||||
PageSize int `json:"pageSize" validate:"required,number"`
|
PageSize int `json:"pageSize" validate:"required,number"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateDescription struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
type OperationWithName struct {
|
type OperationWithName struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,6 @@ package dto
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type MysqlDescription struct {
|
|
||||||
ID uint `json:"id" validate:"required"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MysqlDBInfo struct {
|
type MysqlDBInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
@ -57,6 +57,11 @@ type SnapshotRecover struct {
|
|||||||
ReDownload bool `json:"reDownload"`
|
ReDownload bool `json:"reDownload"`
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
type SnapshotImport struct {
|
||||||
|
From string `json:"from"`
|
||||||
|
Names []string `json:"names"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
type SnapshotInfo struct {
|
type SnapshotInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -29,6 +29,8 @@ type IBackupService interface {
|
|||||||
BatchDelete(ids []uint) error
|
BatchDelete(ids []uint) error
|
||||||
BatchDeleteRecord(ids []uint) error
|
BatchDeleteRecord(ids []uint) error
|
||||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||||
|
|
||||||
|
ListFiles(req dto.BackupSearchFile) ([]interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIBackupService() IBackupService {
|
func NewIBackupService() IBackupService {
|
||||||
@ -226,6 +228,18 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *BackupService) ListFiles(req dto.BackupSearchFile) ([]interface{}, error) {
|
||||||
|
backup, err := backupRepo.Get(backupRepo.WithByType(req.Type))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client, err := u.NewClient(&backup)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client.ListObjects("system_snapshot/")
|
||||||
|
}
|
||||||
|
|
||||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||||
varMap := make(map[string]interface{})
|
varMap := make(map[string]interface{})
|
||||||
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
|
||||||
|
@ -37,7 +37,7 @@ type IMysqlService interface {
|
|||||||
ChangePassword(info dto.ChangeDBInfo) error
|
ChangePassword(info dto.ChangeDBInfo) error
|
||||||
UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
|
||||||
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
|
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
|
||||||
UpdateDescription(req dto.MysqlDescription) error
|
UpdateDescription(req dto.UpdateDescription) error
|
||||||
|
|
||||||
RecoverByUpload(req dto.UploadRecover) error
|
RecoverByUpload(req dto.UploadRecover) error
|
||||||
Backup(db dto.BackupDB) error
|
Backup(db dto.BackupDB) error
|
||||||
@ -201,7 +201,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
|||||||
return &mysql, nil
|
return &mysql, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *MysqlService) UpdateDescription(req dto.MysqlDescription) error {
|
func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
|
||||||
return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,10 @@ type ISnapshotService interface {
|
|||||||
SnapshotCreate(req dto.SnapshotCreate) error
|
SnapshotCreate(req dto.SnapshotCreate) error
|
||||||
SnapshotRecover(req dto.SnapshotRecover) error
|
SnapshotRecover(req dto.SnapshotRecover) error
|
||||||
SnapshotRollback(req dto.SnapshotRecover) error
|
SnapshotRollback(req dto.SnapshotRecover) error
|
||||||
|
SnapshotImport(req dto.SnapshotImport) error
|
||||||
Delete(req dto.BatchDeleteReq) error
|
Delete(req dto.BatchDeleteReq) error
|
||||||
|
|
||||||
|
UpdateDescription(req dto.UpdateDescription) error
|
||||||
readFromJson(path string) (SnapshotJson, error)
|
readFromJson(path string) (SnapshotJson, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,11 +53,48 @@ func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interfa
|
|||||||
return total, dtoSnap, err
|
return total, dtoSnap, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||||
|
if len(req.Names) == 0 {
|
||||||
|
return fmt.Errorf("incorrect snapshot request body: %v", req.Names)
|
||||||
|
}
|
||||||
|
for _, snap := range req.Names {
|
||||||
|
nameItems := strings.Split(snap, "_")
|
||||||
|
if !strings.HasPrefix(snap, "1panel_v") || !strings.HasSuffix(snap, ".tar.gz") || len(nameItems) != 3 {
|
||||||
|
return fmt.Errorf("incorrect snapshot name format of %s", snap)
|
||||||
|
}
|
||||||
|
formatTime, err := time.Parse("20060102150405", strings.ReplaceAll(nameItems[2], ".tar.gz", ""))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("incorrect snapshot name format of %s", snap)
|
||||||
|
}
|
||||||
|
itemSnap := model.Snapshot{
|
||||||
|
Name: snap,
|
||||||
|
From: req.From,
|
||||||
|
Version: nameItems[1],
|
||||||
|
Description: req.Description,
|
||||||
|
Status: constant.StatusSuccess,
|
||||||
|
BaseModel: model.BaseModel{
|
||||||
|
CreatedAt: formatTime,
|
||||||
|
UpdatedAt: formatTime,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := snapshotRepo.Create(&itemSnap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SnapshotService) UpdateDescription(req dto.UpdateDescription) error {
|
||||||
|
return snapshotRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
|
||||||
|
}
|
||||||
|
|
||||||
type SnapshotJson struct {
|
type SnapshotJson struct {
|
||||||
|
OldBaseDir string `json:"oldBaseDir"`
|
||||||
OldDockerDataDir string `json:"oldDockerDataDir"`
|
OldDockerDataDir string `json:"oldDockerDataDir"`
|
||||||
OldBackupDataDir string `json:"oldDackupDataDir"`
|
OldBackupDataDir string `json:"oldDackupDataDir"`
|
||||||
OldPanelDataDir string `json:"oldPanelDataDir"`
|
OldPanelDataDir string `json:"oldPanelDataDir"`
|
||||||
|
|
||||||
|
BaseDir string `json:"baseDir"`
|
||||||
DockerDataDir string `json:"dockerDataDir"`
|
DockerDataDir string `json:"dockerDataDir"`
|
||||||
BackupDataDir string `json:"backupDataDir"`
|
BackupDataDir string `json:"backupDataDir"`
|
||||||
PanelDataDir string `json:"panelDataDir"`
|
PanelDataDir string `json:"panelDataDir"`
|
||||||
@ -78,15 +117,15 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeNow := time.Now().Format("20060102150405")
|
timeNow := time.Now().Format("20060102150405")
|
||||||
rootDir := fmt.Sprintf("%s/system/1panel_snapshot_%s", localDir, timeNow)
|
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
|
||||||
|
rootDir := fmt.Sprintf("%s/system/1panel_%s_%s", localDir, versionItem.Value, timeNow)
|
||||||
backupPanelDir := fmt.Sprintf("%s/1panel", rootDir)
|
backupPanelDir := fmt.Sprintf("%s/1panel", rootDir)
|
||||||
_ = os.MkdirAll(backupPanelDir, os.ModePerm)
|
_ = os.MkdirAll(backupPanelDir, os.ModePerm)
|
||||||
backupDockerDir := fmt.Sprintf("%s/docker", rootDir)
|
backupDockerDir := fmt.Sprintf("%s/docker", rootDir)
|
||||||
_ = os.MkdirAll(backupDockerDir, os.ModePerm)
|
_ = os.MkdirAll(backupDockerDir, os.ModePerm)
|
||||||
|
|
||||||
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
|
|
||||||
snap := model.Snapshot{
|
snap := model.Snapshot{
|
||||||
Name: "1panel_snapshot_" + timeNow,
|
Name: fmt.Sprintf("1panel_%s_%s", versionItem.Value, timeNow),
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
From: req.From,
|
From: req.From,
|
||||||
Version: versionItem.Value,
|
Version: versionItem.Value,
|
||||||
@ -139,13 +178,19 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
snapJson := SnapshotJson{DockerDataDir: dockerDataDir, BackupDataDir: localDir, PanelDataDir: global.CONF.BaseDir + "/1panel", LiveRestoreEnabled: liveRestoreStatus}
|
snapJson := SnapshotJson{
|
||||||
|
BaseDir: global.CONF.BaseDir,
|
||||||
|
DockerDataDir: dockerDataDir,
|
||||||
|
BackupDataDir: localDir,
|
||||||
|
PanelDataDir: global.CONF.BaseDir + "/1panel",
|
||||||
|
LiveRestoreEnabled: liveRestoreStatus,
|
||||||
|
}
|
||||||
if err := u.saveJson(snapJson, rootDir); err != nil {
|
if err := u.saveJson(snapJson, rootDir); err != nil {
|
||||||
updateSnapshotStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("save snapshot json failed, err: %v", err))
|
updateSnapshotStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("save snapshot json failed, err: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fileOp.Compress([]string{rootDir}, fmt.Sprintf("%s/system", localDir), fmt.Sprintf("1panel_snapshot_%s.tar.gz", timeNow), files.TarGz); err != nil {
|
if err := fileOp.Compress([]string{rootDir}, fmt.Sprintf("%s/system", localDir), fmt.Sprintf("1panel_%s_%s.tar.gz", versionItem.Value, timeNow), files.TarGz); err != nil {
|
||||||
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
|
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -154,14 +199,14 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
|
|||||||
|
|
||||||
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
|
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
|
||||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
|
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
|
||||||
localPath := fmt.Sprintf("%s/system/1panel_snapshot_%s.tar.gz", localDir, timeNow)
|
localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow)
|
||||||
if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_snapshot_%s.tar.gz", timeNow)); err != nil || !ok {
|
if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok {
|
||||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||||
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
|
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||||
_ = os.RemoveAll(fmt.Sprintf("%s/system/1panel_snapshot_%s.tar.gz", localDir, timeNow))
|
_ = os.RemoveAll(fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow))
|
||||||
|
|
||||||
global.LOG.Infof("upload snapshot to %s success", backup.Type)
|
global.LOG.Infof("upload snapshot to %s success", backup.Type)
|
||||||
}()
|
}()
|
||||||
@ -230,8 +275,6 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
|||||||
isReTry = false
|
isReTry = false
|
||||||
}
|
}
|
||||||
rootDir := fmt.Sprintf("%s/%s", baseDir, snap.Name)
|
rootDir := fmt.Sprintf("%s/%s", baseDir, snap.Name)
|
||||||
u.OriginalPath = fmt.Sprintf("%s/original_%s", global.CONF.BaseDir, snap.Name)
|
|
||||||
_ = os.MkdirAll(u.OriginalPath, os.ModePerm)
|
|
||||||
|
|
||||||
snapJson, err := u.readFromJson(fmt.Sprintf("%s/snapshot.json", rootDir))
|
snapJson, err := u.readFromJson(fmt.Sprintf("%s/snapshot.json", rootDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -241,7 +284,10 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
|||||||
if snap.InterruptStep == "Readjson" {
|
if snap.InterruptStep == "Readjson" {
|
||||||
isReTry = false
|
isReTry = false
|
||||||
}
|
}
|
||||||
|
u.OriginalPath = fmt.Sprintf("%s/original_%s", snapJson.BaseDir, snap.Name)
|
||||||
|
_ = os.MkdirAll(u.OriginalPath, os.ModePerm)
|
||||||
|
|
||||||
|
snapJson.OldBaseDir = global.CONF.BaseDir
|
||||||
snapJson.OldPanelDataDir = global.CONF.BaseDir + "/1panel"
|
snapJson.OldPanelDataDir = global.CONF.BaseDir + "/1panel"
|
||||||
snapJson.OldBackupDataDir = localDir
|
snapJson.OldBackupDataDir = localDir
|
||||||
recoverPanelDir := fmt.Sprintf("%s/%s/1panel", baseDir, snap.Name)
|
recoverPanelDir := fmt.Sprintf("%s/%s/1panel", baseDir, snap.Name)
|
||||||
@ -340,10 +386,6 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
|
|||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
|
|
||||||
rootDir := fmt.Sprintf("%s/system/%s/%s", localDir, snap.Name, snap.Name)
|
rootDir := fmt.Sprintf("%s/system/%s/%s", localDir, snap.Name, snap.Name)
|
||||||
u.OriginalPath = fmt.Sprintf("%s/original_%s", global.CONF.BaseDir, snap.Name)
|
|
||||||
if _, err := os.Stat(u.OriginalPath); err != nil && os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("load original dir failed, err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = settingRepo.Update("SystemStatus", "Rollbacking")
|
_ = settingRepo.Update("SystemStatus", "Rollbacking")
|
||||||
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"rollback_status": constant.StatusWaiting})
|
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"rollback_status": constant.StatusWaiting})
|
||||||
@ -353,6 +395,10 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
|
|||||||
updateRollbackStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("decompress file failed, err: %v", err))
|
updateRollbackStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("decompress file failed, err: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
u.OriginalPath = fmt.Sprintf("%s/original_%s", snapJson.OldBaseDir, snap.Name)
|
||||||
|
if _, err := os.Stat(u.OriginalPath); err != nil && os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, _ = cmd.Exec("systemctl stop docker")
|
_, _ = cmd.Exec("systemctl stop docker")
|
||||||
if err := u.handleDockerDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldDockerDataDir); err != nil {
|
if err := u.handleDockerDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldDockerDataDir); err != nil {
|
||||||
|
@ -58,7 +58,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
|||||||
}
|
}
|
||||||
if len(releaseInfo.NewVersion) != 0 {
|
if len(releaseInfo.NewVersion) != 0 {
|
||||||
isNew, err := compareVersion(currentVersion.Value, releaseInfo.NewVersion)
|
isNew, err := compareVersion(currentVersion.Value, releaseInfo.NewVersion)
|
||||||
if !isNew && err != nil {
|
if !isNew || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &releaseInfo, nil
|
return &releaseInfo, nil
|
||||||
|
@ -17,6 +17,7 @@ func (s *BackupRouter) InitBackupRouter(Router *gin.RouterGroup) {
|
|||||||
baseApi := v1.ApiGroupApp.BaseApi
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
baRouter.GET("/search", baseApi.ListBackup)
|
baRouter.GET("/search", baseApi.ListBackup)
|
||||||
|
baRouter.POST("/search/files", baseApi.LoadFilesFromBackup)
|
||||||
baRouter.POST("/buckets", baseApi.ListBuckets)
|
baRouter.POST("/buckets", baseApi.ListBuckets)
|
||||||
baRouter.POST("", baseApi.CreateBackup)
|
baRouter.POST("", baseApi.CreateBackup)
|
||||||
baRouter.POST("/del", baseApi.DeleteBackup)
|
baRouter.POST("/del", baseApi.DeleteBackup)
|
||||||
|
@ -27,9 +27,11 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
|||||||
settingRouter.POST("/mfa/bind", baseApi.MFABind)
|
settingRouter.POST("/mfa/bind", baseApi.MFABind)
|
||||||
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
||||||
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
|
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
|
||||||
|
settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot)
|
||||||
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("/snapshot/description/update", baseApi.UpdateSnapDescription)
|
||||||
settingRouter.POST("/upgrade", baseApi.Upgrade)
|
settingRouter.POST("/upgrade", baseApi.Upgrade)
|
||||||
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
||||||
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
|
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
|
||||||
|
@ -15,7 +15,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userinfoCmd = &cobra.Command{
|
var userinfoCmd = &cobra.Command{
|
||||||
Use: "userinfo",
|
Use: "user-info",
|
||||||
Short: "获取用户信息",
|
Short: "获取用户信息",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
fullPath := "/opt/1panel/db/1Panel.db"
|
fullPath := "/opt/1panel/db/1Panel.db"
|
@ -1169,6 +1169,42 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/backups/search/files": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取备份账号内文件列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Backup Account"
|
||||||
|
],
|
||||||
|
"summary": "List files from backup accounts",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.BackupSearchFile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "anrry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/backups/update": {
|
"/backups/update": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -3907,7 +3943,7 @@ var doc = `{
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/dto.MysqlDescription"
|
"$ref": "#/definitions/dto.UpdateDescription"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -6285,7 +6321,7 @@ var doc = `{
|
|||||||
"tags": [
|
"tags": [
|
||||||
"System Setting"
|
"System Setting"
|
||||||
],
|
],
|
||||||
"summary": "Create system backup",
|
"summary": "Create system snapshot",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "request",
|
"description": "request",
|
||||||
@ -6365,6 +6401,101 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/settings/snapshot/description/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新快照描述信息",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"System Setting"
|
||||||
|
],
|
||||||
|
"summary": "Update snapshot description",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.UpdateDescription"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [
|
||||||
|
{
|
||||||
|
"db": "snapshots",
|
||||||
|
"input_colume": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": false,
|
||||||
|
"output_colume": "name",
|
||||||
|
"output_value": "name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"formatEN": "The description of the snapshot [name] is modified =\u003e [description]",
|
||||||
|
"formatZH": "快照 [name] 描述信息修改 [description]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/settings/snapshot/import": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "导入已有快照",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"System Setting"
|
||||||
|
],
|
||||||
|
"summary": "Import system snapshot",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.SnapshotImport"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"from",
|
||||||
|
"names"
|
||||||
|
],
|
||||||
|
"formatEN": "Sync system snapshots [names] from [from]",
|
||||||
|
"formatZH": "从 [from] 同步系统快照 [names]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/settings/snapshot/recover": {
|
"/settings/snapshot/recover": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -8474,6 +8605,17 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.BackupSearchFile": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.BatchDelete": {
|
"dto.BatchDelete": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -9751,20 +9893,6 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dto.MysqlDescription": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dto.MysqlStatus": {
|
"dto.MysqlStatus": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -10451,6 +10579,23 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.SnapshotImport": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"from": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"names": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.SnapshotRecover": {
|
"dto.SnapshotRecover": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -10468,6 +10613,20 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.UpdateDescription": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.Upgrade": {
|
"dto.Upgrade": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -1155,6 +1155,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/backups/search/files": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取备份账号内文件列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Backup Account"
|
||||||
|
],
|
||||||
|
"summary": "List files from backup accounts",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.BackupSearchFile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "anrry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/backups/update": {
|
"/backups/update": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -3893,7 +3929,7 @@
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/dto.MysqlDescription"
|
"$ref": "#/definitions/dto.UpdateDescription"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -6271,7 +6307,7 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"System Setting"
|
"System Setting"
|
||||||
],
|
],
|
||||||
"summary": "Create system backup",
|
"summary": "Create system snapshot",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "request",
|
"description": "request",
|
||||||
@ -6351,6 +6387,101 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/settings/snapshot/description/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新快照描述信息",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"System Setting"
|
||||||
|
],
|
||||||
|
"summary": "Update snapshot description",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.UpdateDescription"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [
|
||||||
|
{
|
||||||
|
"db": "snapshots",
|
||||||
|
"input_colume": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": false,
|
||||||
|
"output_colume": "name",
|
||||||
|
"output_value": "name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"formatEN": "The description of the snapshot [name] is modified =\u003e [description]",
|
||||||
|
"formatZH": "快照 [name] 描述信息修改 [description]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/settings/snapshot/import": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "导入已有快照",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"System Setting"
|
||||||
|
],
|
||||||
|
"summary": "Import system snapshot",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.SnapshotImport"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"from",
|
||||||
|
"names"
|
||||||
|
],
|
||||||
|
"formatEN": "Sync system snapshots [names] from [from]",
|
||||||
|
"formatZH": "从 [from] 同步系统快照 [names]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/settings/snapshot/recover": {
|
"/settings/snapshot/recover": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -8460,6 +8591,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.BackupSearchFile": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.BatchDelete": {
|
"dto.BatchDelete": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -9737,20 +9879,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dto.MysqlDescription": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dto.MysqlStatus": {
|
"dto.MysqlStatus": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -10437,6 +10565,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.SnapshotImport": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"from": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"names": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.SnapshotRecover": {
|
"dto.SnapshotRecover": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -10454,6 +10599,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.UpdateDescription": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.Upgrade": {
|
"dto.Upgrade": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -28,6 +28,13 @@ definitions:
|
|||||||
- type
|
- type
|
||||||
- vars
|
- vars
|
||||||
type: object
|
type: object
|
||||||
|
dto.BackupSearchFile:
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
dto.BatchDelete:
|
dto.BatchDelete:
|
||||||
properties:
|
properties:
|
||||||
names:
|
names:
|
||||||
@ -890,15 +897,6 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
type: object
|
type: object
|
||||||
dto.MysqlDescription:
|
|
||||||
properties:
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- id
|
|
||||||
type: object
|
|
||||||
dto.MysqlStatus:
|
dto.MysqlStatus:
|
||||||
properties:
|
properties:
|
||||||
Aborted_clients:
|
Aborted_clients:
|
||||||
@ -1354,6 +1352,17 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- from
|
- from
|
||||||
type: object
|
type: object
|
||||||
|
dto.SnapshotImport:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
from:
|
||||||
|
type: string
|
||||||
|
names:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
dto.SnapshotRecover:
|
dto.SnapshotRecover:
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
@ -1365,6 +1374,15 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
type: object
|
type: object
|
||||||
|
dto.UpdateDescription:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
type: object
|
||||||
dto.Upgrade:
|
dto.Upgrade:
|
||||||
properties:
|
properties:
|
||||||
version:
|
version:
|
||||||
@ -3358,6 +3376,28 @@ paths:
|
|||||||
summary: List buckets
|
summary: List buckets
|
||||||
tags:
|
tags:
|
||||||
- Backup Account
|
- Backup Account
|
||||||
|
/backups/search/files:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取备份账号内文件列表
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.BackupSearchFile'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: anrry
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: List files from backup accounts
|
||||||
|
tags:
|
||||||
|
- Backup Account
|
||||||
/backups/update:
|
/backups/update:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@ -5100,7 +5140,7 @@ paths:
|
|||||||
name: request
|
name: request
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/dto.MysqlDescription'
|
$ref: '#/definitions/dto.UpdateDescription'
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: ""
|
description: ""
|
||||||
@ -6621,7 +6661,7 @@ paths:
|
|||||||
description: ""
|
description: ""
|
||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
summary: Create system backup
|
summary: Create system snapshot
|
||||||
tags:
|
tags:
|
||||||
- System Setting
|
- System Setting
|
||||||
x-panel-log:
|
x-panel-log:
|
||||||
@ -6665,6 +6705,68 @@ paths:
|
|||||||
formatEN: Delete system backup [name]
|
formatEN: Delete system backup [name]
|
||||||
formatZH: 删除系统快照 [name]
|
formatZH: 删除系统快照 [name]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/settings/snapshot/description/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 更新快照描述信息
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.UpdateDescription'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: ""
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Update snapshot description
|
||||||
|
tags:
|
||||||
|
- System Setting
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFuntions:
|
||||||
|
- db: snapshots
|
||||||
|
input_colume: id
|
||||||
|
input_value: id
|
||||||
|
isList: false
|
||||||
|
output_colume: name
|
||||||
|
output_value: name
|
||||||
|
bodyKeys:
|
||||||
|
- id
|
||||||
|
- description
|
||||||
|
formatEN: The description of the snapshot [name] is modified => [description]
|
||||||
|
formatZH: 快照 [name] 描述信息修改 [description]
|
||||||
|
paramKeys: []
|
||||||
|
/settings/snapshot/import:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 导入已有快照
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.SnapshotImport'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: ""
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Import system snapshot
|
||||||
|
tags:
|
||||||
|
- System Setting
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFuntions: []
|
||||||
|
bodyKeys:
|
||||||
|
- from
|
||||||
|
- names
|
||||||
|
formatEN: Sync system snapshots [names] from [from]
|
||||||
|
formatZH: 从 [from] 同步系统快照 [names]
|
||||||
|
paramKeys: []
|
||||||
/settings/snapshot/recover:
|
/settings/snapshot/recover:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -5,10 +5,6 @@ export namespace Database {
|
|||||||
mysqlName: string;
|
mysqlName: string;
|
||||||
dbName: string;
|
dbName: string;
|
||||||
}
|
}
|
||||||
export interface DescriptionUpdate {
|
|
||||||
id: number;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
export interface Backup {
|
export interface Backup {
|
||||||
mysqlName: string;
|
mysqlName: string;
|
||||||
dbName: string;
|
dbName: string;
|
||||||
|
@ -28,6 +28,10 @@ export interface CommonModel {
|
|||||||
CreatedAt?: string;
|
CreatedAt?: string;
|
||||||
UpdatedAt?: string;
|
UpdatedAt?: string;
|
||||||
}
|
}
|
||||||
|
export interface DescriptionUpdate {
|
||||||
|
id: number;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
// * 文件上传模块
|
// * 文件上传模块
|
||||||
export namespace Upload {
|
export namespace Upload {
|
||||||
|
@ -53,6 +53,11 @@ export namespace Setting {
|
|||||||
from: string;
|
from: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
export interface SnapshotImport {
|
||||||
|
from: string;
|
||||||
|
names: Array<string>;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
export interface SnapshotRecover {
|
export interface SnapshotRecover {
|
||||||
id: number;
|
id: number;
|
||||||
isNew: boolean;
|
isNew: boolean;
|
||||||
|
@ -6,6 +6,10 @@ export const getBackupList = () => {
|
|||||||
return http.get<Array<Backup.BackupInfo>>(`/backups/search`);
|
return http.get<Array<Backup.BackupInfo>>(`/backups/search`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getFilesFromBackup = (type: string) => {
|
||||||
|
return http.post<Array<any>>(`/backups/search/files`, { type: type });
|
||||||
|
};
|
||||||
|
|
||||||
export const addBackup = (params: Backup.BackupOperate) => {
|
export const addBackup = (params: Backup.BackupOperate) => {
|
||||||
return http.post<Backup.BackupOperate>(`/backups`, params);
|
return http.post<Backup.BackupOperate>(`/backups`, params);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import http from '@/api';
|
import http from '@/api';
|
||||||
import { SearchWithPage, ReqPage, ResPage } from '../interface';
|
import { SearchWithPage, ReqPage, ResPage, DescriptionUpdate } from '../interface';
|
||||||
import { Database } from '../interface/database';
|
import { Database } from '../interface/database';
|
||||||
|
|
||||||
export const searchMysqlDBs = (params: SearchWithPage) => {
|
export const searchMysqlDBs = (params: SearchWithPage) => {
|
||||||
@ -25,7 +25,7 @@ export const updateMysqlAccess = (params: Database.ChangeInfo) => {
|
|||||||
export const updateMysqlPassword = (params: Database.ChangeInfo) => {
|
export const updateMysqlPassword = (params: Database.ChangeInfo) => {
|
||||||
return http.post(`/databases/change/password`, params);
|
return http.post(`/databases/change/password`, params);
|
||||||
};
|
};
|
||||||
export const updateMysqlDescription = (params: Database.DescriptionUpdate) => {
|
export const updateMysqlDescription = (params: DescriptionUpdate) => {
|
||||||
return http.post(`/databases/description/update`, params);
|
return http.post(`/databases/description/update`, params);
|
||||||
};
|
};
|
||||||
export const updateMysqlVariables = (params: Array<Database.VariablesUpdate>) => {
|
export const updateMysqlVariables = (params: Array<Database.VariablesUpdate>) => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import http from '@/api';
|
import http from '@/api';
|
||||||
import { ResPage, SearchWithPage } from '../interface';
|
import { ResPage, SearchWithPage, DescriptionUpdate } from '../interface';
|
||||||
import { Setting } from '../interface/setting';
|
import { Setting } from '../interface/setting';
|
||||||
|
|
||||||
export const getSettingInfo = () => {
|
export const getSettingInfo = () => {
|
||||||
@ -53,6 +53,12 @@ export const loadBaseDir = () => {
|
|||||||
export const snapshotCreate = (param: Setting.SnapshotCreate) => {
|
export const snapshotCreate = (param: Setting.SnapshotCreate) => {
|
||||||
return http.post(`/settings/snapshot`, param);
|
return http.post(`/settings/snapshot`, param);
|
||||||
};
|
};
|
||||||
|
export const snapshotImport = (param: Setting.SnapshotImport) => {
|
||||||
|
return http.post(`/settings/snapshot/import`, param);
|
||||||
|
};
|
||||||
|
export const updateSnapshotDescription = (param: DescriptionUpdate) => {
|
||||||
|
return http.post(`/settings/snapshot/description/update`, param);
|
||||||
|
};
|
||||||
export const snapshotDelete = (param: { ids: number[] }) => {
|
export const snapshotDelete = (param: { ids: number[] }) => {
|
||||||
return http.post(`/settings/snapshot/del`, param);
|
return http.post(`/settings/snapshot/del`, param);
|
||||||
};
|
};
|
||||||
|
@ -770,6 +770,7 @@ export default {
|
|||||||
thirdPartySupport: 'Only third-party accounts are supported',
|
thirdPartySupport: 'Only third-party accounts are supported',
|
||||||
recoverDetail: 'Recover detail',
|
recoverDetail: 'Recover detail',
|
||||||
createSnapshot: 'Create snapshot',
|
createSnapshot: 'Create snapshot',
|
||||||
|
importSnapshot: 'Sync snapshot',
|
||||||
recover: 'Recover',
|
recover: 'Recover',
|
||||||
noRecoverRecord: 'No recovery record has been recorded',
|
noRecoverRecord: 'No recovery record has been recorded',
|
||||||
lastRecoverAt: 'Last recovery time',
|
lastRecoverAt: 'Last recovery time',
|
||||||
|
@ -768,6 +768,7 @@ export default {
|
|||||||
thirdPartySupport: '仅支持第三方账号',
|
thirdPartySupport: '仅支持第三方账号',
|
||||||
recoverDetail: '恢复详情',
|
recoverDetail: '恢复详情',
|
||||||
createSnapshot: '创建快照',
|
createSnapshot: '创建快照',
|
||||||
|
importSnapshot: '同步快照',
|
||||||
recover: '恢复',
|
recover: '恢复',
|
||||||
noRecoverRecord: '暂无恢复记录',
|
noRecoverRecord: '暂无恢复记录',
|
||||||
lastRecoverAt: '上次恢复时间',
|
lastRecoverAt: '上次恢复时间',
|
||||||
|
124
frontend/src/views/setting/snapshot/import/index.vue
Normal file
124
frontend/src/views/setting/snapshot/import/index.vue
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<el-drawer v-model="drawerVisiable" size="50%">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="$t('setting.importSnapshot')" :back="handleClose" />
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" label-position="top" :model="form" :rules="rules">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-form-item :label="$t('setting.backupAccount')" prop="from">
|
||||||
|
<el-select style="width: 100%" v-model="form.from" @change="loadFiles" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="item in backupOptions"
|
||||||
|
:key="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="names">
|
||||||
|
<el-select style="width: 100%" v-model="form.names" multiple clearable>
|
||||||
|
<el-option v-for="item in fileNames" :key="item" :value="item" :label="item" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
|
<el-input type="textarea" clearable v-model="form.description" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button :disabled="loading" @click="drawerVisiable = false">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="loading" type="primary" @click="submitImport(formRef)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { ElMessage, FormInstance } from 'element-plus';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
import { snapshotImport } from '@/api/modules/setting';
|
||||||
|
import { getBackupList, getFilesFromBackup } from '@/api/modules/backup';
|
||||||
|
import { loadBackupName } from '../../helper';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
|
||||||
|
const drawerVisiable = ref(false);
|
||||||
|
const loading = ref();
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const backupOptions = ref();
|
||||||
|
const fileNames = ref();
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
from: '',
|
||||||
|
names: [],
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
from: [Rules.requiredSelect],
|
||||||
|
name: [Rules.requiredSelect],
|
||||||
|
});
|
||||||
|
|
||||||
|
const acceptParams = (): void => {
|
||||||
|
form.from = '';
|
||||||
|
form.names = [] as Array<string>;
|
||||||
|
loadBackups();
|
||||||
|
drawerVisiable.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawerVisiable.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitImport = async (formEl: FormInstance | undefined) => {
|
||||||
|
loading.value = true;
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
await snapshotImport(form)
|
||||||
|
.then(() => {
|
||||||
|
emit('search');
|
||||||
|
loading.value = false;
|
||||||
|
drawerVisiable.value = false;
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadBackups = async () => {
|
||||||
|
const res = await getBackupList();
|
||||||
|
backupOptions.value = [];
|
||||||
|
for (const item of res.data) {
|
||||||
|
if (item.type !== 'LOCAL' && item.id !== 0) {
|
||||||
|
backupOptions.value.push({ label: loadBackupName(item.type), value: item.type });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadFiles = async () => {
|
||||||
|
const res = await getFilesFromBackup(form.from);
|
||||||
|
fileNames.value = res.data || [];
|
||||||
|
for (let i = 0; i < fileNames.value.length; i++) {
|
||||||
|
fileNames.value[i] = fileNames.value[i].replaceAll('system_snapshot/', '');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -7,6 +7,9 @@
|
|||||||
<el-button type="primary" @click="onCreate()">
|
<el-button type="primary" @click="onCreate()">
|
||||||
{{ $t('setting.createSnapshot') }}
|
{{ $t('setting.createSnapshot') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button @click="onImport()">
|
||||||
|
{{ $t('setting.importSnapshot') }}
|
||||||
|
</el-button>
|
||||||
<el-button type="primary" plain :disabled="selects.length === 0" @click="batchDelete(null)">
|
<el-button type="primary" plain :disabled="selects.length === 0" @click="batchDelete(null)">
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -43,14 +46,9 @@
|
|||||||
prop="name"
|
prop="name"
|
||||||
fix
|
fix
|
||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column prop="version" :label="$t('app.version')" />
|
||||||
:label="$t('commons.table.description')"
|
<el-table-column :label="$t('setting.backupAccount')" min-width="80" prop="from" />
|
||||||
min-width="150"
|
<el-table-column :label="$t('commons.table.status')" min-width="80" prop="status">
|
||||||
show-overflow-tooltip
|
|
||||||
prop="description"
|
|
||||||
/>
|
|
||||||
<el-table-column :label="$t('setting.backupAccount')" min-width="150" prop="from" />
|
|
||||||
<el-table-column :label="$t('setting.backup')" min-width="80" prop="status">
|
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag v-if="row.status === 'Success'" type="success">
|
<el-tag v-if="row.status === 'Success'" type="success">
|
||||||
{{ $t('commons.table.statusSuccess') }}
|
{{ $t('commons.table.statusSuccess') }}
|
||||||
@ -72,6 +70,13 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.table.description')" prop="description">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<fu-read-write-switch :data="row.description" v-model="row.edit" @change="onChange(row)">
|
||||||
|
<el-input v-model="row.description" @blur="row.edit = false" />
|
||||||
|
</fu-read-write-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="createdAt"
|
prop="createdAt"
|
||||||
:label="$t('commons.table.date')"
|
:label="$t('commons.table.date')"
|
||||||
@ -89,6 +94,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
<RecoverStatus ref="recoverStatusRef" @search="search()"></RecoverStatus>
|
<RecoverStatus ref="recoverStatusRef" @search="search()"></RecoverStatus>
|
||||||
|
<SnapshotImport ref="importRef" @search="search()" />
|
||||||
<el-drawer v-model="drawerVisiable" size="50%">
|
<el-drawer v-model="drawerVisiable" size="50%">
|
||||||
<template #header>
|
<template #header>
|
||||||
<DrawerHeader :header="$t('setting.createSnapshot')" :back="handleClose" />
|
<DrawerHeader :header="$t('setting.createSnapshot')" :back="handleClose" />
|
||||||
@ -139,7 +145,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
import TableSetting from '@/components/table-setting/index.vue';
|
import TableSetting from '@/components/table-setting/index.vue';
|
||||||
import { snapshotCreate, searchSnapshotPage, snapshotDelete } from '@/api/modules/setting';
|
import { snapshotCreate, searchSnapshotPage, snapshotDelete, updateSnapshotDescription } from '@/api/modules/setting';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { dateFormat } from '@/utils/util';
|
import { dateFormat } from '@/utils/util';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
@ -150,6 +156,7 @@ import { ElMessage } from 'element-plus';
|
|||||||
import { Setting } from '@/api/interface/setting';
|
import { Setting } from '@/api/interface/setting';
|
||||||
import LayoutContent from '@/layout/layout-content.vue';
|
import LayoutContent from '@/layout/layout-content.vue';
|
||||||
import RecoverStatus from '@/views/setting/snapshot/status/index.vue';
|
import RecoverStatus from '@/views/setting/snapshot/status/index.vue';
|
||||||
|
import SnapshotImport from '@/views/setting/snapshot/import/index.vue';
|
||||||
import { getBackupList } from '@/api/modules/backup';
|
import { getBackupList } from '@/api/modules/backup';
|
||||||
import { loadBackupName } from '../helper';
|
import { loadBackupName } from '../helper';
|
||||||
|
|
||||||
@ -164,6 +171,7 @@ const paginationConfig = reactive({
|
|||||||
const searchName = ref();
|
const searchName = ref();
|
||||||
|
|
||||||
const recoverStatusRef = ref();
|
const recoverStatusRef = ref();
|
||||||
|
const importRef = ref();
|
||||||
const isRecordShow = ref();
|
const isRecordShow = ref();
|
||||||
const backupOptions = ref();
|
const backupOptions = ref();
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
@ -184,10 +192,21 @@ const onCreate = async () => {
|
|||||||
drawerVisiable.value = true;
|
drawerVisiable.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onImport = () => {
|
||||||
|
importRef.value.acceptParams();
|
||||||
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
drawerVisiable.value = false;
|
drawerVisiable.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChange = async (info: any) => {
|
||||||
|
if (!info.edit) {
|
||||||
|
await updateSnapshotDescription({ id: info.id, description: info.description });
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const submitAddSnapshot = (formEl: FormInstance | undefined) => {
|
const submitAddSnapshot = (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user