From a031d3ba41dd33e4bdcd0613b20e122dbe6d81d0 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:19:30 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E9=80=89=E9=A1=B9=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=89=8D=E7=BC=80=20(#1820)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/database_mysql.go | 4 +-- backend/app/dto/database.go | 6 +++++ backend/app/service/backup_app.go | 2 +- backend/app/service/backup_mysql.go | 8 +++--- backend/app/service/cronjob_helper.go | 26 +++++++++---------- backend/app/service/database_mysql.go | 19 ++++++++++---- backend/utils/mysql/client/local.go | 6 ++--- backend/utils/mysql/client/remote.go | 4 +++ cmd/server/docs/docs.go | 20 +++++++++++--- cmd/server/docs/swagger.json | 16 +++++++++++- cmd/server/docs/swagger.yaml | 11 +++++++- frontend/src/api/interface/database.ts | 5 ++++ frontend/src/api/modules/database.ts | 4 +-- frontend/src/views/cronjob/operate/index.vue | 22 ++++++++++++---- .../src/views/database/mysql/conn/index.vue | 1 + 15 files changed, 114 insertions(+), 40 deletions(-) diff --git a/backend/app/api/v1/database_mysql.go b/backend/app/api/v1/database_mysql.go index ff7a8379d..e082645d6 100644 --- a/backend/app/api/v1/database_mysql.go +++ b/backend/app/api/v1/database_mysql.go @@ -216,11 +216,11 @@ func (b *BaseApi) SearchMysql(c *gin.Context) { // @Description 获取 mysql 数据库列表 // @Accept json // @Param request body dto.PageInfo true "request" -// @Success 200 {array} string +// @Success 200 {array} dto.MysqlOption // @Security ApiKeyAuth // @Router /databases/options [get] func (b *BaseApi) ListDBName(c *gin.Context) { - list, err := mysqlService.ListDBName() + list, err := mysqlService.ListDBOption() if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return diff --git a/backend/app/dto/database.go b/backend/app/dto/database.go index f0de793f0..01a51f1be 100644 --- a/backend/app/dto/database.go +++ b/backend/app/dto/database.go @@ -24,6 +24,12 @@ type MysqlDBInfo struct { Description string `json:"description"` } +type MysqlOption struct { + ID uint `json:"id"` + Name string `json:"name"` + From string `json:"from"` +} + type MysqlDBCreate struct { Name string `json:"name" validate:"required"` From string `json:"from" validate:"required"` diff --git a/backend/app/service/backup_app.go b/backend/app/service/backup_app.go index f0da8abd8..debdaa8ea 100644 --- a/backend/app/service/backup_app.go +++ b/backend/app/service/backup_app.go @@ -110,7 +110,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro if err != nil { return err } - if err := handleMysqlBackup(db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil { + if err := handleMysqlBackup(db.MysqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil { return err } } diff --git a/backend/app/service/backup_mysql.go b/backend/app/service/backup_mysql.go index aa9c6eb7a..86270fd7a 100644 --- a/backend/app/service/backup_mysql.go +++ b/backend/app/service/backup_mysql.go @@ -26,7 +26,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error { targetDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", req.Name, req.DetailName)) fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow) - if err := handleMysqlBackup(req.DetailName, targetDir, fileName); err != nil { + if err := handleMysqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil { return err } @@ -97,8 +97,8 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error { return nil } -func handleMysqlBackup(dbName, targetDir, fileName string) error { - dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName)) +func handleMysqlBackup(name, dbName, targetDir, fileName string) error { + dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName), mysqlRepo.WithByMysqlName(name)) if err != nil { return err } @@ -127,7 +127,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error { if !fileOp.Stat(req.File) { return errors.New(fmt.Sprintf("%s file is not exist", req.File)) } - dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName)) + dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name)) if err != nil { return err } diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go index 1c9c67573..568977a7e 100644 --- a/backend/app/service/cronjob_helper.go +++ b/backend/app/service/cronjob_helper.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path" + "strconv" "strings" "sync" "time" @@ -254,15 +255,18 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back return paths, err } - var dblist []string + var dbs []model.DatabaseMysql if cronjob.DBName == "all" { - mysqlService := NewIMysqlService() - dblist, err = mysqlService.ListDBName() + dbs, err = mysqlRepo.List() if err != nil { return paths, err } } else { - dblist = append(dblist, cronjob.DBName) + itemID, _ := (strconv.Atoi(cronjob.DBName)) + dbs, err = mysqlRepo.List(commonRepo.WithByID(uint(itemID))) + if err != nil { + return paths, err + } } var client cloud_storage.CloudStorageClient @@ -273,25 +277,21 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back } } - for _, dbName := range dblist { + for _, dbInfo := range dbs { var record model.BackupRecord record.Type = "mysql" record.Source = "LOCAL" record.BackupType = backup.Type - dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName)) - if err != nil { - return paths, err - } record.Name = dbInfo.MysqlName - backupDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", record.Name, dbName)) - record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbName, startTime.Format("20060102150405")) - if err = handleMysqlBackup(dbName, backupDir, record.FileName); err != nil { + backupDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", record.Name, dbInfo.Name)) + record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405")) + if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil { return paths, err } - record.DetailName = dbName + record.DetailName = dbInfo.Name record.FileDir = backupDir itemFileDir := strings.TrimPrefix(backupDir, localDir+"/") if !cronjob.KeepLocal && backup.Type != "LOCAL" { diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index 76ed08f0a..3735c0d32 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -32,7 +32,7 @@ type MysqlService struct{} type IMysqlService interface { SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error) - ListDBName() ([]string, error) + ListDBOption() ([]dto.MysqlOption, error) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) LoadFromRemote(from string) error ChangeAccess(info dto.ChangeDBInfo) error @@ -71,13 +71,17 @@ func (u *MysqlService) SearchWithPage(search dto.MysqlDBSearch) (int64, interfac return total, dtoMysqls, err } -func (u *MysqlService) ListDBName() ([]string, error) { +func (u *MysqlService) ListDBOption() ([]dto.MysqlOption, error) { mysqls, err := mysqlRepo.List() - var dbNames []string + var dbs []dto.MysqlOption for _, mysql := range mysqls { - dbNames = append(dbNames, mysql.Name) + var item dto.MysqlOption + if err := copier.Copy(&item, &mysql); err != nil { + return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + dbs = append(dbs, item) } - return dbNames, err + return dbs, err } func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) { @@ -85,6 +89,11 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode return nil, buserr.New(constant.ErrCmdIllegal) } + mysql, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name), remoteDBRepo.WithByFrom(req.From)) + if mysql.ID != 0 { + return nil, constant.ErrRecordExist + } + var createItem model.DatabaseMysql if err := copier.Copy(&createItem, &req); err != nil { return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) diff --git a/backend/utils/mysql/client/local.go b/backend/utils/mysql/client/local.go index 28d5b574f..dbf08075d 100644 --- a/backend/utils/mysql/client/local.go +++ b/backend/utils/mysql/client/local.go @@ -60,6 +60,9 @@ func (r *Local) CreateUser(info CreateInfo) error { for _, user := range userlist { if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil { + if strings.Contains(err.Error(), "ERROR 1396") { + return buserr.New(constant.ErrUserIsExist) + } _ = r.Delete(DeleteInfo{ Name: info.Name, Version: info.Version, @@ -67,9 +70,6 @@ func (r *Local) CreateUser(info CreateInfo) error { Permission: info.Permission, ForceDelete: true, Timeout: 300}) - if strings.Contains(err.Error(), "ERROR 1396") { - return buserr.New(constant.ErrUserIsExist) - } return err } grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", info.Name, user) diff --git a/backend/utils/mysql/client/remote.go b/backend/utils/mysql/client/remote.go index 4307fd0e4..c4479ac51 100644 --- a/backend/utils/mysql/client/remote.go +++ b/backend/utils/mysql/client/remote.go @@ -242,6 +242,10 @@ func (r *Remote) Recover(info RecoverInfo) error { if err != nil { return fmt.Errorf("gunzip file %s failed, stdout: %v, err: %v", info.SourceFile, string(stdout), err) } + defer func() { + gzipCmd := exec.Command("gzip", fileName) + _, _ = gzipCmd.CombinedOutput() + }() } dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F") f, err := os.Open(fileName) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 380c8e48f..56de16255 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1,5 +1,5 @@ -// Package docs GENERATED BY SWAG; DO NOT EDIT -// This file was generated by swaggo/swag +// Code generated by swaggo/swag. DO NOT EDIT. + package docs import "github.com/swaggo/swag" @@ -4056,7 +4056,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/dto.MysqlOption" } } } @@ -13147,6 +13147,20 @@ const docTemplate = `{ } } }, + "dto.MysqlOption": { + "type": "object", + "properties": { + "from": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, "dto.MysqlStatus": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 9a2d70f37..4b4f1a0eb 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -4049,7 +4049,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/dto.MysqlOption" } } } @@ -13140,6 +13140,20 @@ } } }, + "dto.MysqlOption": { + "type": "object", + "properties": { + "from": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, "dto.MysqlStatus": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index a33b5e003..d8092371b 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -1149,6 +1149,15 @@ definitions: - page - pageSize type: object + dto.MysqlOption: + properties: + from: + type: string + id: + type: integer + name: + type: string + type: object dto.MysqlStatus: properties: Aborted_clients: @@ -6414,7 +6423,7 @@ paths: description: OK schema: items: - type: string + $ref: '#/definitions/dto.MysqlOption' type: array security: - ApiKeyAuth: [] diff --git a/frontend/src/api/interface/database.ts b/frontend/src/api/interface/database.ts index 10764cb30..531064588 100644 --- a/frontend/src/api/interface/database.ts +++ b/frontend/src/api/interface/database.ts @@ -111,6 +111,11 @@ export namespace Database { File: string; Position: number; } + export interface MysqlOption { + id: number; + name: string; + from: string; + } export interface ChangeInfo { id: number; value: string; diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 6070968d5..30b2a6cf7 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -59,8 +59,8 @@ export const loadMysqlStatus = () => { export const loadRemoteAccess = () => { return http.get(`/databases/remote`); }; -export const loadDBNames = () => { - return http.get>(`/databases/options`); +export const loadDBOptions = () => { + return http.get>(`/databases/options`); }; // redis diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue index 5f9d3bc21..837ffa2ba 100644 --- a/frontend/src/views/cronjob/operate/index.vue +++ b/frontend/src/views/cronjob/operate/index.vue @@ -122,7 +122,18 @@ - +
+ + +
@@ -219,12 +230,13 @@ import i18n from '@/lang'; import { ElForm } from 'element-plus'; import { Cronjob } from '@/api/interface/cronjob'; import { addCronjob, editCronjob } from '@/api/modules/cronjob'; -import { loadDBNames } from '@/api/modules/database'; +import { loadDBOptions } from '@/api/modules/database'; import { GetWebsiteOptions } from '@/api/modules/website'; import DrawerHeader from '@/components/drawer-header/index.vue'; import { MsgError, MsgSuccess } from '@/utils/message'; import { useRouter } from 'vue-router'; import { listContainer } from '@/api/modules/container'; +import { Database } from '@/api/interface/database'; const router = useRouter(); interface DialogProps { @@ -275,7 +287,7 @@ const mysqlInfo = reactive({ isExist: false, name: '', version: '', - dbNames: [] as Array, + dbs: [] as Array, }); const varifySpec = (rule: any, value: any, callback: any) => { @@ -458,8 +470,8 @@ const loadContainers = async () => { }; const checkMysqlInstalled = async () => { - const data = await loadDBNames(); - mysqlInfo.dbNames = data.data || []; + const data = await loadDBOptions(); + mysqlInfo.dbs = data.data || []; }; function isBackup() { diff --git a/frontend/src/views/database/mysql/conn/index.vue b/frontend/src/views/database/mysql/conn/index.vue index e41e193fb..c2b86010b 100644 --- a/frontend/src/views/database/mysql/conn/index.vue +++ b/frontend/src/views/database/mysql/conn/index.vue @@ -190,6 +190,7 @@ const onSave = async (formEl: FormInstance | undefined) => { const onSubmitAccess = async () => { let param = { id: 0, + from: form.from, value: form.privilege ? '%' : 'localhost', }; loading.value = true;