mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 19:19:15 +08:00
feat: 实现远程数据备份恢复功能 (#1774)
This commit is contained in:
parent
87a7cf3aca
commit
e83e592e0a
@ -15,6 +15,7 @@ type MysqlDBInfo struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
MysqlName string `json:"mysqlName"`
|
||||
Format string `json:"format"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
|
@ -57,7 +57,7 @@ func (u *RemoteDBRepo) GetList(opts ...DBOption) ([]model.RemoteDB, error) {
|
||||
|
||||
func (c *RemoteDBRepo) WithByFrom(from string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("`from` != ?", from)
|
||||
return g.Where("`from` == ?", from)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,15 +106,11 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
|
||||
resource, _ := appInstallResourceRepo.GetFirst(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
if resource.ID != 0 && resource.ResourceId != 0 {
|
||||
mysqlInfo, err := appInstallRepo.LoadBaseInfo(constant.AppMysql, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlBackup(mysqlInfo, tmpDir, db.Name, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
if err := handleMysqlBackup(db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -198,7 +194,11 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
}
|
||||
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, commonRepo.WithByID(resource.ID))
|
||||
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, newDB.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
if err := handleMysqlRecover(dto.CommonRecover{
|
||||
Name: newDB.MysqlName,
|
||||
DetailName: newDB.Name,
|
||||
File: tmpPath + "/" + fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
||||
}, true); err != nil {
|
||||
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -12,9 +10,9 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -23,24 +21,22 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
targetDir := fmt.Sprintf("%s/database/mysql/%s/%s", localDir, req.Name, req.DetailName)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
|
||||
|
||||
if err := handleMysqlBackup(req.DetailName, targetDir, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeNow := time.Now().Format("20060102150405")
|
||||
backupDir := fmt.Sprintf("%s/database/mysql/%s/%s", localDir, req.Name, req.DetailName)
|
||||
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
|
||||
if err := handleMysqlBackup(app, backupDir, req.DetailName, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
Type: "mysql",
|
||||
Name: app.Name,
|
||||
Name: req.Name,
|
||||
DetailName: req.DetailName,
|
||||
Source: "LOCAL",
|
||||
BackupType: "LOCAL",
|
||||
FileDir: backupDir,
|
||||
FileDir: targetDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
@ -50,26 +46,13 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||
}
|
||||
|
||||
func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
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), false); err != nil {
|
||||
if err := handleMysqlRecover(req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file := req.File
|
||||
fileName := path.Base(req.File)
|
||||
if strings.HasSuffix(fileName, ".tar.gz") {
|
||||
@ -106,78 +89,92 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
|
||||
}()
|
||||
}
|
||||
|
||||
if err := handleMysqlRecover(app, path.Dir(file), req.DetailName, fileName, false); err != nil {
|
||||
req.File = path.Dir(file) + "/" + fileName
|
||||
if err := handleMysqlRecover(req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("recover from uploads successful!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlBackup(app *repo.RootInfo, backupDir, dbName, fileName string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(backupDir) {
|
||||
if err := os.MkdirAll(backupDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
|
||||
}
|
||||
func handleMysqlBackup(dbName, targetDir, fileName string) error {
|
||||
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, _, err := LoadMysqlClientByFrom(dbInfo.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outfile, _ := os.OpenFile(backupDir+"/"+fileName, os.O_RDWR|os.O_CREATE, 0755)
|
||||
global.LOG.Infof("start to mysqldump | gzip > %s.gzip", backupDir+"/"+fileName)
|
||||
cmd := exec.Command("docker", "exec", app.ContainerName, "mysqldump", "-uroot", "-p"+app.Password, dbName)
|
||||
gzipCmd := exec.Command("gzip", "-cf")
|
||||
gzipCmd.Stdin, _ = cmd.StdoutPipe()
|
||||
gzipCmd.Stdout = outfile
|
||||
_ = gzipCmd.Start()
|
||||
_ = cmd.Run()
|
||||
_ = gzipCmd.Wait()
|
||||
|
||||
backupInfo := client.BackupInfo{
|
||||
Name: dbName,
|
||||
Format: dbInfo.Format,
|
||||
TargetDir: targetDir,
|
||||
FileName: fileName,
|
||||
|
||||
Timeout: 300,
|
||||
}
|
||||
if err := cli.Backup(backupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMysqlRecover(mysqlInfo *repo.RootInfo, recoverDir, dbName, fileName string, isRollback bool) error {
|
||||
func handleMysqlRecover(req dto.CommonRecover, 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 {
|
||||
return fmt.Errorf("backup mysql db %s for rollback before recover failed, err: %v", mysqlInfo.Name, err)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
|
||||
}
|
||||
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, _, err := LoadMysqlClientByFrom(dbInfo.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isRollback {
|
||||
rollbackFile := fmt.Sprintf("%s/original/database/%s_%s.sql.gz", global.CONF.System.BaseDir, req.DetailName, time.Now().Format("20060102150405"))
|
||||
if err := cli.Backup(client.BackupInfo{
|
||||
Name: req.DetailName,
|
||||
Format: dbInfo.Format,
|
||||
TargetDir: path.Dir(rollbackFile),
|
||||
FileName: path.Base(rollbackFile),
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("backup mysql db %s for rollback before recover failed, err: %v", req.DetailName, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
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)
|
||||
return
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
Format: dbInfo.Format,
|
||||
SourceFile: rollbackFile,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
}
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", dbName, rollbackFile)
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
file := recoverDir + "/" + fileName
|
||||
fi, _ := os.Open(file)
|
||||
defer fi.Close()
|
||||
cmd := exec.Command("docker", "exec", "-i", mysqlInfo.ContainerName, "mysql", "-uroot", "-p"+mysqlInfo.Password, dbName)
|
||||
if strings.HasSuffix(fileName, ".gz") {
|
||||
gzipFile, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gzipFile.Close()
|
||||
gzipReader, err := gzip.NewReader(gzipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gzipReader.Close()
|
||||
cmd.Stdin = gzipReader
|
||||
} else {
|
||||
cmd.Stdin = fi
|
||||
if err := cli.Recover(client.RecoverInfo{
|
||||
Name: req.DetailName,
|
||||
Format: dbInfo.Format,
|
||||
SourceFile: req.File,
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
return errors.New(stdStr)
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
@ -118,11 +117,7 @@ func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Tim
|
||||
|
||||
switch cronjob.Type {
|
||||
case "database":
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
paths, err := u.handleDatabase(*cronjob, app, backup, startTime)
|
||||
paths, err := u.handleDatabase(*cronjob, backup, startTime)
|
||||
return strings.Join(paths, ","), err
|
||||
case "website":
|
||||
paths, err := u.handleWebsite(*cronjob, backup, startTime)
|
||||
@ -252,7 +247,7 @@ func handleUnTar(sourceFile, targetDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInfo, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
if err != nil {
|
||||
@ -282,15 +277,20 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
||||
var record model.BackupRecord
|
||||
|
||||
record.Type = "mysql"
|
||||
record.Name = app.Name
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = backup.Type
|
||||
|
||||
backupDir := fmt.Sprintf("%s/database/mysql/%s/%s", localDir, app.Name, dbName)
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbName, startTime.Format("20060102150405"))
|
||||
if err = handleMysqlBackup(app, backupDir, dbName, record.FileName); err != nil {
|
||||
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName))
|
||||
if err != nil {
|
||||
return paths, err
|
||||
}
|
||||
record.Name = dbInfo.MysqlName
|
||||
backupDir := fmt.Sprintf("%s/database/mysql/%s/%s", localDir, record.Name, dbName)
|
||||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbName, startTime.Format("20060102150405"))
|
||||
if err = handleMysqlBackup(dbName, backupDir, record.FileName); err != nil {
|
||||
return paths, err
|
||||
}
|
||||
|
||||
record.DetailName = dbName
|
||||
record.FileDir = backupDir
|
||||
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
|
||||
|
@ -93,7 +93,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
return nil, errors.New("Cannot set 127.0.0.1 as address")
|
||||
}
|
||||
|
||||
cli, version, err := loadClientByFrom(req.From)
|
||||
cli, version, err := LoadMysqlClientByFrom(req.From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -105,11 +105,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
}
|
||||
createItem.MysqlName = app.Name
|
||||
} else {
|
||||
mysqlData, err := remoteDBRepo.Get(remoteDBRepo.WithByFrom(req.From))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
createItem.MysqlName = mysqlData.Name
|
||||
createItem.MysqlName = req.From
|
||||
}
|
||||
defer cli.Close()
|
||||
if err := cli.Create(client.CreateInfo{
|
||||
@ -162,7 +158,7 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
cli, version, err := loadClientByFrom(db.From)
|
||||
cli, version, err := LoadMysqlClientByFrom(db.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -207,7 +203,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(info.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
cli, version, err := loadClientByFrom(info.From)
|
||||
cli, version, err := LoadMysqlClientByFrom(info.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -236,22 +232,31 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
|
||||
if info.ID != 0 {
|
||||
// appRess, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysqlData.ID))
|
||||
// for _, appRes := range appRess {
|
||||
// appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
var appRess []model.AppInstallResource
|
||||
if info.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysqlData.ID))
|
||||
} else {
|
||||
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(mysqlData.ID))
|
||||
}
|
||||
for _, appRes := range appRess {
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
// if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, info.Value); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, info.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
global.LOG.Info("excute password change sql successful")
|
||||
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"password": info.Value})
|
||||
return nil
|
||||
@ -270,7 +275,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(info.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
cli, version, err := loadClientByFrom(info.From)
|
||||
cli, version, err := LoadMysqlClientByFrom(info.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -535,15 +540,17 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{
|
||||
return newFiles
|
||||
}
|
||||
|
||||
func loadClientByFrom(from string) (mysql.MysqlClient, string, error) {
|
||||
func LoadMysqlClientByFrom(from string) (mysql.MysqlClient, string, error) {
|
||||
var (
|
||||
dbInfo client.DBInfo
|
||||
version string
|
||||
err error
|
||||
)
|
||||
|
||||
dbInfo.From = from
|
||||
dbInfo.Timeout = 300
|
||||
if from != "local" {
|
||||
databaseItem, err := remoteDBRepo.Get(remoteDBRepo.WithByFrom(from))
|
||||
databaseItem, err := remoteDBRepo.Get(commonRepo.WithByName(from))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ type MysqlClient interface {
|
||||
ChangePassword(info client.PasswordChangeInfo) error
|
||||
ChangeAccess(info client.AccessChangeInfo) error
|
||||
|
||||
Backup(info client.BackupInfo) (string, error)
|
||||
Backup(info client.BackupInfo) error
|
||||
Recover(info client.RecoverInfo) error
|
||||
|
||||
Close()
|
||||
|
@ -56,6 +56,7 @@ type BackupInfo struct {
|
||||
Name string `json:"name"`
|
||||
Format string `json:"format"`
|
||||
TargetDir string `json:"targetDir"`
|
||||
FileName string `json:"fileName"`
|
||||
|
||||
Timeout uint `json:"timeout"` // second
|
||||
}
|
||||
|
@ -205,16 +205,15 @@ func (r *Local) ChangeAccess(info AccessChangeInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Local) Backup(info BackupInfo) (string, error) {
|
||||
func (r *Local) Backup(info BackupInfo) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(info.TargetDir) {
|
||||
if err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
|
||||
}
|
||||
}
|
||||
fileName := fmt.Sprintf("%s/%s_%s.sql.gz", info.TargetDir, info.Name, time.Now().Format("20060102150405"))
|
||||
outfile, _ := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0755)
|
||||
global.LOG.Infof("start to mysqldump | gzip > %s.gzip", info.TargetDir+"/"+fileName)
|
||||
outfile, _ := os.OpenFile(info.FileName, os.O_RDWR|os.O_CREATE, 0755)
|
||||
global.LOG.Infof("start to mysqldump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName)
|
||||
cmd := exec.Command("docker", "exec", r.ContainerName, "mysqldump", "-uroot", "-p"+r.Password, info.Name)
|
||||
gzipCmd := exec.Command("gzip", "-cf")
|
||||
gzipCmd.Stdin, _ = cmd.StdoutPipe()
|
||||
@ -222,7 +221,7 @@ func (r *Local) Backup(info BackupInfo) (string, error) {
|
||||
_ = gzipCmd.Start()
|
||||
_ = cmd.Run()
|
||||
_ = gzipCmd.Wait()
|
||||
return fileName, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Local) Recover(info RecoverInfo) error {
|
||||
|
@ -208,26 +208,26 @@ func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Remote) Backup(info BackupInfo) (string, error) {
|
||||
func (r *Remote) Backup(info BackupInfo) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(info.TargetDir) {
|
||||
if err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
|
||||
}
|
||||
}
|
||||
fileName := fmt.Sprintf("%s/%s_%s.sql", info.TargetDir, info.Name, time.Now().Format("20060102150405"))
|
||||
fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz")
|
||||
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F")
|
||||
|
||||
f, _ := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0755)
|
||||
f, _ := os.OpenFile(fileNameItem, os.O_RDWR|os.O_CREATE, 0755)
|
||||
defer f.Close()
|
||||
if err := mysqldump.Dump(dns, mysqldump.WithData(), mysqldump.WithWriter(f)); err != nil {
|
||||
return "", err
|
||||
if err := mysqldump.Dump(dns, mysqldump.WithData(), mysqldump.WithDropTable(), mysqldump.WithWriter(f)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fileOp.Compress([]string{fileName}, info.TargetDir, path.Base(fileName)+".gz", files.Gz); err != nil {
|
||||
return "", err
|
||||
if err := fileOp.Compress([]string{fileNameItem}, info.TargetDir, info.FileName, files.Gz); err != nil {
|
||||
return err
|
||||
}
|
||||
return fileName, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Remote) Recover(info RecoverInfo) error {
|
||||
@ -235,13 +235,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
|
||||
fileName := info.SourceFile
|
||||
if strings.HasSuffix(info.SourceFile, ".sql.gz") {
|
||||
fileName = strings.TrimSuffix(info.SourceFile, ".gz")
|
||||
if err := fileOp.Decompress(info.SourceFile, fileName, files.Gz); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(info.SourceFile, ".tar.gz") {
|
||||
fileName = strings.TrimSuffix(info.SourceFile, ".tar.gz")
|
||||
if err := fileOp.Decompress(info.SourceFile, fileName, files.TarGz); err != nil {
|
||||
if err := fileOp.Decompress(info.SourceFile, path.Dir(fileName), files.Gz); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -251,7 +245,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := mysqldump.Source(dns, f); err != nil {
|
||||
if err := mysqldump.Source(dns, f, mysqldump.WithMergeInsert(1000)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -17,6 +17,7 @@ export namespace Database {
|
||||
id: number;
|
||||
createdAt: Date;
|
||||
name: string;
|
||||
mysqlName: string;
|
||||
from: string;
|
||||
format: string;
|
||||
username: string;
|
||||
|
@ -215,7 +215,6 @@ import { ElForm } from 'element-plus';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { addCronjob, editCronjob } from '@/api/modules/cronjob';
|
||||
import { loadDBNames } from '@/api/modules/database';
|
||||
import { CheckAppInstalled } from '@/api/modules/app';
|
||||
import { GetWebsiteOptions } from '@/api/modules/website';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
@ -454,14 +453,8 @@ const loadContainers = async () => {
|
||||
};
|
||||
|
||||
const checkMysqlInstalled = async () => {
|
||||
const res = await CheckAppInstalled('mysql');
|
||||
mysqlInfo.isExist = res.data.isExist;
|
||||
mysqlInfo.name = res.data.name;
|
||||
mysqlInfo.version = res.data.version;
|
||||
if (mysqlInfo.isExist) {
|
||||
const data = await loadDBNames();
|
||||
mysqlInfo.dbNames = data.data;
|
||||
}
|
||||
const data = await loadDBNames();
|
||||
mysqlInfo.dbNames = data.data;
|
||||
};
|
||||
|
||||
function isBackup() {
|
||||
|
@ -21,7 +21,7 @@
|
||||
<el-option
|
||||
v-for="(item, index) in dbOptions"
|
||||
:key="index"
|
||||
:value="item.address"
|
||||
:value="item.name"
|
||||
:label="item.name"
|
||||
></el-option>
|
||||
</el-option-group>
|
||||
@ -265,14 +265,6 @@ const onOpenDialog = async () => {
|
||||
};
|
||||
|
||||
const dialogBackupRef = ref();
|
||||
const onOpenBackupDialog = async (dbName: string) => {
|
||||
let params = {
|
||||
type: 'mysql',
|
||||
name: mysqlName.value,
|
||||
detailName: dbName,
|
||||
};
|
||||
dialogBackupRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
const uploadRef = ref();
|
||||
|
||||
@ -422,22 +414,21 @@ const buttons = [
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('database.backupList'),
|
||||
disabled: (row: Database.MysqlDBInfo) => {
|
||||
return row.from !== 'local';
|
||||
},
|
||||
click: (row: Database.MysqlDBInfo) => {
|
||||
onOpenBackupDialog(row.name);
|
||||
let params = {
|
||||
type: 'mysql',
|
||||
name: row.mysqlName,
|
||||
detailName: row.name,
|
||||
};
|
||||
dialogBackupRef.value!.acceptParams(params);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('database.loadBackup'),
|
||||
disabled: (row: Database.MysqlDBInfo) => {
|
||||
return row.from !== 'local';
|
||||
},
|
||||
click: (row: Database.MysqlDBInfo) => {
|
||||
let params = {
|
||||
type: 'mysql',
|
||||
name: mysqlName.value,
|
||||
name: mysqlName.value || row.name,
|
||||
detailName: row.name,
|
||||
};
|
||||
uploadRef.value!.acceptParams(params);
|
||||
|
Loading…
Reference in New Issue
Block a user