feat: 数据库实现远程服务器获取功能 (#1775)

This commit is contained in:
ssongliu 2023-07-27 16:07:27 +08:00 committed by GitHub
parent e83e592e0a
commit 40aaa1ceb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 440 additions and 77 deletions

View File

@ -229,6 +229,25 @@ func (b *BaseApi) ListDBName(c *gin.Context) {
helper.SuccessWithData(c, list)
}
// @Tags Database Mysql
// @Summary Load mysql database from remote
// @Description 从服务器获取
// @Security ApiKeyAuth
// @Router /databases/load/:from [get]
func (b *BaseApi) LoadDBFromRemote(c *gin.Context) {
from, err := helper.GetStrParamByKey(c, "from")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := mysqlService.LoadFromRemote(from); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Check before delete mysql database
// @Description Mysql 数据库删除前检查

View File

@ -63,7 +63,7 @@ func (b *BaseApi) SearchRemoteDB(c *gin.Context) {
// @Tags Database
// @Summary List remote databases
// @Description 获取快速命令列表
// @Description 获取远程数据库列表
// @Success 200 {array} dto.RemoteDBOption
// @Security ApiKeyAuth
// @Router /databases/remote/list/:type [get]
@ -82,6 +82,27 @@ func (b *BaseApi) ListRemoteDB(c *gin.Context) {
helper.SuccessWithData(c, list)
}
// @Tags Database
// @Summary Get remote databases
// @Description 获取远程数据库
// @Success 200 dto.RemoteDBOption
// @Security ApiKeyAuth
// @Router /databases/remote/:name [get]
func (b *BaseApi) GetRemoteDB(c *gin.Context) {
name, err := helper.GetStrParamByKey(c, "name")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := remoteDBService.Get(name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Database
// @Summary Delete remote database
// @Description 删除远程数据库

View File

@ -3,7 +3,7 @@ package model
type DatabaseMysql struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(256);not null"`
From string `json:"type" gorm:"type:varchar(256);not null;default:'local'"`
From string `json:"from" gorm:"type:varchar(256);not null;default:local"`
MysqlName string `json:"mysqlName" gorm:"type:varchar(64);not null"`
Format string `json:"format" gorm:"type:varchar(64);not null"`
Username string `json:"username" gorm:"type:varchar(256);not null"`

View File

@ -33,6 +33,7 @@ type IMysqlService interface {
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
ListDBName() ([]string, error)
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
LoadFromRemote(from string) error
ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(updates []dto.MysqlVariablesUpdate) error
@ -89,9 +90,6 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
}
if req.From == "127.0.0.1" {
return nil, errors.New("Cannot set 127.0.0.1 as address")
}
cli, version, err := LoadMysqlClientByFrom(req.From)
if err != nil {
@ -127,22 +125,57 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return &createItem, nil
}
func (u *MysqlService) LoadFromRemote(from string) error {
client, version, err := LoadMysqlClientByFrom(from)
if err != nil {
return err
}
databases, err := mysqlRepo.List(remoteDBRepo.WithByFrom(from))
if err != nil {
return err
}
datas, err := client.SyncDB(version)
if err != nil {
return err
}
for _, data := range datas {
hasOld := false
for _, oldData := range databases {
if oldData.Name == data.Name {
hasOld = true
break
}
}
if !hasOld {
var createItem model.DatabaseMysql
if err := copier.Copy(&createItem, &data); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := mysqlRepo.Create(context.Background(), &createItem); err != nil {
return err
}
}
}
return nil
}
func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *MysqlService) DeleteCheck(id uint) ([]string, error) {
var appInUsed []string
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil {
return appInUsed, err
}
db, err := mysqlRepo.Get(commonRepo.WithByID(id))
if err != nil {
return appInUsed, err
}
if db.From == "local" {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil {
return appInUsed, err
}
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
@ -150,6 +183,16 @@ func (u *MysqlService) DeleteCheck(id uint) ([]string, error) {
appInUsed = append(appInUsed, appInstall.Name)
}
}
} else {
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
}
}
}
return appInUsed, nil
}

View File

@ -1,6 +1,8 @@
package service
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/mysql"
@ -12,6 +14,7 @@ import (
type RemoteDBService struct{}
type IRemoteDBService interface {
Get(name string) (dto.RemoteDBInfo, error)
SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error)
Create(req dto.RemoteDBCreate) error
Update(req dto.RemoteDBUpdate) error
@ -40,6 +43,18 @@ func (u *RemoteDBService) SearchWithPage(search dto.RemoteDBSearch) (int64, inte
return total, datas, err
}
func (u *RemoteDBService) Get(name string) (dto.RemoteDBInfo, error) {
var data dto.RemoteDBInfo
remote, err := remoteDBRepo.Get(commonRepo.WithByName(name))
if err != nil {
return data, err
}
if err := copier.Copy(&data, &remote); err != nil {
return data, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
return data, nil
}
func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) {
dbs, err := remoteDBRepo.GetList(commonRepo.WithByType(dbType))
var datas []dto.RemoteDBOption
@ -82,7 +97,15 @@ func (u *RemoteDBService) Delete(id uint) error {
if db.ID == 0 {
return constant.ErrRecordNotFound
}
return remoteDBRepo.Delete(commonRepo.WithByID(id))
if err := remoteDBRepo.Delete(commonRepo.WithByID(id)); err != nil {
return err
}
if db.From != "local" {
if err := mysqlRepo.Delete(context.Background(), remoteDBRepo.WithByFrom(db.Name)); err != nil {
return err
}
}
return nil
}
func (u *RemoteDBService) Update(req dto.RemoteDBUpdate) error {

View File

@ -17,6 +17,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
cmdRouter.POST("", baseApi.CreateMysql)
cmdRouter.GET("load/:from", baseApi.LoadDBFromRemote)
cmdRouter.POST("/change/access", baseApi.ChangeMysqlAccess)
cmdRouter.POST("/change/password", baseApi.ChangeMysqlPassword)
cmdRouter.POST("/del/check", baseApi.DeleteCheckMysql)
@ -42,6 +43,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
cmdRouter.POST("/remote", baseApi.CreateRemoteDB)
cmdRouter.GET("/remote/:name", baseApi.GetRemoteDB)
cmdRouter.GET("/remote/list/:type", baseApi.ListRemoteDB)
cmdRouter.POST("/remote/update", baseApi.UpdateRemoteDB)
cmdRouter.POST("/remote/search", baseApi.SearchRemoteDB)

View File

@ -20,6 +20,7 @@ type MysqlClient interface {
Backup(info client.BackupInfo) error
Recover(info client.RecoverInfo) error
SyncDB(version string) ([]client.SyncDBInfo, error)
Close()
}
@ -29,7 +30,7 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
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, conn.Password), nil
return client.NewLocal(connArgs, conn.Address, conn.Password, conn.From), nil
}
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.Username, conn.Password, conn.Address, conn.Port)
@ -42,6 +43,7 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
}
return client.NewRemote(client.Remote{
Client: db,
From: conn.From,
User: conn.Username,
Password: conn.Password,
Address: conn.Address,

View File

@ -69,6 +69,16 @@ type RecoverInfo struct {
Timeout uint `json:"timeout"` // second
}
type SyncDBInfo struct {
Name string `json:"name"`
From string `json:"from"`
MysqlName string `json:"mysqlName"`
Format string `json:"format"`
Username string `json:"username"`
Password string `json:"password"`
Permission string `json:"permission"`
}
var formatMap = map[string]string{
"utf8": "utf8_general_ci",
"utf8mb4": "utf8mb4_general_ci",

View File

@ -13,17 +13,19 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
type Local struct {
PrefixCommand []string
From string
Password string
ContainerName string
}
func NewLocal(command []string, containerName, password string) *Local {
return &Local{PrefixCommand: command, ContainerName: containerName, Password: password}
func NewLocal(command []string, containerName, password, from string) *Local {
return &Local{PrefixCommand: command, ContainerName: containerName, Password: password, From: from}
}
func (r *Local) Create(info CreateInfo) error {
@ -252,6 +254,81 @@ func (r *Local) Recover(info RecoverInfo) error {
return nil
}
func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
var datas []SyncDBInfo
lines, err := r.ExecSQLForRows("SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA", 300)
if err != nil {
return datas, err
}
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) != 2 {
continue
}
if parts[0] == "SCHEMA_NAME" || parts[0] == "information_schema" || parts[0] == "mysql" || parts[0] == "performance_schema" || parts[0] == "sys" {
continue
}
dataItem := SyncDBInfo{
Name: parts[0],
From: r.From,
MysqlName: r.From,
Format: parts[1],
}
userLines, err := r.ExecSQLForRows(fmt.Sprintf("SELECT USER,HOST FROM mysql.DB WHERE DB = '%s'", parts[0]), 300)
if err != nil {
return datas, err
}
var permissionItem []string
isLocal := true
i := 0
for _, userline := range userLines {
userparts := strings.Fields(userline)
if len(userparts) != 2 {
continue
}
if userparts[0] == "root" {
continue
}
if i == 0 {
dataItem.Username = userparts[0]
}
dataItem.Username = userparts[0]
if dataItem.Username == userparts[0] && userparts[1] == "%" {
isLocal = false
dataItem.Permission = "%"
} else if dataItem.Username == userparts[0] && userparts[1] != "localhost" {
isLocal = false
permissionItem = append(permissionItem, userparts[1])
}
}
if len(dataItem.Username) == 0 {
if err := r.CreateUser(CreateInfo{
Name: parts[0],
Format: parts[1],
Version: version,
Username: parts[0],
Password: common.RandStr(16),
Permission: "%",
Timeout: 300,
}); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}
dataItem.Username = parts[0]
dataItem.Permission = "%"
} else {
if isLocal {
dataItem.Permission = "localhost"
}
if len(dataItem.Permission) == 0 {
dataItem.Permission = strings.Join(permissionItem, ",")
}
}
datas = append(datas, dataItem)
}
return datas, nil
}
func (r *Local) Close() {}
func (r *Local) ExecSQL(command string, timeout uint) error {

View File

@ -12,6 +12,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/jarvanstack/mysqldump"
@ -19,6 +20,7 @@ import (
type Remote struct {
Client *sql.DB
From string
User string
Password string
Address string
@ -251,6 +253,86 @@ func (r *Remote) Recover(info RecoverInfo) error {
return nil
}
func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
var datas []SyncDBInfo
rows, err := r.Client.Query("SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA")
if err != nil {
return datas, err
}
defer rows.Close()
for rows.Next() {
var dbName, charsetName string
if err = rows.Scan(&dbName, &charsetName); err != nil {
return datas, err
}
if dbName == "information_schema" || dbName == "mysql" || dbName == "performance_schema" || dbName == "sys" {
continue
}
dataItem := SyncDBInfo{
Name: dbName,
From: r.From,
MysqlName: r.From,
Format: charsetName,
}
userRows, err := r.Client.Query("SELECT USER,HOST FROM mysql.DB WHERE DB = ?", dbName)
if err != nil {
return datas, err
}
var permissionItem []string
isLocal := true
i := 0
for userRows.Next() {
var user, host string
if err = userRows.Scan(&user, &host); err != nil {
return datas, err
}
if user == "root" {
continue
}
if i == 0 {
dataItem.Username = user
}
if dataItem.Username == user && host == "%" {
isLocal = false
dataItem.Permission = "%"
} else if dataItem.Username == user && host != "localhost" {
isLocal = false
permissionItem = append(permissionItem, host)
}
i++
}
if len(dataItem.Username) == 0 {
if err := r.CreateUser(CreateInfo{
Name: dbName,
Format: charsetName,
Version: version,
Username: dbName,
Password: common.RandStr(16),
Permission: "%",
Timeout: 300,
}); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}
dataItem.Username = dbName
dataItem.Permission = "%"
} else {
if isLocal {
dataItem.Permission = "localhost"
}
if len(dataItem.Permission) == 0 {
dataItem.Permission = strings.Join(permissionItem, ",")
}
}
datas = append(datas, dataItem)
}
if err = rows.Err(); err != nil {
return datas, err
}
return datas, nil
}
func (r *Remote) Close() {
_ = r.Client.Close()
}

View File

@ -15,6 +15,9 @@ export const addMysqlDB = (params: Database.MysqlDBCreate) => {
}
return http.post(`/databases`, reqest);
};
export const loadDBFromRemote = (from: string) => {
return http.get(`/databases/load/${from}`);
};
export const updateMysqlAccess = (params: Database.ChangeInfo) => {
return http.post(`/databases/change/access`, params);
};
@ -85,6 +88,9 @@ export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) =>
};
// remote
export const getRemoteDB = (name: string) => {
return http.get<Database.RemoteDBInfo>(`/databases/remote/${name}`);
};
export const searchRemoteDBs = (params: Database.SearchRemoteDBPage) => {
return http.post<ResPage<Database.RemoteDBInfo>>(`/databases/remote/search`, params);
};

View File

@ -335,15 +335,16 @@ const message = {
portHelper:
'This port is the exposed port of the container. You need to save the modification separately and restart the container!',
loadFromRemote: 'load from Server',
passwordHelper: 'Unable to retrieve, please modify',
remote: 'Remote',
remoteDB: 'Remote DB',
createRemoteDB: 'Create Remote DB',
editRemoteDB: 'Edit Remote DB',
createRemoteDB: 'Create Remote Server',
editRemoteDB: 'Edit Remote Server',
localDB: 'Local DB',
address: 'DB address',
version: 'DB version',
versionHelper: 'Currently, only versions 5.6, 5.7, and 8.0 are supported',
addressHelper: 'The remote database address except 127.0.0.1.',
userHelper: 'The root user or a database user with root privileges can access the remote database.',
selectFile: 'Select file',

View File

@ -330,15 +330,16 @@ const message = {
confChange: '配置修改',
loadFromRemote: '從服務器獲取',
passwordHelper: '無法獲取密碼請修改',
remote: '遠程',
remoteDB: '遠程數據庫',
createRemoteDB: '創建遠程數據庫',
editRemoteDB: '編輯遠程數據庫',
remoteDB: '遠程服務器',
createRemoteDB: '添加遠程服務器',
editRemoteDB: '編輯遠程服務器',
localDB: '本地數據庫',
address: '數據庫地址',
version: '數據庫版本',
versionHelper: '當前僅支持 5.6 5.7 8.0 三個版本',
addressHelper: ' 127.0.0.1 的遠程數據庫地址',
userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶',
selectFile: '選擇文件',

View File

@ -330,15 +330,16 @@ const message = {
confChange: '配置修改',
loadFromRemote: '从服务器获取',
passwordHelper: '无法获取密码请修改',
remote: '远程',
remoteDB: '远程数据库',
createRemoteDB: '创建远程数据库',
editRemoteDB: '编辑远程数据库',
remoteDB: '远程服务器',
createRemoteDB: '添加远程服务器',
editRemoteDB: '编辑远程服务器',
localDB: '本地数据库',
address: '数据库地址',
version: '数据库版本',
versionHelper: '当前仅支持 5.6 5.7 8.0 三个版本',
addressHelper: ' 127.0.0.1 的远程数据库地址',
userHelper: 'root 用户或者拥有 root 权限的数据库用户',
selectFile: '选择文件',

View File

@ -454,7 +454,7 @@ const loadContainers = async () => {
const checkMysqlInstalled = async () => {
const data = await loadDBNames();
mysqlInfo.dbNames = data.data;
mysqlInfo.dbNames = data.data || [];
};
function isBackup() {

View File

@ -4,7 +4,7 @@
<DrawerHeader :header="$t('database.databaseConnInfo')" :back="handleClose" />
</template>
<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" v-if="form.from === 'local'">
<el-col :span="22">
<el-form-item :label="$t('database.containerConn')">
<el-tag>
@ -39,6 +39,19 @@
</el-form-item>
</el-col>
</el-row>
<el-row type="flex" justify="center" v-if="form.from !== 'local'">
<el-col :span="22">
<el-form-item :label="$t('database.remoteConn')">
<el-tag>{{ form.remoteIP + ':' + form.port }}</el-tag>
</el-form-item>
<el-form-item :label="$t('commons.login.username')">
<el-tag>{{ form.username }}</el-tag>
</el-form-item>
<el-form-item :label="$t('commons.login.password')">
<el-tag>{{ form.password }}</el-tag>
</el-form-item>
</el-col>
</el-row>
</el-form>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" @cancel="loadPassword"></ConfirmDialog>
@ -58,28 +71,31 @@
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { reactive, ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { loadRemoteAccess, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';
import { getRemoteDB, loadRemoteAccess, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { GetAppConnInfo } from '@/api/modules/app';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgError, MsgSuccess } from '@/utils/message';
import { getRandomStr } from '@/utils/util';
import { App } from '@/api/interface/app';
import useClipboard from 'vue-clipboard3';
const { toClipboard } = useClipboard();
const loading = ref(false);
const dialogVisiable = ref(false);
const form = ref<App.DatabaseConnInfo>({
const form = reactive({
password: '',
serviceName: '',
privilege: false,
port: 0,
from: '',
username: '',
remoteIP: '',
});
const confirmDialogRef = ref();
@ -88,15 +104,24 @@ const confirmAccessDialogRef = ref();
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const acceptParams = (): void => {
form.value.password = '';
interface DialogProps {
from: string;
remoteIP: string;
}
const acceptParams = (param: DialogProps): void => {
form.password = '';
form.from = param.from;
if (form.from !== 'local') {
loadRemoteInfo();
}
loadPassword();
loadAccess();
dialogVisiable.value = true;
};
const random = async () => {
form.value.password = getRandomStr(16);
form.password = getRandomStr(16);
};
const onCopy = async (value: string) => {
@ -114,18 +139,27 @@ const handleClose = () => {
const loadAccess = async () => {
const res = await loadRemoteAccess();
form.value.privilege = res.data;
form.privilege = res.data;
};
const loadRemoteInfo = async () => {
const res = await getRemoteDB(form.from);
form.remoteIP = res.data.address;
form.username = res.data.username;
form.password = res.data.password;
};
const loadPassword = async () => {
const res = await GetAppConnInfo('mysql');
form.value = res.data;
form.password = res.data.password || '';
form.port = res.data.port || 3306;
form.serviceName = res.data.serviceName || '';
};
const onSubmit = async () => {
let param = {
id: 0,
value: form.value.password,
value: form.password,
};
loading.value = true;
await updateMysqlPassword(param)
@ -155,7 +189,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
const onSubmitAccess = async () => {
let param = {
id: 0,
value: form.value.privilege ? '%' : 'localhost',
value: form.privilege ? '%' : 'localhost',
};
loading.value = true;
await updateMysqlAccess(param)

View File

@ -38,13 +38,16 @@
>
{{ $t('database.create') }}
</el-button>
<el-button @click="onChangeConn" type="primary" plain>
{{ $t('database.databaseConnInfo') }}
</el-button>
<el-button
v-if="mysqlIsExist && mysqlStatus === 'Running' && isLocal()"
@click="onChangeRootPassword"
v-if="(mysqlIsExist && mysqlStatus === 'Running') || !isLocal()"
@click="loadDB"
type="primary"
plain
>
{{ $t('database.databaseConnInfo') }}
{{ $t('database.loadFromRemote') }}
</el-button>
<el-button @click="goRemoteDB" type="primary" plain>
{{ $t('database.remoteDB') }}
@ -83,15 +86,10 @@
:class="{ mask: mysqlStatus != 'Running' && isLocal() }"
>
<el-table-column :label="$t('commons.table.name')" prop="name" sortable />
<el-table-column :label="$t('commons.login.username')" prop="from">
<template #default="{ row }">
<span>{{ row.from === 'local' ? $t('database.localDB') : row.from }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.login.username')" prop="username" />
<el-table-column :label="$t('commons.login.password')" prop="password">
<template #default="{ row }">
<div>
<div v-if="row.password">
<span style="float: left; line-height: 25px" v-if="!row.showPassword">***********</span>
<div style="cursor: pointer; float: left" v-if="!row.showPassword">
<el-icon
@ -104,20 +102,21 @@
</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"
>
<el-icon class="iconInTable" @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)">
<el-icon class="iconInTable" :size="16" @click="onCopy(row)">
<DocumentCopy />
</el-icon>
</div>
</div>
<div v-else>
<el-link @click="onChangePassword(row)">
<span style="font-size: 12px">{{ $t('database.passwordHelper') }}</span>
</el-link>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.description')" prop="description">
@ -191,7 +190,7 @@
</el-dialog>
<PasswordDialog ref="passwordRef" @search="search" />
<RootPasswordDialog ref="rootPasswordRef" />
<RootPasswordDialog ref="connRef" />
<UploadDialog ref="uploadRef" />
<OperateDialog @search="search" ref="dialogRef" />
<Backups ref="dialogBackupRef" />
@ -207,7 +206,7 @@
import OperateDialog from '@/views/database/mysql/create/index.vue';
import DeleteDialog from '@/views/database/mysql/delete/index.vue';
import PasswordDialog from '@/views/database/mysql/password/index.vue';
import RootPasswordDialog from '@/views/database/mysql/root-password/index.vue';
import RootPasswordDialog from '@/views/database/mysql/conn/index.vue';
import AppResources from '@/views/database/mysql/check/index.vue';
import Setting from '@/views/database/mysql/setting/index.vue';
import AppStatus from '@/components/app-status/index.vue';
@ -216,7 +215,13 @@ import UploadDialog from '@/components/upload/index.vue';
import PortJumpDialog from '@/components/port-jump/index.vue';
import { dateFormat } from '@/utils/util';
import { onMounted, reactive, ref } from 'vue';
import { deleteCheckMysqlDB, listRemoteDBs, searchMysqlDBs, updateMysqlDescription } from '@/api/modules/database';
import {
deleteCheckMysqlDB,
listRemoteDBs,
loadDBFromRemote,
searchMysqlDBs,
updateMysqlDescription,
} from '@/api/modules/database';
import i18n from '@/lang';
import { Database } from '@/api/interface/database';
import { App } from '@/api/interface/app';
@ -268,9 +273,9 @@ const dialogBackupRef = ref();
const uploadRef = ref();
const rootPasswordRef = ref();
const onChangeRootPassword = async () => {
rootPasswordRef.value!.acceptParams();
const connRef = ref();
const onChangeConn = async () => {
connRef.value!.acceptParams({ from: paginationConfig.from });
};
const goRemoteDB = async () => {
@ -308,6 +313,18 @@ const search = async (column?: any) => {
paginationConfig.total = res.data.total;
};
const loadDB = async () => {
loading.value = true;
await loadDBFromRemote(paginationConfig.from)
.then(() => {
loading.value = false;
search();
})
.catch(() => {
loading.value = false;
});
};
const goRouter = async () => {
router.push({ name: 'AppDetail', params: { appKey: 'mysql' } });
};
@ -376,10 +393,7 @@ const onDelete = async (row: Database.MysqlDBInfo) => {
}
};
const buttons = [
{
label: i18n.global.t('database.changePassword'),
click: (row: Database.MysqlDBInfo) => {
const onChangePassword = async (row: Database.MysqlDBInfo) => {
let param = {
id: row.id,
from: row.from,
@ -389,10 +403,20 @@ const buttons = [
password: row.password,
};
passwordRef.value.acceptParams(param);
};
const buttons = [
{
label: i18n.global.t('database.changePassword'),
click: (row: Database.MysqlDBInfo) => {
onChangePassword(row);
},
},
{
label: i18n.global.t('database.permission'),
disabled: (row: Database.MysqlDBInfo) => {
return !row.password;
},
click: (row: Database.MysqlDBInfo) => {
let param = {
id: row.id,
@ -403,7 +427,7 @@ const buttons = [
privilegeIPs: '',
password: '',
};
if (row.permission === '%') {
if (row.permission === '%' || row.permission === 'localhost') {
param.privilege = row.permission;
} else {
param.privilegeIPs = row.permission;
@ -446,3 +470,10 @@ onMounted(() => {
loadDBOptions();
});
</script>
<style lang="scss" scoped>
.iconInTable {
margin-left: 5px;
margin-top: 3px;
}
</style>

View File

@ -29,6 +29,11 @@
<el-form-item :label="$t('database.permission')" prop="privilege">
<el-select style="width: 100%" v-model="changeForm.privilege">
<el-option value="%" :label="$t('database.permissionAll')" />
<el-option
v-if="changeForm.from !== 'local'"
value="localhost"
:label="$t('terminal.localhost')"
/>
<el-option value="ip" :label="$t('database.permissionForIP')" />
</el-select>
</el-form-item>
@ -110,6 +115,7 @@ const acceptParams = (params: DialogProps): void => {
: i18n.global.t('database.permission');
changeForm.id = params.id;
changeForm.from = params.from;
console.log(changeForm.from);
changeForm.mysqlName = params.mysqlName;
changeForm.userName = params.username;
changeForm.password = params.password;

View File

@ -1,5 +1,5 @@
<template>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<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>
@ -7,7 +7,12 @@
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.name')" prop="name">
<el-input clearable v-model.trim="dialogData.rowData!.name" />
<el-input
v-if="dialogData.title === 'create'"
clearable
v-model.trim="dialogData.rowData!.name"
/>
<el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>
</el-form-item>
<el-form-item :label="$t('database.version')" prop="version">
<el-select v-model="dialogData.rowData!.version">
@ -19,7 +24,6 @@
</el-form-item>
<el-form-item :label="$t('database.address')" prop="address">
<el-input clearable v-model.trim="dialogData.rowData!.address" />
<span class="input-help">{{ $t('database.addressHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('commons.table.port')" prop="port">
<el-input clearable v-model.trim="dialogData.rowData!.port" />