mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-12-18 11:17:49 +08:00
feat: 数据库实现远程服务器获取功能 (#1775)
This commit is contained in:
parent
e83e592e0a
commit
40aaa1ceb0
@ -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 数据库删除前检查
|
||||
|
@ -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 删除远程数据库
|
||||
|
@ -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"`
|
||||
|
@ -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,22 +125,57 @@ 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
|
||||
}
|
||||
|
||||
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))
|
||||
@ -150,6 +183,16 @@ func (u *MysqlService) DeleteCheck(id uint) ([]string, error) {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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<Database.RemoteDBInfo>(`/databases/remote/${name}`);
|
||||
};
|
||||
export const searchRemoteDBs = (params: Database.SearchRemoteDBPage) => {
|
||||
return http.post<ResPage<Database.RemoteDBInfo>>(`/databases/remote/search`, params);
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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: '選擇文件',
|
||||
|
@ -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: '选择文件',
|
||||
|
@ -454,7 +454,7 @@ const loadContainers = async () => {
|
||||
|
||||
const checkMysqlInstalled = async () => {
|
||||
const data = await loadDBNames();
|
||||
mysqlInfo.dbNames = data.data;
|
||||
mysqlInfo.dbNames = data.data || [];
|
||||
};
|
||||
|
||||
function isBackup() {
|
||||
|
@ -4,7 +4,7 @@
|
||||
<DrawerHeader :header="$t('database.databaseConnInfo')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-row type="flex" justify="center" v-if="form.from === 'local'">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.containerConn')">
|
||||
<el-tag>
|
||||
@ -39,6 +39,19 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="center" v-if="form.from !== 'local'">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.remoteConn')">
|
||||
<el-tag>{{ form.remoteIP + ':' + form.port }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.login.username')">
|
||||
<el-tag>{{ form.username }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.login.password')">
|
||||
<el-tag>{{ form.password }}</el-tag>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" @cancel="loadPassword"></ConfirmDialog>
|
||||
@ -58,28 +71,31 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { loadRemoteAccess, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';
|
||||
import { getRemoteDB, loadRemoteAccess, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import { GetAppConnInfo } from '@/api/modules/app';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { getRandomStr } from '@/utils/util';
|
||||
import { App } from '@/api/interface/app';
|
||||
import useClipboard from 'vue-clipboard3';
|
||||
const { toClipboard } = useClipboard();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const dialogVisiable = ref(false);
|
||||
const form = ref<App.DatabaseConnInfo>({
|
||||
const form = reactive({
|
||||
password: '',
|
||||
serviceName: '',
|
||||
privilege: false,
|
||||
port: 0,
|
||||
|
||||
from: '',
|
||||
username: '',
|
||||
remoteIP: '',
|
||||
});
|
||||
|
||||
const confirmDialogRef = ref();
|
||||
@ -88,15 +104,24 @@ const confirmAccessDialogRef = ref();
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const acceptParams = (): void => {
|
||||
form.value.password = '';
|
||||
interface DialogProps {
|
||||
from: string;
|
||||
remoteIP: string;
|
||||
}
|
||||
|
||||
const acceptParams = (param: DialogProps): void => {
|
||||
form.password = '';
|
||||
form.from = param.from;
|
||||
if (form.from !== 'local') {
|
||||
loadRemoteInfo();
|
||||
}
|
||||
loadPassword();
|
||||
loadAccess();
|
||||
dialogVisiable.value = true;
|
||||
};
|
||||
|
||||
const random = async () => {
|
||||
form.value.password = getRandomStr(16);
|
||||
form.password = getRandomStr(16);
|
||||
};
|
||||
|
||||
const onCopy = async (value: string) => {
|
||||
@ -114,18 +139,27 @@ const handleClose = () => {
|
||||
|
||||
const loadAccess = async () => {
|
||||
const res = await loadRemoteAccess();
|
||||
form.value.privilege = res.data;
|
||||
form.privilege = res.data;
|
||||
};
|
||||
|
||||
const loadRemoteInfo = async () => {
|
||||
const res = await getRemoteDB(form.from);
|
||||
form.remoteIP = res.data.address;
|
||||
form.username = res.data.username;
|
||||
form.password = res.data.password;
|
||||
};
|
||||
|
||||
const loadPassword = async () => {
|
||||
const res = await GetAppConnInfo('mysql');
|
||||
form.value = res.data;
|
||||
form.password = res.data.password || '';
|
||||
form.port = res.data.port || 3306;
|
||||
form.serviceName = res.data.serviceName || '';
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
let param = {
|
||||
id: 0,
|
||||
value: form.value.password,
|
||||
value: form.password,
|
||||
};
|
||||
loading.value = true;
|
||||
await updateMysqlPassword(param)
|
||||
@ -155,7 +189,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
const onSubmitAccess = async () => {
|
||||
let param = {
|
||||
id: 0,
|
||||
value: form.value.privilege ? '%' : 'localhost',
|
||||
value: form.privilege ? '%' : 'localhost',
|
||||
};
|
||||
loading.value = true;
|
||||
await updateMysqlAccess(param)
|
@ -38,13 +38,16 @@
|
||||
>
|
||||
{{ $t('database.create') }}
|
||||
</el-button>
|
||||
<el-button @click="onChangeConn" type="primary" plain>
|
||||
{{ $t('database.databaseConnInfo') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="mysqlIsExist && mysqlStatus === 'Running' && isLocal()"
|
||||
@click="onChangeRootPassword"
|
||||
v-if="(mysqlIsExist && mysqlStatus === 'Running') || !isLocal()"
|
||||
@click="loadDB"
|
||||
type="primary"
|
||||
plain
|
||||
>
|
||||
{{ $t('database.databaseConnInfo') }}
|
||||
{{ $t('database.loadFromRemote') }}
|
||||
</el-button>
|
||||
<el-button @click="goRemoteDB" type="primary" plain>
|
||||
{{ $t('database.remoteDB') }}
|
||||
@ -83,15 +86,10 @@
|
||||
:class="{ mask: mysqlStatus != 'Running' && isLocal() }"
|
||||
>
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name" sortable />
|
||||
<el-table-column :label="$t('commons.login.username')" prop="from">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.from === 'local' ? $t('database.localDB') : row.from }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.login.username')" prop="username" />
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<div v-if="row.password">
|
||||
<span style="float: left; line-height: 25px" v-if="!row.showPassword">***********</span>
|
||||
<div style="cursor: pointer; float: left" v-if="!row.showPassword">
|
||||
<el-icon
|
||||
@ -104,20 +102,21 @@
|
||||
</div>
|
||||
<span style="float: left" v-if="row.showPassword">{{ row.password }}</span>
|
||||
<div style="cursor: pointer; float: left" v-if="row.showPassword">
|
||||
<el-icon
|
||||
style="margin-left: 5px; margin-top: 3px"
|
||||
@click="row.showPassword = false"
|
||||
:size="16"
|
||||
>
|
||||
<el-icon class="iconInTable" @click="row.showPassword = false" :size="16">
|
||||
<Hide />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div style="cursor: pointer; float: left">
|
||||
<el-icon style="margin-left: 5px; margin-top: 3px" :size="16" @click="onCopy(row)">
|
||||
<el-icon class="iconInTable" :size="16" @click="onCopy(row)">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-link @click="onChangePassword(row)">
|
||||
<span style="font-size: 12px">{{ $t('database.passwordHelper') }}</span>
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.description')" prop="description">
|
||||
@ -191,7 +190,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<PasswordDialog ref="passwordRef" @search="search" />
|
||||
<RootPasswordDialog ref="rootPasswordRef" />
|
||||
<RootPasswordDialog ref="connRef" />
|
||||
<UploadDialog ref="uploadRef" />
|
||||
<OperateDialog @search="search" ref="dialogRef" />
|
||||
<Backups ref="dialogBackupRef" />
|
||||
@ -207,7 +206,7 @@
|
||||
import OperateDialog from '@/views/database/mysql/create/index.vue';
|
||||
import DeleteDialog from '@/views/database/mysql/delete/index.vue';
|
||||
import PasswordDialog from '@/views/database/mysql/password/index.vue';
|
||||
import RootPasswordDialog from '@/views/database/mysql/root-password/index.vue';
|
||||
import RootPasswordDialog from '@/views/database/mysql/conn/index.vue';
|
||||
import AppResources from '@/views/database/mysql/check/index.vue';
|
||||
import Setting from '@/views/database/mysql/setting/index.vue';
|
||||
import AppStatus from '@/components/app-status/index.vue';
|
||||
@ -216,7 +215,13 @@ import UploadDialog from '@/components/upload/index.vue';
|
||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteCheckMysqlDB, listRemoteDBs, searchMysqlDBs, updateMysqlDescription } from '@/api/modules/database';
|
||||
import {
|
||||
deleteCheckMysqlDB,
|
||||
listRemoteDBs,
|
||||
loadDBFromRemote,
|
||||
searchMysqlDBs,
|
||||
updateMysqlDescription,
|
||||
} from '@/api/modules/database';
|
||||
import i18n from '@/lang';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { App } from '@/api/interface/app';
|
||||
@ -268,9 +273,9 @@ const dialogBackupRef = ref();
|
||||
|
||||
const uploadRef = ref();
|
||||
|
||||
const rootPasswordRef = ref();
|
||||
const onChangeRootPassword = async () => {
|
||||
rootPasswordRef.value!.acceptParams();
|
||||
const connRef = ref();
|
||||
const onChangeConn = async () => {
|
||||
connRef.value!.acceptParams({ from: paginationConfig.from });
|
||||
};
|
||||
|
||||
const goRemoteDB = async () => {
|
||||
@ -308,6 +313,18 @@ const search = async (column?: any) => {
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
||||
const loadDB = async () => {
|
||||
loading.value = true;
|
||||
await loadDBFromRemote(paginationConfig.from)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
search();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const goRouter = async () => {
|
||||
router.push({ name: 'AppDetail', params: { appKey: 'mysql' } });
|
||||
};
|
||||
@ -376,10 +393,7 @@ const onDelete = async (row: Database.MysqlDBInfo) => {
|
||||
}
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('database.changePassword'),
|
||||
click: (row: Database.MysqlDBInfo) => {
|
||||
const onChangePassword = async (row: Database.MysqlDBInfo) => {
|
||||
let param = {
|
||||
id: row.id,
|
||||
from: row.from,
|
||||
@ -389,10 +403,20 @@ const buttons = [
|
||||
password: row.password,
|
||||
};
|
||||
passwordRef.value.acceptParams(param);
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('database.changePassword'),
|
||||
click: (row: Database.MysqlDBInfo) => {
|
||||
onChangePassword(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('database.permission'),
|
||||
disabled: (row: Database.MysqlDBInfo) => {
|
||||
return !row.password;
|
||||
},
|
||||
click: (row: Database.MysqlDBInfo) => {
|
||||
let param = {
|
||||
id: row.id,
|
||||
@ -403,7 +427,7 @@ const buttons = [
|
||||
privilegeIPs: '',
|
||||
password: '',
|
||||
};
|
||||
if (row.permission === '%') {
|
||||
if (row.permission === '%' || row.permission === 'localhost') {
|
||||
param.privilege = row.permission;
|
||||
} else {
|
||||
param.privilegeIPs = row.permission;
|
||||
@ -446,3 +470,10 @@ onMounted(() => {
|
||||
loadDBOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iconInTable {
|
||||
margin-left: 5px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
</style>
|
||||
|
@ -29,6 +29,11 @@
|
||||
<el-form-item :label="$t('database.permission')" prop="privilege">
|
||||
<el-select style="width: 100%" v-model="changeForm.privilege">
|
||||
<el-option value="%" :label="$t('database.permissionAll')" />
|
||||
<el-option
|
||||
v-if="changeForm.from !== 'local'"
|
||||
value="localhost"
|
||||
:label="$t('terminal.localhost')"
|
||||
/>
|
||||
<el-option value="ip" :label="$t('database.permissionForIP')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -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;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
|
||||
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :resource="dialogData.rowData?.name" :back="handleClose" />
|
||||
</template>
|
||||
@ -7,7 +7,12 @@
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.name" />
|
||||
<el-input
|
||||
v-if="dialogData.title === 'create'"
|
||||
clearable
|
||||
v-model.trim="dialogData.rowData!.name"
|
||||
/>
|
||||
<el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.version')" prop="version">
|
||||
<el-select v-model="dialogData.rowData!.version">
|
||||
@ -19,7 +24,6 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.address')" prop="address">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.address" />
|
||||
<span class="input-help">{{ $t('database.addressHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.port')" prop="port">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.port" />
|
||||
|
Loading…
Reference in New Issue
Block a user