1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 同步用户时,移除创建用户逻辑 (#3442)

This commit is contained in:
ssongliu 2023-12-25 17:08:09 +08:00 committed by GitHub
parent 57f496fe12
commit 33484a9436
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 458 additions and 59 deletions

View File

@ -41,6 +41,37 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Database Mysql
// @Summary Bind user of mysql database
// @Description 绑定 mysql 数据库用户
// @Accept json
// @Param request body dto.BindUser true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/bind [post]
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mysql 数据库名 [database] [username]","formatEN":"bind mysql database [database] [username]"}
func (b *BaseApi) BindUser(c *gin.Context) {
var req dto.BindUser
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if err := mysqlService.BindUser(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql // @Tags Database Mysql
// @Summary Update mysql database description // @Summary Update mysql database description
// @Description 更新 mysql 数据库库描述信息 // @Description 更新 mysql 数据库库描述信息

View File

@ -43,6 +43,14 @@ type MysqlDBCreate struct {
Description string `json:"description"` Description string `json:"description"`
} }
type BindUser struct {
Database string `json:"database" validate:"required"`
DB string `json:"db" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Permission string `json:"permission" validate:"required"`
}
type MysqlLoadDB struct { type MysqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"` From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"` Type string `json:"type" validate:"required,oneof=mysql mariadb"`

View File

@ -35,6 +35,7 @@ type IMysqlService interface {
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error) SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
ListDBOption() ([]dto.MysqlOption, error) ListDBOption() ([]dto.MysqlOption, error)
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
BindUser(req dto.BindUser) error
LoadFromRemote(req dto.MysqlLoadDB) error LoadFromRemote(req dto.MysqlLoadDB) error
ChangeAccess(info dto.ChangeDBInfo) error ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error ChangePassword(info dto.ChangeDBInfo) error
@ -144,6 +145,42 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return &createItem, nil return &createItem, nil
} }
func (u *MysqlService) BindUser(req dto.BindUser) error {
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.CreateUser(client.CreateInfo{
Name: dbItem.Name,
Format: dbItem.Format,
Username: req.Username,
Password: req.Password,
Permission: req.Permission,
Version: version,
Timeout: 300,
}, false); err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
if err := mysqlRepo.Update(dbItem.ID, map[string]interface{}{
"username": req.Username,
"password": pass,
"permission": req.Permission,
}); err != nil {
return err
}
return nil
}
func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error { func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
client, version, err := LoadMysqlClientByFrom(req.Database) client, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil { if err != nil {

View File

@ -17,6 +17,7 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi baseApi := v1.ApiGroupApp.BaseApi
{ {
cmdRouter.POST("", baseApi.CreateMysql) cmdRouter.POST("", baseApi.CreateMysql)
cmdRouter.POST("/bind", baseApi.BindUser)
cmdRouter.POST("load", baseApi.LoadDBFromRemote) cmdRouter.POST("load", baseApi.LoadDBFromRemote)
cmdRouter.POST("/change/access", baseApi.ChangeMysqlAccess) cmdRouter.POST("/change/access", baseApi.ChangeMysqlAccess)
cmdRouter.POST("/change/password", baseApi.ChangeMysqlPassword) cmdRouter.POST("/change/password", baseApi.ChangeMysqlPassword)

View File

@ -14,6 +14,7 @@ import (
type MysqlClient interface { type MysqlClient interface {
Create(info client.CreateInfo) error Create(info client.CreateInfo) error
CreateUser(info client.CreateInfo, withDeleteDB bool) error
Delete(info client.DeleteInfo) error Delete(info client.DeleteInfo) error
ChangePassword(info client.PasswordChangeInfo) error ChangePassword(info client.PasswordChangeInfo) error

View File

@ -4,9 +4,7 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"errors" "errors"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/go-sql-driver/mysql" "github.com/go-sql-driver/mysql"
) )
@ -103,28 +101,6 @@ var formatMap = map[string]string{
"big5": "big5_chinese_ci", "big5": "big5_chinese_ci",
} }
func loadNameByDB(name, version string) string {
nameItem := common.ConvertToPinyin(name)
if strings.HasPrefix(version, "5.6") {
if len(nameItem) <= 16 {
return nameItem
}
return strings.TrimSuffix(nameItem[:10], "_") + "_" + common.RandStr(5)
}
if len(nameItem) <= 32 {
return nameItem
}
return strings.TrimSuffix(nameItem[:25], "_") + "_" + common.RandStr(5)
}
func randomPassword(user string) string {
passwdItem := user
if len(user) > 6 {
passwdItem = user[:6]
}
return passwdItem + "@" + common.RandStrAndNum(8)
}
func VerifyPeerCertFunc(pool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error { func VerifyPeerCertFunc(pool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error {
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error { return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if len(rawCerts) == 0 { if len(rawCerts) == 0 {

View File

@ -307,19 +307,6 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
} }
} }
if len(dataItem.Username) == 0 { if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(parts[0], version)
dataItem.Password = randomPassword(dataItem.Username)
if err := r.CreateUser(CreateInfo{
Name: parts[0],
Format: parts[1],
Version: version,
Username: dataItem.Username,
Password: dataItem.Password,
Permission: "%",
Timeout: 300,
}, false); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}
dataItem.Permission = "%" dataItem.Permission = "%"
} else { } else {
if isLocal { if isLocal {

View File

@ -333,19 +333,6 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
i++ i++
} }
if len(dataItem.Username) == 0 { if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(dbName, version)
dataItem.Password = randomPassword(dataItem.Username)
if err := r.CreateUser(CreateInfo{
Name: dbName,
Format: charsetName,
Version: version,
Username: dataItem.Username,
Password: dataItem.Password,
Permission: "%",
Timeout: 300,
}, false); err != nil {
return datas, fmt.Errorf("sync db from remote server failed, err: create user failed %v", err)
}
dataItem.Permission = "%" dataItem.Permission = "%"
} else { } else {
if isLocal { if isLocal {

View File

@ -3952,6 +3952,49 @@ const docTemplate = `{
} }
} }
}, },
"/databases/bind": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "绑定 mysql 数据库用户",
"consumes": [
"application/json"
],
"tags": [
"Database Mysql"
],
"summary": "Bind user of mysql database",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.BindUser"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"database",
"username"
],
"formatEN": "bind mysql database [database] [username]",
"formatZH": "绑定 mysql 数据库名 [database] [username]",
"paramKeys": []
}
}
},
"/databases/change/access": { "/databases/change/access": {
"post": { "post": {
"security": [ "security": [
@ -10013,7 +10056,7 @@ const docTemplate = `{
"bodyKeys": [ "bodyKeys": [
"version" "version"
], ],
"formatEN": "upgrade service =\u003e [version]", "formatEN": "upgrade system =\u003e [version]",
"formatZH": "更新系统 =\u003e [version]", "formatZH": "更新系统 =\u003e [version]",
"paramKeys": [] "paramKeys": []
} }
@ -13601,6 +13644,33 @@ const docTemplate = `{
} }
} }
}, },
"dto.BindUser": {
"type": "object",
"required": [
"database",
"db",
"password",
"permission",
"username"
],
"properties": {
"database": {
"type": "string"
},
"db": {
"type": "string"
},
"password": {
"type": "string"
},
"permission": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"dto.CaptchaResponse": { "dto.CaptchaResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -3945,6 +3945,49 @@
} }
} }
}, },
"/databases/bind": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "绑定 mysql 数据库用户",
"consumes": [
"application/json"
],
"tags": [
"Database Mysql"
],
"summary": "Bind user of mysql database",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.BindUser"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"database",
"username"
],
"formatEN": "bind mysql database [database] [username]",
"formatZH": "绑定 mysql 数据库名 [database] [username]",
"paramKeys": []
}
}
},
"/databases/change/access": { "/databases/change/access": {
"post": { "post": {
"security": [ "security": [
@ -10006,7 +10049,7 @@
"bodyKeys": [ "bodyKeys": [
"version" "version"
], ],
"formatEN": "upgrade service =\u003e [version]", "formatEN": "upgrade system =\u003e [version]",
"formatZH": "更新系统 =\u003e [version]", "formatZH": "更新系统 =\u003e [version]",
"paramKeys": [] "paramKeys": []
} }
@ -13594,6 +13637,33 @@
} }
} }
}, },
"dto.BindUser": {
"type": "object",
"required": [
"database",
"db",
"password",
"permission",
"username"
],
"properties": {
"database": {
"type": "string"
},
"db": {
"type": "string"
},
"password": {
"type": "string"
},
"permission": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"dto.CaptchaResponse": { "dto.CaptchaResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -137,6 +137,25 @@ definitions:
- bindAddress - bindAddress
- ipv6 - ipv6
type: object type: object
dto.BindUser:
properties:
database:
type: string
db:
type: string
password:
type: string
permission:
type: string
username:
type: string
required:
- database
- db
- password
- permission
- username
type: object
dto.CaptchaResponse: dto.CaptchaResponse:
properties: properties:
captchaID: captchaID:
@ -7268,6 +7287,34 @@ paths:
summary: Load mysql base info summary: Load mysql base info
tags: tags:
- Database Mysql - Database Mysql
/databases/bind:
post:
consumes:
- application/json
description: 绑定 mysql 数据库用户
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.BindUser'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Bind user of mysql database
tags:
- Database Mysql
x-panel-log:
BeforeFunctions: []
bodyKeys:
- database
- username
formatEN: bind mysql database [database] [username]
formatZH: 绑定 mysql 数据库名 [database] [username]
paramKeys: []
/databases/change/access: /databases/change/access:
post: post:
consumes: consumes:
@ -11106,7 +11153,7 @@ paths:
BeforeFunctions: [] BeforeFunctions: []
bodyKeys: bodyKeys:
- version - version
formatEN: upgrade service => [version] formatEN: upgrade system => [version]
formatZH: 更新系统 => [version] formatZH: 更新系统 => [version]
paramKeys: [] paramKeys: []
/toolbox/clean: /toolbox/clean:

View File

@ -48,6 +48,13 @@ export namespace Database {
permission: string; permission: string;
description: string; description: string;
} }
export interface BindUser {
database: string;
db: string;
username: string;
password: string;
permission: string;
}
export interface MysqlLoadDB { export interface MysqlLoadDB {
from: string; from: string;
type: string; type: string;

View File

@ -19,6 +19,13 @@ export const addMysqlDB = (params: Database.MysqlDBCreate) => {
} }
return http.post(`/databases`, request); return http.post(`/databases`, request);
}; };
export const bindUser = (params: Database.BindUser) => {
let request = deepCopy(params) as Database.BindUser;
if (request.password) {
request.password = Base64.encode(request.password);
}
return http.post(`/databases/bind`, request);
};
export const loadDBFromRemote = (params: Database.MysqlLoadDB) => { export const loadDBFromRemote = (params: Database.MysqlLoadDB) => {
return http.post(`/databases/load`, params); return http.post(`/databases/load`, params);
}; };

View File

@ -380,6 +380,7 @@ const message = {
'This port is the exposed port of the container. You need to save the modification separately and restart the container!', 'This port is the exposed port of the container. You need to save the modification separately and restart the container!',
loadFromRemote: 'Load from server', loadFromRemote: 'Load from server',
userBind: 'Bind User',
loadFromRemoteHelper: loadFromRemoteHelper:
'This action will synchronize the database info on the server to 1Panel, do you want to continue?', 'This action will synchronize the database info on the server to 1Panel, do you want to continue?',
passwordHelper: 'Unable to retrieve, please modify', passwordHelper: 'Unable to retrieve, please modify',

View File

@ -373,6 +373,7 @@ const message = {
confNotFound: '未能找到該應用配置文件請在應用商店升級該應用至最新版本後重試', confNotFound: '未能找到該應用配置文件請在應用商店升級該應用至最新版本後重試',
loadFromRemote: '從服務器獲取', loadFromRemote: '從服務器獲取',
userBind: '綁定使用者',
loadFromRemoteHelper: '此操作將同步服務器上數據庫信息到 1Panel是否繼續', loadFromRemoteHelper: '此操作將同步服務器上數據庫信息到 1Panel是否繼續',
passwordHelper: '無法獲取密碼請修改', passwordHelper: '無法獲取密碼請修改',
local: '本地', local: '本地',

View File

@ -373,6 +373,7 @@ const message = {
confNotFound: '未能找到该应用配置文件请在应用商店升级该应用至最新版本后重试', confNotFound: '未能找到该应用配置文件请在应用商店升级该应用至最新版本后重试',
loadFromRemote: '从服务器获取', loadFromRemote: '从服务器获取',
userBind: '绑定用户',
loadFromRemoteHelper: '此操作将同步服务器上数据库信息到 1Panel是否继续', loadFromRemoteHelper: '此操作将同步服务器上数据库信息到 1Panel是否继续',
passwordHelper: '无法获取密码请修改', passwordHelper: '无法获取密码请修改',
local: '本地', local: '本地',

View File

@ -0,0 +1,139 @@
<template>
<div>
<el-drawer v-model="bindVisible" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<template #header>
<DrawerHeader :header="$t('database.userBind')" :resource="form.mysqlName" :back="handleClose" />
</template>
<el-form v-loading="loading" ref="changeFormRef" :model="form" :rules="rules" label-position="top">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.login.username')" prop="username">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="password">
<el-input type="password" clearable show-password v-model="form.password"></el-input>
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="permission">
<el-select v-model="form.permission">
<el-option value="%" :label="$t('database.permissionAll')" />
<el-option
v-if="form.from !== 'local'"
value="localhost"
:label="$t('terminal.localhost')"
/>
<el-option value="ip" :label="$t('database.permissionForIP')" />
</el-select>
</el-form-item>
<el-form-item v-if="form.permission === 'ip'" prop="permissionIPs">
<el-input
clearable
:autosize="{ minRows: 2, maxRows: 5 }"
type="textarea"
v-model="form.permissionIPs"
/>
<span class="input-help">{{ $t('database.remoteHelper') }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="bindVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(changeFormRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit"></ConfirmDialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { bindUser } from '@/api/modules/database';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { Rules } from '@/global/form-rules';
import { MsgSuccess } from '@/utils/message';
import { checkIp } from '@/utils/util';
const loading = ref();
const bindVisible = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const changeFormRef = ref<FormInstance>();
const form = reactive({
database: '',
mysqlName: '',
username: '',
password: '',
permission: '',
permissionIPs: '',
});
const confirmDialogRef = ref();
const rules = reactive({
username: [Rules.requiredInput, Rules.name],
password: [Rules.paramComplexity],
permission: [Rules.requiredSelect],
permissionIPs: [{ validator: checkIPs, trigger: 'blur', required: true }],
});
function checkIPs(rule: any, value: any, callback: any) {
let ips = form.permissionIPs.split(',');
for (const item of ips) {
if (checkIp(item)) {
return callback(new Error(i18n.global.t('commons.rule.ip')));
}
}
callback();
}
interface DialogProps {
database: string;
mysqlName: string;
}
const acceptParams = (params: DialogProps): void => {
form.id = params.id;
form.database = params.database;
form.mysqlName = params.mysqlName;
bindVisible.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();
const handleClose = () => {
bindVisible.value = false;
};
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
let param = {
database: form.database,
db: form.mysqlName,
username: form.username,
password: form.password,
permission: form.permission === 'ip' ? form.permissionIPs : form.permission,
};
loading.value = true;
await bindUser(param)
.then(() => {
loading.value = false;
emit('search');
bindVisible.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -124,10 +124,24 @@
<template #main v-if="currentDB"> <template #main v-if="currentDB">
<ComplexTable :pagination-config="paginationConfig" @sort-change="search" @search="search" :data="data"> <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.table.name')" prop="name" sortable />
<el-table-column :label="$t('commons.login.username')" prop="username" /> <el-table-column :label="$t('commons.login.username')" prop="username">
<template #default="{ row }">
<div class="flex items-center" v-if="row.username">
<span>
{{ row.username }}
</span>
</div>
<div v-else>
<el-button style="margin-left: -3px" type="primary" link @click="onBind(row)">
{{ $t('database.userBind') }}
</el-button>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('commons.login.password')" prop="password"> <el-table-column :label="$t('commons.login.password')" prop="password">
<template #default="{ row }"> <template #default="{ row }">
<div class="flex items-center" v-if="row.password"> <span v-if="row.username === ''">-</span>
<div class="flex items-center" v-if="row.password && row.username">
<div class="star-center" v-if="!row.showPassword"> <div class="star-center" v-if="!row.showPassword">
<span>**********</span> <span>**********</span>
</div> </div>
@ -154,10 +168,10 @@
<CopyButton :content="row.password" type="icon" /> <CopyButton :content="row.password" type="icon" />
</div> </div>
</div> </div>
<div v-else> <div v-if="row.password === '' && row.username">
<el-link @click="onChangePassword(row)"> <el-button style="margin-left: -3px" link type="primary" @click="onChangePassword(row)">
<span style="font-size: 12px">{{ $t('database.passwordHelper') }}</span> {{ $t('database.passwordHelper') }}
</el-link> </el-button>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -221,6 +235,7 @@
</template> </template>
</el-dialog> </el-dialog>
<BindDialog ref="bindRef" @search="search" />
<PasswordDialog ref="passwordRef" @search="search" /> <PasswordDialog ref="passwordRef" @search="search" />
<RootPasswordDialog ref="connRef" /> <RootPasswordDialog ref="connRef" />
<UploadDialog ref="uploadRef" /> <UploadDialog ref="uploadRef" />
@ -235,6 +250,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import BindDialog from '@/views/database/mysql/bind/index.vue';
import OperateDialog from '@/views/database/mysql/create/index.vue'; 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';
@ -274,6 +290,7 @@ const dbOptionsRemote = ref<Array<Database.DatabaseOption>>([]);
const currentDB = ref<Database.DatabaseOption>(); const currentDB = ref<Database.DatabaseOption>();
const currentDBName = ref(); const currentDBName = ref();
const bindRef = ref();
const checkRef = ref(); const checkRef = ref();
const deleteRef = ref(); const deleteRef = ref();
@ -508,6 +525,14 @@ const onDelete = async (row: Database.MysqlDBInfo) => {
} }
}; };
const onBind = async (row: Database.MysqlDBInfo) => {
let param = {
database: currentDBName.value,
mysqlName: row.name,
};
bindRef.value.acceptParams(param);
};
const onChangePassword = async (row: Database.MysqlDBInfo) => { const onChangePassword = async (row: Database.MysqlDBInfo) => {
let param = { let param = {
id: row.id, id: row.id,
@ -525,6 +550,9 @@ const onChangePassword = async (row: Database.MysqlDBInfo) => {
const buttons = [ const buttons = [
{ {
label: i18n.global.t('database.changePassword'), label: i18n.global.t('database.changePassword'),
disabled: (row: Database.MysqlDBInfo) => {
return !row.username;
},
click: (row: Database.MysqlDBInfo) => { click: (row: Database.MysqlDBInfo) => {
onChangePassword(row); onChangePassword(row);
}, },