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

feat: 远程数据库支持添加 mariadb (#2139)

This commit is contained in:
ssongliu 2023-09-01 12:28:12 +08:00 committed by GitHub
parent 66c824a841
commit 149d44dbbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 55 additions and 48 deletions

View File

@ -244,6 +244,7 @@ type DatabaseInfo struct {
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"` Name string `json:"name" validate:"max=256"`
From string `json:"from"` From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"` Version string `json:"version"`
Address string `json:"address"` Address string `json:"address"`
Port uint `json:"port"` Port uint `json:"port"`
@ -263,7 +264,7 @@ type DatabaseOption struct {
type DatabaseCreate struct { type DatabaseCreate struct {
Name string `json:"name" validate:"required,max=256"` Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required,oneof=mysql"` Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"` From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"` Version string `json:"version" validate:"required"`
Address string `json:"address"` Address string `json:"address"`

View File

@ -2,6 +2,8 @@ package repo
import ( import (
"context" "context"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm" "gorm.io/gorm"
@ -18,9 +20,8 @@ type IDatabaseRepo interface {
Get(opts ...DBOption) (model.Database, error) Get(opts ...DBOption) (model.Database, error)
WithByFrom(from string) DBOption WithByFrom(from string) DBOption
WithoutByFrom(from string) DBOption WithoutByFrom(from string) DBOption
WithByMysqlList() DBOption
WithAppInstallID(appInstallID uint) DBOption WithAppInstallID(appInstallID uint) DBOption
WithType(dbType string) DBOption WithTypeList(dbType string) DBOption
} }
func NewIDatabaseRepo() IDatabaseRepo { func NewIDatabaseRepo() IDatabaseRepo {
@ -59,12 +60,6 @@ func (d *DatabaseRepo) GetList(opts ...DBOption) ([]model.Database, error) {
return databases, err return databases, err
} }
func (d *DatabaseRepo) WithByMysqlList() DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("type = ? OR type = ?", "mysql", "mariadb")
}
}
func (d *DatabaseRepo) WithByFrom(from string) DBOption { func (d *DatabaseRepo) WithByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("`from` = ?", from) return g.Where("`from` = ?", from)
@ -77,9 +72,18 @@ func (d *DatabaseRepo) WithoutByFrom(from string) DBOption {
} }
} }
func (d *DatabaseRepo) WithType(dbType string) DBOption { func (d *DatabaseRepo) WithTypeList(dbType string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("`type` = ?", dbType) types := strings.Split(dbType, ",")
if len(types) == 1 {
return g.Where("`type` = ?", dbType)
}
for _, ty := range types {
if len(ty) != 0 {
g.Or("`type` = ?", ty)
}
}
return g
} }
} }

View File

@ -428,7 +428,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) { func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
var res []response.AppService var res []response.AppService
if DatabaseKeys[key] { if DatabaseKeys[key] {
dbs, _ := databaseRepo.GetList(databaseRepo.WithByFrom("local"), databaseRepo.WithType(key)) dbs, _ := databaseRepo.GetList(databaseRepo.WithByFrom("local"), commonRepo.WithByType(key))
if len(dbs) == 0 { if len(dbs) == 0 {
return res, nil return res, nil
} }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/mysql" "github.com/1Panel-dev/1Panel/backend/utils/mysql"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client" "github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
@ -30,7 +29,7 @@ func NewIDatabaseService() IDatabaseService {
func (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error) { func (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error) {
total, dbs, err := databaseRepo.Page(search.Page, search.PageSize, total, dbs, err := databaseRepo.Page(search.Page, search.PageSize,
commonRepo.WithByType(search.Type), databaseRepo.WithTypeList(search.Type),
commonRepo.WithLikeName(search.Info), commonRepo.WithLikeName(search.Info),
databaseRepo.WithoutByFrom("local"), databaseRepo.WithoutByFrom("local"),
) )
@ -58,13 +57,7 @@ func (u *DatabaseService) Get(name string) (dto.DatabaseInfo, error) {
} }
func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) { func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
var opts []repo.DBOption dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
if dbType == "mysql" {
opts = append(opts, databaseRepo.WithByMysqlList())
} else {
opts = append(opts, commonRepo.WithByType(dbType))
}
dbs, err := databaseRepo.GetList(opts...)
var datas []dto.DatabaseOption var datas []dto.DatabaseOption
for _, db := range dbs { for _, db := range dbs {
var item dto.DatabaseOption var item dto.DatabaseOption

View File

@ -147,7 +147,7 @@ func (r *Local) ChangePassword(info PasswordChangeInfo) error {
for _, user := range userlist { for _, user := range userlist {
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password) 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") { if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
passwordChangeSql = fmt.Sprintf("alter user %s identified by '%s';", user, info.Password) passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED BY '%s';", user, info.Password)
} }
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil { if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
return err return err

View File

@ -152,7 +152,7 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
for _, user := range userlist { for _, user := range userlist {
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password) 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") { 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) passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED BY '%s';", user, info.Password)
} }
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil { if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
return err return err

View File

@ -354,7 +354,7 @@ const message = {
localDB: 'Local DB', localDB: 'Local DB',
address: 'DB address', address: 'DB address',
version: 'DB version', version: 'DB version',
versionHelper: 'Currently, only versions 5.6, 5.7, and 8.0 are supported', versionHelper: 'Currently, only versions {0} are supported',
userHelper: 'The root user or a database user with root privileges can access the remote database.', userHelper: 'The root user or a database user with root privileges can access the remote database.',
selectFile: 'Select file', selectFile: 'Select file',

View File

@ -346,7 +346,7 @@ const message = {
localDB: '本地數據庫', localDB: '本地數據庫',
address: '數據庫地址', address: '數據庫地址',
version: '數據庫版本', version: '數據庫版本',
versionHelper: '當前僅支持 5.6 5.7 8.0 三個版本', versionHelper: '當前僅支持 {0} 三個版本',
userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶', userHelper: 'root 用戶或者擁有 root 權限的數據庫用戶',
selectFile: '選擇文件', selectFile: '選擇文件',

View File

@ -346,7 +346,7 @@ const message = {
localDB: '本地数据库', localDB: '本地数据库',
address: '数据库地址', address: '数据库地址',
version: '数据库版本', version: '数据库版本',
versionHelper: '当前仅支持 5.6 5.7 8.0 三个版本', versionHelper: '当前仅支持 {0} 版本',
userHelper: 'root 用户或者拥有 root 权限的数据库用户', userHelper: 'root 用户或者拥有 root 权限的数据库用户',
selectFile: '选择文件', selectFile: '选择文件',

View File

@ -386,7 +386,7 @@ const checkExist = (data: App.CheckInstalled) => {
}; };
const loadDBOptions = async () => { const loadDBOptions = async () => {
const res = await listDatabases('mysql'); const res = await listDatabases('mysql,mariadb');
let datas = res.data || []; let datas = res.data || [];
dbOptionsLocal.value = []; dbOptionsLocal.value = [];
dbOptionsRemote.value = []; dbOptionsRemote.value = [];

View File

@ -121,7 +121,7 @@ const search = async (column?: any) => {
page: paginationConfig.currentPage, page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize, pageSize: paginationConfig.pageSize,
info: searchName.value, info: searchName.value,
type: 'mysql', type: 'mysql,mariadb',
orderBy: paginationConfig.orderBy, orderBy: paginationConfig.orderBy,
order: paginationConfig.order, order: paginationConfig.order,
}; };

View File

@ -19,13 +19,29 @@
/> />
<el-tag v-else>{{ dialogData.rowData!.name }}</el-tag> <el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.table.type')" prop="type">
<el-select v-model="dialogData.rowData!.type" @change="changeType">
<div>
<el-option value="mysql" label="MySQL" />
<el-option value="mariadb" label="Mariadb" />
</div>
</el-select>
</el-form-item>
<el-form-item :label="$t('database.version')" prop="version"> <el-form-item :label="$t('database.version')" prop="version">
<el-select @change="isOK = false" v-model="dialogData.rowData!.version"> <el-select @change="isOK = false" v-model="dialogData.rowData!.version">
<el-option value="5.6" label="5.6" /> <div v-if="dialogData.rowData!.type === 'mysql'">
<el-option value="5.7" label="5.7" /> <el-option value="5.6" label="5.6" />
<el-option value="8.0" label="8.0" /> <el-option value="5.7" label="5.7" />
<el-option value="8.x" label="8.x" />
</div>
<el-option v-else value="10.x" label="10.x" />
</el-select> </el-select>
<span class="input-help">{{ $t('database.versionHelper') }}</span> <span v-if="dialogData.rowData!.type === 'mysql'" class="input-help">
{{ $t('database.versionHelper', ['5.6 5.7 8.x']) }}
</span>
<span v-else class="input-help">
{{ $t('database.versionHelper', ['10.x']) }}
</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('database.address')" prop="address"> <el-form-item :label="$t('database.address')" prop="address">
<el-input @change="isOK = false" clearable v-model.trim="dialogData.rowData!.address" /> <el-input @change="isOK = false" clearable v-model.trim="dialogData.rowData!.address" />
@ -112,26 +128,19 @@ const rules = reactive({
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
const changeType = () => {
dialogData.value.rowData.version = dialogData.value.rowData.type === 'mysql' ? '5.6' : '10.x';
isOK.value = false;
};
const onSubmit = async (formEl: FormInstance | undefined, operation: string) => { const onSubmit = async (formEl: FormInstance | undefined, operation: string) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
let param = { dialogData.value.rowData.from = 'remote';
id: dialogData.value.rowData.id,
name: dialogData.value.rowData.name,
type: 'mysql',
version: dialogData.value.rowData.version,
from: 'remote',
address: dialogData.value.rowData.address,
port: dialogData.value.rowData.port,
username: dialogData.value.rowData.username,
password: dialogData.value.rowData.password,
description: dialogData.value.rowData.description,
};
loading.value = true; loading.value = true;
if (operation === 'check') { if (operation === 'check') {
await checkDatabase(param) await checkDatabase(dialogData.value.rowData)
.then((res) => { .then((res) => {
loading.value = false; loading.value = false;
if (res.data) { if (res.data) {
@ -148,7 +157,7 @@ const onSubmit = async (formEl: FormInstance | undefined, operation: string) =>
} }
if (operation === 'create') { if (operation === 'create') {
await addDatabase(param) await addDatabase(dialogData.value.rowData)
.then(() => { .then(() => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@ -160,7 +169,7 @@ const onSubmit = async (formEl: FormInstance | undefined, operation: string) =>
}); });
} }
if (operation === 'edit') { if (operation === 'edit') {
await editDatabase(param) await editDatabase(dialogData.value.rowData)
.then(() => { .then(() => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));