mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 16:13:59 +08:00
* feat: Optimize application upgrade logic * feat: Optimize application upgrade logic * feat: Optimize application upgrade logic
617 lines
20 KiB
Go
617 lines
20 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
|
"github.com/subosito/gotenv"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
|
"github.com/1Panel-dev/1Panel/agent/i18n"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"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) AppBackup(req dto.CommonBackup) (*model.BackupRecord, error) {
|
|
app, err := appRepo.GetFirst(appRepo.WithKey(req.Name))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
install, err := appInstallRepo.GetFirst(repo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
|
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
|
|
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
|
|
|
fileName := req.FileName
|
|
if req.FileName == "" {
|
|
fileName = fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
|
|
}
|
|
|
|
record := &model.BackupRecord{
|
|
Type: "app",
|
|
Name: req.Name,
|
|
DetailName: req.DetailName,
|
|
SourceAccountIDs: "1",
|
|
DownloadAccountID: 1,
|
|
FileDir: itemDir,
|
|
FileName: fileName,
|
|
TaskID: req.TaskID,
|
|
Status: constant.StatusWaiting,
|
|
Description: req.Description,
|
|
}
|
|
if err := backupRepo.CreateRecord(record); err != nil {
|
|
global.LOG.Errorf("save backup record failed, err: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
if !req.IsImmediate {
|
|
if err = handleAppBackup(&install, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
|
|
markBackupFailed(record.ID, err)
|
|
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
|
|
return nil, err
|
|
}
|
|
return record, nil
|
|
}
|
|
|
|
backupTask, err := task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeBackup, req.TaskID, install.ID)
|
|
if err != nil {
|
|
markBackupFailed(record.ID, err)
|
|
record.Status = constant.StatusFailed
|
|
record.Message = err.Error()
|
|
return nil, err
|
|
}
|
|
if err = doAppBackup(&install, backupTask, backupDir, fileName, "", req.Secret); err != nil {
|
|
markBackupFailed(record.ID, err)
|
|
record.Status = constant.StatusFailed
|
|
record.Message = err.Error()
|
|
return nil, err
|
|
}
|
|
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
|
record.Status = constant.StatusSuccess
|
|
return record, nil
|
|
}
|
|
|
|
func backupAppWithParentTask(install *model.AppInstall, parentTask *task.Task, fileName string) (*model.BackupRecord, error) {
|
|
itemDir := fmt.Sprintf("app/%s/%s", install.App.Key, install.Name)
|
|
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
|
record := &model.BackupRecord{
|
|
Type: "app",
|
|
Name: install.App.Key,
|
|
DetailName: install.Name,
|
|
SourceAccountIDs: "1",
|
|
DownloadAccountID: 1,
|
|
FileDir: itemDir,
|
|
FileName: fileName,
|
|
TaskID: parentTask.TaskID,
|
|
Status: constant.StatusWaiting,
|
|
}
|
|
if err := backupRepo.CreateRecord(record); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := handleAppBackup(install, parentTask, record.ID, backupDir, fileName, "", "", parentTask.TaskID); err != nil {
|
|
markBackupFailed(record.ID, err)
|
|
record.Status = constant.StatusFailed
|
|
record.Message = err.Error()
|
|
return record, err
|
|
}
|
|
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusSuccess})
|
|
record.Status = constant.StatusSuccess
|
|
return record, nil
|
|
}
|
|
|
|
func (u *BackupService) AppRecover(req dto.CommonRecover) error {
|
|
app, err := appRepo.GetFirst(appRepo.WithKey(req.Name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
install, err := appInstallRepo.GetFirst(repo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fileOp := files.NewFileOp()
|
|
if !fileOp.Stat(req.File) {
|
|
return buserr.WithName("ErrFileNotFound", req.File)
|
|
}
|
|
if _, err := compose.Down(install.GetComposePath()); err != nil {
|
|
return err
|
|
}
|
|
if err := handleAppRecover(&install, nil, req.File, false, req.Secret, req.TaskID); err != nil {
|
|
global.LOG.Errorf("recover app %s failed, err: %v", req.DetailName, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name string, databaseID uint) error {
|
|
switch resourceKey {
|
|
case constant.AppMysql, constant.AppMariaDB:
|
|
db, err := mysqlRepo.Get(repo.WithByID(databaseID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
|
databaseHelper := DatabaseHelper{Database: db.MysqlName, DBType: resourceKey, Name: db.Name}
|
|
if err := handleMysqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), "", ""); err != nil {
|
|
return err
|
|
}
|
|
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
|
case constant.AppPostgresql:
|
|
db, err := postgresqlRepo.Get(repo.WithByID(databaseID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
|
|
databaseHelper := DatabaseHelper{Database: db.PostgresqlName, DBType: resourceKey, Name: db.Name}
|
|
if err := handlePostgresqlBackup(databaseHelper, parentTask, 0, 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, recordID uint, 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.TaskScopeBackup, taskID, install.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
itemHandler := func() error { return doAppBackup(install, backupTask, backupDir, fileName, excludes, secret) }
|
|
if parentTask != nil {
|
|
return itemHandler()
|
|
}
|
|
|
|
backupTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
|
|
go func() {
|
|
if err := backupTask.Execute(); err != nil {
|
|
markBackupFailed(recordID, err)
|
|
return
|
|
}
|
|
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {
|
|
var (
|
|
err error
|
|
recoverTask *task.Task
|
|
isOk = false
|
|
rollbackFile string
|
|
)
|
|
recoverTask = parentTask
|
|
if parentTask == nil {
|
|
recoverTask, err = task.NewTaskWithOps(install.Name, task.TaskRecover, task.TaskScopeBackup, taskID, install.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
recoverApp := func(t *task.Task) error {
|
|
fileOp := files.NewFileOp()
|
|
if !isRollback {
|
|
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
|
|
if err := handleAppBackup(install, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
|
|
t.Log(fmt.Sprintf("backup app %s for rollback before recover failed, err: %v", install.Name, err))
|
|
}
|
|
}
|
|
|
|
if err := fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {
|
|
return err
|
|
}
|
|
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
|
if err := fileOp.TarGzExtractPro(tmpPath+"/app.tar.gz", tmpPath, ""); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if isRollback {
|
|
_, _ = compose.UpWithoutBuild(install.GetComposePath())
|
|
} else {
|
|
_, _ = compose.Up(install.GetComposePath())
|
|
}
|
|
_ = os.RemoveAll(strings.ReplaceAll(recoverFile, ".tar.gz", ""))
|
|
}()
|
|
|
|
if !fileOp.Stat(tmpPath+"/app.json") || !fileOp.Stat(tmpPath+"/app.tar.gz") {
|
|
return errors.New(i18n.GetMsgByKey("AppBackupFileIncomplete"))
|
|
}
|
|
var backupInstall model.AppInstall
|
|
appJson, err := os.ReadFile(tmpPath + "/app.json")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := json.Unmarshal(appJson, &backupInstall); err != nil {
|
|
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
|
}
|
|
if backupInstall.App.Key != install.App.Key || backupInstall.Name != install.Name {
|
|
return errors.New(i18n.GetMsgByKey("AppAttributesNotMatch"))
|
|
}
|
|
backupEnvMap, err := getEnvMapByPath(path.Join(tmpPath, install.Name, ".env"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
installedEnvMap, err := getEnvMapByPath(install.GetEnvPath())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var mergedEnvContent string
|
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
|
for _, resource := range resources {
|
|
var database model.Database
|
|
switch resource.From {
|
|
case constant.AppResourceRemote:
|
|
database, err = databaseRepo.Get(repo.WithByID(resource.LinkId))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case constant.AppResourceLocal:
|
|
resourceApp, err := appInstallRepo.GetFirst(repo.WithByID(resource.LinkId))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
database, err = databaseRepo.Get(databaseRepo.WithAppInstallID(resourceApp.ID), repo.WithByType(resource.Key), repo.WithByFrom(constant.AppResourceLocal), repo.WithByName(resourceApp.Name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
switch database.Type {
|
|
case constant.AppPostgresql:
|
|
db, err := postgresqlRepo.Get(repo.WithByID(resource.ResourceId))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newDB, err := reCreatePostgresqlDB(db.ID, database, backupEnvMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
backupInstall.Env, mergedEnvContent, err = buildRecoverEnv(backupInstall.Env, backupEnvMap, installedEnvMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, repo.WithByID(resource.ID))
|
|
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
|
|
t.LogStart(taskName)
|
|
if err := handlePostgresqlRecover(dto.CommonRecover{
|
|
Name: newDB.PostgresqlName,
|
|
DetailName: newDB.Name,
|
|
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
|
}, parentTask, true); err != nil {
|
|
t.LogFailedWithErr(taskName, err)
|
|
return err
|
|
}
|
|
t.LogSuccess(taskName)
|
|
case constant.AppMysql, constant.AppMariaDB:
|
|
db, err := mysqlRepo.Get(repo.WithByID(resource.ResourceId))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newDB, err := reCreateDB(db.ID, database, backupEnvMap, install.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
backupInstall.Env, mergedEnvContent, err = buildRecoverEnv(backupInstall.Env, backupEnvMap, installedEnvMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, repo.WithByID(resource.ID))
|
|
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
|
|
t.LogStart(taskName)
|
|
if err := handleMysqlRecover(dto.CommonRecover{
|
|
Name: newDB.MysqlName,
|
|
DetailName: newDB.Name,
|
|
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
|
}, parentTask, true); err != nil {
|
|
t.LogFailedWithErr(taskName, err)
|
|
return err
|
|
}
|
|
t.LogSuccess(taskName)
|
|
}
|
|
}
|
|
|
|
appDir := install.GetPath()
|
|
backPath := fmt.Sprintf("%s_bak", appDir)
|
|
_ = fileOp.Rename(appDir, backPath)
|
|
_ = fileOp.CreateDir(appDir, constant.DirPerm)
|
|
|
|
deCompressName := i18n.GetWithName("DeCompressFile", "app.tar.gz")
|
|
t.LogStart(deCompressName)
|
|
if err := fileOp.TarGzExtractPro(tmpPath+"/app.tar.gz", install.GetAppPath(), ""); err != nil {
|
|
t.LogFailedWithErr(deCompressName, err)
|
|
_ = fileOp.DeleteDir(appDir)
|
|
_ = fileOp.Rename(backPath, appDir)
|
|
return err
|
|
}
|
|
t.LogSuccess(deCompressName)
|
|
_ = fileOp.DeleteDir(backPath)
|
|
|
|
if len(mergedEnvContent) != 0 {
|
|
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
|
|
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
_, _ = file.WriteString(mergedEnvContent)
|
|
}
|
|
|
|
backupInstall.ID = install.ID
|
|
backupInstall.Status = constant.StatusRunning
|
|
backupInstall.AppId = install.AppId
|
|
backupInstall.AppDetailId = install.AppDetailId
|
|
backupInstall.App.ID = install.AppId
|
|
if err := appInstallRepo.Save(context.Background(), &backupInstall); err != nil {
|
|
global.LOG.Errorf("save db app install failed, err: %v", err)
|
|
return err
|
|
}
|
|
isOk = true
|
|
|
|
return nil
|
|
}
|
|
|
|
if parentTask != nil {
|
|
return recoverApp(parentTask)
|
|
}
|
|
|
|
rollBackApp := func(t *task.Task) {
|
|
if isRollback {
|
|
return
|
|
}
|
|
if !isOk {
|
|
t.Log(i18n.GetMsgByKey("RecoverFailedStartRollBack"))
|
|
if err := handleAppRecover(install, t, rollbackFile, true, "", ""); err != nil {
|
|
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
|
|
return
|
|
}
|
|
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
|
|
_ = os.RemoveAll(rollbackFile)
|
|
} else {
|
|
_ = os.RemoveAll(rollbackFile)
|
|
}
|
|
}
|
|
recoverTask.AddSubTask(task.GetTaskName(install.Name, task.TaskRecover, task.TaskScopeBackup), recoverApp, rollBackApp)
|
|
go func() {
|
|
_ = recoverTask.Execute()
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, 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(install)
|
|
remarkInfoPath := fmt.Sprintf("%s/app.json", tmpDir)
|
|
if err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
appPath := install.GetPath()
|
|
parentTask.LogStart(i18n.GetMsgByKey("TaskBackup"))
|
|
if err := fileOp.TarGzCompressPro(true, appPath, path.Join(tmpDir, "app.tar.gz"), "", excludes); err != nil {
|
|
return err
|
|
}
|
|
|
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
|
for _, resource := range resources {
|
|
if err := backupDatabaseWithTask(parentTask, resource.Key, tmpDir, install.Name, resource.ResourceId); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
parentTask.LogStart(i18n.GetMsgByKey("CompressDir"))
|
|
if err := fileOp.TarGzCompressPro(true, tmpDir, path.Join(backupDir, fileName), secret, ""); err != nil {
|
|
return err
|
|
}
|
|
parentTask.Log(i18n.GetWithName("CompressFileSuccess", fileName))
|
|
return nil
|
|
}
|
|
|
|
func reCreateDB(dbID uint, database model.Database, envMap map[string]interface{}, appInstallName string) (*model.DatabaseMysql, error) {
|
|
mysqlService := NewIMysqlService()
|
|
ctx := context.Background()
|
|
if err := deleteMysqlDatabaseForResourceOwner(
|
|
ctx,
|
|
dto.MysqlDBDelete{ID: dbID, Database: database.Name, Type: database.Type, DeleteBackup: false, ForceDelete: true},
|
|
dto.DBResource{Type: constant.TypeApp, Name: appInstallName},
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dbInfo := getDBCreateInfoFromEnv(envMap, "utf8mb4")
|
|
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
|
|
Name: dbInfo.Name,
|
|
From: database.From,
|
|
Database: database.Name,
|
|
Format: dbInfo.Format,
|
|
Permission: "%",
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(dbInfo.User) != 0 {
|
|
if err := ensureMysqlDBUser(mysqlService, database, dbInfo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
updateCronjobsDBRef(dbID, createDB.ID)
|
|
return createDB, nil
|
|
}
|
|
|
|
func ensureMysqlDBUser(mysqlService IMysqlService, database model.Database, dbInfo dbRecreateInfo) error {
|
|
const host = "%"
|
|
users, err := mysqlService.ListUsers(dto.MysqlUserSearch{Database: database.Name})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var oldUser dto.MysqlUser
|
|
exists := false
|
|
for _, user := range users {
|
|
if user.Username == dbInfo.User && user.Host == host && !user.IsDelete {
|
|
oldUser = user
|
|
exists = true
|
|
break
|
|
}
|
|
}
|
|
if exists {
|
|
if len(oldUser.Password) != 0 && oldUser.Password != dbInfo.Password {
|
|
return buserr.New("ErrDbUserNotValid")
|
|
}
|
|
} else {
|
|
if err := mysqlService.CreateUser(dto.MysqlUserCreate{
|
|
Database: database.Name,
|
|
Username: dbInfo.User,
|
|
Host: host,
|
|
Password: dbInfo.Password,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return mysqlService.GrantUser(dto.MysqlGrantCreate{
|
|
Database: database.Name,
|
|
DB: dbInfo.Name,
|
|
Username: dbInfo.User,
|
|
Host: host,
|
|
})
|
|
}
|
|
|
|
func reCreatePostgresqlDB(dbID uint, database model.Database, envMap map[string]interface{}) (*model.DatabasePostgresql, error) {
|
|
postgresqlService := NewIPostgresqlService()
|
|
_ = postgresqlService.Delete(context.Background(), dto.PostgresqlDBDelete{
|
|
ID: dbID,
|
|
Type: database.Type,
|
|
Database: database.Name,
|
|
DeleteBackup: false,
|
|
ForceDelete: true,
|
|
})
|
|
|
|
dbInfo := getDBCreateInfoFromEnv(envMap, "UTF8")
|
|
createDB, err := postgresqlService.Create(context.Background(), dto.PostgresqlDBCreate{
|
|
Name: dbInfo.Name,
|
|
From: database.From,
|
|
Database: database.Name,
|
|
Format: dbInfo.Format,
|
|
Username: dbInfo.User,
|
|
Password: dbInfo.Password,
|
|
SuperUser: true,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
updateCronjobsDBRef(dbID, createDB.ID)
|
|
return createDB, nil
|
|
}
|
|
|
|
type dbRecreateInfo struct {
|
|
Name string
|
|
User string
|
|
Password string
|
|
Format string
|
|
}
|
|
|
|
func getDBCreateInfoFromEnv(envMap map[string]interface{}, defaultFormat string) dbRecreateInfo {
|
|
name, _ := envMap["PANEL_DB_NAME"].(string)
|
|
user, _ := envMap["PANEL_DB_USER"].(string)
|
|
password, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
|
format, _ := envMap["format"].(string)
|
|
if len(format) == 0 {
|
|
format = defaultFormat
|
|
}
|
|
return dbRecreateInfo{
|
|
Name: name,
|
|
User: user,
|
|
Password: password,
|
|
Format: format,
|
|
}
|
|
}
|
|
|
|
func updateCronjobsDBRef(oldDBID, newDBID uint) {
|
|
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", oldDBID)))
|
|
for _, job := range cronjobs {
|
|
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", newDBID)})
|
|
}
|
|
}
|
|
|
|
func buildRecoverEnv(appEnv string, backupEnvMap, installedEnvMap map[string]interface{}) (string, string, error) {
|
|
currentHostVal, hasCurrentHost := installedEnvMap["PANEL_DB_HOST"]
|
|
if hasCurrentHost && fmt.Sprintf("%v", currentHostVal) != "" {
|
|
backupHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", backupEnvMap["PANEL_DB_HOST"])
|
|
currentHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", currentHostVal)
|
|
appEnv = strings.ReplaceAll(appEnv, backupHost, currentHost)
|
|
if _, ok := backupEnvMap["CASDOOR_DATASOURCE_NAME"]; ok {
|
|
backupEnvMap["CASDOOR_DATASOURCE_NAME"] = strings.ReplaceAll(
|
|
fmt.Sprintf("%v", backupEnvMap["CASDOOR_DATASOURCE_NAME"]),
|
|
fmt.Sprintf("%v", backupEnvMap["PANEL_DB_HOST"]),
|
|
fmt.Sprintf("%v", currentHostVal),
|
|
)
|
|
}
|
|
backupEnvMap["PANEL_DB_HOST"] = currentHostVal
|
|
}
|
|
newEnvMap := make(map[string]string, len(backupEnvMap))
|
|
handleMap(backupEnvMap, newEnvMap)
|
|
mergedEnvContent, err := gotenv.Marshal(newEnvMap)
|
|
if err != nil {
|
|
return appEnv, "", err
|
|
}
|
|
return appEnv, mergedEnvContent, nil
|
|
}
|
|
|
|
func getEnvMapByPath(envPath string) (map[string]interface{}, error) {
|
|
envMap := make(map[string]interface{})
|
|
envFile, err := os.ReadFile(envPath)
|
|
if err != nil {
|
|
return envMap, err
|
|
}
|
|
lines := strings.Split(string(envFile), "\n")
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "=", 2)
|
|
if len(parts) == 2 {
|
|
value := strings.TrimSpace(parts[1])
|
|
if len(value) >= 2 {
|
|
if (value[0] == '"' && value[len(value)-1] == '"') || (value[0] == '\'' && value[len(value)-1] == '\'') {
|
|
value = value[1 : len(value)-1]
|
|
}
|
|
}
|
|
envMap[parts[0]] = value
|
|
}
|
|
}
|
|
return envMap, nil
|
|
}
|