feat: Mysql 数据库增删、改密、改权限方法封装

This commit is contained in:
ssongliu 2023-07-19 16:25:41 +08:00 committed by ssongliu
parent 34e8d88a53
commit bd5dc56b66
15 changed files with 878 additions and 4 deletions

View 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);"`
}

View File

@ -585,7 +585,7 @@ func excSQL(containerName, password, command string) error {
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput()
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", "")
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {

View File

@ -76,7 +76,7 @@ ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
#mysql
ErrUserIsExist: "The current user already exists. Please enter a new user"
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
ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again"

View File

@ -76,7 +76,7 @@ ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
#mysql
ErrUserIsExist: "當前用戶已存在,請重新輸入"
ErrDatabaseIsExist: "當前資料庫已存在,請重新輸入"
ErrExecTimeOut: "SQL 執行超時,請檢查{{ .detail }}容器"
ErrExecTimeOut: "SQL 執行超時,請檢查數據庫"
#redis
ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後重試"

View File

@ -76,7 +76,7 @@ ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
#mysql
ErrUserIsExist: "当前用户已存在,请重新输入"
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
ErrExecTimeOut: "SQL 执行超时,请检查{{ .detail }}容器"
ErrExecTimeOut: "SQL 执行超时,请检查数据库"
#redis
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"

View File

@ -35,6 +35,7 @@ func Init() {
migrations.AddMfaInterval,
migrations.UpdateAppDetail,
migrations.EncryptHostPassword,
migrations.AddRemoteDB,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -479,3 +479,13 @@ var EncryptHostPassword = &gormigrate.Migration{
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
},
}

View 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")
}

View 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",
}

View 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
}

View 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
}

View File

@ -160,4 +160,17 @@ export namespace Database {
fileName: 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;
}
}

View File

@ -83,3 +83,17 @@ export const updateRedisConf = (params: Database.RedisConfUpdate) => {
export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => {
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 });
};

View 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>

View 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>