From 205d406761da06f294068e4662478272e94801f3 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Mon, 13 Feb 2023 15:48:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=BF=AB=E7=85=A7?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/backup.go | 27 +++ backend/app/api/v1/database_mysql.go | 4 +- backend/app/api/v1/snapshot.go | 54 ++++- backend/app/dto/backup.go | 4 + backend/app/dto/common_req.go | 5 + backend/app/dto/database.go | 5 - backend/app/dto/setting.go | 5 + backend/app/service/backup.go | 14 ++ backend/app/service/database_mysql.go | 4 +- backend/app/service/snapshot.go | 74 +++++-- backend/app/service/upgrade.go | 2 +- backend/router/ro_backup.go | 1 + backend/router/ro_setting.go | 2 + cmd/server/cmd/{userinfo.go => user-info.go} | 2 +- cmd/server/docs/docs.go | 191 ++++++++++++++++-- cmd/server/docs/swagger.json | 191 ++++++++++++++++-- cmd/server/docs/swagger.yaml | 124 +++++++++++- frontend/src/api/interface/database.ts | 4 - frontend/src/api/interface/index.ts | 4 + frontend/src/api/interface/setting.ts | 5 + frontend/src/api/modules/backup.ts | 4 + frontend/src/api/modules/database.ts | 4 +- frontend/src/api/modules/setting.ts | 8 +- frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + .../views/setting/snapshot/import/index.vue | 124 ++++++++++++ frontend/src/views/setting/snapshot/index.vue | 37 +++- 27 files changed, 816 insertions(+), 85 deletions(-) rename cmd/server/cmd/{userinfo.go => user-info.go} (97%) create mode 100644 frontend/src/views/setting/snapshot/import/index.vue diff --git a/backend/app/api/v1/backup.go b/backend/app/api/v1/backup.go index 818e50fc2..9d5383960 100644 --- a/backend/app/api/v1/backup.go +++ b/backend/app/api/v1/backup.go @@ -210,3 +210,30 @@ func (b *BaseApi) ListBackup(c *gin.Context) { 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) +} diff --git a/backend/app/api/v1/database_mysql.go b/backend/app/api/v1/database_mysql.go index dc56ab1ec..0e050110e 100644 --- a/backend/app/api/v1/database_mysql.go +++ b/backend/app/api/v1/database_mysql.go @@ -40,13 +40,13 @@ func (b *BaseApi) CreateMysql(c *gin.Context) { // @Summary Update mysql database description // @Description 更新 mysql 数据库库描述信息 // @Accept json -// @Param request body dto.MysqlDescription true "request" +// @Param request body dto.UpdateDescription true "request" // @Success 200 // @Security ApiKeyAuth // @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]"} func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) { - var req dto.MysqlDescription + var req dto.UpdateDescription if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) return diff --git a/backend/app/api/v1/snapshot.go b/backend/app/api/v1/snapshot.go index 2f9e40b1f..40aaaf513 100644 --- a/backend/app/api/v1/snapshot.go +++ b/backend/app/api/v1/snapshot.go @@ -9,7 +9,7 @@ import ( ) // @Tags System Setting -// @Summary Create system backup +// @Summary Create system snapshot // @Description 创建系统快照 // @Accept json // @Param request body dto.SnapshotCreate true "request" @@ -34,6 +34,58 @@ func (b *BaseApi) CreateSnapshot(c *gin.Context) { 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 // @Summary Page system snapshot // @Description 获取系统快照列表分页 diff --git a/backend/app/dto/backup.go b/backend/app/dto/backup.go index 3ab874914..40d5ac09e 100644 --- a/backend/app/dto/backup.go +++ b/backend/app/dto/backup.go @@ -26,6 +26,10 @@ type BackupSearch struct { DetailName string `json:"detailName"` } +type BackupSearchFile struct { + Type string `json:"type" validate:"required"` +} + type RecordSearch struct { PageInfo Type string `json:"type" validate:"required"` diff --git a/backend/app/dto/common_req.go b/backend/app/dto/common_req.go index 809ea720b..5742289f7 100644 --- a/backend/app/dto/common_req.go +++ b/backend/app/dto/common_req.go @@ -10,6 +10,11 @@ type PageInfo struct { PageSize int `json:"pageSize" validate:"required,number"` } +type UpdateDescription struct { + ID uint `json:"id" validate:"required"` + Description string `json:"description"` +} + type OperationWithName struct { Name string `json:"name" validate:"required"` } diff --git a/backend/app/dto/database.go b/backend/app/dto/database.go index 254020aea..86bdbdfeb 100644 --- a/backend/app/dto/database.go +++ b/backend/app/dto/database.go @@ -2,11 +2,6 @@ package dto import "time" -type MysqlDescription struct { - ID uint `json:"id" validate:"required"` - Description string `json:"description"` -} - type MysqlDBInfo struct { ID uint `json:"id"` CreatedAt time.Time `json:"createdAt"` diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index 74c60d8eb..cd7b517a6 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -57,6 +57,11 @@ type SnapshotRecover struct { ReDownload bool `json:"reDownload"` ID uint `json:"id" validate:"required"` } +type SnapshotImport struct { + From string `json:"from"` + Names []string `json:"names"` + Description string `json:"description"` +} type SnapshotInfo struct { ID uint `json:"id"` Name string `json:"name"` diff --git a/backend/app/service/backup.go b/backend/app/service/backup.go index 1c4bc198e..e91915b8d 100644 --- a/backend/app/service/backup.go +++ b/backend/app/service/backup.go @@ -29,6 +29,8 @@ type IBackupService interface { BatchDelete(ids []uint) error BatchDeleteRecord(ids []uint) error NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) + + ListFiles(req dto.BackupSearchFile) ([]interface{}, error) } func NewIBackupService() IBackupService { @@ -226,6 +228,18 @@ func (u *BackupService) Update(req dto.BackupOperate) error { 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) { varMap := make(map[string]interface{}) if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index db63af635..f8526c58e 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -37,7 +37,7 @@ type IMysqlService interface { ChangePassword(info dto.ChangeDBInfo) error UpdateVariables(updatas []dto.MysqlVariablesUpdate) error UpdateConfByFile(info dto.MysqlConfUpdateByFile) error - UpdateDescription(req dto.MysqlDescription) error + UpdateDescription(req dto.UpdateDescription) error RecoverByUpload(req dto.UploadRecover) error Backup(db dto.BackupDB) error @@ -201,7 +201,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode 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}) } diff --git a/backend/app/service/snapshot.go b/backend/app/service/snapshot.go index 4ff88dc5a..586ea15cb 100644 --- a/backend/app/service/snapshot.go +++ b/backend/app/service/snapshot.go @@ -29,8 +29,10 @@ type ISnapshotService interface { SnapshotCreate(req dto.SnapshotCreate) error SnapshotRecover(req dto.SnapshotRecover) error SnapshotRollback(req dto.SnapshotRecover) error + SnapshotImport(req dto.SnapshotImport) error Delete(req dto.BatchDeleteReq) error + UpdateDescription(req dto.UpdateDescription) error readFromJson(path string) (SnapshotJson, error) } @@ -51,11 +53,48 @@ func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interfa 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 { + OldBaseDir string `json:"oldBaseDir"` OldDockerDataDir string `json:"oldDockerDataDir"` OldBackupDataDir string `json:"oldDackupDataDir"` OldPanelDataDir string `json:"oldPanelDataDir"` + BaseDir string `json:"baseDir"` DockerDataDir string `json:"dockerDataDir"` BackupDataDir string `json:"backupDataDir"` PanelDataDir string `json:"panelDataDir"` @@ -78,15 +117,15 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error { } 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) _ = os.MkdirAll(backupPanelDir, os.ModePerm) backupDockerDir := fmt.Sprintf("%s/docker", rootDir) _ = os.MkdirAll(backupDockerDir, os.ModePerm) - versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion")) snap := model.Snapshot{ - Name: "1panel_snapshot_" + timeNow, + Name: fmt.Sprintf("1panel_%s_%s", versionItem.Value, timeNow), Description: req.Description, From: req.From, Version: versionItem.Value, @@ -139,13 +178,19 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error { 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 { updateSnapshotStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("save snapshot json failed, err: %v", err)) 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()) 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) _ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading}) - localPath := fmt.Sprintf("%s/system/1panel_snapshot_%s.tar.gz", localDir, timeNow) - if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_snapshot_%s.tar.gz", timeNow)); err != nil || !ok { + 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_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok { _ = 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) return } _ = 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) }() @@ -230,8 +275,6 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error { isReTry = false } 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)) if err != nil { @@ -241,7 +284,10 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error { if snap.InterruptStep == "Readjson" { 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.OldBackupDataDir = localDir recoverPanelDir := fmt.Sprintf("%s/%s/1panel", baseDir, snap.Name) @@ -340,10 +386,6 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error { fileOp := files.NewFileOp() 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") _ = 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)) 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") if err := u.handleDockerDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldDockerDataDir); err != nil { diff --git a/backend/app/service/upgrade.go b/backend/app/service/upgrade.go index 628021d9c..7dc30e9db 100644 --- a/backend/app/service/upgrade.go +++ b/backend/app/service/upgrade.go @@ -58,7 +58,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) { } if len(releaseInfo.NewVersion) != 0 { isNew, err := compareVersion(currentVersion.Value, releaseInfo.NewVersion) - if !isNew && err != nil { + if !isNew || err != nil { return nil, err } return &releaseInfo, nil diff --git a/backend/router/ro_backup.go b/backend/router/ro_backup.go index 46c87d5ed..a430de353 100644 --- a/backend/router/ro_backup.go +++ b/backend/router/ro_backup.go @@ -17,6 +17,7 @@ func (s *BackupRouter) InitBackupRouter(Router *gin.RouterGroup) { baseApi := v1.ApiGroupApp.BaseApi { baRouter.GET("/search", baseApi.ListBackup) + baRouter.POST("/search/files", baseApi.LoadFilesFromBackup) baRouter.POST("/buckets", baseApi.ListBuckets) baRouter.POST("", baseApi.CreateBackup) baRouter.POST("/del", baseApi.DeleteBackup) diff --git a/backend/router/ro_setting.go b/backend/router/ro_setting.go index e37cc5b68..1d7bc2284 100644 --- a/backend/router/ro_setting.go +++ b/backend/router/ro_setting.go @@ -27,9 +27,11 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) { settingRouter.POST("/mfa/bind", baseApi.MFABind) settingRouter.POST("/snapshot", baseApi.CreateSnapshot) settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot) + settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot) settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot) settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot) settingRouter.POST("/snapshot/rollback", baseApi.RollbackSnapshot) + settingRouter.POST("/snapshot/description/update", baseApi.UpdateSnapDescription) settingRouter.POST("/upgrade", baseApi.Upgrade) settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo) settingRouter.GET("/basedir", baseApi.LoadBaseDir) diff --git a/cmd/server/cmd/userinfo.go b/cmd/server/cmd/user-info.go similarity index 97% rename from cmd/server/cmd/userinfo.go rename to cmd/server/cmd/user-info.go index d558a5994..d0e7e6aa3 100644 --- a/cmd/server/cmd/userinfo.go +++ b/cmd/server/cmd/user-info.go @@ -15,7 +15,7 @@ func init() { } var userinfoCmd = &cobra.Command{ - Use: "userinfo", + Use: "user-info", Short: "获取用户信息", RunE: func(cmd *cobra.Command, args []string) error { fullPath := "/opt/1panel/db/1Panel.db" diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index cc05de2bc..dd587aaff 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -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": { "post": { "security": [ @@ -3907,7 +3943,7 @@ var doc = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.MysqlDescription" + "$ref": "#/definitions/dto.UpdateDescription" } } ], @@ -6285,7 +6321,7 @@ var doc = `{ "tags": [ "System Setting" ], - "summary": "Create system backup", + "summary": "Create system snapshot", "parameters": [ { "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": { "post": { "security": [ @@ -8474,6 +8605,17 @@ var doc = `{ } } }, + "dto.BackupSearchFile": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string" + } + } + }, "dto.BatchDelete": { "type": "object", "required": [ @@ -9751,20 +9893,6 @@ var doc = `{ } } }, - "dto.MysqlDescription": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "description": { - "type": "string" - }, - "id": { - "type": "integer" - } - } - }, "dto.MysqlStatus": { "type": "object", "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": { "type": "object", "required": [ @@ -10468,6 +10613,20 @@ var doc = `{ } } }, + "dto.UpdateDescription": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + } + } + }, "dto.Upgrade": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 935f47004..524887830 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -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": { "post": { "security": [ @@ -3893,7 +3929,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.MysqlDescription" + "$ref": "#/definitions/dto.UpdateDescription" } } ], @@ -6271,7 +6307,7 @@ "tags": [ "System Setting" ], - "summary": "Create system backup", + "summary": "Create system snapshot", "parameters": [ { "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": { "post": { "security": [ @@ -8460,6 +8591,17 @@ } } }, + "dto.BackupSearchFile": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string" + } + } + }, "dto.BatchDelete": { "type": "object", "required": [ @@ -9737,20 +9879,6 @@ } } }, - "dto.MysqlDescription": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "description": { - "type": "string" - }, - "id": { - "type": "integer" - } - } - }, "dto.MysqlStatus": { "type": "object", "properties": { @@ -10437,6 +10565,23 @@ } } }, + "dto.SnapshotImport": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "from": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "dto.SnapshotRecover": { "type": "object", "required": [ @@ -10454,6 +10599,20 @@ } } }, + "dto.UpdateDescription": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + } + } + }, "dto.Upgrade": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 787614360..26b8d5874 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -28,6 +28,13 @@ definitions: - type - vars type: object + dto.BackupSearchFile: + properties: + type: + type: string + required: + - type + type: object dto.BatchDelete: properties: names: @@ -890,15 +897,6 @@ definitions: required: - id type: object - dto.MysqlDescription: - properties: - description: - type: string - id: - type: integer - required: - - id - type: object dto.MysqlStatus: properties: Aborted_clients: @@ -1354,6 +1352,17 @@ definitions: required: - from type: object + dto.SnapshotImport: + properties: + description: + type: string + from: + type: string + names: + items: + type: string + type: array + type: object dto.SnapshotRecover: properties: id: @@ -1365,6 +1374,15 @@ definitions: required: - id type: object + dto.UpdateDescription: + properties: + description: + type: string + id: + type: integer + required: + - id + type: object dto.Upgrade: properties: version: @@ -3358,6 +3376,28 @@ paths: summary: List buckets tags: - 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: post: consumes: @@ -5100,7 +5140,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/dto.MysqlDescription' + $ref: '#/definitions/dto.UpdateDescription' responses: "200": description: "" @@ -6621,7 +6661,7 @@ paths: description: "" security: - ApiKeyAuth: [] - summary: Create system backup + summary: Create system snapshot tags: - System Setting x-panel-log: @@ -6665,6 +6705,68 @@ paths: formatEN: Delete system backup [name] formatZH: 删除系统快照 [name] 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: post: consumes: diff --git a/frontend/src/api/interface/database.ts b/frontend/src/api/interface/database.ts index 3ad8f62f1..648a211c7 100644 --- a/frontend/src/api/interface/database.ts +++ b/frontend/src/api/interface/database.ts @@ -5,10 +5,6 @@ export namespace Database { mysqlName: string; dbName: string; } - export interface DescriptionUpdate { - id: number; - description: string; - } export interface Backup { mysqlName: string; dbName: string; diff --git a/frontend/src/api/interface/index.ts b/frontend/src/api/interface/index.ts index d8f285d36..a47119908 100644 --- a/frontend/src/api/interface/index.ts +++ b/frontend/src/api/interface/index.ts @@ -28,6 +28,10 @@ export interface CommonModel { CreatedAt?: string; UpdatedAt?: string; } +export interface DescriptionUpdate { + id: number; + description: string; +} // * 文件上传模块 export namespace Upload { diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts index bf3c5f372..cad1609e9 100644 --- a/frontend/src/api/interface/setting.ts +++ b/frontend/src/api/interface/setting.ts @@ -53,6 +53,11 @@ export namespace Setting { from: string; description: string; } + export interface SnapshotImport { + from: string; + names: Array; + description: string; + } export interface SnapshotRecover { id: number; isNew: boolean; diff --git a/frontend/src/api/modules/backup.ts b/frontend/src/api/modules/backup.ts index f9b3daaae..046f335d0 100644 --- a/frontend/src/api/modules/backup.ts +++ b/frontend/src/api/modules/backup.ts @@ -6,6 +6,10 @@ export const getBackupList = () => { return http.get>(`/backups/search`); }; +export const getFilesFromBackup = (type: string) => { + return http.post>(`/backups/search/files`, { type: type }); +}; + export const addBackup = (params: Backup.BackupOperate) => { return http.post(`/backups`, params); }; diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 121c04f4c..9c5d337de 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -1,5 +1,5 @@ import http from '@/api'; -import { SearchWithPage, ReqPage, ResPage } from '../interface'; +import { SearchWithPage, ReqPage, ResPage, DescriptionUpdate } from '../interface'; import { Database } from '../interface/database'; export const searchMysqlDBs = (params: SearchWithPage) => { @@ -25,7 +25,7 @@ export const updateMysqlAccess = (params: Database.ChangeInfo) => { export const updateMysqlPassword = (params: Database.ChangeInfo) => { 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); }; export const updateMysqlVariables = (params: Array) => { diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index 0c73a165f..112b58919 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -1,5 +1,5 @@ import http from '@/api'; -import { ResPage, SearchWithPage } from '../interface'; +import { ResPage, SearchWithPage, DescriptionUpdate } from '../interface'; import { Setting } from '../interface/setting'; export const getSettingInfo = () => { @@ -53,6 +53,12 @@ export const loadBaseDir = () => { export const snapshotCreate = (param: Setting.SnapshotCreate) => { 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[] }) => { return http.post(`/settings/snapshot/del`, param); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index aae718731..fcfbc7b23 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -770,6 +770,7 @@ export default { thirdPartySupport: 'Only third-party accounts are supported', recoverDetail: 'Recover detail', createSnapshot: 'Create snapshot', + importSnapshot: 'Sync snapshot', recover: 'Recover', noRecoverRecord: 'No recovery record has been recorded', lastRecoverAt: 'Last recovery time', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 2e5e1bff6..391477248 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -768,6 +768,7 @@ export default { thirdPartySupport: '仅支持第三方账号', recoverDetail: '恢复详情', createSnapshot: '创建快照', + importSnapshot: '同步快照', recover: '恢复', noRecoverRecord: '暂无恢复记录', lastRecoverAt: '上次恢复时间', diff --git a/frontend/src/views/setting/snapshot/import/index.vue b/frontend/src/views/setting/snapshot/import/index.vue new file mode 100644 index 000000000..d7be8c630 --- /dev/null +++ b/frontend/src/views/setting/snapshot/import/index.vue @@ -0,0 +1,124 @@ + + + diff --git a/frontend/src/views/setting/snapshot/index.vue b/frontend/src/views/setting/snapshot/index.vue index 9e7b32119..d4f7a7d49 100644 --- a/frontend/src/views/setting/snapshot/index.vue +++ b/frontend/src/views/setting/snapshot/index.vue @@ -7,6 +7,9 @@ {{ $t('setting.createSnapshot') }} + + {{ $t('setting.importSnapshot') }} + {{ $t('commons.button.delete') }} @@ -43,14 +46,9 @@ prop="name" fix /> - - - + + + + + + +