diff --git a/agent/app/service/backup_postgresql.go b/agent/app/service/backup_postgresql.go index b815cfe31..43b5e5e94 100644 --- a/agent/app/service/backup_postgresql.go +++ b/agent/app/service/backup_postgresql.go @@ -82,7 +82,7 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID u } } - itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName, secret) } + itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName, secret, backupTask) } if parentTask != nil { return itemHandler() } @@ -189,17 +189,19 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol return nil } -func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName, secret string) error { +func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName, secret string, task *task.Task) error { cli, err := LoadPostgresqlClientByFrom(db.Database) if err != nil { return err } defer cli.Close() backupInfo := pgclient.BackupInfo{ + Database: db.Database, Name: db.Name, TargetDir: targetDir, FileName: fileName, + Task: task, Timeout: 300, } if err := cli.Backup(backupInfo); err != nil { diff --git a/agent/app/service/cronjob_backup.go b/agent/app/service/cronjob_backup.go index e232df29b..e5a0c312f 100644 --- a/agent/app/service/cronjob_backup.go +++ b/agent/app/service/cronjob_backup.go @@ -178,7 +178,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti } } } else { - if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName, cronjob.Secret); err != nil { + if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName, cronjob.Secret, taskItem); err != nil { if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr { retry++ return err diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 73742a6c1..6134ab316 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -197,6 +197,8 @@ ErrDatabaseIsExist: 'The current database already exists, please re-enter' ErrExecTimeOut: 'SQL execution timed out, please check the database' ErrRemoteExist: 'The remote database already exists with this name, please modify it and try again' ErrLocalExist: 'The name already exists in the local database, please modify it and try again' +RemoteBackup: "To back up the remote database, the local container database service needs to be started first using the image {{ .name }}, please wait..." +RemoteRecover: "To restore the remote database, the local container database service needs to be started first using the image {{ .name }}, please wait..." #redis ErrTypeOfRedis: 'The recovery file type does not match the current persistence method, please modify it and try again' diff --git a/agent/i18n/lang/es-ES.yaml b/agent/i18n/lang/es-ES.yaml index 65254f445..5fc5e3bb0 100644 --- a/agent/i18n/lang/es-ES.yaml +++ b/agent/i18n/lang/es-ES.yaml @@ -196,6 +196,8 @@ ErrDatabaseIsExist: 'La base de datos actual ya existe, intente con otro nombre' ErrExecTimeOut: 'Tiempo de espera en la ejecución SQL, revise la base de datos' ErrRemoteExist: 'La base de datos remota ya existe con ese nombre, modifíquelo e intente de nuevo' ErrLocalExist: 'El nombre ya existe en la base de datos local, modifíquelo e intente de nuevo' +RemoteBackup: "Para hacer una copia de seguridad de la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere por favor..." +RemoteRecover: "Para restaurar la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere por favor..." #redis ErrTypeOfRedis: 'El tipo de archivo de recuperación no coincide con el método de persistencia actual, modifíquelo e intente' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index 0af00753a..13c93c797 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -196,6 +196,8 @@ ErrDatabaseIsExist: '現在のデータベースは既に存在します。再 ErrExecTimeOut: 'SQL 実行がタイムアウトしました。データベースを確認してください' ErrRemoteExist: 'この名前のリモート データベースは既に存在します。変更してもう一度お試しください' ErrLocalExist: '名前はローカル データベースに既に存在します。変更してもう一度お試しください' +RemoteBackup: "リモートデータベースをバックアップするには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください..." +RemoteRecover: "リモートデータベースを復元するには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください..." #redis ErrTypeOfRedis: 'リカバリ ファイルの種類が現在の永続化方法と一致しません。変更して再試行してください' diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index 77d595749..fbeb78d51 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -197,6 +197,8 @@ ErrDatabaseIsExist: '현재 데이터베이스가 이미 존재합니다. 다시 ErrExecTimeOut: 'SQL 실행 시간이 초과되었습니다. 데이터베이스를 확인하십시오.' ErrRemoteExist: '이 이름을 가진 원격 데이터베이스가 이미 존재합니다. 수정하고 다시 시도하세요' ErrLocalExist: '이름이 로컬 데이터베이스에 이미 존재합니다. 이름을 수정하고 다시 시도하세요' +RemoteBackup: "원격 데이터베이스를 백업하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요..." +RemoteRecover: "원격 데이터베이스를 복원하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요..." #레디스 ErrTypeOfRedis: '복구 파일 유형이 현재 지속성 방법과 일치하지 않습니다. 수정하고 다시 시도하세요' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index a98ff6657..130f57b0a 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -197,6 +197,8 @@ ErrDatabaseIsExist: 'Pangkalan data semasa sudah wujud, sila masukkan semula' ErrExecTimeOut: 'Pelaksanaan SQL tamat masa, sila semak pangkalan data' ErrRemoteExist: 'Pangkalan data jauh sudah wujud dengan nama ini, sila ubah suainya dan cuba lagi' ErrLocalExist: 'Nama sudah wujud dalam pangkalan data tempatan, sila ubah suai dan cuba lagi' +RemoteBackup: "Untuk menyandarkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu..." +RemoteRecover: "Untuk memulihkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu..." #redis ErrTypeOfRedis: 'Jenis fail pemulihan tidak sepadan dengan kaedah kegigihan semasa, sila ubah suai dan cuba lagi' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 040683869..9ed3477b2 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -197,6 +197,8 @@ ErrDatabaseIsExist: 'O banco de dados atual já existe, digite novamente' ErrExecTimeOut: 'Tempo limite de execução do SQL expirou, verifique o banco de dados' ErrRemoteExist: 'O banco de dados remoto já existe com este nome, modifique-o e tente novamente' ErrLocalExist: 'O nome já existe no banco de dados local, modifique-o e tente novamente' +RemoteBackup: "Para fazer backup do banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }}, por favor aguarde..." +RemoteRecover: "Para restaurar o banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }}, por favor aguarde..." #redis ErrTypeOfRedis: 'O tipo de arquivo de recuperação não corresponde ao método de persistência atual, modifique-o e tente novamente' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index a0f408d3b..160f72be8 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -197,6 +197,8 @@ ErrDatabaseIsExist: 'Текущая база данных уже существ ErrExecTimeOut: 'Время выполнения SQL истекло, проверьте базу данных' ErrRemoteExist: 'Удаленная база данных с таким именем уже существует. Измените его и повторите попытку' ErrLocalExist: 'Имя уже существует в локальной базе данных, измените его и повторите попытку' +RemoteBackup: "Для резервного копирования удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите..." +RemoteRecover: "Для восстановления удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите..." #редис ErrTypeOfRedis: 'Тип файла восстановления не соответствует текущему методу сохранения. Измените его и повторите попытку' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index 5d0425fe3..8f110a8b1 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -198,6 +198,8 @@ ErrDatabaseIsExist: 'Mevcut veritabanı zaten mevcut, lütfen yeniden girin' ErrExecTimeOut: 'SQL yürütme zaman aşımı, lütfen veritabanını kontrol edin' ErrRemoteExist: 'Uzak veritabanında bu adla zaten mevcut, lütfen değiştirin ve tekrar deneyin' ErrLocalExist: 'Yerel veritabanında bu ad zaten mevcut, lütfen değiştirin ve tekrar deneyin' +RemoteBackup: "Uzak veritabanını yedeklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin..." +RemoteRecover: "Uzak veritabanını geri yüklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin..." #redis ErrTypeOfRedis: 'Kurtarma dosyası türü mevcut kalıcılık yöntemiyle eşleşmiyor, lütfen değiştirin ve tekrar deneyin' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 790692125..fcf5536ed 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -196,6 +196,8 @@ ErrDatabaseIsExist: '目前資料庫已存在,請重新輸入' ErrExecTimeOut: 'SQL 執行逾時,請檢查資料庫' ErrRemoteExist: '遠端資料庫已存在該名稱,請修改後重試' ErrLocalExist: '本機資料庫已存在該名稱,請修改後重試' +RemoteBackup: "備份遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候..." +RemoteRecover: "恢復遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候..." #redis ErrTypeOfRedis: '復原檔案類型與目前持久化方式不符,請修改後重試' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index c26d62096..6ca42badc 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -197,6 +197,8 @@ ErrDatabaseIsExist: "当前数据库已存在,请重新输入" ErrExecTimeOut: "SQL 执行超时,请检查数据库" ErrRemoteExist: "远程数据库已存在该名称,请修改后重试" ErrLocalExist: "本地数据库已存在该名称,请修改后重试" +RemoteBackup: "备份远程数据库需要先使用镜像 {{ .name }} 启动本地容器数据库服务,请稍候..." +RemoteRecover: "恢复远程数据库需要先使用镜像 {{ .name }} 启动本地容器数据库服务,请稍候..." #redis ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试" diff --git a/agent/utils/postgresql/client/info.go b/agent/utils/postgresql/client/info.go index 5db3a4344..b51b7b0a4 100644 --- a/agent/utils/postgresql/client/info.go +++ b/agent/utils/postgresql/client/info.go @@ -1,6 +1,7 @@ package client import ( + "github.com/1Panel-dev/1Panel/agent/app/task" _ "github.com/jackc/pgx/v5/stdlib" ) @@ -49,19 +50,23 @@ type PasswordChangeInfo struct { } type BackupInfo struct { + Database string `json:"database"` Name string `json:"name"` TargetDir string `json:"targetDir"` FileName string `json:"fileName"` - Timeout uint `json:"timeout"` // second + Task *task.Task `json:"-"` + Timeout uint `json:"timeout"` // second } type RecoverInfo struct { + Database string `json:"database"` Name string `json:"name"` SourceFile string `json:"sourceFile"` Username string `json:"username"` - Timeout uint `json:"timeout"` // second + Task *task.Task `json:"-"` + Timeout uint `json:"timeout"` // second } type SyncDBInfo struct { diff --git a/agent/utils/postgresql/client/remote.go b/agent/utils/postgresql/client/remote.go index 6e7c64fd7..f74fd971f 100644 --- a/agent/utils/postgresql/client/remote.go +++ b/agent/utils/postgresql/client/remote.go @@ -8,12 +8,12 @@ import ( "io" "os" "os/exec" - "sort" "strings" "time" "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/i18n" "github.com/docker/docker/api/types/image" "github.com/pkg/errors" @@ -119,10 +119,11 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error { } func (r *Remote) Backup(info BackupInfo) error { - imageTag, err := loadImageTag() + imageTag, err := loadImageTag(info.Database) if err != nil { return err } + info.Task.Log(i18n.GetWithName("RemoteBackup", imageTag)) fileOp := files.NewFileOp() if !fileOp.Stat(info.TargetDir) { if err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil { @@ -144,7 +145,7 @@ func (r *Remote) Backup(info BackupInfo) error { _, _ = handle.Read(b) if string(b) != string(n) { errBytes, _ := os.ReadFile(fileNameItem) - return fmt.Errorf("backup failed,err:%s", string(errBytes)) + return fmt.Errorf("backup failed, err: %s", string(errBytes)) } gzipCmd := exec.Command("gzip", fileNameItem) @@ -156,10 +157,11 @@ func (r *Remote) Backup(info BackupInfo) error { } func (r *Remote) Recover(info RecoverInfo) error { - imageTag, err := loadImageTag() + imageTag, err := loadImageTag(info.Database) if err != nil { return err } + info.Task.Log(i18n.GetWithName("RemoteRecover", imageTag)) fileName := info.SourceFile if strings.HasSuffix(info.SourceFile, ".sql.gz") { fileName = strings.TrimSuffix(info.SourceFile, ".gz") @@ -244,69 +246,24 @@ func (r *Remote) ExecSQL(command string, timeout uint) error { return nil } -func loadImageTag() (string, error) { - var ( - app model.App - appDetails []model.AppDetail - versions []string - ) - if err := global.DB.Where("key = ?", "postgresql").First(&app).Error; err != nil { - versions = []string{"postgres:16.1-alpine", "postgres:16.0-alpine"} - } else { - if err := global.DB.Where("app_id = ?", app.ID).Find(&appDetails).Error; err != nil { - versions = []string{"postgres:16.1-alpine", "postgres:16.0-alpine"} - } else { - for _, item := range appDetails { - versions = append(versions, "postgres:"+item.Version) - } - } +func loadImageTag(database string) (string, error) { + var db model.Database + if err := global.DB.Model(&model.Database{}).Where("name = ?", database).First(&db).Error; err != nil { + return "", fmt.Errorf("load database %s info failed, err: %v", database, err) } client, err := docker.NewDockerClient() if err != nil { - return "", err + return "", fmt.Errorf("create docker client failed, err: %v", err) } defer client.Close() - images, err := client.ImageList(context.Background(), image.ListOptions{}) - if err != nil { - return "", err - } - - itemTag := "" - for _, item := range versions { - for _, image := range images { - for _, tag := range image.RepoTags { - if tag == item { - itemTag = tag - break - } - } - if len(itemTag) != 0 { - break + images, _ := client.ImageList(context.Background(), image.ListOptions{}) + for _, image := range images { + for _, tag := range image.RepoTags { + if strings.HasPrefix(tag, "postgres:"+strings.TrimSuffix(db.Version, "x")) { + return tag, nil } } - if len(itemTag) != 0 { - break - } } - if len(itemTag) != 0 { - return itemTag, nil - } - - sort.Strings(versions) - if len(versions) != 0 { - itemTag = versions[len(versions)-1] - } - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) - defer cancel() - if _, err := client.ImagePull(ctx, itemTag, image.PullOptions{}); err != nil { - if errors.Is(ctx.Err(), context.DeadlineExceeded) { - return itemTag, buserr.WithName("ErrPgImagePull", itemTag) - } - global.LOG.Errorf("image %s pull failed, err: %v", itemTag, err) - return itemTag, fmt.Errorf("image %s pull failed, err: %v", itemTag, err) - } - - return itemTag, nil + return "postgres:" + strings.ReplaceAll(db.Version, ".x", "-alpine"), nil } diff --git a/frontend/src/views/database/postgresql/remote/index.vue b/frontend/src/views/database/postgresql/remote/index.vue index d05d60869..cbec9a385 100644 --- a/frontend/src/views/database/postgresql/remote/index.vue +++ b/frontend/src/views/database/postgresql/remote/index.vue @@ -121,7 +121,7 @@ const onOpenDialog = async ( rowData: Partial = { name: '', type: 'postgresql', - version: '17.x', + version: '18.x', address: '', port: 5432, username: '', diff --git a/frontend/src/views/database/postgresql/remote/operate/index.vue b/frontend/src/views/database/postgresql/remote/operate/index.vue index 5ff32d6ab..fc1e9dc2c 100644 --- a/frontend/src/views/database/postgresql/remote/operate/index.vue +++ b/frontend/src/views/database/postgresql/remote/operate/index.vue @@ -13,6 +13,7 @@ +