diff --git a/backend/app/api/v1/database_mysql.go b/backend/app/api/v1/database_mysql.go index 9d26b5a4a..4f455213a 100644 --- a/backend/app/api/v1/database_mysql.go +++ b/backend/app/api/v1/database_mysql.go @@ -229,6 +229,25 @@ func (b *BaseApi) ListDBName(c *gin.Context) { helper.SuccessWithData(c, list) } +// @Tags Database Mysql +// @Summary Load mysql database from remote +// @Description 从服务器获取 +// @Security ApiKeyAuth +// @Router /databases/load/:from [get] +func (b *BaseApi) LoadDBFromRemote(c *gin.Context) { + from, err := helper.GetStrParamByKey(c, "from") + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := mysqlService.LoadFromRemote(from); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, nil) +} + // @Tags Database Mysql // @Summary Check before delete mysql database // @Description Mysql 数据库删除前检查 diff --git a/backend/app/api/v1/remote_db.go b/backend/app/api/v1/remote_db.go index 53f060557..a276e1d5e 100644 --- a/backend/app/api/v1/remote_db.go +++ b/backend/app/api/v1/remote_db.go @@ -63,7 +63,7 @@ func (b *BaseApi) SearchRemoteDB(c *gin.Context) { // @Tags Database // @Summary List remote databases -// @Description 获取快速命令列表 +// @Description 获取远程数据库列表 // @Success 200 {array} dto.RemoteDBOption // @Security ApiKeyAuth // @Router /databases/remote/list/:type [get] @@ -82,6 +82,27 @@ func (b *BaseApi) ListRemoteDB(c *gin.Context) { helper.SuccessWithData(c, list) } +// @Tags Database +// @Summary Get remote databases +// @Description 获取远程数据库 +// @Success 200 dto.RemoteDBOption +// @Security ApiKeyAuth +// @Router /databases/remote/:name [get] +func (b *BaseApi) GetRemoteDB(c *gin.Context) { + name, err := helper.GetStrParamByKey(c, "name") + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + data, err := remoteDBService.Get(name) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, data) +} + // @Tags Database // @Summary Delete remote database // @Description 删除远程数据库 diff --git a/backend/app/model/database_mysql.go b/backend/app/model/database_mysql.go index e4b6f2a0e..95bc41dbb 100644 --- a/backend/app/model/database_mysql.go +++ b/backend/app/model/database_mysql.go @@ -3,7 +3,7 @@ package model type DatabaseMysql struct { BaseModel Name string `json:"name" gorm:"type:varchar(256);not null"` - From string `json:"type" gorm:"type:varchar(256);not null;default:'local'"` + From string `json:"from" gorm:"type:varchar(256);not null;default:local"` MysqlName string `json:"mysqlName" gorm:"type:varchar(64);not null"` Format string `json:"format" gorm:"type:varchar(64);not null"` Username string `json:"username" gorm:"type:varchar(256);not null"` diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index 1d55464d2..a47f1f2d7 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -33,6 +33,7 @@ type IMysqlService interface { SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error) ListDBName() ([]string, error) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) + LoadFromRemote(from string) error ChangeAccess(info dto.ChangeDBInfo) error ChangePassword(info dto.ChangeDBInfo) error UpdateVariables(updates []dto.MysqlVariablesUpdate) error @@ -89,9 +90,6 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode if req.From == "local" && req.Username == "root" { return nil, errors.New("Cannot set root as user name") } - if req.From == "127.0.0.1" { - return nil, errors.New("Cannot set 127.0.0.1 as address") - } cli, version, err := LoadMysqlClientByFrom(req.From) if err != nil { @@ -127,29 +125,74 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode return &createItem, nil } +func (u *MysqlService) LoadFromRemote(from string) error { + client, version, err := LoadMysqlClientByFrom(from) + if err != nil { + return err + } + + databases, err := mysqlRepo.List(remoteDBRepo.WithByFrom(from)) + if err != nil { + return err + } + datas, err := client.SyncDB(version) + if err != nil { + return err + } + for _, data := range datas { + hasOld := false + for _, oldData := range databases { + if oldData.Name == data.Name { + hasOld = true + break + } + } + if !hasOld { + var createItem model.DatabaseMysql + if err := copier.Copy(&createItem, &data); err != nil { + return errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + if err := mysqlRepo.Create(context.Background(), &createItem); err != nil { + return err + } + } + } + return nil +} + func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error { return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description}) } func (u *MysqlService) DeleteCheck(id uint) ([]string, error) { var appInUsed []string - app, err := appInstallRepo.LoadBaseInfo("mysql", "") - if err != nil { - return appInUsed, err - } - db, err := mysqlRepo.Get(commonRepo.WithByID(id)) if err != nil { return appInUsed, err } - apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID)) - for _, app := range apps { - appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId)) - if appInstall.ID != 0 { - appInUsed = append(appInUsed, appInstall.Name) + if db.From == "local" { + app, err := appInstallRepo.LoadBaseInfo("mysql", "") + if err != nil { + return appInUsed, err + } + apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID)) + for _, app := range apps { + appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId)) + if appInstall.ID != 0 { + appInUsed = append(appInUsed, appInstall.Name) + } + } + } else { + apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID)) + for _, app := range apps { + appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId)) + if appInstall.ID != 0 { + appInUsed = append(appInUsed, appInstall.Name) + } } } + return appInUsed, nil } diff --git a/backend/app/service/remote_db.go b/backend/app/service/remote_db.go index 99c4f3c88..61219df75 100644 --- a/backend/app/service/remote_db.go +++ b/backend/app/service/remote_db.go @@ -1,6 +1,8 @@ package service import ( + "context" + "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/utils/mysql" @@ -12,6 +14,7 @@ import ( type RemoteDBService struct{} type IRemoteDBService interface { + Get(name string) (dto.RemoteDBInfo, error) SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error) Create(req dto.RemoteDBCreate) error Update(req dto.RemoteDBUpdate) error @@ -40,6 +43,18 @@ func (u *RemoteDBService) SearchWithPage(search dto.RemoteDBSearch) (int64, inte return total, datas, err } +func (u *RemoteDBService) Get(name string) (dto.RemoteDBInfo, error) { + var data dto.RemoteDBInfo + remote, err := remoteDBRepo.Get(commonRepo.WithByName(name)) + if err != nil { + return data, err + } + if err := copier.Copy(&data, &remote); err != nil { + return data, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + return data, nil +} + func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) { dbs, err := remoteDBRepo.GetList(commonRepo.WithByType(dbType)) var datas []dto.RemoteDBOption @@ -82,7 +97,15 @@ func (u *RemoteDBService) Delete(id uint) error { if db.ID == 0 { return constant.ErrRecordNotFound } - return remoteDBRepo.Delete(commonRepo.WithByID(id)) + if err := remoteDBRepo.Delete(commonRepo.WithByID(id)); err != nil { + return err + } + if db.From != "local" { + if err := mysqlRepo.Delete(context.Background(), remoteDBRepo.WithByFrom(db.Name)); err != nil { + return err + } + } + return nil } func (u *RemoteDBService) Update(req dto.RemoteDBUpdate) error { diff --git a/backend/router/ro_database.go b/backend/router/ro_database.go index b58ec6584..639bad3b0 100644 --- a/backend/router/ro_database.go +++ b/backend/router/ro_database.go @@ -17,6 +17,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) { baseApi := v1.ApiGroupApp.BaseApi { cmdRouter.POST("", baseApi.CreateMysql) + cmdRouter.GET("load/:from", baseApi.LoadDBFromRemote) cmdRouter.POST("/change/access", baseApi.ChangeMysqlAccess) cmdRouter.POST("/change/password", baseApi.ChangeMysqlPassword) cmdRouter.POST("/del/check", baseApi.DeleteCheckMysql) @@ -42,6 +43,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) { cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf) cmdRouter.POST("/remote", baseApi.CreateRemoteDB) + cmdRouter.GET("/remote/:name", baseApi.GetRemoteDB) cmdRouter.GET("/remote/list/:type", baseApi.ListRemoteDB) cmdRouter.POST("/remote/update", baseApi.UpdateRemoteDB) cmdRouter.POST("/remote/search", baseApi.SearchRemoteDB) diff --git a/backend/utils/mysql/client.go b/backend/utils/mysql/client.go index 4ac8fa436..18fce53cb 100644 --- a/backend/utils/mysql/client.go +++ b/backend/utils/mysql/client.go @@ -20,6 +20,7 @@ type MysqlClient interface { Backup(info client.BackupInfo) error Recover(info client.RecoverInfo) error + SyncDB(version string) ([]client.SyncDBInfo, error) Close() } @@ -29,7 +30,7 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) { return nil, buserr.New(constant.ErrCmdIllegal) } connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password, "-e"} - return client.NewLocal(connArgs, conn.Address, conn.Password), nil + return client.NewLocal(connArgs, conn.Address, conn.Password, conn.From), nil } connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.Username, conn.Password, conn.Address, conn.Port) @@ -42,6 +43,7 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) { } return client.NewRemote(client.Remote{ Client: db, + From: conn.From, User: conn.Username, Password: conn.Password, Address: conn.Address, diff --git a/backend/utils/mysql/client/info.go b/backend/utils/mysql/client/info.go index 682a6d787..42467fa0b 100644 --- a/backend/utils/mysql/client/info.go +++ b/backend/utils/mysql/client/info.go @@ -69,6 +69,16 @@ type RecoverInfo struct { Timeout uint `json:"timeout"` // second } +type SyncDBInfo struct { + Name string `json:"name"` + From string `json:"from"` + MysqlName string `json:"mysqlName"` + Format string `json:"format"` + Username string `json:"username"` + Password string `json:"password"` + Permission string `json:"permission"` +} + var formatMap = map[string]string{ "utf8": "utf8_general_ci", "utf8mb4": "utf8mb4_general_ci", diff --git a/backend/utils/mysql/client/local.go b/backend/utils/mysql/client/local.go index a26649b0c..ead53ce51 100644 --- a/backend/utils/mysql/client/local.go +++ b/backend/utils/mysql/client/local.go @@ -13,17 +13,19 @@ import ( "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/files" ) type Local struct { PrefixCommand []string + From string Password string ContainerName string } -func NewLocal(command []string, containerName, password string) *Local { - return &Local{PrefixCommand: command, ContainerName: containerName, Password: password} +func NewLocal(command []string, containerName, password, from string) *Local { + return &Local{PrefixCommand: command, ContainerName: containerName, Password: password, From: from} } func (r *Local) Create(info CreateInfo) error { @@ -252,6 +254,81 @@ func (r *Local) Recover(info RecoverInfo) error { return nil } +func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) { + var datas []SyncDBInfo + lines, err := r.ExecSQLForRows("SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA", 300) + if err != nil { + return datas, err + } + for _, line := range lines { + parts := strings.Fields(line) + if len(parts) != 2 { + continue + } + if parts[0] == "SCHEMA_NAME" || parts[0] == "information_schema" || parts[0] == "mysql" || parts[0] == "performance_schema" || parts[0] == "sys" { + continue + } + dataItem := SyncDBInfo{ + Name: parts[0], + From: r.From, + MysqlName: r.From, + Format: parts[1], + } + userLines, err := r.ExecSQLForRows(fmt.Sprintf("SELECT USER,HOST FROM mysql.DB WHERE DB = '%s'", parts[0]), 300) + if err != nil { + return datas, err + } + + var permissionItem []string + isLocal := true + i := 0 + for _, userline := range userLines { + userparts := strings.Fields(userline) + if len(userparts) != 2 { + continue + } + if userparts[0] == "root" { + continue + } + if i == 0 { + dataItem.Username = userparts[0] + } + dataItem.Username = userparts[0] + if dataItem.Username == userparts[0] && userparts[1] == "%" { + isLocal = false + dataItem.Permission = "%" + } else if dataItem.Username == userparts[0] && userparts[1] != "localhost" { + isLocal = false + permissionItem = append(permissionItem, userparts[1]) + } + } + if len(dataItem.Username) == 0 { + if err := r.CreateUser(CreateInfo{ + Name: parts[0], + Format: parts[1], + Version: version, + Username: parts[0], + Password: common.RandStr(16), + Permission: "%", + Timeout: 300, + }); err != nil { + global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err) + } + dataItem.Username = parts[0] + dataItem.Permission = "%" + } else { + if isLocal { + dataItem.Permission = "localhost" + } + if len(dataItem.Permission) == 0 { + dataItem.Permission = strings.Join(permissionItem, ",") + } + } + datas = append(datas, dataItem) + } + return datas, nil +} + func (r *Local) Close() {} func (r *Local) ExecSQL(command string, timeout uint) error { diff --git a/backend/utils/mysql/client/remote.go b/backend/utils/mysql/client/remote.go index b81eed229..4e3315b06 100644 --- a/backend/utils/mysql/client/remote.go +++ b/backend/utils/mysql/client/remote.go @@ -12,6 +12,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/jarvanstack/mysqldump" @@ -19,6 +20,7 @@ import ( type Remote struct { Client *sql.DB + From string User string Password string Address string @@ -251,6 +253,86 @@ func (r *Remote) Recover(info RecoverInfo) error { return nil } +func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) { + var datas []SyncDBInfo + rows, err := r.Client.Query("SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA") + if err != nil { + return datas, err + } + defer rows.Close() + + for rows.Next() { + var dbName, charsetName string + if err = rows.Scan(&dbName, &charsetName); err != nil { + return datas, err + } + if dbName == "information_schema" || dbName == "mysql" || dbName == "performance_schema" || dbName == "sys" { + continue + } + dataItem := SyncDBInfo{ + Name: dbName, + From: r.From, + MysqlName: r.From, + Format: charsetName, + } + userRows, err := r.Client.Query("SELECT USER,HOST FROM mysql.DB WHERE DB = ?", dbName) + if err != nil { + return datas, err + } + + var permissionItem []string + isLocal := true + i := 0 + for userRows.Next() { + var user, host string + if err = userRows.Scan(&user, &host); err != nil { + return datas, err + } + if user == "root" { + continue + } + if i == 0 { + dataItem.Username = user + } + if dataItem.Username == user && host == "%" { + isLocal = false + dataItem.Permission = "%" + } else if dataItem.Username == user && host != "localhost" { + isLocal = false + permissionItem = append(permissionItem, host) + } + i++ + } + if len(dataItem.Username) == 0 { + if err := r.CreateUser(CreateInfo{ + Name: dbName, + Format: charsetName, + Version: version, + Username: dbName, + Password: common.RandStr(16), + Permission: "%", + Timeout: 300, + }); err != nil { + global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err) + } + dataItem.Username = dbName + dataItem.Permission = "%" + } else { + if isLocal { + dataItem.Permission = "localhost" + } + if len(dataItem.Permission) == 0 { + dataItem.Permission = strings.Join(permissionItem, ",") + } + } + datas = append(datas, dataItem) + } + if err = rows.Err(); err != nil { + return datas, err + } + return datas, nil +} + func (r *Remote) Close() { _ = r.Client.Close() } diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 7fe1a56c5..16b875682 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -15,6 +15,9 @@ export const addMysqlDB = (params: Database.MysqlDBCreate) => { } return http.post(`/databases`, reqest); }; +export const loadDBFromRemote = (from: string) => { + return http.get(`/databases/load/${from}`); +}; export const updateMysqlAccess = (params: Database.ChangeInfo) => { return http.post(`/databases/change/access`, params); }; @@ -85,6 +88,9 @@ export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => }; // remote +export const getRemoteDB = (name: string) => { + return http.get(`/databases/remote/${name}`); +}; export const searchRemoteDBs = (params: Database.SearchRemoteDBPage) => { return http.post>(`/databases/remote/search`, params); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 757d1d26b..0d0909c6e 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -335,15 +335,16 @@ const message = { portHelper: 'This port is the exposed port of the container. You need to save the modification separately and restart the container!', + loadFromRemote: 'load from Server', + passwordHelper: 'Unable to retrieve, please modify', remote: 'Remote', remoteDB: 'Remote DB', - createRemoteDB: 'Create Remote DB', - editRemoteDB: 'Edit Remote DB', + createRemoteDB: 'Create Remote Server', + editRemoteDB: 'Edit Remote Server', localDB: 'Local DB', address: 'DB address', version: 'DB version', versionHelper: 'Currently, only versions 5.6, 5.7, and 8.0 are supported', - addressHelper: 'The remote database address except 127.0.0.1.', userHelper: 'The root user or a database user with root privileges can access the remote database.', selectFile: 'Select file', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 721036dc5..23c033f7b 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -330,15 +330,16 @@ const message = { confChange: '配置修改', + loadFromRemote: '從服務器獲取', + passwordHelper: '無法獲取密碼,請修改', remote: '遠程', - remoteDB: '遠程數據庫', - createRemoteDB: '創建遠程數據庫', - editRemoteDB: '編輯遠程數據庫', + remoteDB: '遠程服務器', + createRemoteDB: '添加遠程服務器', + editRemoteDB: '編輯遠程服務器', localDB: '本地數據庫', address: '數據庫地址', version: '數據庫版本', versionHelper: '當前僅支持 5.6 5.7 8.0 三個版本', - addressHelper: '非 127.0.0.1 的遠程數據庫地址', userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶', selectFile: '選擇文件', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index cf192016c..a8fe9620e 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -330,15 +330,16 @@ const message = { confChange: '配置修改', + loadFromRemote: '从服务器获取', + passwordHelper: '无法获取密码,请修改', remote: '远程', - remoteDB: '远程数据库', - createRemoteDB: '创建远程数据库', - editRemoteDB: '编辑远程数据库', + remoteDB: '远程服务器', + createRemoteDB: '添加远程服务器', + editRemoteDB: '编辑远程服务器', localDB: '本地数据库', address: '数据库地址', version: '数据库版本', versionHelper: '当前仅支持 5.6 5.7 8.0 三个版本', - addressHelper: '非 127.0.0.1 的远程数据库地址', userHelper: 'root 用户或者拥有 root 权限的数据库用户', selectFile: '选择文件', diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue index 37c61f7a2..c416f2431 100644 --- a/frontend/src/views/cronjob/operate/index.vue +++ b/frontend/src/views/cronjob/operate/index.vue @@ -454,7 +454,7 @@ const loadContainers = async () => { const checkMysqlInstalled = async () => { const data = await loadDBNames(); - mysqlInfo.dbNames = data.data; + mysqlInfo.dbNames = data.data || []; }; function isBackup() { diff --git a/frontend/src/views/database/mysql/root-password/index.vue b/frontend/src/views/database/mysql/conn/index.vue similarity index 77% rename from frontend/src/views/database/mysql/root-password/index.vue rename to frontend/src/views/database/mysql/conn/index.vue index cae9893f7..a6fb276da 100644 --- a/frontend/src/views/database/mysql/root-password/index.vue +++ b/frontend/src/views/database/mysql/conn/index.vue @@ -4,7 +4,7 @@ - + @@ -39,6 +39,19 @@ + + + + {{ form.remoteIP + ':' + form.port }} + + + {{ form.username }} + + + {{ form.password }} + + + @@ -58,28 +71,31 @@ + + diff --git a/frontend/src/views/database/mysql/password/index.vue b/frontend/src/views/database/mysql/password/index.vue index 2c24ac695..347f86c63 100644 --- a/frontend/src/views/database/mysql/password/index.vue +++ b/frontend/src/views/database/mysql/password/index.vue @@ -29,6 +29,11 @@ + @@ -110,6 +115,7 @@ const acceptParams = (params: DialogProps): void => { : i18n.global.t('database.permission'); changeForm.id = params.id; changeForm.from = params.from; + console.log(changeForm.from); changeForm.mysqlName = params.mysqlName; changeForm.userName = params.username; changeForm.password = params.password; diff --git a/frontend/src/views/database/mysql/remote/operate/index.vue b/frontend/src/views/database/mysql/remote/operate/index.vue index c761a738e..562c58894 100644 --- a/frontend/src/views/database/mysql/remote/operate/index.vue +++ b/frontend/src/views/database/mysql/remote/operate/index.vue @@ -1,5 +1,5 @@