mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 实现远程数据库增删改查
This commit is contained in:
parent
bd5dc56b66
commit
cb7351a9fb
@ -21,8 +21,9 @@ var (
|
||||
imageService = service.NewIImageService()
|
||||
dockerService = service.NewIDockerService()
|
||||
|
||||
mysqlService = service.NewIMysqlService()
|
||||
redisService = service.NewIRedisService()
|
||||
mysqlService = service.NewIMysqlService()
|
||||
remoteDBService = service.NewIRemoteDBService()
|
||||
redisService = service.NewIRedisService()
|
||||
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
|
133
backend/app/api/v1/remote_db.go
Normal file
133
backend/app/api/v1/remote_db.go
Normal file
@ -0,0 +1,133 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Create remote database
|
||||
// @Description 创建远程数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.DatabaseCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/remote [post]
|
||||
// @x-panel-log {"bodyKeys":["name", "type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建远程数据库 [name][type]","formatEN":"create remote database [name][type]"}
|
||||
func (b *BaseApi) CreateRemoteDB(c *gin.Context) {
|
||||
var req dto.RemoteDBCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := remoteDBService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Page remote databases
|
||||
// @Description 获取远程数据库列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/remote/search [post]
|
||||
func (b *BaseApi) SearchRemoteDB(c *gin.Context) {
|
||||
var req dto.RemoteDBSearch
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := remoteDBService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary List remote databases
|
||||
// @Description 获取快速命令列表
|
||||
// @Success 200 {array} dto.RemoteDBOption
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/remote/list/:type [get]
|
||||
func (b *BaseApi) ListRemoteDB(c *gin.Context) {
|
||||
dbType := c.Query("type")
|
||||
list, err := remoteDBService.List(dbType)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Delete remote database
|
||||
// @Description 删除远程数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/remote/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"databases","output_column":"name","output_value":"names"}],"formatZH":"删除远程数据库 [names]","formatEN":"delete remote database [names]"}
|
||||
func (b *BaseApi) DeleteRemoteDB(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := remoteDBService.Delete(req.ID); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Database
|
||||
// @Summary Update remote database
|
||||
// @Description 更新远程数据库
|
||||
// @Accept json
|
||||
// @Param request body dto.DatabaseUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /databases/remote/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新远程数据库 [name]","formatEN":"update remote database [name]"}
|
||||
func (b *BaseApi) UpdateRemoteDB(c *gin.Context) {
|
||||
var req dto.RemoteDBUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := remoteDBService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
@ -16,6 +16,7 @@ type MysqlDBInfo struct {
|
||||
|
||||
type MysqlDBCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
From string `json:"from" validate:"required"`
|
||||
Format string `json:"format" validate:"required,oneof=utf8mb4 utf8 gbk big5"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
@ -100,6 +101,7 @@ type MysqlConfUpdateByFile struct {
|
||||
|
||||
type ChangeDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
From string `json:"from" validate:"required"`
|
||||
Value string `json:"value" validate:"required"`
|
||||
}
|
||||
|
||||
|
52
backend/app/dto/remote_db.go
Normal file
52
backend/app/dto/remote_db.go
Normal file
@ -0,0 +1,52 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type RemoteDBSearch struct {
|
||||
PageInfo
|
||||
Info string `json:"info"`
|
||||
Type string `json:"type"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Order string `json:"order"`
|
||||
}
|
||||
|
||||
type RemoteDBInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
Version string `json:"version"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type RemoteDBOption struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type RemoteDBCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=mysql"`
|
||||
From string `json:"from" validate:"required,oneof=local remote"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type RemoteDBUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
}
|
@ -3,6 +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"`
|
||||
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"`
|
||||
|
@ -1,13 +1,14 @@
|
||||
package model
|
||||
|
||||
type Database struct {
|
||||
type RemoteDB struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Version string `json:"version" gorm:"type:varchar(64);not null"`
|
||||
From string `json:"from" gorm:"type:varchar(64);not null"`
|
||||
Address string `json:"address" gorm:"type:varchar(64);not null"`
|
||||
Port uint `json:"port" gorm:"type:decimal;not null"`
|
||||
Username string `json:"username" gorm:"type:varchar(64)"`
|
||||
Password string `json:"password" gorm:"type:varchar(64)"`
|
||||
Format string `json:"format" gorm:"type:varchar(64)"`
|
||||
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||
}
|
77
backend/app/repo/remote_db.go
Normal file
77
backend/app/repo/remote_db.go
Normal file
@ -0,0 +1,77 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RemoteDBRepo struct{}
|
||||
|
||||
type IRemoteDBRepo interface {
|
||||
GetList(opts ...DBOption) ([]model.RemoteDB, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.RemoteDB, error)
|
||||
Create(database *model.RemoteDB) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
Get(opts ...DBOption) (model.RemoteDB, error)
|
||||
WithoutByFrom(from string) DBOption
|
||||
}
|
||||
|
||||
func NewIRemoteDBRepo() IRemoteDBRepo {
|
||||
return &RemoteDBRepo{}
|
||||
}
|
||||
|
||||
func (u *RemoteDBRepo) Get(opts ...DBOption) (model.RemoteDB, error) {
|
||||
var database model.RemoteDB
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&database).Error
|
||||
return database, err
|
||||
}
|
||||
|
||||
func (u *RemoteDBRepo) Page(page, size int, opts ...DBOption) (int64, []model.RemoteDB, error) {
|
||||
var users []model.RemoteDB
|
||||
db := global.DB.Model(&model.RemoteDB{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *RemoteDBRepo) GetList(opts ...DBOption) ([]model.RemoteDB, error) {
|
||||
var databases []model.RemoteDB
|
||||
db := global.DB.Model(&model.RemoteDB{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&databases).Error
|
||||
return databases, err
|
||||
}
|
||||
|
||||
func (c *RemoteDBRepo) WithoutByFrom(from string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("`from` != ?", from)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *RemoteDBRepo) Create(database *model.RemoteDB) error {
|
||||
return global.DB.Create(database).Error
|
||||
}
|
||||
|
||||
func (u *RemoteDBRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.RemoteDB{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *RemoteDBRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.RemoteDB{}).Error
|
||||
}
|
@ -20,6 +20,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@ -70,50 +72,69 @@ func (u *MysqlService) ListDBName() ([]string, error) {
|
||||
return dbNames, err
|
||||
}
|
||||
|
||||
var formatMap = map[string]string{
|
||||
"utf8": "utf8_general_ci",
|
||||
"utf8mb4": "utf8mb4_general_ci",
|
||||
"gbk": "gbk_chinese_ci",
|
||||
"big5": "big5_chinese_ci",
|
||||
}
|
||||
|
||||
func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) {
|
||||
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Permission) {
|
||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
if req.Username == "root" {
|
||||
return nil, errors.New("Cannot set root as user name")
|
||||
}
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if mysql.ID != 0 {
|
||||
return nil, constant.ErrRecordExist
|
||||
}
|
||||
if err := copier.Copy(&mysql, &req); err != nil {
|
||||
var createItem model.DatabaseMysql
|
||||
if err := copier.Copy(&createItem, &req); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
|
||||
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", req.Name, req.Format, formatMap[req.Format])
|
||||
if err := excSQL(app.ContainerName, app.Password, createSql); err != nil {
|
||||
if strings.Contains(err.Error(), "ERROR 1007") {
|
||||
return nil, buserr.New(constant.ErrDatabaseIsExist)
|
||||
if req.Username == "root" {
|
||||
return nil, errors.New("Cannot set root as user name")
|
||||
}
|
||||
|
||||
dbInfo := client.DBInfo{
|
||||
From: req.From,
|
||||
Timeout: 300,
|
||||
}
|
||||
version := ""
|
||||
if req.From == "local" {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mysqlData, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if mysqlData.ID != 0 {
|
||||
return nil, constant.ErrRecordExist
|
||||
}
|
||||
dbInfo.Address = app.ContainerName
|
||||
dbInfo.Username = "root"
|
||||
dbInfo.Password = app.Password
|
||||
version = app.Version
|
||||
createItem.MysqlName = app.Name
|
||||
} else {
|
||||
mysqlData, err := remoteDBRepo.Get(commonRepo.WithByName(req.From))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbInfo.Address = mysqlData.Address
|
||||
dbInfo.Port = mysqlData.Port
|
||||
dbInfo.Username = mysqlData.Username
|
||||
dbInfo.Password = mysqlData.Password
|
||||
version = mysqlData.Version
|
||||
createItem.MysqlName = mysqlData.Name
|
||||
}
|
||||
|
||||
cli, err := mysql.NewMysqlClient(dbInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, req); err != nil {
|
||||
if err := cli.Create(client.CreateInfo{
|
||||
Name: req.Name,
|
||||
Format: req.Format,
|
||||
Version: version,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
global.LOG.Infof("create database %s successful!", req.Name)
|
||||
mysql.MysqlName = app.Name
|
||||
if err := mysqlRepo.Create(ctx, &mysql); err != nil {
|
||||
if err := mysqlRepo.Create(ctx, &createItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mysql, nil
|
||||
return &createItem, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
@ -143,29 +164,28 @@ func (u *MysqlService) DeleteCheck(id uint) ([]string, error) {
|
||||
}
|
||||
|
||||
func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil && !req.ForceDelete {
|
||||
cli, version, err := loadClientByID(req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := mysqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(app.Version, "5.6") {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database if exists `%s`", db.Name)); err != nil && !req.ForceDelete {
|
||||
if err := cli.Delete(client.DeleteInfo{
|
||||
Name: db.Name,
|
||||
Version: version,
|
||||
Username: db.Username,
|
||||
Permission: db.Permission,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil && !req.ForceDelete {
|
||||
return err
|
||||
}
|
||||
global.LOG.Info("execute delete database sql successful, now start to drop uploads and records")
|
||||
|
||||
uploadDir := fmt.Sprintf("%s/1panel/uploads/database/mysql/%s/%s", global.CONF.System.BaseDir, app.Name, db.Name)
|
||||
if _, err := os.Stat(uploadDir); err == nil {
|
||||
@ -192,66 +212,55 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(info.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
|
||||
cli, version, err := loadClientByID(info.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
mysql model.DatabaseMysql
|
||||
err error
|
||||
mysqlData model.DatabaseMysql
|
||||
passwordInfo client.PasswordChangeInfo
|
||||
)
|
||||
passwordInfo.Password = info.Value
|
||||
passwordInfo.Timeout = 300
|
||||
passwordInfo.Version = version
|
||||
|
||||
if info.ID != 0 {
|
||||
mysql, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
||||
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordInfo.Name = mysqlData.Name
|
||||
passwordInfo.Username = mysqlData.Username
|
||||
passwordInfo.Permission = mysqlData.Permission
|
||||
} else {
|
||||
passwordInfo.Username = "root"
|
||||
}
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
if err := cli.ChangePassword(passwordInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passwordChangeCMD := fmt.Sprintf("set password for '%s'@'%s' = password('%s')", mysql.Username, mysql.Permission, info.Value)
|
||||
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||
passwordChangeCMD = fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED WITH mysql_native_password BY '%s';", mysql.Username, mysql.Permission, info.Value)
|
||||
}
|
||||
if info.ID != 0 {
|
||||
appRess, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysql.ID))
|
||||
for _, appRes := range appRess {
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// appRess, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysqlData.ID))
|
||||
// for _, appRes := range appRess {
|
||||
// appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, info.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, passwordChangeCMD); err != nil {
|
||||
return err
|
||||
}
|
||||
// global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
|
||||
// if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, info.Value); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
global.LOG.Info("excute password change sql successful")
|
||||
_ = mysqlRepo.Update(mysql.ID, map[string]interface{}{"password": info.Value})
|
||||
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"password": info.Value})
|
||||
return nil
|
||||
}
|
||||
|
||||
hosts, err := excuteSqlForRows(app.ContainerName, app.Password, "select host from mysql.user where user='root';")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, host := range hosts {
|
||||
if host == "%" || host == "localhost" {
|
||||
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value)
|
||||
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
|
||||
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Value)
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, passwordRootChangeCMD); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := updateInstallInfoInDB("mysql", "", "password", false, info.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -265,71 +274,36 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
if cmd.CheckIllegal(info.Value) {
|
||||
return buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
var (
|
||||
mysql model.DatabaseMysql
|
||||
err error
|
||||
)
|
||||
if info.ID != 0 {
|
||||
mysql, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Value == mysql.Permission {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
cli, version, err := loadClientByID(info.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.ID == 0 {
|
||||
mysql.Name = "*"
|
||||
mysql.Username = "root"
|
||||
mysql.Permission = "%"
|
||||
mysql.Password = app.Password
|
||||
}
|
||||
var (
|
||||
mysqlData model.DatabaseMysql
|
||||
accessInfo client.AccessChangeInfo
|
||||
)
|
||||
accessInfo.Permission = info.Value
|
||||
accessInfo.Timeout = 300
|
||||
accessInfo.Version = version
|
||||
|
||||
if info.Value != mysql.Permission {
|
||||
var userlist []string
|
||||
if strings.Contains(mysql.Permission, ",") {
|
||||
userlist = strings.Split(mysql.Permission, ",")
|
||||
} else {
|
||||
userlist = append(userlist, mysql.Permission)
|
||||
}
|
||||
for _, user := range userlist {
|
||||
if len(user) != 0 {
|
||||
if strings.HasPrefix(app.Version, "5.6") {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user '%s'@'%s'", mysql.Username, user)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, user)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if info.ID == 0 {
|
||||
return nil
|
||||
if info.ID != 0 {
|
||||
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessInfo.Name = mysqlData.Name
|
||||
accessInfo.Username = mysqlData.Username
|
||||
accessInfo.OldPermission = mysqlData.Permission
|
||||
} else {
|
||||
accessInfo.Username = "root"
|
||||
}
|
||||
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, dto.MysqlDBCreate{
|
||||
Username: mysql.Username,
|
||||
Name: mysql.Name,
|
||||
Permission: info.Value,
|
||||
Password: mysql.Password,
|
||||
}); err != nil {
|
||||
if err := cli.ChangeAccess(accessInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, "flush privileges"); err != nil {
|
||||
return err
|
||||
}
|
||||
if info.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = mysqlRepo.Update(mysql.ID, map[string]interface{}{"permission": info.Value})
|
||||
if mysqlData.ID != 0 {
|
||||
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"permission": info.Value})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -493,53 +467,6 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) createUser(container, password, version string, req dto.MysqlDBCreate) error {
|
||||
var userlist []string
|
||||
if strings.Contains(req.Permission, ",") {
|
||||
ips := strings.Split(req.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", req.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", req.Username, req.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(container, password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||
handleCreateError(container, password, req.Name, userlist, false)
|
||||
return buserr.New(constant.ErrUserIsExist)
|
||||
}
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
||||
if req.Name == "*" {
|
||||
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||
}
|
||||
if strings.HasPrefix(version, "5.7") || strings.HasPrefix(version, "5.6") {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||
}
|
||||
if err := excSQL(container, password, grantStr); err != nil {
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func handleCreateError(contaienr, password, dbName string, userlist []string, dropUser bool) {
|
||||
_ = excSQL(contaienr, password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
if dropUser {
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(contaienr, password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
@ -569,31 +496,6 @@ func excuteSqlForRows(containerName, password, command string) ([]string, error)
|
||||
return strings.Split(stdStr, "\n"), nil
|
||||
}
|
||||
|
||||
func excuteSql(containerName, password, command string) error {
|
||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
return errors.New(stdStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func excSQL(containerName, password, command string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return buserr.New(constant.ErrExecTimeOut)
|
||||
}
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
return errors.New(stdStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMyCnf(oldFiles []string, group string, param string, value interface{}) []string {
|
||||
isOn := false
|
||||
hasGroup := false
|
||||
@ -634,3 +536,46 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{
|
||||
}
|
||||
return newFiles
|
||||
}
|
||||
|
||||
func loadClientByID(id uint) (mysql.MysqlClient, string, error) {
|
||||
var (
|
||||
mysqlData model.DatabaseMysql
|
||||
dbInfo client.DBInfo
|
||||
version string
|
||||
err error
|
||||
)
|
||||
if id != 0 {
|
||||
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
if mysqlData.From != "local" {
|
||||
databaseItem, err := remoteDBRepo.Get(commonRepo.WithByName(mysqlData.From))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
dbInfo.Address = databaseItem.Address
|
||||
dbInfo.Port = databaseItem.Port
|
||||
dbInfo.Username = databaseItem.Username
|
||||
dbInfo.Password = databaseItem.Password
|
||||
version = databaseItem.Version
|
||||
|
||||
} else {
|
||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
dbInfo.Address = app.ContainerName
|
||||
dbInfo.Username = "root"
|
||||
dbInfo.Password = app.Password
|
||||
version = app.Version
|
||||
}
|
||||
|
||||
cli, err := mysql.NewMysqlClient(dbInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return cli, version, nil
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ var (
|
||||
appInstallRepo = repo.NewIAppInstallRepo()
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
remoteDBRepo = repo.NewIRemoteDBRepo()
|
||||
|
||||
imageRepoRepo = repo.NewIImageRepoRepo()
|
||||
composeRepo = repo.NewIComposeTemplateRepo()
|
||||
|
108
backend/app/service/remote_db.go
Normal file
108
backend/app/service/remote_db.go
Normal file
@ -0,0 +1,108 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type RemoteDBService struct{}
|
||||
|
||||
type IRemoteDBService interface {
|
||||
SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error)
|
||||
Create(req dto.RemoteDBCreate) error
|
||||
Update(req dto.RemoteDBUpdate) error
|
||||
Delete(id uint) error
|
||||
List(dbType string) ([]dto.RemoteDBOption, error)
|
||||
}
|
||||
|
||||
func NewIRemoteDBService() IRemoteDBService {
|
||||
return &RemoteDBService{}
|
||||
}
|
||||
|
||||
func (u *RemoteDBService) SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error) {
|
||||
total, dbs, err := remoteDBRepo.Page(search.Page, search.PageSize,
|
||||
commonRepo.WithByType(search.Type),
|
||||
commonRepo.WithLikeName(search.Info),
|
||||
remoteDBRepo.WithoutByFrom("local"),
|
||||
)
|
||||
var datas []dto.RemoteDBInfo
|
||||
for _, db := range dbs {
|
||||
var item dto.RemoteDBInfo
|
||||
if err := copier.Copy(&item, &db); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
datas = append(datas, item)
|
||||
}
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) {
|
||||
dbs, err := remoteDBRepo.GetList(commonRepo.WithByType(dbType), remoteDBRepo.WithoutByFrom("local"))
|
||||
var datas []dto.RemoteDBOption
|
||||
for _, db := range dbs {
|
||||
var item dto.RemoteDBOption
|
||||
if err := copier.Copy(&item, &db); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
datas = append(datas, item)
|
||||
}
|
||||
return datas, err
|
||||
}
|
||||
|
||||
func (u *RemoteDBService) Create(req dto.RemoteDBCreate) error {
|
||||
db, _ := remoteDBRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if db.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copier.Copy(&db, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := remoteDBRepo.Create(&db); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RemoteDBService) Delete(id uint) error {
|
||||
db, _ := remoteDBRepo.Get(commonRepo.WithByID(id))
|
||||
if db.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
return remoteDBRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (u *RemoteDBService) Update(req dto.RemoteDBUpdate) error {
|
||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||
From: "remote",
|
||||
Address: req.Address,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["version"] = req.Version
|
||||
upMap["address"] = req.Address
|
||||
upMap["port"] = req.Port
|
||||
upMap["username"] = req.Username
|
||||
upMap["password"] = req.Password
|
||||
upMap["description"] = req.Description
|
||||
return remoteDBRepo.Update(req.ID, upMap)
|
||||
}
|
@ -481,9 +481,9 @@ var EncryptHostPassword = &gormigrate.Migration{
|
||||
}
|
||||
|
||||
var AddRemoteDB = &gormigrate.Migration{
|
||||
ID: "20230718-add-remote-db",
|
||||
ID: "20230720-add-remote-db",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Database{}); err != nil {
|
||||
if err := tx.AutoMigrate(&model.RemoteDB{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -40,5 +40,11 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
||||
cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
|
||||
cmdRouter.POST("/redis/conffile/update", baseApi.UpdateRedisConfByFile)
|
||||
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
|
||||
|
||||
cmdRouter.POST("/remote", baseApi.CreateRemoteDB)
|
||||
cmdRouter.POST("/remote/list/:type", baseApi.ListRemoteDB)
|
||||
cmdRouter.POST("/remote/update", baseApi.UpdateRemoteDB)
|
||||
cmdRouter.POST("/remote/search", baseApi.SearchRemoteDB)
|
||||
cmdRouter.POST("/remote/del", baseApi.DeleteRemoteDB)
|
||||
}
|
||||
}
|
||||
|
@ -22,19 +22,22 @@ type MysqlClient interface {
|
||||
}
|
||||
|
||||
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||
if conn.Type == "remote" {
|
||||
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.UserName, conn.Password, conn.Address, conn.Port)
|
||||
if conn.From == "remote" {
|
||||
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.Username, conn.Password, conn.Address, conn.Port)
|
||||
db, err := sql.Open("mysql", connArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := db.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.NewRemote(db), nil
|
||||
}
|
||||
if conn.Type == "local" {
|
||||
if cmd.CheckIllegal(conn.Address, conn.UserName, conn.Password) {
|
||||
if conn.From == "local" {
|
||||
if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) {
|
||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.UserName, "-p" + conn.Password + "-e"}
|
||||
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password + "-e"}
|
||||
return client.NewLocal(connArgs, conn.Address), nil
|
||||
}
|
||||
return nil, errors.New("no such type")
|
||||
|
@ -1,12 +1,11 @@
|
||||
package client
|
||||
|
||||
type DBInfo struct {
|
||||
Type string `json:"type"` // local remote
|
||||
From string `json:"from"` // local remote
|
||||
Address string `json:"address"`
|
||||
Port uint `json:"port"`
|
||||
UserName string `json:"userName"`
|
||||
Username string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
Format string `json:"format"`
|
||||
|
||||
Timeout uint `json:"timeout"` // second
|
||||
}
|
||||
@ -15,7 +14,7 @@ type CreateInfo struct {
|
||||
Name string `json:"name"`
|
||||
Format string `json:"format"`
|
||||
Version string `json:"version"`
|
||||
UserName string `json:"userName"`
|
||||
Username string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
Permission string `json:"permission"`
|
||||
|
||||
@ -25,7 +24,7 @@ type CreateInfo struct {
|
||||
type DeleteInfo struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
UserName string `json:"userName"`
|
||||
Username string `json:"userName"`
|
||||
Permission string `json:"permission"`
|
||||
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
@ -35,7 +34,7 @@ type DeleteInfo struct {
|
||||
type PasswordChangeInfo struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
UserName string `json:"userName"`
|
||||
Username string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
Permission string `json:"permission"`
|
||||
|
||||
@ -45,7 +44,7 @@ type PasswordChangeInfo struct {
|
||||
type AccessChangeInfo struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
UserName string `json:"userName"`
|
||||
Username string `json:"userName"`
|
||||
OldPermission string `json:"oldPermission"`
|
||||
Permission string `json:"permission"`
|
||||
|
||||
|
@ -31,7 +31,7 @@ func (r *Local) Create(info CreateInfo) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, UserName: info.UserName, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -44,11 +44,11 @@ func (r *Local) CreateUser(info CreateInfo) error {
|
||||
ips := strings.Split(info.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, info.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
@ -56,7 +56,7 @@ func (r *Local) CreateUser(info CreateInfo) error {
|
||||
_ = r.Delete(DeleteInfo{
|
||||
Name: info.Name,
|
||||
Version: info.Version,
|
||||
UserName: info.UserName,
|
||||
Username: info.Username,
|
||||
Permission: info.Permission,
|
||||
ForceDelete: true,
|
||||
Timeout: 300})
|
||||
@ -76,7 +76,7 @@ func (r *Local) CreateUser(info CreateInfo) error {
|
||||
_ = r.Delete(DeleteInfo{
|
||||
Name: info.Name,
|
||||
Version: info.Version,
|
||||
UserName: info.UserName,
|
||||
Username: info.Username,
|
||||
Permission: info.Permission,
|
||||
ForceDelete: true,
|
||||
Timeout: 300})
|
||||
@ -92,11 +92,11 @@ func (r *Local) Delete(info DeleteInfo) error {
|
||||
ips := strings.Split(info.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, info.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
@ -123,17 +123,17 @@ func (r *Local) Delete(info DeleteInfo) error {
|
||||
}
|
||||
|
||||
func (r *Local) ChangePassword(info PasswordChangeInfo) error {
|
||||
if info.UserName != "root" {
|
||||
if info.Username != "root" {
|
||||
var userlist []string
|
||||
if strings.Contains(info.Permission, ",") {
|
||||
ips := strings.Split(info.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, info.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
@ -168,24 +168,27 @@ func (r *Local) ChangePassword(info PasswordChangeInfo) error {
|
||||
}
|
||||
|
||||
func (r *Local) ChangeAccess(info AccessChangeInfo) error {
|
||||
if info.UserName == "root" {
|
||||
if info.Username == "root" {
|
||||
info.OldPermission = "%"
|
||||
info.Name = "*"
|
||||
}
|
||||
if info.Permission != info.OldPermission {
|
||||
if err := r.Delete(DeleteInfo{
|
||||
Version: info.Version,
|
||||
UserName: info.UserName,
|
||||
Username: info.Username,
|
||||
Permission: info.OldPermission,
|
||||
ForceDelete: true,
|
||||
Timeout: 300}); err != nil {
|
||||
return err
|
||||
}
|
||||
if info.UserName == "root" {
|
||||
if info.Username == "root" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, UserName: info.UserName, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.ExecSQL("flush privileges", 300); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -29,7 +29,7 @@ func (r *Remote) Create(info CreateInfo) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, UserName: info.UserName, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -42,11 +42,11 @@ func (r *Remote) CreateUser(info CreateInfo) error {
|
||||
ips := strings.Split(info.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, info.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
@ -54,7 +54,7 @@ func (r *Remote) CreateUser(info CreateInfo) error {
|
||||
_ = r.Delete(DeleteInfo{
|
||||
Name: info.Name,
|
||||
Version: info.Version,
|
||||
UserName: info.UserName,
|
||||
Username: info.Username,
|
||||
Permission: info.Permission,
|
||||
ForceDelete: true,
|
||||
Timeout: 300})
|
||||
@ -74,7 +74,7 @@ func (r *Remote) CreateUser(info CreateInfo) error {
|
||||
_ = r.Delete(DeleteInfo{
|
||||
Name: info.Name,
|
||||
Version: info.Version,
|
||||
UserName: info.UserName,
|
||||
Username: info.Username,
|
||||
Permission: info.Permission,
|
||||
ForceDelete: true,
|
||||
Timeout: 300})
|
||||
@ -90,11 +90,11 @@ func (r *Remote) Delete(info DeleteInfo) error {
|
||||
ips := strings.Split(info.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, info.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
@ -121,17 +121,17 @@ func (r *Remote) Delete(info DeleteInfo) error {
|
||||
}
|
||||
|
||||
func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
|
||||
if info.UserName != "root" {
|
||||
if info.Username != "root" {
|
||||
var userlist []string
|
||||
if strings.Contains(info.Permission, ",") {
|
||||
ips := strings.Split(info.Permission, ",")
|
||||
for _, ip := range ips {
|
||||
if len(ip) != 0 {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, ip))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.Username, info.Permission))
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
@ -166,24 +166,27 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
|
||||
}
|
||||
|
||||
func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
|
||||
if info.UserName == "root" {
|
||||
if info.Username == "root" {
|
||||
info.OldPermission = "%"
|
||||
info.Name = "*"
|
||||
}
|
||||
if info.Permission != info.OldPermission {
|
||||
if err := r.Delete(DeleteInfo{
|
||||
Version: info.Version,
|
||||
UserName: info.UserName,
|
||||
Username: info.Username,
|
||||
Permission: info.OldPermission,
|
||||
ForceDelete: true,
|
||||
Timeout: 300}); err != nil {
|
||||
return err
|
||||
}
|
||||
if info.UserName == "root" {
|
||||
if info.Username == "root" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, UserName: info.UserName, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.ExecSQL("flush privileges", 300); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -131,6 +131,7 @@ export namespace App {
|
||||
|
||||
export interface DatabaseConnInfo {
|
||||
password: string;
|
||||
privilege: boolean;
|
||||
serviceName: string;
|
||||
port: number;
|
||||
}
|
||||
|
@ -167,6 +167,40 @@ export namespace Database {
|
||||
createdAt: Date;
|
||||
name: string;
|
||||
type: string;
|
||||
version: string;
|
||||
from: string;
|
||||
address: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
description: string;
|
||||
}
|
||||
export interface SearchRemoteDBPage {
|
||||
info: string;
|
||||
type: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
orderBy?: string;
|
||||
order?: string;
|
||||
}
|
||||
export interface RemoteDBOption {
|
||||
id: number;
|
||||
name: string;
|
||||
address: string;
|
||||
}
|
||||
export interface RemoteDBCreate {
|
||||
name: string;
|
||||
version: string;
|
||||
from: string;
|
||||
address: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
description: string;
|
||||
}
|
||||
export interface RemoteDBUpdate {
|
||||
id: number;
|
||||
version: string;
|
||||
address: string;
|
||||
port: number;
|
||||
username: string;
|
||||
|
@ -85,13 +85,16 @@ export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) =>
|
||||
};
|
||||
|
||||
// remote
|
||||
export const searchRemoteDBs = (params: SearchWithPage) => {
|
||||
export const searchRemoteDBs = (params: Database.SearchRemoteDBPage) => {
|
||||
return http.post<ResPage<Database.RemoteDBInfo>>(`/databases/remote/search`, params);
|
||||
};
|
||||
export const addRemoteDB = (params: Database.RemoteDBInfo) => {
|
||||
export const listRemoteDBs = (type: string) => {
|
||||
return http.get<Array<Database.RemoteDBOption>>(`/databases/remote/list/${type}`);
|
||||
};
|
||||
export const addRemoteDB = (params: Database.RemoteDBCreate) => {
|
||||
return http.post(`/databases/remote`, params);
|
||||
};
|
||||
export const editRemoteDB = (params: Database.RemoteDBInfo) => {
|
||||
export const editRemoteDB = (params: Database.RemoteDBUpdate) => {
|
||||
return http.post(`/databases/remote/update`, params);
|
||||
};
|
||||
export const deleteRemoteDB = (id: number) => {
|
||||
|
@ -330,6 +330,13 @@ const message = {
|
||||
|
||||
confChange: '配置修改',
|
||||
|
||||
remoteDB: '远程数据库',
|
||||
createRemoteDB: '创建远程数据库',
|
||||
editRemoteDB: '编辑远程数据库',
|
||||
localDB: '本地数据库',
|
||||
address: '数据库地址',
|
||||
version: '数据库版本',
|
||||
|
||||
selectFile: '选择文件',
|
||||
dropHelper: '将上传文件拖拽到此处,或者',
|
||||
clickHelper: '点击上传',
|
||||
|
@ -27,6 +27,16 @@ const databaseRouter = {
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'mysql/remote',
|
||||
name: 'MySQL-Remote',
|
||||
component: () => import('@/views/database/mysql/remote/index.vue'),
|
||||
hidden: true,
|
||||
meta: {
|
||||
activeMenu: '/databases',
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'redis',
|
||||
name: 'Redis',
|
||||
|
@ -20,8 +20,8 @@
|
||||
<el-button @click="onChangeRootPassword" type="primary" plain>
|
||||
{{ $t('database.databaseConnInfo') }}
|
||||
</el-button>
|
||||
<el-button @click="onChangeAccess" type="primary" plain>
|
||||
{{ $t('database.remoteAccess') }}
|
||||
<el-button @click="goRemoteDB" type="primary" plain>
|
||||
{{ $t('database.remoteDB') }}
|
||||
</el-button>
|
||||
<el-button @click="goDashboard" icon="Position" type="primary" plain>phpMyAdmin</el-button>
|
||||
</el-col>
|
||||
@ -134,7 +134,6 @@
|
||||
|
||||
<PasswordDialog ref="passwordRef" @search="search" />
|
||||
<RootPasswordDialog ref="rootPasswordRef" />
|
||||
<RemoteAccessDialog ref="remoteAccessRef" />
|
||||
<UploadDialog ref="uploadRef" />
|
||||
<OperateDialog @search="search" ref="dialogRef" />
|
||||
<Backups ref="dialogBackupRef" />
|
||||
@ -151,7 +150,6 @@ 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 RemoteAccessDialog from '@/views/database/mysql/remote/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';
|
||||
@ -160,7 +158,7 @@ import UploadDialog from '@/components/upload/index.vue';
|
||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { deleteCheckMysqlDB, loadRemoteAccess, searchMysqlDBs, updateMysqlDescription } from '@/api/modules/database';
|
||||
import { deleteCheckMysqlDB, searchMysqlDBs, updateMysqlDescription } from '@/api/modules/database';
|
||||
import i18n from '@/lang';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { App } from '@/api/interface/app';
|
||||
@ -222,13 +220,8 @@ const onChangeRootPassword = async () => {
|
||||
rootPasswordRef.value!.acceptParams();
|
||||
};
|
||||
|
||||
const remoteAccessRef = ref();
|
||||
const onChangeAccess = async () => {
|
||||
const res = await loadRemoteAccess();
|
||||
let param = {
|
||||
privilege: res.data,
|
||||
};
|
||||
remoteAccessRef.value!.acceptParams(param);
|
||||
const goRemoteDB = async () => {
|
||||
router.push({ name: 'MySQL-Remote' });
|
||||
};
|
||||
|
||||
const passwordRef = ref();
|
||||
|
@ -1,98 +1,181 @@
|
||||
<template>
|
||||
<el-drawer v-model="dialogVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('database.remoteAccess')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.remoteAccess')" :rules="Rules.requiredInput" prop="privilege">
|
||||
<el-switch v-model="form.privilege" />
|
||||
<span class="input-help">{{ $t('database.remoteConnHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div v-loading="loading">
|
||||
<LayoutContent>
|
||||
<template #title>
|
||||
<back-button name="MySQL" :header="'MySQL ' + $t('database.remoteDB')" />
|
||||
</template>
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20">
|
||||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('database.createRemoteDB') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4">
|
||||
<div class="search-button">
|
||||
<el-input
|
||||
v-model="searchName"
|
||||
clearable
|
||||
@clear="search()"
|
||||
suffix-icon="Search"
|
||||
@keyup.enter="search()"
|
||||
@change="search()"
|
||||
:placeholder="$t('commons.button.search')"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable :pagination-config="paginationConfig" @sort-change="search" @search="search" :data="data">
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name" sortable />
|
||||
<el-table-column :label="$t('database.address')" prop="address" />
|
||||
<el-table-column :label="$t('commons.login.username')" prop="username" />
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<span style="float: left; line-height: 25px" v-if="!row.showPassword">***********</span>
|
||||
<div style="cursor: pointer; float: left" v-if="!row.showPassword">
|
||||
<el-icon
|
||||
style="margin-left: 5px; margin-top: 3px"
|
||||
@click="row.showPassword = true"
|
||||
:size="16"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
</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"
|
||||
>
|
||||
<Hide />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div style="cursor: pointer; float: left">
|
||||
<el-icon style="margin-left: 5px; margin-top: 3px" :size="16" @click="onCopy(row)">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="description"
|
||||
:label="$t('commons.table.description')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
:label="$t('commons.table.date')"
|
||||
:formatter="dateFormat"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<fu-table-operations
|
||||
width="370px"
|
||||
:buttons="buttons"
|
||||
:ellipsis="10"
|
||||
:label="$t('commons.table.operate')"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit"></ConfirmDialog>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="onSave(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
<OperateDialog ref="dialogRef" @search="search" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { deleteRemoteDB, searchRemoteDBs } from '@/api/modules/database';
|
||||
import OperateDialog from '@/views/database/mysql/remote/operate/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { updateMysqlAccess } from '@/api/modules/database';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import useClipboard from 'vue-clipboard3';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
const { toClipboard } = useClipboard();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const dialogVisiable = ref(false);
|
||||
const form = reactive({
|
||||
privilege: false,
|
||||
const dialogRef = ref();
|
||||
|
||||
const data = ref();
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const confirmDialogRef = ref();
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
interface DialogProps {
|
||||
privilege: boolean;
|
||||
}
|
||||
|
||||
const acceptParams = (prop: DialogProps): void => {
|
||||
form.privilege = prop.privilege;
|
||||
dialogVisiable.value = true;
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisiable.value = false;
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
let param = {
|
||||
id: 0,
|
||||
value: form.privilege ? '%' : 'localhost',
|
||||
const search = async (column?: any) => {
|
||||
let params = {
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
info: searchName.value,
|
||||
type: 'mysql',
|
||||
orderBy: column?.order ? column.prop : 'created_at',
|
||||
order: column?.order ? column.order : 'null',
|
||||
};
|
||||
loading.value = true;
|
||||
await updateMysqlAccess(param)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
dialogVisiable.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
const res = await searchRemoteDBs(params);
|
||||
data.value = res.data.items || [];
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
||||
const onSave = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let params = {
|
||||
header: i18n.global.t('database.confChange'),
|
||||
operationInfo: i18n.global.t('database.restartNowHelper'),
|
||||
submitInputInfo: i18n.global.t('database.restartNow'),
|
||||
};
|
||||
confirmDialogRef.value!.acceptParams(params);
|
||||
});
|
||||
const onOpenDialog = async (
|
||||
title: string,
|
||||
rowData: Partial<Database.RemoteDBInfo> = {
|
||||
name: '',
|
||||
type: 'mysql',
|
||||
version: '5.6.x',
|
||||
address: '',
|
||||
port: 3306,
|
||||
username: '',
|
||||
password: '',
|
||||
description: '',
|
||||
},
|
||||
) => {
|
||||
let params = {
|
||||
title,
|
||||
rowData: { ...rowData },
|
||||
};
|
||||
dialogRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
const onCopy = async (row: any) => {
|
||||
try {
|
||||
await toClipboard(row.password);
|
||||
MsgSuccess(i18n.global.t('commons.msg.copySuccess'));
|
||||
} catch (e) {
|
||||
MsgError(i18n.global.t('commons.msg.copyfailed'));
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = async (row: Database.RemoteDBInfo) => {
|
||||
await useDeleteData(deleteRemoteDB, row.id, 'commons.msg.delete');
|
||||
search();
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
click: (row: Database.RemoteDBInfo) => {
|
||||
onOpenDialog('edit', row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: (row: Database.RemoteDBInfo) => {
|
||||
onDelete(row);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
||||
|
@ -6,33 +6,33 @@
|
||||
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
||||
<el-input
|
||||
:disabled="dialogData.title === 'edit'"
|
||||
clearable
|
||||
v-model.trim="dialogData.rowData!.name"
|
||||
/>
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.name" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
||||
<el-select v-model="dialogData.rowData!.type">
|
||||
<el-option value="Mysql" label="Mysql" />
|
||||
<el-option value="Redis" label="Redis" />
|
||||
<el-form-item :label="$t('database.version')" prop="version">
|
||||
<el-select v-model="dialogData.rowData!.version">
|
||||
<el-option value="5.6.x" label="5.6.x" />
|
||||
<el-option value="5.7.x" label="5.7.x" />
|
||||
<el-option value="8.0.x" label="8.0.x" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="address">
|
||||
<el-form-item :label="$t('database.address')" prop="address">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.address" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="port">
|
||||
<el-input clearable v-model.number="dialogData.rowData!.port" />
|
||||
<el-form-item :label="$t('commons.table.port')" prop="port">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.port" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="username">
|
||||
<el-form-item :label="$t('commons.login.username')" prop="username">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.username" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="password">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.password" />
|
||||
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||
<el-input type="password" clearable show-password v-model.trim="dialogData.rowData!.password">
|
||||
<template #append>
|
||||
<el-button @click="random">{{ $t('commons.button.random') }}</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('cronjob.description')" prop="description">
|
||||
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.description" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -53,14 +53,16 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { addCronjob, editCronjob } from '@/api/modules/cronjob';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { getRandomStr } from '@/utils/util';
|
||||
import { addRemoteDB, editRemoteDB } from '@/api/modules/database';
|
||||
|
||||
interface DialogProps {
|
||||
title: string;
|
||||
rowData?: Cronjob.CronjobInfo;
|
||||
rowData?: Database.RemoteDBInfo;
|
||||
getTableList?: () => Promise<any>;
|
||||
}
|
||||
const title = ref<string>('');
|
||||
@ -70,7 +72,7 @@ const dialogData = ref<DialogProps>({
|
||||
});
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
dialogData.value = params;
|
||||
title.value = i18n.global.t('cronjob.' + dialogData.value.title);
|
||||
title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');
|
||||
drawerVisiable.value = true;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
@ -81,23 +83,49 @@ const handleClose = () => {
|
||||
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput],
|
||||
type: [Rules.requiredSelect],
|
||||
address: [Rules.requiredInput],
|
||||
version: [Rules.requiredSelect],
|
||||
address: [Rules.ip],
|
||||
port: [Rules.port],
|
||||
username: [Rules.requiredInput],
|
||||
password: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const random = async () => {
|
||||
dialogData.value.rowData!.password = getRandomStr(16);
|
||||
};
|
||||
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
if (dialogData.value.title === 'create') {
|
||||
await addCronjob(dialogData.value.rowData);
|
||||
let param = {
|
||||
name: dialogData.value.rowData.name,
|
||||
type: 'mysql',
|
||||
version: dialogData.value.rowData.version,
|
||||
from: 'remote',
|
||||
address: dialogData.value.rowData.address,
|
||||
port: dialogData.value.rowData.port,
|
||||
username: dialogData.value.rowData.username,
|
||||
password: dialogData.value.rowData.password,
|
||||
description: dialogData.value.rowData.description,
|
||||
};
|
||||
await addRemoteDB(param);
|
||||
}
|
||||
if (dialogData.value.title === 'edit') {
|
||||
await editCronjob(dialogData.value.rowData);
|
||||
let param = {
|
||||
id: dialogData.value.rowData.id,
|
||||
version: dialogData.value.rowData.version,
|
||||
address: dialogData.value.rowData.address,
|
||||
port: dialogData.value.rowData.port,
|
||||
username: dialogData.value.rowData.username,
|
||||
password: dialogData.value.rowData.password,
|
||||
description: dialogData.value.rowData.description,
|
||||
};
|
||||
await editRemoteDB(param);
|
||||
}
|
||||
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
@ -6,22 +6,6 @@
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.rootPassword')" :rules="Rules.requiredInput" prop="password">
|
||||
<el-input type="password" show-password clearable v-model="form.password">
|
||||
<template #append>
|
||||
<el-button @click="onCopy(form.password)">{{ $t('commons.button.copy') }}</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button @click="random">
|
||||
{{ $t('commons.button.random') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.serviceName')" prop="serviceName">
|
||||
<el-tag>{{ form.serviceName }}</el-tag>
|
||||
<el-button @click="onCopy(form.serviceName)" icon="DocumentCopy" link></el-button>
|
||||
<span class="input-help">{{ $t('database.serviceNameHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.containerConn')">
|
||||
<el-tag>
|
||||
{{ form.serviceName + ':3306' }}
|
||||
@ -35,11 +19,30 @@
|
||||
<el-tag>{{ $t('database.localIP') + ':' + form.port }}</el-tag>
|
||||
<span class="input-help">{{ $t('database.remoteConnHelper2') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider border-style="dashed" />
|
||||
|
||||
<el-form-item :label="$t('database.remoteAccess')" prop="privilege">
|
||||
<el-switch v-model="form.privilege" @change="onSaveAccess" />
|
||||
<span class="input-help">{{ $t('database.remoteConnHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.rootPassword')" :rules="Rules.requiredInput" prop="password">
|
||||
<el-input type="password" show-password clearable v-model="form.password">
|
||||
<template #append>
|
||||
<el-button @click="onCopy(form.password)">{{ $t('commons.button.copy') }}</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button @click="random">
|
||||
{{ $t('commons.button.random') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit"></ConfirmDialog>
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" @cancel="loadPassword"></ConfirmDialog>
|
||||
<ConfirmDialog ref="confirmAccessDialogRef" @confirm="onSubmitAccess" @cancel="loadAccess"></ConfirmDialog>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@ -59,7 +62,7 @@ import { ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { updateMysqlPassword } from '@/api/modules/database';
|
||||
import { 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';
|
||||
@ -75,10 +78,12 @@ const dialogVisiable = ref(false);
|
||||
const form = ref<App.DatabaseConnInfo>({
|
||||
password: '',
|
||||
serviceName: '',
|
||||
privilege: false,
|
||||
port: 0,
|
||||
});
|
||||
|
||||
const confirmDialogRef = ref();
|
||||
const confirmAccessDialogRef = ref();
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
@ -86,6 +91,7 @@ const formRef = ref<FormInstance>();
|
||||
const acceptParams = (): void => {
|
||||
form.value.password = '';
|
||||
loadPassword();
|
||||
loadAccess();
|
||||
dialogVisiable.value = true;
|
||||
};
|
||||
|
||||
@ -106,6 +112,11 @@ const handleClose = () => {
|
||||
dialogVisiable.value = false;
|
||||
};
|
||||
|
||||
const loadAccess = async () => {
|
||||
const res = await loadRemoteAccess();
|
||||
form.value.privilege = res.data;
|
||||
};
|
||||
|
||||
const loadPassword = async () => {
|
||||
const res = await GetAppConnInfo('mysql');
|
||||
form.value = res.data;
|
||||
@ -141,6 +152,32 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmitAccess = async () => {
|
||||
let param = {
|
||||
id: 0,
|
||||
value: form.value.privilege ? '%' : 'localhost',
|
||||
};
|
||||
loading.value = true;
|
||||
await updateMysqlAccess(param)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
dialogVisiable.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const onSaveAccess = () => {
|
||||
let params = {
|
||||
header: i18n.global.t('database.confChange'),
|
||||
operationInfo: i18n.global.t('database.restartNowHelper'),
|
||||
submitInputInfo: i18n.global.t('database.restartNow'),
|
||||
};
|
||||
confirmAccessDialogRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
|
@ -1,152 +0,0 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<LayoutContent :title="'MySQL ' + $t('menu.database')">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20">
|
||||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('database.create') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4">
|
||||
<div class="search-button">
|
||||
<el-input
|
||||
v-model="searchName"
|
||||
clearable
|
||||
@clear="search()"
|
||||
suffix-icon="Search"
|
||||
@keyup.enter="search()"
|
||||
@change="search()"
|
||||
:placeholder="$t('commons.button.search')"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable :pagination-config="paginationConfig" @sort-change="search" @search="search" :data="data">
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name" sortable />
|
||||
<el-table-column :label="$t('commons.login.username')" prop="address" />
|
||||
<el-table-column :label="$t('commons.login.username')" prop="username" />
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<span style="float: left; line-height: 25px" v-if="!row.showPassword">***********</span>
|
||||
<div style="cursor: pointer; float: left" v-if="!row.showPassword">
|
||||
<el-icon
|
||||
style="margin-left: 5px; margin-top: 3px"
|
||||
@click="row.showPassword = true"
|
||||
:size="16"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
</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"
|
||||
>
|
||||
<Hide />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div style="cursor: pointer; float: left">
|
||||
<el-icon style="margin-left: 5px; margin-top: 3px" :size="16" @click="onCopy(row)">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
:label="$t('commons.table.date')"
|
||||
:formatter="dateFormat"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
|
||||
<OperateDialog ref="dialogRef" @search="search" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { searchRemoteDBs } from '@/api/modules/database';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import useClipboard from 'vue-clipboard3';
|
||||
import { Database } from '@/api/interface/database';
|
||||
const { toClipboard } = useClipboard();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const dialogRef = ref();
|
||||
|
||||
const data = ref();
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const search = async (column?: any) => {
|
||||
let params = {
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
info: searchName.value,
|
||||
orderBy: column?.order ? column.prop : 'created_at',
|
||||
order: column?.order ? column.order : 'null',
|
||||
};
|
||||
const res = await searchRemoteDBs(params);
|
||||
data.value = res.data.items || [];
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
||||
const onOpenDialog = async (
|
||||
title: string,
|
||||
rowData: Partial<Database.RemoteDBInfo> = {
|
||||
name: '',
|
||||
type: 'Mysql',
|
||||
address: '',
|
||||
port: 3306,
|
||||
username: '',
|
||||
password: '',
|
||||
description: '',
|
||||
},
|
||||
) => {
|
||||
let params = {
|
||||
title,
|
||||
rowData: { ...rowData },
|
||||
};
|
||||
dialogRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
const onCopy = async (row: any) => {
|
||||
try {
|
||||
await toClipboard(row.password);
|
||||
MsgSuccess(i18n.global.t('commons.msg.copySuccess'));
|
||||
} catch (e) {
|
||||
MsgError(i18n.global.t('commons.msg.copyfailed'));
|
||||
}
|
||||
};
|
||||
|
||||
// const onDelete = async (row: Database.MysqlDBInfo) => {
|
||||
// const res = await deleteCheckMysqlDB(row.id);
|
||||
// deleteRef.value.acceptParams({ id: row.id, name: row.name });
|
||||
// };
|
||||
|
||||
// const buttons = [
|
||||
// {
|
||||
// label: i18n.global.t('commons.button.delete'),
|
||||
// click: (row: Database.MysqlDBInfo) => {
|
||||
// onDelete(row);
|
||||
// },
|
||||
// },
|
||||
// ];
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user