mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-02-12 11:30:07 +08:00
feat: 实现远程数据库增删改查
This commit is contained in:
parent
bd5dc56b66
commit
cb7351a9fb
@ -22,6 +22,7 @@ var (
|
|||||||
dockerService = service.NewIDockerService()
|
dockerService = service.NewIDockerService()
|
||||||
|
|
||||||
mysqlService = service.NewIMysqlService()
|
mysqlService = service.NewIMysqlService()
|
||||||
|
remoteDBService = service.NewIRemoteDBService()
|
||||||
redisService = service.NewIRedisService()
|
redisService = service.NewIRedisService()
|
||||||
|
|
||||||
cronjobService = service.NewICronjobService()
|
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 {
|
type MysqlDBCreate struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
|
From string `json:"from" validate:"required"`
|
||||||
Format string `json:"format" validate:"required,oneof=utf8mb4 utf8 gbk big5"`
|
Format string `json:"format" validate:"required,oneof=utf8mb4 utf8 gbk big5"`
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username" validate:"required"`
|
||||||
Password string `json:"password" validate:"required"`
|
Password string `json:"password" validate:"required"`
|
||||||
@ -100,6 +101,7 @@ type MysqlConfUpdateByFile struct {
|
|||||||
|
|
||||||
type ChangeDBInfo struct {
|
type ChangeDBInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
|
From string `json:"from" validate:"required"`
|
||||||
Value string `json:"value" 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 {
|
type DatabaseMysql struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `json:"name" gorm:"type:varchar(256);not null"`
|
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"`
|
MysqlName string `json:"mysqlName" gorm:"type:varchar(64);not null"`
|
||||||
Format string `json:"format" 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"`
|
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type Database struct {
|
type RemoteDB struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||||
Type string `json:"type" 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"`
|
Address string `json:"address" gorm:"type:varchar(64);not null"`
|
||||||
Port uint `json:"port" gorm:"type:decimal;not null"`
|
Port uint `json:"port" gorm:"type:decimal;not null"`
|
||||||
Username string `json:"username" gorm:"type:varchar(64)"`
|
Username string `json:"username" gorm:"type:varchar(64)"`
|
||||||
Password string `json:"password" 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);"`
|
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/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
"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/go-sql-driver/mysql"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -70,50 +72,69 @@ func (u *MysqlService) ListDBName() ([]string, error) {
|
|||||||
return dbNames, err
|
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) {
|
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) {
|
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Permission) {
|
||||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var createItem model.DatabaseMysql
|
||||||
|
if err := copier.Copy(&createItem, &req); err != nil {
|
||||||
|
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
if req.Username == "root" {
|
if req.Username == "root" {
|
||||||
return nil, errors.New("Cannot set root as user name")
|
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", "")
|
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name))
|
mysqlData, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name))
|
||||||
if mysql.ID != 0 {
|
if mysqlData.ID != 0 {
|
||||||
return nil, constant.ErrRecordExist
|
return nil, constant.ErrRecordExist
|
||||||
}
|
}
|
||||||
if err := copier.Copy(&mysql, &req); err != nil {
|
dbInfo.Address = app.ContainerName
|
||||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
dbInfo.Username = "root"
|
||||||
}
|
dbInfo.Password = app.Password
|
||||||
|
version = app.Version
|
||||||
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", req.Name, req.Format, formatMap[req.Format])
|
createItem.MysqlName = app.Name
|
||||||
if err := excSQL(app.ContainerName, app.Password, createSql); err != nil {
|
} else {
|
||||||
if strings.Contains(err.Error(), "ERROR 1007") {
|
mysqlData, err := remoteDBRepo.Get(commonRepo.WithByName(req.From))
|
||||||
return nil, buserr.New(constant.ErrDatabaseIsExist)
|
if err != nil {
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, req); err != nil {
|
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 := cli.Create(client.CreateInfo{
|
||||||
|
Name: req.Name,
|
||||||
|
Format: req.Format,
|
||||||
|
Version: version,
|
||||||
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
global.LOG.Infof("create database %s successful!", req.Name)
|
global.LOG.Infof("create database %s successful!", req.Name)
|
||||||
mysql.MysqlName = app.Name
|
if err := mysqlRepo.Create(ctx, &createItem); err != nil {
|
||||||
if err := mysqlRepo.Create(ctx, &mysql); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &mysql, nil
|
return &createItem, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
|
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 {
|
func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error {
|
||||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
cli, version, err := loadClientByID(req.ID)
|
||||||
if err != nil && !req.ForceDelete {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := mysqlRepo.Get(commonRepo.WithByID(req.ID))
|
db, err := mysqlRepo.Get(commonRepo.WithByID(req.ID))
|
||||||
if err != nil && !req.ForceDelete {
|
if err != nil && !req.ForceDelete {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := cli.Delete(client.DeleteInfo{
|
||||||
|
Name: db.Name,
|
||||||
|
Version: version,
|
||||||
|
Username: db.Username,
|
||||||
|
Permission: db.Permission,
|
||||||
|
Timeout: 300,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(app.Version, "5.6") {
|
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
||||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user '%s'@'%s'", db.Username, db.Permission)); err != nil && !req.ForceDelete {
|
if err != nil && !req.ForceDelete {
|
||||||
return err
|
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 {
|
|
||||||
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)
|
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 {
|
if _, err := os.Stat(uploadDir); err == nil {
|
||||||
@ -192,66 +212,55 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
|
|||||||
if cmd.CheckIllegal(info.Value) {
|
if cmd.CheckIllegal(info.Value) {
|
||||||
return buserr.New(constant.ErrCmdIllegal)
|
return buserr.New(constant.ErrCmdIllegal)
|
||||||
}
|
}
|
||||||
|
cli, version, err := loadClientByID(info.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
mysql model.DatabaseMysql
|
mysqlData model.DatabaseMysql
|
||||||
err error
|
passwordInfo client.PasswordChangeInfo
|
||||||
)
|
)
|
||||||
|
passwordInfo.Password = info.Value
|
||||||
|
passwordInfo.Timeout = 300
|
||||||
|
passwordInfo.Version = version
|
||||||
|
|
||||||
if info.ID != 0 {
|
if info.ID != 0 {
|
||||||
mysql, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 := cli.ChangePassword(passwordInfo); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
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 {
|
if info.ID != 0 {
|
||||||
appRess, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysql.ID))
|
// appRess, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysqlData.ID))
|
||||||
for _, appRes := range appRess {
|
// for _, appRes := range appRess {
|
||||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
// appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
// appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
|
// 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 {
|
// if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, info.Value); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if err := excuteSql(app.ContainerName, app.Password, passwordChangeCMD); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
global.LOG.Info("excute password change sql successful")
|
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
|
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 {
|
if err := updateInstallInfoInDB("mysql", "", "password", false, info.Value); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -265,71 +274,36 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
|||||||
if cmd.CheckIllegal(info.Value) {
|
if cmd.CheckIllegal(info.Value) {
|
||||||
return buserr.New(constant.ErrCmdIllegal)
|
return buserr.New(constant.ErrCmdIllegal)
|
||||||
}
|
}
|
||||||
|
cli, version, err := loadClientByID(info.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
mysql model.DatabaseMysql
|
mysqlData model.DatabaseMysql
|
||||||
err error
|
accessInfo client.AccessChangeInfo
|
||||||
)
|
)
|
||||||
|
accessInfo.Permission = info.Value
|
||||||
|
accessInfo.Timeout = 300
|
||||||
|
accessInfo.Version = version
|
||||||
|
|
||||||
if info.ID != 0 {
|
if info.ID != 0 {
|
||||||
mysql, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.Value == mysql.Permission {
|
accessInfo.Name = mysqlData.Name
|
||||||
return nil
|
accessInfo.Username = mysqlData.Username
|
||||||
}
|
accessInfo.OldPermission = mysqlData.Permission
|
||||||
}
|
|
||||||
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.ID == 0 {
|
|
||||||
mysql.Name = "*"
|
|
||||||
mysql.Username = "root"
|
|
||||||
mysql.Permission = "%"
|
|
||||||
mysql.Password = app.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Value != mysql.Permission {
|
|
||||||
var userlist []string
|
|
||||||
if strings.Contains(mysql.Permission, ",") {
|
|
||||||
userlist = strings.Split(mysql.Permission, ",")
|
|
||||||
} else {
|
} else {
|
||||||
userlist = append(userlist, mysql.Permission)
|
accessInfo.Username = "root"
|
||||||
}
|
}
|
||||||
for _, user := range userlist {
|
if err := cli.ChangeAccess(accessInfo); err != nil {
|
||||||
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
|
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 err := u.createUser(app.ContainerName, app.Password, app.Version, dto.MysqlDBCreate{
|
if mysqlData.ID != 0 {
|
||||||
Username: mysql.Username,
|
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"permission": info.Value})
|
||||||
Name: mysql.Name,
|
|
||||||
Permission: info.Value,
|
|
||||||
Password: mysql.Password,
|
|
||||||
}); 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})
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -493,53 +467,6 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
|||||||
return &info, nil
|
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) {
|
func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) {
|
||||||
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||||
stdout, err := cmd.CombinedOutput()
|
stdout, err := cmd.CombinedOutput()
|
||||||
@ -569,31 +496,6 @@ func excuteSqlForRows(containerName, password, command string) ([]string, error)
|
|||||||
return strings.Split(stdStr, "\n"), nil
|
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 {
|
func updateMyCnf(oldFiles []string, group string, param string, value interface{}) []string {
|
||||||
isOn := false
|
isOn := false
|
||||||
hasGroup := false
|
hasGroup := false
|
||||||
@ -634,3 +536,46 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{
|
|||||||
}
|
}
|
||||||
return newFiles
|
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
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ var (
|
|||||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||||
|
|
||||||
mysqlRepo = repo.NewIMysqlRepo()
|
mysqlRepo = repo.NewIMysqlRepo()
|
||||||
|
remoteDBRepo = repo.NewIRemoteDBRepo()
|
||||||
|
|
||||||
imageRepoRepo = repo.NewIImageRepoRepo()
|
imageRepoRepo = repo.NewIImageRepoRepo()
|
||||||
composeRepo = repo.NewIComposeTemplateRepo()
|
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{
|
var AddRemoteDB = &gormigrate.Migration{
|
||||||
ID: "20230718-add-remote-db",
|
ID: "20230720-add-remote-db",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -40,5 +40,11 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
|||||||
cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
|
cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
|
||||||
cmdRouter.POST("/redis/conffile/update", baseApi.UpdateRedisConfByFile)
|
cmdRouter.POST("/redis/conffile/update", baseApi.UpdateRedisConfByFile)
|
||||||
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
|
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) {
|
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||||
if conn.Type == "remote" {
|
if conn.From == "remote" {
|
||||||
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.UserName, conn.Password, conn.Address, conn.Port)
|
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.Username, conn.Password, conn.Address, conn.Port)
|
||||||
db, err := sql.Open("mysql", connArgs)
|
db, err := sql.Open("mysql", connArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := db.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return client.NewRemote(db), nil
|
return client.NewRemote(db), nil
|
||||||
}
|
}
|
||||||
if conn.Type == "local" {
|
if conn.From == "local" {
|
||||||
if cmd.CheckIllegal(conn.Address, conn.UserName, conn.Password) {
|
if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) {
|
||||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
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 client.NewLocal(connArgs, conn.Address), nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("no such type")
|
return nil, errors.New("no such type")
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
type DBInfo struct {
|
type DBInfo struct {
|
||||||
Type string `json:"type"` // local remote
|
From string `json:"from"` // local remote
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Port uint `json:"port"`
|
Port uint `json:"port"`
|
||||||
UserName string `json:"userName"`
|
Username string `json:"userName"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Format string `json:"format"`
|
|
||||||
|
|
||||||
Timeout uint `json:"timeout"` // second
|
Timeout uint `json:"timeout"` // second
|
||||||
}
|
}
|
||||||
@ -15,7 +14,7 @@ type CreateInfo struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Format string `json:"format"`
|
Format string `json:"format"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
UserName string `json:"userName"`
|
Username string `json:"userName"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Permission string `json:"permission"`
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ type CreateInfo struct {
|
|||||||
type DeleteInfo struct {
|
type DeleteInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
UserName string `json:"userName"`
|
Username string `json:"userName"`
|
||||||
Permission string `json:"permission"`
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
ForceDelete bool `json:"forceDelete"`
|
ForceDelete bool `json:"forceDelete"`
|
||||||
@ -35,7 +34,7 @@ type DeleteInfo struct {
|
|||||||
type PasswordChangeInfo struct {
|
type PasswordChangeInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
UserName string `json:"userName"`
|
Username string `json:"userName"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Permission string `json:"permission"`
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ type PasswordChangeInfo struct {
|
|||||||
type AccessChangeInfo struct {
|
type AccessChangeInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
UserName string `json:"userName"`
|
Username string `json:"userName"`
|
||||||
OldPermission string `json:"oldPermission"`
|
OldPermission string `json:"oldPermission"`
|
||||||
Permission string `json:"permission"`
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func (r *Local) Create(info CreateInfo) error {
|
|||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,11 +44,11 @@ func (r *Local) CreateUser(info CreateInfo) error {
|
|||||||
ips := strings.Split(info.Permission, ",")
|
ips := strings.Split(info.Permission, ",")
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) != 0 {
|
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 {
|
} 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 {
|
for _, user := range userlist {
|
||||||
@ -56,7 +56,7 @@ func (r *Local) CreateUser(info CreateInfo) error {
|
|||||||
_ = r.Delete(DeleteInfo{
|
_ = r.Delete(DeleteInfo{
|
||||||
Name: info.Name,
|
Name: info.Name,
|
||||||
Version: info.Version,
|
Version: info.Version,
|
||||||
UserName: info.UserName,
|
Username: info.Username,
|
||||||
Permission: info.Permission,
|
Permission: info.Permission,
|
||||||
ForceDelete: true,
|
ForceDelete: true,
|
||||||
Timeout: 300})
|
Timeout: 300})
|
||||||
@ -76,7 +76,7 @@ func (r *Local) CreateUser(info CreateInfo) error {
|
|||||||
_ = r.Delete(DeleteInfo{
|
_ = r.Delete(DeleteInfo{
|
||||||
Name: info.Name,
|
Name: info.Name,
|
||||||
Version: info.Version,
|
Version: info.Version,
|
||||||
UserName: info.UserName,
|
Username: info.Username,
|
||||||
Permission: info.Permission,
|
Permission: info.Permission,
|
||||||
ForceDelete: true,
|
ForceDelete: true,
|
||||||
Timeout: 300})
|
Timeout: 300})
|
||||||
@ -92,11 +92,11 @@ func (r *Local) Delete(info DeleteInfo) error {
|
|||||||
ips := strings.Split(info.Permission, ",")
|
ips := strings.Split(info.Permission, ",")
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) != 0 {
|
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 {
|
} 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 {
|
for _, user := range userlist {
|
||||||
@ -123,17 +123,17 @@ func (r *Local) Delete(info DeleteInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Local) ChangePassword(info PasswordChangeInfo) error {
|
func (r *Local) ChangePassword(info PasswordChangeInfo) error {
|
||||||
if info.UserName != "root" {
|
if info.Username != "root" {
|
||||||
var userlist []string
|
var userlist []string
|
||||||
if strings.Contains(info.Permission, ",") {
|
if strings.Contains(info.Permission, ",") {
|
||||||
ips := strings.Split(info.Permission, ",")
|
ips := strings.Split(info.Permission, ",")
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) != 0 {
|
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 {
|
} 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 {
|
for _, user := range userlist {
|
||||||
@ -168,24 +168,27 @@ func (r *Local) ChangePassword(info PasswordChangeInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Local) ChangeAccess(info AccessChangeInfo) error {
|
func (r *Local) ChangeAccess(info AccessChangeInfo) error {
|
||||||
if info.UserName == "root" {
|
if info.Username == "root" {
|
||||||
info.OldPermission = "%"
|
info.OldPermission = "%"
|
||||||
info.Name = "*"
|
info.Name = "*"
|
||||||
}
|
}
|
||||||
if info.Permission != info.OldPermission {
|
if info.Permission != info.OldPermission {
|
||||||
if err := r.Delete(DeleteInfo{
|
if err := r.Delete(DeleteInfo{
|
||||||
Version: info.Version,
|
Version: info.Version,
|
||||||
UserName: info.UserName,
|
Username: info.Username,
|
||||||
Permission: info.OldPermission,
|
Permission: info.OldPermission,
|
||||||
ForceDelete: true,
|
ForceDelete: true,
|
||||||
Timeout: 300}); err != nil {
|
Timeout: 300}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.UserName == "root" {
|
if info.Username == "root" {
|
||||||
return nil
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -29,7 +29,7 @@ func (r *Remote) Create(info CreateInfo) error {
|
|||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,11 +42,11 @@ func (r *Remote) CreateUser(info CreateInfo) error {
|
|||||||
ips := strings.Split(info.Permission, ",")
|
ips := strings.Split(info.Permission, ",")
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) != 0 {
|
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 {
|
} 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 {
|
for _, user := range userlist {
|
||||||
@ -54,7 +54,7 @@ func (r *Remote) CreateUser(info CreateInfo) error {
|
|||||||
_ = r.Delete(DeleteInfo{
|
_ = r.Delete(DeleteInfo{
|
||||||
Name: info.Name,
|
Name: info.Name,
|
||||||
Version: info.Version,
|
Version: info.Version,
|
||||||
UserName: info.UserName,
|
Username: info.Username,
|
||||||
Permission: info.Permission,
|
Permission: info.Permission,
|
||||||
ForceDelete: true,
|
ForceDelete: true,
|
||||||
Timeout: 300})
|
Timeout: 300})
|
||||||
@ -74,7 +74,7 @@ func (r *Remote) CreateUser(info CreateInfo) error {
|
|||||||
_ = r.Delete(DeleteInfo{
|
_ = r.Delete(DeleteInfo{
|
||||||
Name: info.Name,
|
Name: info.Name,
|
||||||
Version: info.Version,
|
Version: info.Version,
|
||||||
UserName: info.UserName,
|
Username: info.Username,
|
||||||
Permission: info.Permission,
|
Permission: info.Permission,
|
||||||
ForceDelete: true,
|
ForceDelete: true,
|
||||||
Timeout: 300})
|
Timeout: 300})
|
||||||
@ -90,11 +90,11 @@ func (r *Remote) Delete(info DeleteInfo) error {
|
|||||||
ips := strings.Split(info.Permission, ",")
|
ips := strings.Split(info.Permission, ",")
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) != 0 {
|
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 {
|
} 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 {
|
for _, user := range userlist {
|
||||||
@ -121,17 +121,17 @@ func (r *Remote) Delete(info DeleteInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
|
func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
|
||||||
if info.UserName != "root" {
|
if info.Username != "root" {
|
||||||
var userlist []string
|
var userlist []string
|
||||||
if strings.Contains(info.Permission, ",") {
|
if strings.Contains(info.Permission, ",") {
|
||||||
ips := strings.Split(info.Permission, ",")
|
ips := strings.Split(info.Permission, ",")
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
if len(ip) != 0 {
|
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 {
|
} 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 {
|
for _, user := range userlist {
|
||||||
@ -166,24 +166,27 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
|
func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
|
||||||
if info.UserName == "root" {
|
if info.Username == "root" {
|
||||||
info.OldPermission = "%"
|
info.OldPermission = "%"
|
||||||
info.Name = "*"
|
info.Name = "*"
|
||||||
}
|
}
|
||||||
if info.Permission != info.OldPermission {
|
if info.Permission != info.OldPermission {
|
||||||
if err := r.Delete(DeleteInfo{
|
if err := r.Delete(DeleteInfo{
|
||||||
Version: info.Version,
|
Version: info.Version,
|
||||||
UserName: info.UserName,
|
Username: info.Username,
|
||||||
Permission: info.OldPermission,
|
Permission: info.OldPermission,
|
||||||
ForceDelete: true,
|
ForceDelete: true,
|
||||||
Timeout: 300}); err != nil {
|
Timeout: 300}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.UserName == "root" {
|
if info.Username == "root" {
|
||||||
return nil
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -131,6 +131,7 @@ export namespace App {
|
|||||||
|
|
||||||
export interface DatabaseConnInfo {
|
export interface DatabaseConnInfo {
|
||||||
password: string;
|
password: string;
|
||||||
|
privilege: boolean;
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
port: number;
|
port: number;
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,40 @@ export namespace Database {
|
|||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
name: string;
|
name: string;
|
||||||
type: 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;
|
address: string;
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
username: string;
|
||||||
|
@ -85,13 +85,16 @@ export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
// remote
|
// remote
|
||||||
export const searchRemoteDBs = (params: SearchWithPage) => {
|
export const searchRemoteDBs = (params: Database.SearchRemoteDBPage) => {
|
||||||
return http.post<ResPage<Database.RemoteDBInfo>>(`/databases/remote/search`, params);
|
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);
|
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);
|
return http.post(`/databases/remote/update`, params);
|
||||||
};
|
};
|
||||||
export const deleteRemoteDB = (id: number) => {
|
export const deleteRemoteDB = (id: number) => {
|
||||||
|
@ -330,6 +330,13 @@ const message = {
|
|||||||
|
|
||||||
confChange: '配置修改',
|
confChange: '配置修改',
|
||||||
|
|
||||||
|
remoteDB: '远程数据库',
|
||||||
|
createRemoteDB: '创建远程数据库',
|
||||||
|
editRemoteDB: '编辑远程数据库',
|
||||||
|
localDB: '本地数据库',
|
||||||
|
address: '数据库地址',
|
||||||
|
version: '数据库版本',
|
||||||
|
|
||||||
selectFile: '选择文件',
|
selectFile: '选择文件',
|
||||||
dropHelper: '将上传文件拖拽到此处,或者',
|
dropHelper: '将上传文件拖拽到此处,或者',
|
||||||
clickHelper: '点击上传',
|
clickHelper: '点击上传',
|
||||||
|
@ -27,6 +27,16 @@ const databaseRouter = {
|
|||||||
requiresAuth: false,
|
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',
|
path: 'redis',
|
||||||
name: 'Redis',
|
name: 'Redis',
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
<el-button @click="onChangeRootPassword" type="primary" plain>
|
<el-button @click="onChangeRootPassword" type="primary" plain>
|
||||||
{{ $t('database.databaseConnInfo') }}
|
{{ $t('database.databaseConnInfo') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="onChangeAccess" type="primary" plain>
|
<el-button @click="goRemoteDB" type="primary" plain>
|
||||||
{{ $t('database.remoteAccess') }}
|
{{ $t('database.remoteDB') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="goDashboard" icon="Position" type="primary" plain>phpMyAdmin</el-button>
|
<el-button @click="goDashboard" icon="Position" type="primary" plain>phpMyAdmin</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -134,7 +134,6 @@
|
|||||||
|
|
||||||
<PasswordDialog ref="passwordRef" @search="search" />
|
<PasswordDialog ref="passwordRef" @search="search" />
|
||||||
<RootPasswordDialog ref="rootPasswordRef" />
|
<RootPasswordDialog ref="rootPasswordRef" />
|
||||||
<RemoteAccessDialog ref="remoteAccessRef" />
|
|
||||||
<UploadDialog ref="uploadRef" />
|
<UploadDialog ref="uploadRef" />
|
||||||
<OperateDialog @search="search" ref="dialogRef" />
|
<OperateDialog @search="search" ref="dialogRef" />
|
||||||
<Backups ref="dialogBackupRef" />
|
<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 DeleteDialog from '@/views/database/mysql/delete/index.vue';
|
||||||
import PasswordDialog from '@/views/database/mysql/password/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/root-password/index.vue';
|
||||||
import RemoteAccessDialog from '@/views/database/mysql/remote/index.vue';
|
|
||||||
import AppResources from '@/views/database/mysql/check/index.vue';
|
import AppResources from '@/views/database/mysql/check/index.vue';
|
||||||
import Setting from '@/views/database/mysql/setting/index.vue';
|
import Setting from '@/views/database/mysql/setting/index.vue';
|
||||||
import AppStatus from '@/components/app-status/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 PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
import { dateFormat } from '@/utils/util';
|
import { dateFormat } from '@/utils/util';
|
||||||
import { reactive, ref } from 'vue';
|
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 i18n from '@/lang';
|
||||||
import { Database } from '@/api/interface/database';
|
import { Database } from '@/api/interface/database';
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
@ -222,13 +220,8 @@ const onChangeRootPassword = async () => {
|
|||||||
rootPasswordRef.value!.acceptParams();
|
rootPasswordRef.value!.acceptParams();
|
||||||
};
|
};
|
||||||
|
|
||||||
const remoteAccessRef = ref();
|
const goRemoteDB = async () => {
|
||||||
const onChangeAccess = async () => {
|
router.push({ name: 'MySQL-Remote' });
|
||||||
const res = await loadRemoteAccess();
|
|
||||||
let param = {
|
|
||||||
privilege: res.data,
|
|
||||||
};
|
|
||||||
remoteAccessRef.value!.acceptParams(param);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const passwordRef = ref();
|
const passwordRef = ref();
|
||||||
|
@ -1,98 +1,181 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-drawer v-model="dialogVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
|
<div v-loading="loading">
|
||||||
<template #header>
|
<LayoutContent>
|
||||||
<DrawerHeader :header="$t('database.remoteAccess')" :back="handleClose" />
|
<template #title>
|
||||||
|
<back-button name="MySQL" :header="'MySQL ' + $t('database.remoteDB')" />
|
||||||
</template>
|
</template>
|
||||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
<template #toolbar>
|
||||||
<el-row type="flex" justify="center">
|
<el-row>
|
||||||
<el-col :span="22">
|
<el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20">
|
||||||
<el-form-item :label="$t('database.remoteAccess')" :rules="Rules.requiredInput" prop="privilege">
|
<el-button type="primary" @click="onOpenDialog('create')">
|
||||||
<el-switch v-model="form.privilege" />
|
{{ $t('database.createRemoteDB') }}
|
||||||
<span class="input-help">{{ $t('database.remoteConnHelper') }}</span>
|
</el-button>
|
||||||
</el-form-item>
|
</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-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<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>
|
</template>
|
||||||
</el-drawer>
|
<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>
|
||||||
|
|
||||||
|
<OperateDialog ref="dialogRef" @search="search" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref } from 'vue';
|
import { dateFormat } from '@/utils/util';
|
||||||
import { Rules } from '@/global/form-rules';
|
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 i18n from '@/lang';
|
||||||
import { ElForm } from 'element-plus';
|
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||||
import { updateMysqlAccess } from '@/api/modules/database';
|
import useClipboard from 'vue-clipboard3';
|
||||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
import { Database } from '@/api/interface/database';
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
const { toClipboard } = useClipboard();
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const dialogVisiable = ref(false);
|
const dialogRef = ref();
|
||||||
const form = reactive({
|
|
||||||
privilege: false,
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
});
|
});
|
||||||
|
const searchName = ref();
|
||||||
|
|
||||||
const confirmDialogRef = ref();
|
const search = async (column?: any) => {
|
||||||
|
|
||||||
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',
|
|
||||||
};
|
|
||||||
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 onSave = async (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.validate(async (valid) => {
|
|
||||||
if (!valid) return;
|
|
||||||
let params = {
|
let params = {
|
||||||
header: i18n.global.t('database.confChange'),
|
page: paginationConfig.currentPage,
|
||||||
operationInfo: i18n.global.t('database.restartNowHelper'),
|
pageSize: paginationConfig.pageSize,
|
||||||
submitInputInfo: i18n.global.t('database.restartNow'),
|
info: searchName.value,
|
||||||
|
type: 'mysql',
|
||||||
|
orderBy: column?.order ? column.prop : 'created_at',
|
||||||
|
order: column?.order ? column.order : 'null',
|
||||||
};
|
};
|
||||||
confirmDialogRef.value!.acceptParams(params);
|
const res = await searchRemoteDBs(params);
|
||||||
});
|
data.value = res.data.items || [];
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({
|
const onOpenDialog = async (
|
||||||
acceptParams,
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
@ -6,33 +6,33 @@
|
|||||||
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
|
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||||
<el-row type="flex" justify="center">
|
<el-row type="flex" justify="center">
|
||||||
<el-col :span="22">
|
<el-col :span="22">
|
||||||
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
<el-input
|
<el-input clearable v-model.trim="dialogData.rowData!.name" />
|
||||||
:disabled="dialogData.title === 'edit'"
|
|
||||||
clearable
|
|
||||||
v-model.trim="dialogData.rowData!.name"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
<el-form-item :label="$t('database.version')" prop="version">
|
||||||
<el-select v-model="dialogData.rowData!.type">
|
<el-select v-model="dialogData.rowData!.version">
|
||||||
<el-option value="Mysql" label="Mysql" />
|
<el-option value="5.6.x" label="5.6.x" />
|
||||||
<el-option value="Redis" label="Redis" />
|
<el-option value="5.7.x" label="5.7.x" />
|
||||||
|
<el-option value="8.0.x" label="8.0.x" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('database.address')" prop="address">
|
||||||
<el-form-item :label="$t('cronjob.taskName')" prop="address">
|
|
||||||
<el-input clearable v-model.trim="dialogData.rowData!.address" />
|
<el-input clearable v-model.trim="dialogData.rowData!.address" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('cronjob.taskName')" prop="port">
|
<el-form-item :label="$t('commons.table.port')" prop="port">
|
||||||
<el-input clearable v-model.number="dialogData.rowData!.port" />
|
<el-input clearable v-model.trim="dialogData.rowData!.port" />
|
||||||
</el-form-item>
|
</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-input clearable v-model.trim="dialogData.rowData!.username" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('cronjob.taskName')" prop="password">
|
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||||
<el-input clearable v-model.trim="dialogData.rowData!.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>
|
||||||
<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-input clearable v-model.trim="dialogData.rowData!.description" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -53,14 +53,16 @@
|
|||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm } from 'element-plus';
|
import { ElForm } from 'element-plus';
|
||||||
import { Cronjob } from '@/api/interface/cronjob';
|
import { Database } from '@/api/interface/database';
|
||||||
import { addCronjob, editCronjob } from '@/api/modules/cronjob';
|
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
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 {
|
interface DialogProps {
|
||||||
title: string;
|
title: string;
|
||||||
rowData?: Cronjob.CronjobInfo;
|
rowData?: Database.RemoteDBInfo;
|
||||||
getTableList?: () => Promise<any>;
|
getTableList?: () => Promise<any>;
|
||||||
}
|
}
|
||||||
const title = ref<string>('');
|
const title = ref<string>('');
|
||||||
@ -70,7 +72,7 @@ const dialogData = ref<DialogProps>({
|
|||||||
});
|
});
|
||||||
const acceptParams = (params: DialogProps): void => {
|
const acceptParams = (params: DialogProps): void => {
|
||||||
dialogData.value = params;
|
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;
|
drawerVisiable.value = true;
|
||||||
};
|
};
|
||||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
@ -81,23 +83,49 @@ const handleClose = () => {
|
|||||||
|
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput],
|
name: [Rules.requiredInput],
|
||||||
type: [Rules.requiredSelect],
|
version: [Rules.requiredSelect],
|
||||||
address: [Rules.requiredInput],
|
address: [Rules.ip],
|
||||||
port: [Rules.port],
|
port: [Rules.port],
|
||||||
|
username: [Rules.requiredInput],
|
||||||
|
password: [Rules.requiredInput],
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const random = async () => {
|
||||||
|
dialogData.value.rowData!.password = getRandomStr(16);
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
if (dialogData.value.title === 'create') {
|
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') {
|
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'));
|
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-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">
|
||||||
<el-col :span="22">
|
<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-form-item :label="$t('database.containerConn')">
|
||||||
<el-tag>
|
<el-tag>
|
||||||
{{ form.serviceName + ':3306' }}
|
{{ form.serviceName + ':3306' }}
|
||||||
@ -35,11 +19,30 @@
|
|||||||
<el-tag>{{ $t('database.localIP') + ':' + form.port }}</el-tag>
|
<el-tag>{{ $t('database.localIP') + ':' + form.port }}</el-tag>
|
||||||
<span class="input-help">{{ $t('database.remoteConnHelper2') }}</span>
|
<span class="input-help">{{ $t('database.remoteConnHelper2') }}</span>
|
||||||
</el-form-item>
|
</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-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</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>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@ -59,7 +62,7 @@ import { ref } from 'vue';
|
|||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm } from 'element-plus';
|
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 ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||||
import { GetAppConnInfo } from '@/api/modules/app';
|
import { GetAppConnInfo } from '@/api/modules/app';
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
@ -75,10 +78,12 @@ const dialogVisiable = ref(false);
|
|||||||
const form = ref<App.DatabaseConnInfo>({
|
const form = ref<App.DatabaseConnInfo>({
|
||||||
password: '',
|
password: '',
|
||||||
serviceName: '',
|
serviceName: '',
|
||||||
|
privilege: false,
|
||||||
port: 0,
|
port: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const confirmDialogRef = ref();
|
const confirmDialogRef = ref();
|
||||||
|
const confirmAccessDialogRef = ref();
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
@ -86,6 +91,7 @@ const formRef = ref<FormInstance>();
|
|||||||
const acceptParams = (): void => {
|
const acceptParams = (): void => {
|
||||||
form.value.password = '';
|
form.value.password = '';
|
||||||
loadPassword();
|
loadPassword();
|
||||||
|
loadAccess();
|
||||||
dialogVisiable.value = true;
|
dialogVisiable.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,6 +112,11 @@ const handleClose = () => {
|
|||||||
dialogVisiable.value = false;
|
dialogVisiable.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadAccess = async () => {
|
||||||
|
const res = await loadRemoteAccess();
|
||||||
|
form.value.privilege = res.data;
|
||||||
|
};
|
||||||
|
|
||||||
const loadPassword = async () => {
|
const loadPassword = async () => {
|
||||||
const res = await GetAppConnInfo('mysql');
|
const res = await GetAppConnInfo('mysql');
|
||||||
form.value = res.data;
|
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({
|
defineExpose({
|
||||||
acceptParams,
|
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