1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-15 02:04:46 +08:00

feat: 实现远程数据库改密、授权功能

This commit is contained in:
ssongliu 2023-07-21 18:28:45 +08:00 committed by ssongliu
parent cb7351a9fb
commit 7f79f5f031
14 changed files with 94 additions and 28 deletions

View File

@ -68,7 +68,11 @@ func (b *BaseApi) SearchRemoteDB(c *gin.Context) {
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /databases/remote/list/:type [get] // @Router /databases/remote/list/:type [get]
func (b *BaseApi) ListRemoteDB(c *gin.Context) { func (b *BaseApi) ListRemoteDB(c *gin.Context) {
dbType := c.Query("type") dbType, err := helper.GetStrParamByKey(c, "type")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
list, err := remoteDBService.List(dbType) list, err := remoteDBService.List(dbType)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)

View File

@ -6,6 +6,7 @@ type MysqlDBInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"` Name string `json:"name"`
From string `json:"from"`
Format string `json:"format"` Format string `json:"format"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`

View File

@ -122,10 +122,15 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cli.Close()
if err := cli.Create(client.CreateInfo{ if err := cli.Create(client.CreateInfo{
Name: req.Name, Name: req.Name,
Format: req.Format, Format: req.Format,
Version: version, Username: req.Username,
Password: req.Password,
Permission: req.Permission,
Version: version,
Timeout: 300,
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@ -168,6 +173,7 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
if err != nil { if err != nil {
return err return err
} }
defer cli.Close()
db, err := mysqlRepo.Get(commonRepo.WithByID(req.ID)) db, err := mysqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil && !req.ForceDelete { if err != nil && !req.ForceDelete {
return err return err
@ -216,6 +222,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
if err != nil { if err != nil {
return err return err
} }
defer cli.Close()
var ( var (
mysqlData model.DatabaseMysql mysqlData model.DatabaseMysql
passwordInfo client.PasswordChangeInfo passwordInfo client.PasswordChangeInfo
@ -278,6 +285,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
if err != nil { if err != nil {
return err return err
} }
defer cli.Close()
var ( var (
mysqlData model.DatabaseMysql mysqlData model.DatabaseMysql
accessInfo client.AccessChangeInfo accessInfo client.AccessChangeInfo
@ -293,6 +301,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
} }
accessInfo.Name = mysqlData.Name accessInfo.Name = mysqlData.Name
accessInfo.Username = mysqlData.Username accessInfo.Username = mysqlData.Username
accessInfo.Password = mysqlData.Password
accessInfo.OldPermission = mysqlData.Permission accessInfo.OldPermission = mysqlData.Permission
} else { } else {
accessInfo.Username = "root" accessInfo.Username = "root"
@ -544,14 +553,17 @@ func loadClientByID(id uint) (mysql.MysqlClient, string, error) {
version string version string
err error err error
) )
dbInfo.From = "local"
if id != 0 { if id != 0 {
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(id)) mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(id))
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
dbInfo.From = mysqlData.From
} }
if mysqlData.From != "local" { if dbInfo.From != "local" {
databaseItem, err := remoteDBRepo.Get(commonRepo.WithByName(mysqlData.From)) databaseItem, err := remoteDBRepo.Get(commonRepo.WithByName(mysqlData.From))
if err != nil { if err != nil {
return nil, "", err return nil, "", err

View File

@ -41,7 +41,7 @@ func (u *RemoteDBService) SearchWithPage(search dto.RemoteDBSearch) (int64, inte
} }
func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) { func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) {
dbs, err := remoteDBRepo.GetList(commonRepo.WithByType(dbType), remoteDBRepo.WithoutByFrom("local")) dbs, err := remoteDBRepo.GetList(commonRepo.WithByType(dbType))
var datas []dto.RemoteDBOption var datas []dto.RemoteDBOption
for _, db := range dbs { for _, db := range dbs {
var item dto.RemoteDBOption var item dto.RemoteDBOption

View File

@ -437,7 +437,7 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
} }
} }
if snap.InterruptStep == "UpdateLiveRestore" { if snap.InterruptStep == "UpdateLiveRestore" {
_, _ = cmd.Exec("systemctl restart dockere") _, _ = cmd.Exec("systemctl restart docker")
return return
} }

View File

@ -42,7 +42,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf) cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
cmdRouter.POST("/remote", baseApi.CreateRemoteDB) cmdRouter.POST("/remote", baseApi.CreateRemoteDB)
cmdRouter.POST("/remote/list/:type", baseApi.ListRemoteDB) cmdRouter.GET("/remote/list/:type", baseApi.ListRemoteDB)
cmdRouter.POST("/remote/update", baseApi.UpdateRemoteDB) cmdRouter.POST("/remote/update", baseApi.UpdateRemoteDB)
cmdRouter.POST("/remote/search", baseApi.SearchRemoteDB) cmdRouter.POST("/remote/search", baseApi.SearchRemoteDB)
cmdRouter.POST("/remote/del", baseApi.DeleteRemoteDB) cmdRouter.POST("/remote/del", baseApi.DeleteRemoteDB)

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
@ -22,23 +21,21 @@ type MysqlClient interface {
} }
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) { func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
if conn.From == "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
}
if err := db.Ping(); err != nil {
return nil, err
}
return client.NewRemote(db), nil
}
if conn.From == "local" { if conn.From == "local" {
if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) { if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) {
return nil, buserr.New(constant.ErrCmdIllegal) return nil, buserr.New(constant.ErrCmdIllegal)
} }
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password + "-e"} connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password, "-e"}
return client.NewLocal(connArgs, conn.Address), nil return client.NewLocal(connArgs, conn.Address), nil
} }
return nil, errors.New("no such type")
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
}
if err := db.Ping(); err != nil {
return nil, err
}
return client.NewRemote(db), nil
} }

View File

@ -1,7 +1,7 @@
package client package client
type DBInfo struct { type DBInfo struct {
From string `json:"from"` // local remote From string `json:"from"`
Address string `json:"address"` Address string `json:"address"`
Port uint `json:"port"` Port uint `json:"port"`
Username string `json:"userName"` Username string `json:"userName"`
@ -45,6 +45,7 @@ type AccessChangeInfo struct {
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
Username string `json:"userName"` Username string `json:"userName"`
Password string `json:"password"`
OldPermission string `json:"oldPermission"` OldPermission string `json:"oldPermission"`
Permission string `json:"permission"` Permission string `json:"permission"`

View File

@ -31,7 +31,7 @@ func (r *Local) Create(info CreateInfo) error {
return err return err
} }
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil { if err := r.CreateUser(info); err != nil {
return err return err
} }
@ -185,7 +185,14 @@ func (r *Local) ChangeAccess(info AccessChangeInfo) error {
return nil return nil
} }
} }
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil { if err := r.CreateUser(CreateInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
return err return err
} }
if err := r.ExecSQL("flush privileges", 300); err != nil { if err := r.ExecSQL("flush privileges", 300); err != nil {

View File

@ -29,7 +29,7 @@ func (r *Remote) Create(info CreateInfo) error {
return err return err
} }
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil { if err := r.CreateUser(info); err != nil {
return err return err
} }
@ -183,7 +183,14 @@ func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
return nil return nil
} }
} }
if err := r.CreateUser(CreateInfo{Name: info.Name, Version: info.Version, Username: info.Username, Permission: info.Permission, Timeout: info.Timeout}); err != nil { if err := r.CreateUser(CreateInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
return err return err
} }
if err := r.ExecSQL("flush privileges", 300); err != nil { if err := r.ExecSQL("flush privileges", 300); err != nil {

View File

@ -9,6 +9,7 @@ export namespace Database {
id: number; id: number;
createdAt: Date; createdAt: Date;
name: string; name: string;
from: string;
format: string; format: string;
username: string; username: string;
password: string; password: string;

View File

@ -45,6 +45,18 @@
/> />
<span class="input-help">{{ $t('database.remoteHelper') }}</span> <span class="input-help">{{ $t('database.remoteHelper') }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.table.name')" prop="name">
<el-select v-model="form.from">
<el-option
v-for="(item, index) in dbOptions"
:key="index"
:value="item.name"
:label="loadLabel(item)"
></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('commons.table.description')" prop="description"> <el-form-item :label="$t('commons.table.description')" prop="description">
<el-input type="textarea" clearable v-model="form.description" /> <el-input type="textarea" clearable v-model="form.description" />
</el-form-item> </el-form-item>
@ -71,15 +83,17 @@ import { reactive, ref } from 'vue';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm } from 'element-plus'; import { ElForm } from 'element-plus';
import { addMysqlDB } from '@/api/modules/database'; import { addMysqlDB, listRemoteDBs } from '@/api/modules/database';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { getRandomStr } from '@/utils/util'; import { getRandomStr } from '@/utils/util';
const loading = ref(); const loading = ref();
const dbOptions = ref();
const createVisiable = ref(false); const createVisiable = ref(false);
const form = reactive({ const form = reactive({
name: '', name: '',
from: 'local',
mysqlName: '', mysqlName: '',
format: '', format: '',
username: '', username: '',
@ -110,12 +124,22 @@ const acceptParams = (params: DialogProps): void => {
form.permissionIPs = ''; form.permissionIPs = '';
form.description = ''; form.description = '';
random(); random();
loadDBOptions();
createVisiable.value = true; createVisiable.value = true;
}; };
const handleClose = () => { const handleClose = () => {
createVisiable.value = false; createVisiable.value = false;
}; };
const loadDBOptions = async () => {
const res = await listRemoteDBs('mysql');
dbOptions.value = res.data;
};
function loadLabel(item: any) {
return (item.name === 'local' ? i18n.global.t('database.localDB') : item.name) + '(' + item.address + ')';
}
const random = async () => { const random = async () => {
form.password = getRandomStr(16); form.password = getRandomStr(16);
}; };

View File

@ -49,6 +49,11 @@
:class="{ mask: mysqlStatus != 'Running' }" :class="{ mask: mysqlStatus != 'Running' }"
> >
<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="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.username')" prop="username" />
<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 }">
@ -310,6 +315,7 @@ const buttons = [
click: (row: Database.MysqlDBInfo) => { click: (row: Database.MysqlDBInfo) => {
let param = { let param = {
id: row.id, id: row.id,
from: row.from,
mysqlName: row.name, mysqlName: row.name,
operation: 'password', operation: 'password',
username: row.username, username: row.username,
@ -323,6 +329,7 @@ const buttons = [
click: (row: Database.MysqlDBInfo) => { click: (row: Database.MysqlDBInfo) => {
let param = { let param = {
id: row.id, id: row.id,
from: row.from,
mysqlName: row.name, mysqlName: row.name,
operation: 'privilege', operation: 'privilege',
privilege: '', privilege: '',

View File

@ -81,6 +81,7 @@ const changeFormRef = ref<FormInstance>();
const title = ref(); const title = ref();
const changeForm = reactive({ const changeForm = reactive({
id: 0, id: 0,
from: '',
mysqlName: '', mysqlName: '',
userName: '', userName: '',
password: '', password: '',
@ -93,6 +94,7 @@ const confirmDialogRef = ref();
interface DialogProps { interface DialogProps {
id: number; id: number;
from: string;
mysqlName: string; mysqlName: string;
username: string; username: string;
password: string; password: string;
@ -107,6 +109,7 @@ const acceptParams = (params: DialogProps): void => {
? i18n.global.t('database.changePassword') ? i18n.global.t('database.changePassword')
: i18n.global.t('database.permission'); : i18n.global.t('database.permission');
changeForm.id = params.id; changeForm.id = params.id;
changeForm.from = params.from === 'local' ? 'local' : 'remote';
changeForm.mysqlName = params.mysqlName; changeForm.mysqlName = params.mysqlName;
changeForm.userName = params.username; changeForm.userName = params.username;
changeForm.password = params.password; changeForm.password = params.password;
@ -128,6 +131,7 @@ const submitChangeInfo = async (formEl: FormInstance | undefined) => {
if (!valid) return; if (!valid) return;
let param = { let param = {
id: changeForm.id, id: changeForm.id,
from: changeForm.from,
value: '', value: '',
}; };
if (changeForm.operation === 'password') { if (changeForm.operation === 'password') {
@ -177,6 +181,7 @@ const submitChangeInfo = async (formEl: FormInstance | undefined) => {
const onSubmit = async () => { const onSubmit = async () => {
let param = { let param = {
id: changeForm.id, id: changeForm.id,
from: changeForm.from,
value: changeForm.password, value: changeForm.password,
}; };
loading.value = true; loading.value = true;