mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-01 03:24:14 +08:00
feat: Mysql 数据库增删、改密、改权限方法封装
This commit is contained in:
parent
34e8d88a53
commit
bd5dc56b66
13
backend/app/model/database.go
Normal file
13
backend/app/model/database.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `json:"name" gorm:"type:varchar(64);not null"`
|
||||||
|
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||||
|
Address string `json:"address" gorm:"type:varchar(64);not null"`
|
||||||
|
Port uint `json:"port" gorm:"type:decimal;not null"`
|
||||||
|
Username string `json:"username" gorm:"type:varchar(64)"`
|
||||||
|
Password string `json:"password" gorm:"type:varchar(64)"`
|
||||||
|
Format string `json:"format" gorm:"type:varchar(64)"`
|
||||||
|
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||||
|
}
|
@ -585,7 +585,7 @@ func excSQL(containerName, password, command string) error {
|
|||||||
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||||
stdout, err := cmd.CombinedOutput()
|
stdout, err := cmd.CombinedOutput()
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
return buserr.WithDetail(constant.ErrExecTimeOut, containerName, nil)
|
return buserr.New(constant.ErrExecTimeOut)
|
||||||
}
|
}
|
||||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
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 ") {
|
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||||
|
@ -76,7 +76,7 @@ ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
|
|||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "The current user already exists. Please enter a new user"
|
ErrUserIsExist: "The current user already exists. Please enter a new user"
|
||||||
ErrDatabaseIsExist: "The current database already exists. Please enter a new database"
|
ErrDatabaseIsExist: "The current database already exists. Please enter a new database"
|
||||||
ErrExecTimeOut: "SQL execution timed out, please check the {{ .detail }} container"
|
ErrExecTimeOut: "SQL execution timed out, please check the database"
|
||||||
|
|
||||||
#redis
|
#redis
|
||||||
ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again"
|
ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again"
|
||||||
|
@ -76,7 +76,7 @@ ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
|
|||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "當前用戶已存在,請重新輸入"
|
ErrUserIsExist: "當前用戶已存在,請重新輸入"
|
||||||
ErrDatabaseIsExist: "當前資料庫已存在,請重新輸入"
|
ErrDatabaseIsExist: "當前資料庫已存在,請重新輸入"
|
||||||
ErrExecTimeOut: "SQL 執行超時,請檢查{{ .detail }}容器"
|
ErrExecTimeOut: "SQL 執行超時,請檢查數據庫"
|
||||||
|
|
||||||
#redis
|
#redis
|
||||||
ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後重試"
|
ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後重試"
|
||||||
|
@ -76,7 +76,7 @@ ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
|
|||||||
#mysql
|
#mysql
|
||||||
ErrUserIsExist: "当前用户已存在,请重新输入"
|
ErrUserIsExist: "当前用户已存在,请重新输入"
|
||||||
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
|
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
|
||||||
ErrExecTimeOut: "SQL 执行超时,请检查{{ .detail }}容器"
|
ErrExecTimeOut: "SQL 执行超时,请检查数据库"
|
||||||
|
|
||||||
#redis
|
#redis
|
||||||
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
|
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
|
||||||
|
@ -35,6 +35,7 @@ func Init() {
|
|||||||
migrations.AddMfaInterval,
|
migrations.AddMfaInterval,
|
||||||
migrations.UpdateAppDetail,
|
migrations.UpdateAppDetail,
|
||||||
migrations.EncryptHostPassword,
|
migrations.EncryptHostPassword,
|
||||||
|
migrations.AddRemoteDB,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@ -479,3 +479,13 @@ var EncryptHostPassword = &gormigrate.Migration{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddRemoteDB = &gormigrate.Migration{
|
||||||
|
ID: "20230718-add-remote-db",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.Database{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
41
backend/utils/mysql/client.go
Normal file
41
backend/utils/mysql/client.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MysqlClient interface {
|
||||||
|
Create(info client.CreateInfo) error
|
||||||
|
Delete(info client.DeleteInfo) error
|
||||||
|
|
||||||
|
ChangePassword(info client.PasswordChangeInfo) error
|
||||||
|
ChangeAccess(info client.AccessChangeInfo) error
|
||||||
|
|
||||||
|
Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||||
|
if conn.Type == "remote" {
|
||||||
|
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.UserName, conn.Password, conn.Address, conn.Port)
|
||||||
|
db, err := sql.Open("mysql", connArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client.NewRemote(db), nil
|
||||||
|
}
|
||||||
|
if conn.Type == "local" {
|
||||||
|
if cmd.CheckIllegal(conn.Address, conn.UserName, conn.Password) {
|
||||||
|
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||||
|
}
|
||||||
|
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.UserName, "-p" + conn.Password + "-e"}
|
||||||
|
return client.NewLocal(connArgs, conn.Address), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("no such type")
|
||||||
|
}
|
60
backend/utils/mysql/client/info.go
Normal file
60
backend/utils/mysql/client/info.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
type DBInfo struct {
|
||||||
|
Type string `json:"type"` // local remote
|
||||||
|
Address string `json:"address"`
|
||||||
|
Port uint `json:"port"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Format string `json:"format"`
|
||||||
|
|
||||||
|
Timeout uint `json:"timeout"` // second
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Format string `json:"format"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
|
Timeout uint `json:"timeout"` // second
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
|
ForceDelete bool `json:"forceDelete"`
|
||||||
|
Timeout uint `json:"timeout"` // second
|
||||||
|
}
|
||||||
|
|
||||||
|
type PasswordChangeInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
|
Timeout uint `json:"timeout"` // second
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessChangeInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
OldPermission string `json:"oldPermission"`
|
||||||
|
Permission string `json:"permission"`
|
||||||
|
|
||||||
|
Timeout uint `json:"timeout"` // second
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatMap = map[string]string{
|
||||||
|
"utf8": "utf8_general_ci",
|
||||||
|
"utf8mb4": "utf8mb4_general_ci",
|
||||||
|
"gbk": "gbk_chinese_ci",
|
||||||
|
"big5": "big5_chinese_ci",
|
||||||
|
}
|
228
backend/utils/mysql/client/local.go
Normal file
228
backend/utils/mysql/client/local.go
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Local struct {
|
||||||
|
PrefixCommand []string
|
||||||
|
ContainerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocal(command []string, containerName string) *Local {
|
||||||
|
return &Local{PrefixCommand: command, ContainerName: containerName}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Local) Create(info CreateInfo) error {
|
||||||
|
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", info.Name, info.Format, formatMap[info.Format])
|
||||||
|
if err := r.ExecSQL(createSql, info.Timeout); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "ERROR 1007") {
|
||||||
|
return buserr.New(constant.ErrDatabaseIsExist)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Local) CreateUser(info CreateInfo) error {
|
||||||
|
var userlist []string
|
||||||
|
if strings.Contains(info.Permission, ",") {
|
||||||
|
ips := strings.Split(info.Permission, ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != 0 {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range userlist {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
|
||||||
|
_ = r.Delete(DeleteInfo{
|
||||||
|
Name: info.Name,
|
||||||
|
Version: info.Version,
|
||||||
|
UserName: info.UserName,
|
||||||
|
Permission: info.Permission,
|
||||||
|
ForceDelete: true,
|
||||||
|
Timeout: 300})
|
||||||
|
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||||
|
return buserr.New(constant.ErrUserIsExist)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", info.Name, user)
|
||||||
|
if info.Name == "*" {
|
||||||
|
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(info.Version, "5.7") || strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, info.Password)
|
||||||
|
}
|
||||||
|
if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
|
||||||
|
_ = r.Delete(DeleteInfo{
|
||||||
|
Name: info.Name,
|
||||||
|
Version: info.Version,
|
||||||
|
UserName: info.UserName,
|
||||||
|
Permission: info.Permission,
|
||||||
|
ForceDelete: true,
|
||||||
|
Timeout: 300})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Local) Delete(info DeleteInfo) error {
|
||||||
|
var userlist []string
|
||||||
|
if strings.Contains(info.Permission, ",") {
|
||||||
|
ips := strings.Split(info.Permission, ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != 0 {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range userlist {
|
||||||
|
if strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("drop user %s", user), info.Timeout); err != nil && !info.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("drop user if exists %s", user), info.Timeout); err != nil && !info.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(info.Name) != 0 {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout); err != nil && !info.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !info.ForceDelete {
|
||||||
|
global.LOG.Info("execute delete database sql successful, now start to drop uploads and records")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Local) ChangePassword(info PasswordChangeInfo) error {
|
||||||
|
if info.UserName != "root" {
|
||||||
|
var userlist []string
|
||||||
|
if strings.Contains(info.Permission, ",") {
|
||||||
|
ips := strings.Split(info.Permission, ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != 0 {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range userlist {
|
||||||
|
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password)
|
||||||
|
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED WITH mysql_native_password BY '%s';", user, info.Password)
|
||||||
|
}
|
||||||
|
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts, err := r.ExecSQLForRows("select host from mysql.user where user='root';", info.Timeout)
|
||||||
|
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.Password)
|
||||||
|
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Password)
|
||||||
|
}
|
||||||
|
if err := r.ExecSQL(passwordRootChangeCMD, info.Timeout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Local) ChangeAccess(info AccessChangeInfo) error {
|
||||||
|
if info.UserName == "root" {
|
||||||
|
info.OldPermission = "%"
|
||||||
|
info.Name = "*"
|
||||||
|
}
|
||||||
|
if info.Permission != info.OldPermission {
|
||||||
|
if err := r.Delete(DeleteInfo{
|
||||||
|
Version: info.Version,
|
||||||
|
UserName: info.UserName,
|
||||||
|
Permission: info.OldPermission,
|
||||||
|
ForceDelete: true,
|
||||||
|
Timeout: 300}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.UserName == "root" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, UserName: info.UserName, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Local) Close() {}
|
||||||
|
|
||||||
|
func (r *Local) ExecSQL(command string, timeout uint) error {
|
||||||
|
itemCommand := r.PrefixCommand[:]
|
||||||
|
itemCommand = append(itemCommand, command)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", itemCommand...)
|
||||||
|
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 (r *Local) ExecSQLForRows(command string, timeout uint) ([]string, error) {
|
||||||
|
itemCommand := r.PrefixCommand[:]
|
||||||
|
itemCommand = append(itemCommand, command)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", itemCommand...)
|
||||||
|
stdout, err := cmd.CombinedOutput()
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return nil, 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 nil, errors.New(stdStr)
|
||||||
|
}
|
||||||
|
return strings.Split(stdStr, "\n"), nil
|
||||||
|
}
|
230
backend/utils/mysql/client/remote.go
Normal file
230
backend/utils/mysql/client/remote.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Remote struct {
|
||||||
|
Client *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRemote(client *sql.DB) *Remote {
|
||||||
|
return &Remote{Client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) Create(info CreateInfo) error {
|
||||||
|
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", info.Name, info.Format, formatMap[info.Format])
|
||||||
|
if err := r.ExecSQL(createSql, info.Timeout); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "ERROR 1007") {
|
||||||
|
return buserr.New(constant.ErrDatabaseIsExist)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) CreateUser(info CreateInfo) error {
|
||||||
|
var userlist []string
|
||||||
|
if strings.Contains(info.Permission, ",") {
|
||||||
|
ips := strings.Split(info.Permission, ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != 0 {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range userlist {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
|
||||||
|
_ = r.Delete(DeleteInfo{
|
||||||
|
Name: info.Name,
|
||||||
|
Version: info.Version,
|
||||||
|
UserName: info.UserName,
|
||||||
|
Permission: info.Permission,
|
||||||
|
ForceDelete: true,
|
||||||
|
Timeout: 300})
|
||||||
|
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||||
|
return buserr.New(constant.ErrUserIsExist)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", info.Name, user)
|
||||||
|
if info.Name == "*" {
|
||||||
|
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(info.Version, "5.7") || strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, info.Password)
|
||||||
|
}
|
||||||
|
if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
|
||||||
|
_ = r.Delete(DeleteInfo{
|
||||||
|
Name: info.Name,
|
||||||
|
Version: info.Version,
|
||||||
|
UserName: info.UserName,
|
||||||
|
Permission: info.Permission,
|
||||||
|
ForceDelete: true,
|
||||||
|
Timeout: 300})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) Delete(info DeleteInfo) error {
|
||||||
|
var userlist []string
|
||||||
|
if strings.Contains(info.Permission, ",") {
|
||||||
|
ips := strings.Split(info.Permission, ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != 0 {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range userlist {
|
||||||
|
if strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("drop user %s", user), info.Timeout); err != nil && !info.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("drop user if exists %s", user), info.Timeout); err != nil && !info.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(info.Name) != 0 {
|
||||||
|
if err := r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout); err != nil && !info.ForceDelete {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !info.ForceDelete {
|
||||||
|
global.LOG.Info("execute delete database sql successful, now start to drop uploads and records")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
|
||||||
|
if info.UserName != "root" {
|
||||||
|
var userlist []string
|
||||||
|
if strings.Contains(info.Permission, ",") {
|
||||||
|
ips := strings.Split(info.Permission, ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != 0 {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", info.UserName, info.Permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range userlist {
|
||||||
|
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password)
|
||||||
|
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED WITH mysql_native_password BY '%s';", user, info.Password)
|
||||||
|
}
|
||||||
|
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts, err := r.ExecSQLForHosts(info.Timeout)
|
||||||
|
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.Password)
|
||||||
|
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
|
||||||
|
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Password)
|
||||||
|
}
|
||||||
|
if err := r.ExecSQL(passwordRootChangeCMD, info.Timeout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
|
||||||
|
if info.UserName == "root" {
|
||||||
|
info.OldPermission = "%"
|
||||||
|
info.Name = "*"
|
||||||
|
}
|
||||||
|
if info.Permission != info.OldPermission {
|
||||||
|
if err := r.Delete(DeleteInfo{
|
||||||
|
Version: info.Version,
|
||||||
|
UserName: info.UserName,
|
||||||
|
Permission: info.OldPermission,
|
||||||
|
ForceDelete: true,
|
||||||
|
Timeout: 300}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.UserName == "root" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, UserName: info.UserName, Permission: info.Permission, Timeout: info.Timeout}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) Close() {
|
||||||
|
_ = r.Client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) ExecSQL(command string, timeout uint) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if _, err := r.Client.ExecContext(ctx, command); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return buserr.New(constant.ErrExecTimeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Remote) ExecSQLForHosts(timeout uint) ([]string, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
results, err := r.Client.QueryContext(ctx, "select host from mysql.user where user='root';")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return nil, buserr.New(constant.ErrExecTimeOut)
|
||||||
|
}
|
||||||
|
var rows []string
|
||||||
|
for results.Next() {
|
||||||
|
var host string
|
||||||
|
if err := results.Scan(&host); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rows = append(rows, host)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
@ -160,4 +160,17 @@ export namespace Database {
|
|||||||
fileName: string;
|
fileName: string;
|
||||||
fileDir: string;
|
fileDir: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remote
|
||||||
|
export interface RemoteDBInfo {
|
||||||
|
id: number;
|
||||||
|
createdAt: Date;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
address: string;
|
||||||
|
port: number;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,3 +83,17 @@ export const updateRedisConf = (params: Database.RedisConfUpdate) => {
|
|||||||
export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => {
|
export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => {
|
||||||
return http.post(`/databases/redis/conffile/update`, params);
|
return http.post(`/databases/redis/conffile/update`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// remote
|
||||||
|
export const searchRemoteDBs = (params: SearchWithPage) => {
|
||||||
|
return http.post<ResPage<Database.RemoteDBInfo>>(`/databases/remote/search`, params);
|
||||||
|
};
|
||||||
|
export const addRemoteDB = (params: Database.RemoteDBInfo) => {
|
||||||
|
return http.post(`/databases/remote`, params);
|
||||||
|
};
|
||||||
|
export const editRemoteDB = (params: Database.RemoteDBInfo) => {
|
||||||
|
return http.post(`/databases/remote/update`, params);
|
||||||
|
};
|
||||||
|
export const deleteRemoteDB = (id: number) => {
|
||||||
|
return http.post(`/databases/remote/del`, { id: id });
|
||||||
|
};
|
||||||
|
152
frontend/src/views/database/remote-db/index.vue
Normal file
152
frontend/src/views/database/remote-db/index.vue
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<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>
|
112
frontend/src/views/database/remote-db/operate/index.vue
Normal file
112
frontend/src/views/database/remote-db/operate/index.vue
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="title" :resource="dialogData.rowData?.name" :back="handleClose" />
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
||||||
|
<el-input
|
||||||
|
:disabled="dialogData.title === 'edit'"
|
||||||
|
clearable
|
||||||
|
v-model.trim="dialogData.rowData!.name"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
||||||
|
<el-select v-model="dialogData.rowData!.type">
|
||||||
|
<el-option value="Mysql" label="Mysql" />
|
||||||
|
<el-option value="Redis" label="Redis" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('cronjob.taskName')" prop="address">
|
||||||
|
<el-input clearable v-model.trim="dialogData.rowData!.address" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('cronjob.taskName')" prop="port">
|
||||||
|
<el-input clearable v-model.number="dialogData.rowData!.port" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('cronjob.taskName')" prop="username">
|
||||||
|
<el-input clearable v-model.trim="dialogData.rowData!.username" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('cronjob.taskName')" prop="password">
|
||||||
|
<el-input clearable v-model.trim="dialogData.rowData!.password" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('cronjob.description')" prop="description">
|
||||||
|
<el-input clearable v-model.trim="dialogData.rowData!.description" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
import { Cronjob } from '@/api/interface/cronjob';
|
||||||
|
import { addCronjob, editCronjob } from '@/api/modules/cronjob';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
title: string;
|
||||||
|
rowData?: Cronjob.CronjobInfo;
|
||||||
|
getTableList?: () => Promise<any>;
|
||||||
|
}
|
||||||
|
const title = ref<string>('');
|
||||||
|
const drawerVisiable = ref(false);
|
||||||
|
const dialogData = ref<DialogProps>({
|
||||||
|
title: '',
|
||||||
|
});
|
||||||
|
const acceptParams = (params: DialogProps): void => {
|
||||||
|
dialogData.value = params;
|
||||||
|
title.value = i18n.global.t('cronjob.' + dialogData.value.title);
|
||||||
|
drawerVisiable.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawerVisiable.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
name: [Rules.requiredInput],
|
||||||
|
type: [Rules.requiredSelect],
|
||||||
|
address: [Rules.requiredInput],
|
||||||
|
port: [Rules.port],
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
if (dialogData.value.title === 'create') {
|
||||||
|
await addCronjob(dialogData.value.rowData);
|
||||||
|
}
|
||||||
|
if (dialogData.value.title === 'edit') {
|
||||||
|
await editCronjob(dialogData.value.rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
drawerVisiable.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user