mirror of https://github.com/1Panel-dev/1Panel
fix: 网站、数据库、应用恢复失败增加回滚机制
parent
aa2bb73199
commit
388c6150c7
|
@ -314,6 +314,16 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
|||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "redis":
|
||||
if err := backupService.RedisRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "app":
|
||||
if err := backupService.AppRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
@ -339,11 +349,6 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
|||
}
|
||||
|
||||
switch req.Type {
|
||||
case "app":
|
||||
if err := backupService.AppRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "mysql":
|
||||
if err := backupService.MysqlRecoverByUpload(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
@ -354,11 +359,6 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
|||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
case "redis":
|
||||
if err := backupService.RedisRecover(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
|||
if _, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppRecover(&install, req.File); err != nil {
|
||||
if err := handleAppRecover(&install, req.File, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -120,7 +120,8 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func handleAppRecover(install *model.AppInstall, recoverFile string) error {
|
||||
func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback bool) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.Decompress(recoverFile, path.Dir(recoverFile), files.TarGz); err != nil {
|
||||
return err
|
||||
|
@ -133,6 +134,24 @@ func handleAppRecover(install *model.AppInstall, recoverFile string) error {
|
|||
if !fileOp.Stat(tmpPath+"/app.json") || !fileOp.Stat(tmpPath+"/app.tar.gz") {
|
||||
return errors.New("the wrong recovery package does not have app.json or app.tar.gz files")
|
||||
}
|
||||
if !isRollback {
|
||||
rollbackFile := fmt.Sprintf("%s/original/app/%s_%s.tar.gz", global.CONF.System.BaseDir, install.Name, time.Now().Format("20060102150405"))
|
||||
if err := handleAppBackup(install, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
global.LOG.Errorf("backup app %s for rollback before recover failed, err: %v", install.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
if err := handleAppRecover(install, rollbackFile, true); err != nil {
|
||||
global.LOG.Errorf("rollback app %s from %s failed, err: %v", install.Name, rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback app %s from %s successful", install.Name, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
appjson, err := os.ReadFile(tmpPath + "/" + "app.json")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -195,5 +214,6 @@ func handleAppRecover(install *model.AppInstall, recoverFile string) error {
|
|||
if err := appInstallRepo.Save(install); err != nil {
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
|
|||
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
|
||||
}
|
||||
global.LOG.Infof("recover database %s-%s from backup file %s", req.Name, req.DetailName, req.File)
|
||||
if err := handleMysqlRecover(app, path.Dir(req.File), req.DetailName, path.Base(req.File)); err != nil {
|
||||
if err := handleMysqlRecover(app, path.Dir(req.File), req.DetailName, path.Base(req.File), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -114,7 +114,7 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
|||
}()
|
||||
}
|
||||
|
||||
if err := handleMysqlRecover(app, path.Dir(file), req.DetailName, fileName); err != nil {
|
||||
if err := handleMysqlRecover(app, path.Dir(file), req.DetailName, fileName, false); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("recover from uploads successful!")
|
||||
|
@ -141,7 +141,25 @@ func handleMysqlBackup(app *repo.RootInfo, backupDir, dbName, fileName string) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlRecover(mysqlInfo *repo.RootInfo, recoverDir, dbName, fileName string) error {
|
||||
func handleMysqlRecover(mysqlInfo *repo.RootInfo, recoverDir, dbName, fileName string, isRollback bool) error {
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
rollbackFile := fmt.Sprintf("%s/original/database/%s_%s.sql.gz", global.CONF.System.BaseDir, mysqlInfo.Name, time.Now().Format("20060102150405"))
|
||||
if err := handleMysqlBackup(mysqlInfo, path.Dir(rollbackFile), dbName, path.Base(rollbackFile)); err != nil {
|
||||
global.LOG.Errorf("backup mysql db %s for rollback before recover failed, err: %v", mysqlInfo.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
if err := handleMysqlRecover(mysqlInfo, path.Dir(rollbackFile), dbName, path.Base(rollbackFile), true); err != nil {
|
||||
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", dbName, rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", dbName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
file := recoverDir + "/" + fileName
|
||||
fi, _ := os.Open(file)
|
||||
defer fi.Close()
|
||||
|
@ -166,5 +184,6 @@ func handleMysqlRecover(mysqlInfo *repo.RootInfo, recoverDir, dbName, fileName s
|
|||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
return errors.New(stdStr)
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -38,8 +39,8 @@ func (u *BackupService) RedisBackup() error {
|
|||
if appendonly == "yes" {
|
||||
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
|
||||
}
|
||||
backupDir := fmt.Sprintf("%s/database/redis/%s/", localDir, redisInfo.Name)
|
||||
if err := handleBackupRedis(redisInfo, backupDir, fileName); err != nil {
|
||||
backupDir := fmt.Sprintf("%s/database/redis/%s", localDir, redisInfo.Name)
|
||||
if err := handleRedisBackup(redisInfo, backupDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
|
@ -62,13 +63,13 @@ func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
|
|||
return err
|
||||
}
|
||||
global.LOG.Infof("recover redis from backup file %s", req.File)
|
||||
if err := handleRecoverRedis(redisInfo, req.File); err != nil {
|
||||
if err := handleRedisRecover(redisInfo, req.File, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleBackupRedis(redisInfo *repo.RootInfo, backupDir, fileName string) error {
|
||||
func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(backupDir) {
|
||||
if err := os.MkdirAll(backupDir, os.ModePerm); err != nil {
|
||||
|
@ -96,17 +97,44 @@ func handleBackupRedis(redisInfo *repo.RootInfo, backupDir, fileName string) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func handleRecoverRedis(redisInfo *repo.RootInfo, recoverFile string) error {
|
||||
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(recoverFile) {
|
||||
return errors.New(fmt.Sprintf("%s file is not exist", recoverFile))
|
||||
return fmt.Errorf("%s file is not exist", recoverFile)
|
||||
}
|
||||
|
||||
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (appendonly == "yes" && !strings.HasSuffix(recoverFile, ".tar.gz")) || (appendonly != "yes" && !strings.HasSuffix(recoverFile, ".rdb")) {
|
||||
return fmt.Errorf("recover redis with appendonly=%s from file %s format error", appendonly, recoverFile)
|
||||
}
|
||||
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
defer func() {
|
||||
suffix := "tar.gz"
|
||||
if appendonly != "yes" {
|
||||
suffix = "rdb"
|
||||
}
|
||||
rollbackFile := fmt.Sprintf("%s/original/database/redis/%s_%s.%s", global.CONF.System.BaseDir, redisInfo.Name, time.Now().Format("20060102150405"), suffix)
|
||||
if err := handleRedisBackup(redisInfo, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
global.LOG.Errorf("backup database %s for rollback before recover failed, err: %v", redisInfo.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
if err := handleRedisRecover(redisInfo, rollbackFile, true); err != nil {
|
||||
global.LOG.Errorf("rollback redis from %s failed, err: %v", rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback redis from %s successful", rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
}
|
||||
composeDir := fmt.Sprintf("%s/redis/%s", constant.AppInstallDir, redisInfo.Name)
|
||||
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
|
||||
return err
|
||||
|
|
|
@ -73,7 +73,7 @@ func (u *BackupService) WebsiteRecoverByUpload(req dto.CommonRecover) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleWebsiteRecover(&website, tmpDir); err != nil {
|
||||
if err := handleWebsiteRecover(&website, tmpDir, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -90,13 +90,13 @@ func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
|
|||
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
|
||||
}
|
||||
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
|
||||
if err := handleWebsiteRecover(&website, req.File); err != nil {
|
||||
if err := handleWebsiteRecover(&website, req.File, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string) error {
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool) error {
|
||||
fileOp := files.NewFileOp()
|
||||
fileDir := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
if err := fileOp.Decompress(recoverFile, path.Dir(recoverFile), files.TarGz); err != nil {
|
||||
|
@ -115,6 +115,24 @@ func handleWebsiteRecover(website *model.Website, recoverFile string) error {
|
|||
return errors.New("the wrong recovery package does not have .sql.gz or .app.tar.gz files")
|
||||
}
|
||||
}
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
rollbackFile := fmt.Sprintf("%s/original/website/%s_%s.tar.gz", global.CONF.System.BaseDir, website.Alias, time.Now().Format("20060102150405"))
|
||||
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
|
||||
global.LOG.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
if err := handleWebsiteRecover(website, rollbackFile, true); err != nil {
|
||||
global.LOG.Errorf("rollback website %s from %s failed, err: %v", website.Alias, rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback website %s from %s successful", website.Alias, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
nginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, "")
|
||||
if err != nil {
|
||||
|
@ -138,14 +156,14 @@ func handleWebsiteRecover(website *model.Website, recoverFile string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlRecover(mysqlInfo, fileDir, db.Name, fmt.Sprintf("%s.sql.gz", website.Alias)); err != nil {
|
||||
if err := handleMysqlRecover(mysqlInfo, fileDir, db.Name, fmt.Sprintf("%s.sql.gz", website.Alias), isRollback); err != nil {
|
||||
return err
|
||||
}
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", fileDir, website.Alias)); err != nil {
|
||||
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", fileDir, website.Alias), isRollback); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, app.App.Key, app.Name)); err != nil {
|
||||
|
|
|
@ -39,7 +39,7 @@ var userinfoCmd = &cobra.Command{
|
|||
user := getSettingByKey(db, "UserName")
|
||||
password := getSettingByKey(db, "Password")
|
||||
port := getSettingByKey(db, "ServerPort")
|
||||
enptrySetting := getSettingByKey(db, "ServerPort")
|
||||
enptrySetting := getSettingByKey(db, "EncryptKey")
|
||||
|
||||
p := ""
|
||||
if len(enptrySetting) == 16 {
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import http from '@/api';
|
||||
import { Backup } from '../interface/backup';
|
||||
import { ResPage } from '../interface';
|
||||
|
||||
export const getBackupList = () => {
|
||||
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) => {
|
||||
return http.post<Backup.BackupOperate>(`/backups`, params);
|
||||
};
|
||||
|
||||
export const editBackup = (params: Backup.BackupOperate) => {
|
||||
return http.post(`/backups/update`, params);
|
||||
};
|
||||
|
||||
export const deleteBackup = (params: { ids: number[] }) => {
|
||||
return http.post(`/backups/del`, params);
|
||||
};
|
||||
|
||||
export const downloadBackupRecord = (params: Backup.RecordDownload) => {
|
||||
return http.download<BlobPart>(`/backups/record/download`, params, { responseType: 'blob' });
|
||||
};
|
||||
export const deleteBackupRecord = (params: { ids: number[] }) => {
|
||||
return http.post(`/backups/record/del`, params);
|
||||
};
|
||||
export const searchBackupRecords = (params: Backup.SearchBackupRecord) => {
|
||||
return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search`, params);
|
||||
};
|
||||
|
||||
export const listBucket = (params: Backup.ForBucket) => {
|
||||
return http.post(`/backups/buckets`, params);
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import http from '@/api';
|
||||
import { SearchWithPage, ReqPage, ResPage, DescriptionUpdate } from '../interface';
|
||||
import { SearchWithPage, ResPage, DescriptionUpdate } from '../interface';
|
||||
import { Database } from '../interface/database';
|
||||
|
||||
export const searchMysqlDBs = (params: SearchWithPage) => {
|
||||
|
@ -54,7 +54,7 @@ export const loadRedisStatus = () => {
|
|||
export const loadRedisConf = () => {
|
||||
return http.get<Database.RedisConf>(`/databases/redis/conf`);
|
||||
};
|
||||
export const RedisPersistenceConf = () => {
|
||||
export const redisPersistenceConf = () => {
|
||||
return http.get<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`);
|
||||
};
|
||||
export const changeRedisPassword = (params: Database.ChangeInfo) => {
|
||||
|
@ -69,9 +69,3 @@ export const updateRedisConf = (params: Database.RedisConfUpdate) => {
|
|||
export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => {
|
||||
return http.post(`/databases/redis/conffile/update`, params);
|
||||
};
|
||||
export const recoverRedis = (param: Database.RedisRecover) => {
|
||||
return http.post(`/databases/redis/recover`, param);
|
||||
};
|
||||
export const redisBackupRedisRecords = (param: ReqPage) => {
|
||||
return http.post<ResPage<Database.FileRecord>>(`/databases/redis/backup/search`, param);
|
||||
};
|
||||
|
|
|
@ -60,6 +60,16 @@ export const handleRecover = (params: Backup.Recover) => {
|
|||
export const handleRecoverByUpload = (params: Backup.Recover) => {
|
||||
return http.post(`/settings/backup/recover/byupload`, params);
|
||||
};
|
||||
export const downloadBackupRecord = (params: Backup.RecordDownload) => {
|
||||
return http.download<BlobPart>(`/settings/backup/record/download`, params, { responseType: 'blob' });
|
||||
};
|
||||
export const deleteBackupRecord = (params: { ids: number[] }) => {
|
||||
return http.post(`/settings/backup/record/del`, params);
|
||||
};
|
||||
export const searchBackupRecords = (params: Backup.SearchBackupRecord) => {
|
||||
return http.post<ResPage<Backup.RecordInfo>>(`/settings/backup/record/search`, params);
|
||||
};
|
||||
|
||||
export const getBackupList = () => {
|
||||
return http.get<Array<Backup.BackupInfo>>(`/settings/backup/search`);
|
||||
};
|
||||
|
@ -75,15 +85,6 @@ export const editBackup = (params: Backup.BackupOperate) => {
|
|||
export const deleteBackup = (params: { ids: number[] }) => {
|
||||
return http.post(`/settings/backup/del`, params);
|
||||
};
|
||||
export const downloadBackupRecord = (params: Backup.RecordDownload) => {
|
||||
return http.download<BlobPart>(`/settings/backup/record/download`, params, { responseType: 'blob' });
|
||||
};
|
||||
export const deleteBackupRecord = (params: { ids: number[] }) => {
|
||||
return http.post(`/settings/backup/record/del`, params);
|
||||
};
|
||||
export const searchBackupRecords = (params: Backup.SearchBackupRecord) => {
|
||||
return http.post<ResPage<Backup.RecordInfo>>(`/settings/backup/record/search`, params);
|
||||
};
|
||||
export const listBucket = (params: Backup.ForBucket) => {
|
||||
return http.post(`/settings/backup/buckets`, params);
|
||||
};
|
||||
|
|
|
@ -77,7 +77,7 @@ const isRefresh = ref();
|
|||
|
||||
const onSetting = async () => {
|
||||
isOnSetting.value = true;
|
||||
terminalRef.value.onClose(false);
|
||||
terminalRef.value?.onClose(false);
|
||||
settingRef.value!.acceptParams({ status: redisStatus.value, redisName: redisName.value });
|
||||
};
|
||||
|
||||
|
@ -124,7 +124,7 @@ const initTerminal = async () => {
|
|||
};
|
||||
const closeTerminal = async (isKeepShow: boolean) => {
|
||||
isRefresh.value = !isRefresh.value;
|
||||
terminalRef.value!.onClose(isKeepShow);
|
||||
terminalRef.value?.onClose(isKeepShow);
|
||||
};
|
||||
|
||||
const onBefore = () => {
|
||||
|
|
|
@ -178,7 +178,6 @@ interface DialogProps {
|
|||
|
||||
const changeTab = (val: string) => {
|
||||
activeName.value = val;
|
||||
console.log(activeName.value);
|
||||
};
|
||||
|
||||
const acceptParams = (prop: DialogProps): void => {
|
||||
|
@ -192,9 +191,6 @@ const acceptParams = (prop: DialogProps): void => {
|
|||
loadform();
|
||||
}
|
||||
};
|
||||
const onClose = (): void => {
|
||||
settingShow.value = false;
|
||||
};
|
||||
|
||||
const portRef = ref();
|
||||
const confirmPortRef = ref();
|
||||
|
@ -322,6 +318,5 @@ const loadConfFile = async () => {
|
|||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
onClose,
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -79,12 +79,7 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
<el-card style="margin-top: 20px">
|
||||
<ComplexTable
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
@search="loadBackupRecords"
|
||||
:data="data"
|
||||
>
|
||||
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" @search="search" :data="data">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" @click="onBackup">{{ $t('setting.backup') }}</el-button>
|
||||
<el-button type="primary" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
||||
|
@ -93,13 +88,9 @@
|
|||
</template>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :label="$t('commons.table.name')" show-overflow-tooltip prop="fileName" />
|
||||
<el-table-column :label="$t('database.source')" prop="backupType" />
|
||||
<el-table-column :label="$t('file.dir')" show-overflow-tooltip prop="fileDir" />
|
||||
<el-table-column :label="$t('file.size')" prop="size">
|
||||
<template #default="{ row }">
|
||||
{{ computeSize(row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.createdAt')" prop="createdAt" />
|
||||
<el-table-column :label="$t('commons.table.createdAt')" :formatter="dateFormat" prop="createdAt" />
|
||||
<fu-table-operations
|
||||
width="300px"
|
||||
:buttons="buttons"
|
||||
|
@ -118,22 +109,16 @@
|
|||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import {
|
||||
recoverRedis,
|
||||
redisBackupRedisRecords,
|
||||
RedisPersistenceConf,
|
||||
updateRedisPersistenceConf,
|
||||
} from '@/api/modules/database';
|
||||
|
||||
import { handleBackup } from '@/api/modules/setting';
|
||||
import { redisPersistenceConf, updateRedisPersistenceConf } from '@/api/modules/database';
|
||||
import { deleteBackupRecord, handleBackup, handleRecover, searchBackupRecords } from '@/api/modules/setting';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import { computeSize } from '@/utils/util';
|
||||
import { BatchDeleteFile } from '@/api/modules/files';
|
||||
import { MsgInfo, MsgSuccess } from '@/utils/message';
|
||||
import { Backup } from '@/api/interface/backup';
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
|
@ -160,12 +145,9 @@ const acceptParams = (prop: DialogProps): void => {
|
|||
persistenceShow.value = true;
|
||||
if (prop.status === 'Running') {
|
||||
loadform();
|
||||
loadBackupRecords();
|
||||
search();
|
||||
}
|
||||
};
|
||||
const onClose = (): void => {
|
||||
persistenceShow.value = false;
|
||||
};
|
||||
|
||||
const data = ref();
|
||||
const selects = ref<any>([]);
|
||||
|
@ -188,12 +170,15 @@ const handleDelete = (index: number) => {
|
|||
form.saves.splice(index, 1);
|
||||
};
|
||||
|
||||
const loadBackupRecords = async () => {
|
||||
const search = async () => {
|
||||
let params = {
|
||||
type: 'redis',
|
||||
name: '',
|
||||
detailName: '',
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
};
|
||||
const res = await redisBackupRedisRecords(params);
|
||||
const res = await searchBackupRecords(params);
|
||||
data.value = res.data.items || [];
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
@ -202,7 +187,7 @@ const onBackup = async () => {
|
|||
await handleBackup({ name: '', detailName: '', type: 'redis' })
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
loadBackupRecords();
|
||||
search();
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
})
|
||||
.catch(() => {
|
||||
|
@ -211,11 +196,13 @@ const onBackup = async () => {
|
|||
};
|
||||
const onRecover = async () => {
|
||||
let param = {
|
||||
fileName: currentRow.value.fileName,
|
||||
fileDir: currentRow.value.fileDir,
|
||||
type: 'redis',
|
||||
name: '',
|
||||
detailName: '',
|
||||
file: currentRow.value.fileDir + '/' + currentRow.value.fileName,
|
||||
};
|
||||
loading.value = true;
|
||||
await recoverRedis(param)
|
||||
await handleRecover(param)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
|
@ -225,22 +212,23 @@ const onRecover = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
const onBatchDelete = async (row: Database.FileRecord | null) => {
|
||||
let files: Array<string> = [];
|
||||
const onBatchDelete = async (row: Backup.RecordInfo | null) => {
|
||||
let ids: Array<number> = [];
|
||||
if (row) {
|
||||
files.push(row.fileDir + '/' + row.fileName);
|
||||
ids.push(row.id);
|
||||
} else {
|
||||
selects.value.forEach((item: Database.FileRecord) => {
|
||||
files.push(item.fileDir + '/' + item.fileName);
|
||||
selects.value.forEach((item: Backup.RecordInfo) => {
|
||||
ids.push(item.id);
|
||||
});
|
||||
}
|
||||
await useDeleteData(BatchDeleteFile, { isDir: false, paths: files }, 'commons.msg.delete');
|
||||
loadBackupRecords();
|
||||
await useDeleteData(deleteBackupRecord, { ids: ids }, 'commons.msg.delete');
|
||||
search();
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.recover'),
|
||||
click: (row: Database.FileRecord) => {
|
||||
click: (row: Backup.RecordInfo) => {
|
||||
currentRow.value = row;
|
||||
let params = {
|
||||
header: i18n.global.t('commons.button.recover'),
|
||||
|
@ -252,7 +240,7 @@ const buttons = [
|
|||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: (row: Database.FileRecord) => {
|
||||
click: (row: Backup.RecordInfo) => {
|
||||
onBatchDelete(row);
|
||||
},
|
||||
},
|
||||
|
@ -302,7 +290,7 @@ const onSave = async (formEl: FormInstance | undefined, type: string) => {
|
|||
|
||||
const loadform = async () => {
|
||||
form.saves = [];
|
||||
const res = await RedisPersistenceConf();
|
||||
const res = await redisPersistenceConf();
|
||||
form.appendonly = res.data?.appendonly;
|
||||
form.appendfsync = res.data?.appendfsync;
|
||||
let itemSaves = res.data?.save.split(' ');
|
||||
|
@ -315,6 +303,5 @@ const loadform = async () => {
|
|||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
onClose,
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -140,9 +140,6 @@ const acceptParams = (prop: DialogProps): void => {
|
|||
loadStatus();
|
||||
}
|
||||
};
|
||||
const onClose = (): void => {
|
||||
statusShow.value = false;
|
||||
};
|
||||
|
||||
const loadStatus = async () => {
|
||||
const res = await loadRedisStatus();
|
||||
|
@ -168,7 +165,6 @@ const loadStatus = async () => {
|
|||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
onClose,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in New Issue