mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 网站备份/恢复增加任务日志 (#6603)
This commit is contained in:
parent
4f1bdf5338
commit
ed75ecc63b
@ -9,6 +9,7 @@ type CommonBackup struct {
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
Secret string `json:"secret"`
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||
@ -17,6 +18,8 @@ type CommonRecover struct {
|
||||
DetailName string `json:"detailName"`
|
||||
File string `json:"file"`
|
||||
Secret string `json:"secret"`
|
||||
TaskID string `json:"taskID"`
|
||||
BackupRecordID uint `json:"backupRecordID"`
|
||||
}
|
||||
|
||||
type RecordSearch struct {
|
||||
|
@ -72,3 +72,13 @@ func (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {
|
||||
return g.Where("cronjob_id = ?", cronjobID)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *BackupRepo) GetRecord(opts ...DBOption) (*model.BackupRecord, error) {
|
||||
var record *model.BackupRecord
|
||||
db := global.DB.Model(&model.BackupRecord{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(record).Error
|
||||
return record, err
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
@ -36,7 +38,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
|
||||
backupDir := path.Join(global.CONF.System.Backup, itemDir)
|
||||
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&install, backupDir, fileName, "", req.Secret); err != nil {
|
||||
if err := handleAppBackup(&install, nil, backupDir, fileName, "", req.Secret, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -80,57 +82,88 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAppBackup(install *model.AppInstall, backupDir, fileName string, excludes string, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
|
||||
func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name string, databaseID uint) error {
|
||||
switch resourceKey {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(databaseID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
remarkInfo, _ := json.Marshal(install)
|
||||
remarkInfoPath := fmt.Sprintf("%s/app.json", tmpDir)
|
||||
if err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appPath := install.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
for _, resource := range resources {
|
||||
switch resource.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB:
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlBackup(db.MysqlName, resource.Key, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
case constant.AppPostgresql:
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handlePostgresqlBackup(db.PostgresqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
if err := handleMysqlBackup(db.MysqlName, resourceKey, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
case constant.AppPostgresql:
|
||||
db, err := postgresqlRepo.Get(commonRepo.WithByID(databaseID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
if err := handlePostgresqlBackup(db.PostgresqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", name)); err != nil {
|
||||
return err
|
||||
}
|
||||
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret, taskID string) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
)
|
||||
backupTask = parentTask
|
||||
if parentTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeApp, taskID, install.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
backupApp := func(t *task.Task) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
remarkInfo, _ := json.Marshal(install)
|
||||
remarkInfoPath := fmt.Sprintf("%s/app.json", tmpDir)
|
||||
if err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appPath := install.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
for _, resource := range resources {
|
||||
if err = backupDatabaseWithTask(t, resource.Key, tmpDir, install.Name, resource.ResourceId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.LogStart(i18n.GetMsgByKey("CompressDir"))
|
||||
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Log(i18n.GetWithName("CompressFileSuccess", fileName))
|
||||
return nil
|
||||
}
|
||||
backupTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backupApp, nil)
|
||||
if parentTask != nil {
|
||||
return backupApp(parentTask)
|
||||
}
|
||||
return backupTask.Execute()
|
||||
}
|
||||
|
||||
func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback bool, secret string) error {
|
||||
isOk := false
|
||||
fileOp := files.NewFileOp()
|
||||
@ -160,7 +193,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleAppBackup(install, path.Dir(rollbackFile), path.Base(rollbackFile), "", ""); err != nil {
|
||||
if err := handleAppBackup(install, nil, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", ""); err != nil {
|
||||
return fmt.Errorf("backup app %s for rollback before recover failed, err: %v", install.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
|
@ -3,6 +3,11 @@ package service
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/pkg/errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
@ -14,11 +19,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
@ -31,214 +33,234 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||
itemDir := fmt.Sprintf("website/%s", req.Name)
|
||||
backupDir := path.Join(global.CONF.System.Backup, itemDir)
|
||||
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&website, backupDir, fileName, "", req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := &model.BackupRecord{
|
||||
Type: "website",
|
||||
Name: website.PrimaryDomain,
|
||||
DetailName: req.DetailName,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err = handleWebsiteBackup(&website, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("backup website %s failed, err: %v", website.Alias, err)
|
||||
return
|
||||
}
|
||||
record := &model.BackupRecord{
|
||||
Type: "website",
|
||||
Name: website.PrimaryDomain,
|
||||
DetailName: req.DetailName,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
}
|
||||
if err = backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(req.File) {
|
||||
return buserr.WithName("ErrFileNotFound", req.File)
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
|
||||
if err := handleWebsiteRecover(&website, req.File, false, req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := handleWebsiteRecover(&website, req.File, false, req.Secret, req.TaskID); err != nil {
|
||||
global.LOG.Errorf("recover website %s failed, err: %v", website.Alias, err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tmpPath)
|
||||
}()
|
||||
|
||||
var oldWebsite model.Website
|
||||
websiteJson, err := os.ReadFile(tmpPath + "/website.json")
|
||||
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool, secret, taskID string) error {
|
||||
recoverTask, err := task.NewTaskWithOps(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite, taskID, website.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(websiteJson, &oldWebsite); err != nil {
|
||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
||||
}
|
||||
|
||||
if err := checkValidOfWebsite(&oldWebsite, website); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
temPathWithName := tmpPath + "/" + website.Alias
|
||||
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
|
||||
}
|
||||
if website.Type == constant.Deployment {
|
||||
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
|
||||
}
|
||||
}
|
||||
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile), "", ""); err != nil {
|
||||
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
|
||||
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite), func(t *task.Task) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
t.Log(i18n.GetWithName("DeCompressFile", recoverFile))
|
||||
if err = handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
global.LOG.Info("recover failed, start to rollback now")
|
||||
if err := handleWebsiteRecover(website, rollbackFile, true, ""); err != nil {
|
||||
global.LOG.Errorf("rollback website %s from %s failed, err: %v", website.Alias, rollbackFile, err)
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("rollback website %s from %s successful", website.Alias, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
_ = os.RemoveAll(tmpPath)
|
||||
}()
|
||||
}
|
||||
|
||||
nginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxConfPath := fmt.Sprintf("%s/openresty/%s/conf/conf.d", constant.AppInstallDir, nginxInfo.Name)
|
||||
if err := fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), nginxConfPath); err != nil {
|
||||
global.LOG.Errorf("handle recover from conf.d failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
var oldWebsite model.Website
|
||||
websiteJson, err := os.ReadFile(tmpPath + "/website.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
|
||||
if err = json.Unmarshal(websiteJson, &oldWebsite); err != nil {
|
||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
||||
}
|
||||
|
||||
if err = checkValidOfWebsite(&oldWebsite, website); err != nil {
|
||||
t.Log(i18n.GetWithName("ErrCheckValid", err.Error()))
|
||||
return err
|
||||
}
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, app.App.Key, app.Name)); err != nil {
|
||||
global.LOG.Errorf("docker-compose restart failed, err: %v", err)
|
||||
return err
|
||||
|
||||
temPathWithName := tmpPath + "/" + website.Alias
|
||||
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
|
||||
}
|
||||
case constant.Runtime:
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if website.Type == constant.Deployment {
|
||||
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
|
||||
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
|
||||
}
|
||||
}
|
||||
|
||||
isOk := false
|
||||
if !isRollback {
|
||||
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
|
||||
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", ""); err != nil {
|
||||
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
|
||||
}
|
||||
defer func() {
|
||||
if !isOk {
|
||||
t.LogStart(i18n.GetMsgByKey("Rollback"))
|
||||
if err := handleWebsiteRecover(website, rollbackFile, true, "", taskID); err != nil {
|
||||
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
|
||||
return
|
||||
}
|
||||
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
nginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo {
|
||||
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
if err = fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), GetSitePath(*website, SiteConfDir)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
|
||||
taskName := task.GetTaskName(app.Name, task.TaskRecover, task.TaskScopeApp)
|
||||
t.LogStart(taskName)
|
||||
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
t.LogSuccess(taskName)
|
||||
if _, err = compose.DownAndUp(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, app.App.Key, app.Name)); err != nil {
|
||||
t.LogFailedWithErr("Run", err)
|
||||
return err
|
||||
}
|
||||
case constant.Runtime:
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskName := task.GetTaskName(runtime.Name, task.TaskRecover, task.TaskScopeRuntime)
|
||||
t.LogStart(taskName)
|
||||
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
t.LogSuccess(taskName)
|
||||
}
|
||||
}
|
||||
|
||||
siteDir := fmt.Sprintf("%s/openresty/%s/www/sites", constant.AppInstallDir, nginxInfo.Name)
|
||||
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir, ""); err != nil {
|
||||
global.LOG.Errorf("handle recover from web.tar.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.Execf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("nginx -s reload failed, err: %s", stdout)
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
|
||||
oldWebsite.ID = website.ID
|
||||
if err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {
|
||||
global.LOG.Errorf("handle save website data failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
taskName := i18n.GetMsgByKey("TaskRecover") + i18n.GetMsgByKey("websiteDir")
|
||||
t.Log(taskName)
|
||||
if err = handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), GetWebSiteRootDir(), ""); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
return err
|
||||
}
|
||||
stdout, err := cmd.Execf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName)
|
||||
if err != nil {
|
||||
return errors.New(stdout)
|
||||
}
|
||||
oldWebsite.ID = website.ID
|
||||
if err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}, nil)
|
||||
return recoverTask.Execute()
|
||||
}
|
||||
|
||||
func handleWebsiteBackup(website *model.Website, backupDir, fileName string, excludes string, secret string) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
remarkInfo, _ := json.Marshal(website)
|
||||
if err := fileOp.SaveFile(tmpDir+"/website.json", string(remarkInfo), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put website.json into tmp dir successful")
|
||||
|
||||
nginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, "")
|
||||
func handleWebsiteBackup(website *model.Website, backupDir, fileName, excludes, secret, taskID string) error {
|
||||
backupTask, err := task.NewTaskWithOps(website.PrimaryDomain, task.TaskBackup, task.TaskScopeWebsite, taskID, website.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxConfFile := fmt.Sprintf("%s/openresty/%s/conf/conf.d/%s.conf", constant.AppInstallDir, nginxInfo.Name, website.Alias)
|
||||
if err := fileOp.CopyFile(nginxConfFile, tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put openresty conf into tmp dir successful")
|
||||
backupTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskBackup, task.TaskScopeWebsite), func(t *task.Task) error {
|
||||
fileOp := files.NewFileOp()
|
||||
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if err != nil {
|
||||
remarkInfo, _ := json.Marshal(website)
|
||||
if err = fileOp.SaveFile(tmpDir+"/website.json", string(remarkInfo), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleAppBackup(&app, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
nginxConfFile := GetSitePath(*website, SiteConf)
|
||||
if err = fileOp.CopyFile(nginxConfFile, tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put app.tar.gz into tmp dir successful")
|
||||
case constant.Runtime:
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo {
|
||||
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
t.Log(i18n.GetMsgByKey("BackupNginxConfig"))
|
||||
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
|
||||
t.LogStart(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
|
||||
if err = handleAppBackup(&app, backupTask, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, "", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
t.LogSuccess(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
|
||||
case constant.Runtime:
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.LogStart(task.GetTaskName(runtime.Name, task.TaskBackup, task.TaskScopeRuntime))
|
||||
if err = handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
t.LogSuccess(task.GetTaskName(runtime.Name, task.TaskBackup, task.TaskScopeRuntime))
|
||||
if website.DbID > 0 {
|
||||
if err = backupDatabaseWithTask(t, website.DbType, tmpDir, website.PrimaryDomain, website.DbID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case constant.Static:
|
||||
if website.DbID > 0 {
|
||||
if err = backupDatabaseWithTask(t, website.DbType, tmpDir, website.PrimaryDomain, website.DbID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
websiteDir := fmt.Sprintf("%s/openresty/%s/www/sites/%s", constant.AppInstallDir, nginxInfo.Name, website.Alias)
|
||||
if err := handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("put web.tar.gz into tmp dir successful, now start to tar tmp dir")
|
||||
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
websiteDir := GetSitePath(*website, SiteDir)
|
||||
t.LogStart(i18n.GetMsgByKey("CompressDir"))
|
||||
if err = handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Log(i18n.GetWithName("CompressFileSuccess", fileName))
|
||||
return nil
|
||||
}, nil)
|
||||
return backupTask.Execute()
|
||||
}
|
||||
|
||||
func checkValidOfWebsite(oldWebsite, website *model.Website) error {
|
||||
|
@ -41,7 +41,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) e
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if err := handleAppBackup(&app, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
if err := handleAppBackup(&app, nil, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
@ -74,7 +74,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s", web.PrimaryDomain))
|
||||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.PrimaryDomain, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if err := handleWebsiteBackup(&web, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
if err := handleWebsiteBackup(&web, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
|
@ -1172,11 +1172,7 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nginx, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sitePath := path.Join(nginx.SiteDir, "sites", website.Alias)
|
||||
sitePath := GetSitePath(website, SiteDir)
|
||||
res := &response.WebsiteLog{
|
||||
Content: "",
|
||||
}
|
||||
@ -1243,7 +1239,7 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||
return nil, err
|
||||
}
|
||||
case constant.DeleteLog:
|
||||
logPath := path.Join(nginx.Install.GetPath(), "www", "sites", website.Alias, "log", req.LogType)
|
||||
logPath := path.Join(sitePath, "log", req.LogType)
|
||||
if err := files.NewFileOp().WriteFile(logPath, strings.NewReader(""), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -3145,7 +3141,7 @@ func (w WebsiteService) ListDatabases() ([]response.Database, error) {
|
||||
}
|
||||
pgSqls, _ := postgresqlRepo.List()
|
||||
for _, db := range pgSqls {
|
||||
database, _ := databaseRepo.Get(commonRepo.WithByName(db.Name))
|
||||
database, _ := databaseRepo.Get(commonRepo.WithByName(db.PostgresqlName))
|
||||
if database.ID > 0 {
|
||||
res = append(res, response.Database{
|
||||
ID: db.ID,
|
||||
|
@ -1205,6 +1205,7 @@ const (
|
||||
SiteReWritePath = "SiteReWritePath"
|
||||
SiteRedirectDir = "SiteRedirectDir"
|
||||
SiteCacheDir = "SiteCacheDir"
|
||||
SiteConfDir = "SiteConfDir"
|
||||
)
|
||||
|
||||
func GetSitePath(website model.Website, confType string) string {
|
||||
@ -1231,7 +1232,8 @@ func GetSitePath(website model.Website, confType string) string {
|
||||
return path.Join(GteSiteDir(website.Alias), "rewrite", website.Alias+".conf")
|
||||
case SiteRedirectDir:
|
||||
return path.Join(GteSiteDir(website.Alias), "redirect")
|
||||
|
||||
case SiteConfDir:
|
||||
return path.Join(GetWebSiteRootDir(), "conf.d")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ func (t *Task) LogFailedWithErr(msg string, err error) {
|
||||
func (t *Task) LogSuccess(msg string) {
|
||||
t.Logger.Printf(msg + i18n.GetMsgByKey("Success"))
|
||||
}
|
||||
func (t *Task) LogSuccessf(format string, v ...any) {
|
||||
func (t *Task) LogSuccessF(format string, v ...any) {
|
||||
t.Logger.Printf(fmt.Sprintf(format, v...) + i18n.GetMsgByKey("Success"))
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "ionCube",
|
||||
"check": "ioncube_loader",
|
||||
"check": "ionCube Loader",
|
||||
"file": "ioncube_loader.so",
|
||||
"versions": ["56", "70", "71", "72", "73", "74", "81", "82"],
|
||||
"installed": false
|
||||
|
@ -251,7 +251,6 @@ TaskSync: "Sync"
|
||||
LocalApp: "Local App"
|
||||
SubTask: "Subtask"
|
||||
RuntimeExtension: "Runtime Extension"
|
||||
TaskBuild: "Build"
|
||||
|
||||
# task - snapshot
|
||||
Snapshot: "Snapshot"
|
||||
@ -300,4 +299,13 @@ ImageRenameTag: "Rename image tag"
|
||||
ImageNewTag: "New image tag {{ .name }}"
|
||||
ImaegPushRes: "Image push output: {{ .name }}"
|
||||
ComposeCreate: "Create compose"
|
||||
ComposeCreateRes: "Compose creation output: {{ .name }}"
|
||||
ComposeCreateRes: "Compose creation output: {{ .name }}"
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: "Backup website OpenResty configuration file"
|
||||
CompressFileSuccess: "Directory compression successful, compressed as {{.name}}"
|
||||
CompressDir: "Compress directory"
|
||||
DeCompressFile: "Decompress file {{ .name }}"
|
||||
ErrCheckValid: "Failed to validate backup file, {{ .name }}"
|
||||
Rollback: "Rollback"
|
||||
websiteDir: "Website directory"
|
@ -253,7 +253,6 @@ TaskSync: "同步"
|
||||
LocalApp: "本地應用"
|
||||
SubTask: "子任務"
|
||||
RuntimeExtension: "運行環境擴展"
|
||||
TaskBuild: "構建"
|
||||
|
||||
|
||||
# task - snapshot
|
||||
@ -303,4 +302,13 @@ ImageRenameTag: "修改鏡像 Tag"
|
||||
ImageNewTag: "新鏡像 Tag {{ .name }}"
|
||||
ImaegPushRes: "鏡像推送輸出:{{ .name }}"
|
||||
ComposeCreate: "創建編排"
|
||||
ComposeCreateRes: "編排創建輸出:{{ .name }}"
|
||||
ComposeCreateRes: "編排創建輸出:{{ .name }}"
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: "備份網站 OpenResty 配置檔案"
|
||||
CompressFileSuccess: "壓縮目錄成功,壓縮為 {{.name}}"
|
||||
CompressDir: "壓縮目錄"
|
||||
DeCompressFile: "解壓檔案 {{ .name }}"
|
||||
ErrCheckValid: "校驗備份檔案失敗,{{ .name }}"
|
||||
Rollback: "回滾"
|
||||
websiteDir: "網站目錄"
|
@ -329,4 +329,13 @@ ImageRenameTag: "修改镜像 Tag"
|
||||
ImageNewTag: "新镜像 Tag {{ .name }}"
|
||||
ImaegPushRes: "镜像推送输出:{{ .name }}"
|
||||
ComposeCreate: "创建编排"
|
||||
ComposeCreateRes: "编排创建输出:{{ .name }}"
|
||||
ComposeCreateRes: "编排创建输出:{{ .name }}"
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: "备份网站 OpenResty 配置文件"
|
||||
CompressFileSuccess: "压缩目录成功,压缩为 {{.name}}"
|
||||
CompressDir: "压缩目录"
|
||||
DeCompressFile: "解压文件 {{ .name }}"
|
||||
ErrCheckValid: "校验备份文件失败,{{ .name }}"
|
||||
Rollback: "回滚"
|
||||
websiteDir: "网站目录"
|
@ -70,6 +70,7 @@ export namespace Backup {
|
||||
name: string;
|
||||
detailName: string;
|
||||
secret: string;
|
||||
taskID: string;
|
||||
}
|
||||
export interface Recover {
|
||||
downloadAccountID: number;
|
||||
@ -78,5 +79,6 @@ export namespace Backup {
|
||||
detailName: string;
|
||||
file: string;
|
||||
secret: string;
|
||||
taskID: string;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ export namespace Website {
|
||||
accessLog?: boolean;
|
||||
errorLog?: boolean;
|
||||
childSites?: string[];
|
||||
dbID: number;
|
||||
dbType: string;
|
||||
}
|
||||
|
||||
export interface WebsiteDTO extends Website {
|
||||
|
@ -85,7 +85,7 @@
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">
|
||||
<el-button @click="handleBackupClose" :disabled="loading">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">
|
||||
@ -96,11 +96,12 @@
|
||||
</el-dialog>
|
||||
|
||||
<OpDialog ref="opRef" @search="search" />
|
||||
<TaskLog ref="taskLogRef" @close="search" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { computeSize, dateFormat, downloadFile } from '@/utils/util';
|
||||
import { computeSize, dateFormat, downloadFile, newUUID } from '@/utils/util';
|
||||
import {
|
||||
getLocalBackupDir,
|
||||
handleBackup,
|
||||
@ -113,10 +114,12 @@ import i18n from '@/lang';
|
||||
import { Backup } from '@/api/interface/backup';
|
||||
import router from '@/routers';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
|
||||
const selects = ref<any>([]);
|
||||
const loading = ref();
|
||||
const opRef = ref();
|
||||
const taskLogRef = ref();
|
||||
|
||||
const data = ref();
|
||||
const paginationConfig = reactive({
|
||||
@ -136,6 +139,7 @@ const secret = ref();
|
||||
|
||||
const open = ref();
|
||||
const isBackup = ref();
|
||||
const recordInfo = ref();
|
||||
|
||||
interface DialogProps {
|
||||
type: string;
|
||||
@ -159,6 +163,7 @@ const handleClose = () => {
|
||||
};
|
||||
const handleBackupClose = () => {
|
||||
open.value = false;
|
||||
search();
|
||||
};
|
||||
|
||||
const loadBackupDir = async () => {
|
||||
@ -190,44 +195,60 @@ const search = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async (row?: any) => {
|
||||
if (isBackup.value) {
|
||||
let params = {
|
||||
type: type.value,
|
||||
name: name.value,
|
||||
detailName: detailName.value,
|
||||
secret: secret.value,
|
||||
};
|
||||
loading.value = true;
|
||||
await handleBackup(params)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
handleClose();
|
||||
handleBackupClose();
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
return;
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const backup = async (close: boolean) => {
|
||||
const taskID = newUUID();
|
||||
let params = {
|
||||
type: type.value,
|
||||
name: name.value,
|
||||
detailName: detailName.value,
|
||||
secret: secret.value,
|
||||
taskID: taskID,
|
||||
};
|
||||
loading.value = true;
|
||||
try {
|
||||
await handleBackup(params);
|
||||
loading.value = false;
|
||||
if (close) {
|
||||
handleClose();
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
} else {
|
||||
openTaskLog(taskID);
|
||||
}
|
||||
handleBackupClose();
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const recover = async (close: boolean, row?: any) => {
|
||||
const taskID = newUUID();
|
||||
let params = {
|
||||
downloadAccountID: row.downloadAccountID,
|
||||
type: type.value,
|
||||
name: name.value,
|
||||
detailName: detailName.value,
|
||||
file: row.file,
|
||||
file: row.fileDir + '/' + row.fileName,
|
||||
secret: secret.value,
|
||||
taskID: taskID,
|
||||
backupRecordID: row.id,
|
||||
};
|
||||
loading.value = true;
|
||||
await handleRecover(params)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
handleClose();
|
||||
handleBackupClose();
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
if (close) {
|
||||
handleClose();
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
} else {
|
||||
openTaskLog(taskID);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
@ -245,7 +266,7 @@ const onBackup = async () => {
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
},
|
||||
).then(async () => {
|
||||
onSubmit();
|
||||
backup(true);
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -263,13 +284,22 @@ const onRecover = async (row: Backup.RecordInfo) => {
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
},
|
||||
).then(async () => {
|
||||
onSubmit(row);
|
||||
recover(true, row);
|
||||
});
|
||||
return;
|
||||
}
|
||||
recordInfo.value = row;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
if (isBackup.value) {
|
||||
backup(false);
|
||||
} else {
|
||||
recover(false, recordInfo.value);
|
||||
}
|
||||
};
|
||||
|
||||
const onDownload = async (row: Backup.RecordInfo) => {
|
||||
let params = {
|
||||
downloadAccountID: row.downloadAccountID,
|
||||
|
@ -45,7 +45,7 @@
|
||||
<PHP :website="website" v-if="tabIndex == '13'"></PHP>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('logs.resource')" name="14">
|
||||
<Resource :id="id" :websiteType="website.type" v-if="tabIndex == '14'"></Resource>
|
||||
<Resource :id="id" v-if="tabIndex == '14'"></Resource>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.other')" name="12">
|
||||
<Other :id="id" v-if="tabIndex == '12'"></Other>
|
||||
|
@ -11,11 +11,16 @@
|
||||
label-position="left"
|
||||
label-width="90px"
|
||||
class="mt-5"
|
||||
v-if="websiteType === 'static' || websiteType === 'runtime'"
|
||||
v-if="website.type === 'static' || website.type === 'runtime'"
|
||||
>
|
||||
<el-form-item :label="$t('website.changeDatabase')" prop="databaseID">
|
||||
<el-select v-model="req.databaseID" class="w-full" @change="changeDatabase">
|
||||
<el-option v-for="(item, index) in databases" :key="index" :label="item.name" :value="item.id">
|
||||
<el-form-item :label="$t('website.changeDatabase')" prop="db">
|
||||
<el-select v-model="req.db" class="w-full" @change="changeDatabase">
|
||||
<el-option
|
||||
v-for="(item, index) in databases"
|
||||
:key="index"
|
||||
:label="item.name"
|
||||
:value="item.id + item.type"
|
||||
>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>{{ item.name }}</span>
|
||||
<el-tag>{{ item.type }}</el-tag>
|
||||
@ -34,7 +39,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ChangeDatabase, GetWebsiteDatabase, GetWebsiteResource } from '@/api/modules/website';
|
||||
import { ChangeDatabase, GetWebsite, GetWebsiteDatabase, GetWebsiteResource } from '@/api/modules/website';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
@ -43,18 +48,20 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
websiteType: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const data = ref([]);
|
||||
const req = reactive({
|
||||
websiteID: props.id,
|
||||
databaseID: 0,
|
||||
databaseType: '',
|
||||
db: '',
|
||||
});
|
||||
const databases = ref([]);
|
||||
const website = ref({
|
||||
type: '',
|
||||
dbID: 0,
|
||||
dbType: '',
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
try {
|
||||
@ -68,13 +75,31 @@ const listDatabases = async () => {
|
||||
const res = await GetWebsiteDatabase();
|
||||
databases.value = res.data;
|
||||
if (databases.value.length > 0) {
|
||||
req.databaseID = databases.value[0].id;
|
||||
if (website.value.dbID > 0) {
|
||||
for (let i = 0; i < databases.value.length; i++) {
|
||||
if (
|
||||
databases.value[i].id === website.value.dbID &&
|
||||
databases.value[i].type === website.value.dbType
|
||||
) {
|
||||
req.db = databases.value[i].id + databases.value[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
req.db = databases.value[0];
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const changeDatabase = () => {
|
||||
req.databaseType = databases.value.find((item) => item.id === req.databaseID)?.type;
|
||||
for (let i = 0; i < databases.value.length; i++) {
|
||||
if (databases.value[i].id + databases.value[i].type === req.db) {
|
||||
req.databaseID = databases.value[i].id;
|
||||
req.databaseType = databases.value[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
@ -85,9 +110,17 @@ const submit = async () => {
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const getwebsite = async () => {
|
||||
try {
|
||||
const res = await GetWebsite(props.id);
|
||||
website.value = res.data;
|
||||
req.db = '';
|
||||
search();
|
||||
listDatabases();
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('websiteType', props.websiteType);
|
||||
search();
|
||||
listDatabases();
|
||||
getwebsite();
|
||||
});
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user