From ed75ecc63bad35f090f4d20da4819f63da84d27f Mon Sep 17 00:00:00 2001
From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com>
Date: Fri, 27 Sep 2024 22:23:38 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BD=91=E7=AB=99=E5=A4=87=E4=BB=BD/?=
=?UTF-8?q?=E6=81=A2=E5=A4=8D=E5=A2=9E=E5=8A=A0=E4=BB=BB=E5=8A=A1=E6=97=A5?=
=?UTF-8?q?=E5=BF=97=20(#6603)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
agent/app/dto/backup.go | 3 +
agent/app/repo/backup.go | 10 +
agent/app/service/backup_app.go | 127 ++++---
agent/app/service/backup_website.go | 354 ++++++++++--------
agent/app/service/cronjob_backup.go | 4 +-
agent/app/service/website.go | 10 +-
agent/app/service/website_utils.go | 4 +-
agent/app/task/task.go | 2 +-
.../cmd/server/nginx_conf/php_extensions.json | 2 +-
agent/i18n/lang/en.yaml | 12 +-
agent/i18n/lang/zh-Hant.yaml | 12 +-
agent/i18n/lang/zh.yaml | 11 +-
frontend/src/api/interface/backup.ts | 2 +
frontend/src/api/interface/website.ts | 2 +
frontend/src/components/backup/index.vue | 88 +++--
.../website/website/config/basic/index.vue | 2 +-
.../website/config/basic/resource/index.vue | 61 ++-
17 files changed, 432 insertions(+), 274 deletions(-)
diff --git a/agent/app/dto/backup.go b/agent/app/dto/backup.go
index 15537a500..555e65fa1 100644
--- a/agent/app/dto/backup.go
+++ b/agent/app/dto/backup.go
@@ -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 {
diff --git a/agent/app/repo/backup.go b/agent/app/repo/backup.go
index 32af7dd5e..695e3a741 100644
--- a/agent/app/repo/backup.go
+++ b/agent/app/repo/backup.go
@@ -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
+}
diff --git a/agent/app/service/backup_app.go b/agent/app/service/backup_app.go
index 660a47773..983de5de1 100644
--- a/agent/app/service/backup_app.go
+++ b/agent/app/service/backup_app.go
@@ -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() {
diff --git a/agent/app/service/backup_website.go b/agent/app/service/backup_website.go
index 1ca1728e5..30e1670da 100644
--- a/agent/app/service/backup_website.go
+++ b/agent/app/service/backup_website.go
@@ -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 {
diff --git a/agent/app/service/cronjob_backup.go b/agent/app/service/cronjob_backup.go
index b00b45976..d017159db 100644
--- a/agent/app/service/cronjob_backup.go
+++ b/agent/app/service/cronjob_backup.go
@@ -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))
diff --git a/agent/app/service/website.go b/agent/app/service/website.go
index cf9061138..d3b420d95 100644
--- a/agent/app/service/website.go
+++ b/agent/app/service/website.go
@@ -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,
diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go
index 189edcfec..5710c11e2 100644
--- a/agent/app/service/website_utils.go
+++ b/agent/app/service/website_utils.go
@@ -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 ""
}
diff --git a/agent/app/task/task.go b/agent/app/task/task.go
index e17f42b52..dcbabe0cb 100644
--- a/agent/app/task/task.go
+++ b/agent/app/task/task.go
@@ -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"))
}
diff --git a/agent/cmd/server/nginx_conf/php_extensions.json b/agent/cmd/server/nginx_conf/php_extensions.json
index bf7ff0e94..5db17a28d 100644
--- a/agent/cmd/server/nginx_conf/php_extensions.json
+++ b/agent/cmd/server/nginx_conf/php_extensions.json
@@ -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
diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml
index b958dfd44..df4a7536c 100644
--- a/agent/i18n/lang/en.yaml
+++ b/agent/i18n/lang/en.yaml
@@ -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 }}"
\ No newline at end of file
+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"
\ No newline at end of file
diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml
index 224ad8dab..89bc40450 100644
--- a/agent/i18n/lang/zh-Hant.yaml
+++ b/agent/i18n/lang/zh-Hant.yaml
@@ -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 }}"
\ No newline at end of file
+ComposeCreateRes: "編排創建輸出:{{ .name }}"
+
+# task - website
+BackupNginxConfig: "備份網站 OpenResty 配置檔案"
+CompressFileSuccess: "壓縮目錄成功,壓縮為 {{.name}}"
+CompressDir: "壓縮目錄"
+DeCompressFile: "解壓檔案 {{ .name }}"
+ErrCheckValid: "校驗備份檔案失敗,{{ .name }}"
+Rollback: "回滾"
+websiteDir: "網站目錄"
\ No newline at end of file
diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml
index 2b8ed3f3f..c9eebbf06 100644
--- a/agent/i18n/lang/zh.yaml
+++ b/agent/i18n/lang/zh.yaml
@@ -329,4 +329,13 @@ ImageRenameTag: "修改镜像 Tag"
ImageNewTag: "新镜像 Tag {{ .name }}"
ImaegPushRes: "镜像推送输出:{{ .name }}"
ComposeCreate: "创建编排"
-ComposeCreateRes: "编排创建输出:{{ .name }}"
\ No newline at end of file
+ComposeCreateRes: "编排创建输出:{{ .name }}"
+
+# task - website
+BackupNginxConfig: "备份网站 OpenResty 配置文件"
+CompressFileSuccess: "压缩目录成功,压缩为 {{.name}}"
+CompressDir: "压缩目录"
+DeCompressFile: "解压文件 {{ .name }}"
+ErrCheckValid: "校验备份文件失败,{{ .name }}"
+Rollback: "回滚"
+websiteDir: "网站目录"
\ No newline at end of file
diff --git a/frontend/src/api/interface/backup.ts b/frontend/src/api/interface/backup.ts
index 487586a65..01cd406a8 100644
--- a/frontend/src/api/interface/backup.ts
+++ b/frontend/src/api/interface/backup.ts
@@ -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;
}
}
diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts
index e556a2b0c..7bdbbbd2f 100644
--- a/frontend/src/api/interface/website.ts
+++ b/frontend/src/api/interface/website.ts
@@ -24,6 +24,8 @@ export namespace Website {
accessLog?: boolean;
errorLog?: boolean;
childSites?: string[];
+ dbID: number;
+ dbType: string;
}
export interface WebsiteDTO extends Website {
diff --git a/frontend/src/components/backup/index.vue b/frontend/src/components/backup/index.vue
index 364adf704..c02cffb77 100644
--- a/frontend/src/components/backup/index.vue
+++ b/frontend/src/components/backup/index.vue
@@ -85,7 +85,7 @@