diff --git a/backend/constant/errs.go b/backend/constant/errs.go index f648a0a50..3b72fb9b7 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -116,6 +116,7 @@ var ( ErrInUsed = "ErrInUsed" ErrObjectInUsed = "ErrObjectInUsed" ErrPortRules = "ErrPortRules" + ErrPgImagePull = "ErrPgImagePull" ) // runtime diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index c78b99c07..f446eaa70 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -125,6 +125,7 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m ErrInUsed: "{{ .detail }} is in use and cannot be deleted" ErrObjectInUsed: "This object is in use and cannot be deleted" ErrPortRules: "The number of ports does not match, please re-enter!" +ErrPgImagePull: "Image pull timeout. Please configure image acceleration or manually pull the postgres:16.0-alpine image and try again" #runtime ErrDirNotFound: "The build folder does not exist! Please check file integrity!" diff --git a/backend/i18n/lang/zh-Hant.yaml b/backend/i18n/lang/zh-Hant.yaml index eb48c00fe..b69b26ce4 100644 --- a/backend/i18n/lang/zh-Hant.yaml +++ b/backend/i18n/lang/zh-Hant.yaml @@ -126,6 +126,7 @@ ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後 ErrInUsed: "{{ .detail }} 正被使用,無法刪除" ErrObjectInUsed: "該對象正被使用,無法刪除" ErrPortRules: "端口數目不匹配,請重新輸入!" +ErrPgImagePull: "鏡像拉取超時,請配置鏡像加速或手動拉取 postgres:16.0-alpine 鏡像後重試" #runtime ErrDirNotFound: "build 文件夾不存在!請檢查文件完整性!" diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index 68945966b..6583f3c2a 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -125,6 +125,7 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后 ErrInUsed: "{{ .detail }} 正被使用,无法删除" ErrObjectInUsed: "该对象正被使用,无法删除" ErrPortRules: "端口数目不匹配,请重新输入!" +ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 postgres:16.0-alpine 镜像后重试" #runtime ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!" diff --git a/backend/utils/postgresql/client/remote.go b/backend/utils/postgresql/client/remote.go index 798520c47..77626d69a 100644 --- a/backend/utils/postgresql/client/remote.go +++ b/backend/utils/postgresql/client/remote.go @@ -122,6 +122,10 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error { } func (r *Remote) Backup(info BackupInfo) error { + imageTag, err := loadImageTag() + if err != nil { + return err + } fileOp := files.NewFileOp() if !fileOp.Stat(info.TargetDir) { if err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil { @@ -129,7 +133,6 @@ func (r *Remote) Backup(info BackupInfo) error { } } fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz") - imageTag := loadImageTag() backupCommand := exec.Command("bash", "-c", fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=%s pg_dump -h %s -p %d --no-owner -Fc -U %s %s' > %s", imageTag, r.Password, r.Address, r.Port, r.User, info.Name, fileNameItem)) @@ -156,6 +159,10 @@ func (r *Remote) Backup(info BackupInfo) error { } func (r *Remote) Recover(info RecoverInfo) error { + imageTag, err := loadImageTag() + if err != nil { + return err + } fileName := info.SourceFile if strings.HasSuffix(info.SourceFile, ".sql.gz") { fileName = strings.TrimSuffix(info.SourceFile, ".gz") @@ -169,7 +176,6 @@ func (r *Remote) Recover(info RecoverInfo) error { _, _ = gzipCmd.CombinedOutput() }() } - imageTag := loadImageTag() recoverCommand := exec.Command("bash", "-c", fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=%s pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -U %s -d %s --role=%s' < %s", imageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName)) @@ -241,36 +247,64 @@ func (r *Remote) ExecSQL(command string, timeout uint) error { return nil } -func loadImageTag() string { +func loadImageTag() (string, error) { var ( app model.App appDetails []model.AppDetail - itemTag = "postgres:16.1-alpine" + versions []string ) if err := global.DB.Where("key = ?", "postgresql").First(&app).Error; err != nil { - return itemTag - } - if err := global.DB.Where("app_id = ?", app.ID).Find(&appDetails).Error; err != nil { - return itemTag + 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) + } + } } client, err := docker.NewDockerClient() if err != nil { - return itemTag + return "", err } images, err := client.ImageList(context.Background(), types.ImageListOptions{}) if err != nil { - return itemTag + return "", err } - for _, item := range appDetails { + itemTag := "" + for _, item := range versions { for _, image := range images { for _, tag := range image.RepoTags { - if tag == "postgres:"+item.Version { - return tag + if tag == item { + itemTag = tag + break } } + if len(itemTag) != 0 { + break + } + } + if len(itemTag) != 0 { + break } } - return itemTag + if len(itemTag) != 0 { + return itemTag, nil + } + + itemTag = "postgres:16.1-alpine" + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + if _, err := client.ImagePull(ctx, itemTag, types.ImagePullOptions{}); err != nil { + if ctx.Err() == context.DeadlineExceeded { + return itemTag, buserr.New(constant.ErrPgImagePull) + } + 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 } diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 311b51812..e9e1081ae 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -159,7 +159,7 @@ const message = { commonName: 'Support English, Chinese, numbers, .-, and _ length 1-128', userName: 'Support English, Chinese, numbers and _ length 3-30', simpleName: 'Supports non-underscore starting, English, numbers, _, length 1-30', - dbName: 'Support English, Chinese, numbers, .-, and _ length 1-64', + dbName: 'Supports non-special character starting, including English, Chinese, numbers, .-_, with a length of 1-64', imageName: 'Support English, numbers, :/.-_, length 1-150', volumeName: 'Support English, numbers, .-_, length 2-30', complexityPassword: diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 8a5cb74b0..997482e66 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -160,7 +160,7 @@ const message = { commonName: '支持英文、中文、數字、.-和_,長度1-128', userName: '支持英文、中文、數字和_,長度3-30', simpleName: '支持非底線開頭,英文、數字、_,長度1-30', - dbName: '支持英文、中文、數字、.-_,長度1-64', + dbName: '支持非特殊字符開頭,英文、中文、數字、.-_,長度1-64', imageName: '支持英文、數字、:/.-_,長度1-150', volumeName: '支持英文、數字、.-和_,長度2-30', complexityPassword: '請輸入長度為 8-30 位,並包含字母、數字、至少兩種特殊字符的密碼組合', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 170a7f0a6..5c86c5e8a 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -160,7 +160,7 @@ const message = { commonName: '支持英文、中文、数字、.-和_,长度1-128', userName: '支持英文、中文、数字和_,长度3-30', simpleName: '支持非下划线开头,英文、数字、_,长度3-30', - dbName: '支持英文、中文、数字、.-_,长度1-64', + dbName: '支持非特殊字符开头,英文、中文、数字、.-_,长度1-64', imageName: '支持英文、数字、:/.-_,长度1-150', volumeName: '支持英文、数字、.-和_,长度2-30', complexityPassword: '请输入长度为 8-30 位且包含字母、数字、特殊字符至少两项的密码组合', diff --git a/frontend/src/views/database/postgresql/index.vue b/frontend/src/views/database/postgresql/index.vue index cf2007cb3..bfe6cc1d6 100644 --- a/frontend/src/views/database/postgresql/index.vue +++ b/frontend/src/views/database/postgresql/index.vue @@ -507,6 +507,9 @@ const onChangePassword = async (row: Database.PostgresqlDBInfo) => { const buttons = [ { label: i18n.global.t('database.changePassword'), + disabled: (row: Database.PostgresqlDBInfo) => { + return !row.username; + }, click: (row: Database.PostgresqlDBInfo) => { onChangePassword(row); }, diff --git a/frontend/src/views/database/postgresql/password/index.vue b/frontend/src/views/database/postgresql/password/index.vue index a585aa66f..580869d2c 100644 --- a/frontend/src/views/database/postgresql/password/index.vue +++ b/frontend/src/views/database/postgresql/password/index.vue @@ -4,12 +4,12 @@ - +
- - + + { title.value = i18n.global.t('database.changePassword'); - changeForm.id = params.id; changeForm.from = params.from; changeForm.type = params.type; changeForm.database = params.database; changeForm.postgresqlName = params.postgresqlName; - changeForm.userName = params.username; + changeForm.username = params.username; changeForm.password = params.password; changeForm.operation = params.operation; changeForm.value = params.value;