fix: 数据库实例删除等问题修复

This commit is contained in:
meilin.huang
2024-07-05 13:14:31 +08:00
parent 10630847df
commit a80221a950
12 changed files with 94 additions and 131 deletions

View File

@@ -16,7 +16,7 @@
"clipboard": "^2.0.11",
"cropperjs": "^1.6.1",
"dayjs": "^1.11.11",
"echarts": "^5.5.0",
"echarts": "^5.5.1",
"element-plus": "^2.7.6",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
@@ -34,7 +34,7 @@
"sql-formatter": "^15.0.2",
"trzsz": "^1.1.5",
"uuid": "^9.0.1",
"vue": "^3.4.30",
"vue": "^3.4.31",
"vue-router": "^4.4.0",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
@@ -48,16 +48,16 @@
"@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/compiler-sfc": "^3.4.30",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/compiler-sfc": "^3.4.31",
"code-inspector-plugin": "^0.4.5",
"dotenv": "^16.3.1",
"eslint": "^8.35.0",
"eslint-plugin-vue": "^9.25.0",
"prettier": "^3.2.5",
"sass": "^1.77.6",
"typescript": "^5.5.2",
"vite": "^5.3.1",
"typescript": "^5.5.3",
"vite": "^5.3.3",
"vue-eslint-parser": "^9.4.2"
},
"browserslist": [

View File

@@ -9,7 +9,19 @@ export function getValueByPath(obj: any, path: string) {
const keys = path.split('.');
let result = obj;
for (let key of keys) {
if (!result || typeof result !== 'object') {
if (!result) {
return undefined;
}
// 如果是字符串则尝试使用json解析
if (typeof result == 'string') {
try {
result = JSON.parse(result);
} catch (e) {
console.error(e);
return undefined;
}
}
if (typeof result !== 'object') {
return undefined;
}
@@ -23,7 +35,18 @@ export function getValueByPath(obj: any, path: string) {
}
const index = parseInt(matchIndex[1]);
result = Array.isArray(result[arrayKey]) ? result[arrayKey][index] : undefined;
let arrValue = result[arrayKey];
if (typeof arrValue == 'string') {
try {
arrValue = JSON.parse(arrValue);
} catch (e) {
result = undefined;
break;
}
}
result = Array.isArray(arrValue) ? arrValue[index] : undefined;
} else {
result = result[key];
}

View File

@@ -3,9 +3,8 @@ module mayfly-go
go 1.22
require (
gitee.com/chunanyong/dm v1.8.14
gitee.com/chunanyong/dm v1.8.15
gitee.com/liuzongyang/libpq v1.0.9
github.com/buger/jsonparser v1.1.1
github.com/emirpasic/gods v1.18.1
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
@@ -21,7 +20,7 @@ require (
github.com/kanzihuang/vitess/go/vt/sqlparser v0.0.0-20231018071450-ac8d9f0167e9
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d
github.com/may-fly/cast v1.6.1
github.com/microsoft/go-mssqldb v1.7.1
github.com/microsoft/go-mssqldb v1.7.2
github.com/mojocn/base64Captcha v1.3.6 //
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.6
@@ -30,8 +29,9 @@ require (
github.com/robfig/cron/v3 v3.0.1 //
github.com/sijms/go-ora/v2 v2.8.19
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.17.1
github.com/veops/go-ansiterm v0.0.5
go.mongodb.org/mongo-driver v1.15.0 // mongo
go.mongodb.org/mongo-driver v1.16.0 // mongo
golang.org/x/crypto v0.24.0 // ssh
golang.org/x/oauth2 v0.21.0
golang.org/x/sync v0.7.0
@@ -77,12 +77,14 @@ require (
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect

View File

@@ -67,17 +67,9 @@ func (app *instanceAppImpl) GetPageList(condition *entity.InstanceQuery, pagePar
func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error {
instanceEntity.Network = instanceEntity.GetNetwork()
if authCert.Id != 0 {
// 密文可能被清除,故需要重新获取
authCert, _ = app.resourceAuthCertApp.GetAuthCert(authCert.Name)
} else {
if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic {
publicAuthCert, err := app.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext)
if err != nil {
return err
}
authCert = publicAuthCert
}
authCert, err := app.resourceAuthCertApp.GetRealAuthCert(authCert)
if err != nil {
return err
}
dbConn, err := dbm.Conn(app.toDbInfoByAc(instanceEntity, authCert, ""))
@@ -176,7 +168,7 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error
DbInstanceId: instanceId,
}
err = app.restoreApp.restoreRepo.GetByCond(restore)
if err != nil {
if err == nil {
return errorx.NewBiz("不能删除数据库实例【%s】,请先删除关联的数据库恢复任务。", instance.Name)
}
@@ -184,7 +176,7 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error
DbInstanceId: instanceId,
}
err = app.backupApp.backupRepo.GetByCond(backup)
if err != nil {
if err == nil {
return errorx.NewBiz("不能删除数据库实例【%s】,请先删除关联的数据库备份任务。", instance.Name)
}

View File

@@ -28,17 +28,19 @@ type Dialect interface {
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
GetDbProgram() (DbProgram, error)
// 批量保存数据
// BatchInsert 批量insert数据
BatchInsert(tx *sql.Tx, tableName string, columns []string, values [][]any, duplicateStrategy int) (int64, error)
// 拷贝表
// CopyTable 拷贝表
CopyTable(copy *DbCopyTable) error
// CreateTable 创建表
CreateTable(columns []Column, tableInfo Table, dropOldTable bool) (int, error)
// CreateIndex 创建索引
CreateIndex(tableInfo Table, indexs []Index) error
// 有些数据库迁移完数据之后,需要更新表自增序列为当前表最大值
// UpdateSequence 有些数据库迁移完数据之后,需要更新表自增序列为当前表最大值
UpdateSequence(tableName string, columns []Column)
}

View File

@@ -13,37 +13,36 @@ import (
type MetaData interface {
BaseMetaData
// 获取数据库服务实例信息
// GetDbServer 获取数据库服务实例信息
GetDbServer() (*DbServer, error)
// 获取数据库名称列表
// GetDbNames 获取数据库名称列表
GetDbNames() ([]string, error)
// 获取表信息
// GetTables 获取表信息
GetTables(tableNames ...string) ([]Table, error)
// 获取指定表名的所有列元信息
// GetColumns 获取指定表名的所有列元信息
GetColumns(tableNames ...string) ([]Column, error)
// 根据数据库类型修复字段长度、精度等
// FixColumn(column *Column)
// 获取表主键字段名,没有主键标识则默认第一个字段
// GetPrimaryKey 获取表主键字段名,没有主键标识则默认第一个字段
GetPrimaryKey(tableName string) (string, error)
// 获取表索引信息
// GetTableIndex 获取表索引信息
GetTableIndex(tableName string) ([]Index, error)
// 获取建表ddl
// GetTableDDL 获取建表ddl
GetTableDDL(tableName string, dropBeforeCreate bool) (string, error)
// GenerateTableDDL 生成建表ddl
GenerateTableDDL(columns []Column, tableInfo Table, dropBeforeCreate bool) []string
// GenerateIndexDDL 生成索引ddl
GenerateIndexDDL(indexs []Index, tableInfo Table) []string
GetSchemas() ([]string, error)
// 获取数据处理助手 用于解析格式化列数据等
// GetDataHelper 获取数据处理助手 用于解析格式化列数据等
GetDataHelper() DataHelper
}

View File

@@ -16,7 +16,7 @@ type DbInstance struct {
Network string `json:"network"`
Extra *string `json:"extra"` // 连接需要的其他额外参数json格式, 如oracle需要sid等
Params *string `json:"params"` // 使用指针类型,可更新为零值(空字符串)
Remark string `json:"remark"`
Remark *string `json:"remark"`
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
}

View File

@@ -159,17 +159,9 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
func (m *machineAppImpl) TestConn(me *entity.Machine, authCert *tagentity.ResourceAuthCert) error {
me.Id = 0
if authCert.Id != 0 {
// 密文可能被清除,故需要重新获取
authCert, _ = m.resourceAuthCertApp.GetAuthCert(authCert.Name)
} else {
if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic {
publicAuthCert, err := m.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext)
if err != nil {
return err
}
authCert = publicAuthCert
}
authCert, err := m.resourceAuthCertApp.GetRealAuthCert(authCert)
if err != nil {
return err
}
mi, err := m.toMi(me, authCert)

View File

@@ -76,18 +76,9 @@ func (r *redisAppImpl) TestConn(param *dto.SaveRedis) error {
db = cast.ToInt(strings.Split(re.Db, ",")[0])
}
authCert := param.AuthCert
if authCert.Id != 0 {
// 密文可能被清除,故需要重新获取
authCert, _ = r.resourceAuthCertApp.GetAuthCert(authCert.Name)
} else {
if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic {
publicAuthCert, err := r.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext)
if err != nil {
return err
}
authCert = publicAuthCert
}
authCert, err := r.resourceAuthCertApp.GetRealAuthCert(param.AuthCert)
if err != nil {
return err
}
rc, err := re.ToRedisInfo(db, authCert).Conn()

View File

@@ -26,6 +26,9 @@ type ResourceAuthCert interface {
// GetAuthCert 根据授权凭证名称获取授权凭证
GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error)
//GetRealAuthCert 获取真实可连接鉴权的授权凭证,主要用于资源测试连接时
GetRealAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error)
// GetResourceAuthCert 获取资源授权凭证,优先获取默认账号,若不存在默认账号则返回特权账号,都不存在则返回第一个
GetResourceAuthCert(resourceType entity.TagType, resourceCode string) (*entity.ResourceAuthCert, error)
@@ -211,6 +214,25 @@ func (r *resourceAuthCertAppImpl) GetAuthCert(authCertName string) (*entity.Reso
return r.decryptAuthCert(authCert)
}
func (r *resourceAuthCertAppImpl) GetRealAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
// 如果使用的是公共授权凭证,则密文为凭证名称
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
return r.GetAuthCert(authCert.Ciphertext)
}
if authCert.Id != 0 && authCert.Ciphertext == "" {
// 密文可能被清除,故需要重新获取
ac, err := r.GetAuthCert(authCert.Name)
if err != nil {
return nil, err
}
authCert.Ciphertext = ac.Ciphertext
return authCert, nil
}
return authCert, nil
}
func (r *resourceAuthCertAppImpl) GetResourceAuthCert(resourceType entity.TagType, resourceCode string) (*entity.ResourceAuthCert, error) {
resourceAuthCerts, err := r.ListByCond(&entity.ResourceAuthCert{
ResourceType: int8(resourceType),

View File

@@ -3,9 +3,8 @@ package jsonx
import (
"encoding/json"
"mayfly-go/pkg/logx"
"strings"
"github.com/buger/jsonparser"
"github.com/tidwall/gjson"
)
// json字符串转map
@@ -42,35 +41,35 @@ func ToStr(val any) string {
//
// @param fieldPath字段路径。如user.username等
func GetStringByBytes(bytes []byte, fieldPath string) (string, error) {
return jsonparser.GetString(bytes, strings.Split(fieldPath, ".")...)
return gjson.GetBytes(bytes, fieldPath).String(), nil
}
// 根据json字符串获取对应字段路径的string类型值
//
// @param fieldPath字段路径。如user.username等
func GetString(jsonStr string, fieldPath string) (string, error) {
return GetStringByBytes([]byte(jsonStr), fieldPath)
return gjson.Get(jsonStr, fieldPath).String(), nil
}
// 根据json字节数组获取对应字段路径的int类型值
//
// @param fieldPath字段路径。如user.age等
func GetIntByBytes(bytes []byte, fieldPath string) (int64, error) {
return jsonparser.GetInt(bytes, strings.Split(fieldPath, ".")...)
return gjson.GetBytes(bytes, fieldPath).Int(), nil
}
// 根据json字符串获取对应字段路径的int类型值
//
// @param fieldPath字段路径。如user.age等
func GetInt(jsonStr string, fieldPath string) (int64, error) {
return GetIntByBytes([]byte(jsonStr), fieldPath)
return gjson.Get(jsonStr, fieldPath).Int(), nil
}
// 根据json字节数组获取对应字段路径的bool类型值
//
// @param fieldPath字段路径。如user.isDeleted等
func GetBoolByBytes(bytes []byte, fieldPath string) (bool, error) {
return jsonparser.GetBoolean(bytes, strings.Split(fieldPath, ".")...)
return gjson.GetBytes(bytes, fieldPath).Bool(), nil
}
// 根据json字符串获取对应字段路径的bool类型值

View File

@@ -3,8 +3,6 @@ package jsonx
import (
"fmt"
"testing"
"github.com/buger/jsonparser"
)
const jsonStr = `{
@@ -36,7 +34,7 @@ func TestGetString(t *testing.T) {
// val, err := GetString(jsonStr, "username1")
// 含有数组的
val, err := GetString(jsonStr, "person.avatars.[0].url")
val, err := GetString(jsonStr, "person.avatars.0.url")
if err != nil {
fmt.Println("error: ", err.Error())
@@ -50,60 +48,3 @@ func TestGetInt(t *testing.T) {
val2, _ := GetInt(jsonStr, "person.github.followers")
fmt.Println(val, ",", val2)
}
// 官方demo
func TestJsonParser(t *testing.T) {
data := []byte(jsonStr)
// You can specify key path by providing arguments to Get function
jsonparser.Get(data, "person", "name", "fullName")
// There is `GetInt` and `GetBoolean` helpers if you exactly know key data type
jsonparser.GetInt(data, "person", "github", "followers")
// When you try to get object, it will return you []byte slice pointer to data containing it
// In `company` it will be `{"name": "Acme"}`
jsonparser.Get(data, "company")
// If the key doesn't exist it will throw an error
var size int64
if value, err := jsonparser.GetInt(data, "company", "size"); err == nil {
size = value
fmt.Println(size)
}
// You can use `ArrayEach` helper to iterate items [item1, item2 .... itemN]
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
fmt.Println(jsonparser.Get(value, "url"))
}, "person", "avatars")
// Or use can access fields by index!
jsonparser.GetString(data, "person", "avatars", "[0]", "url")
// You can use `ObjectEach` helper to iterate objects { "key1":object1, "key2":object2, .... "keyN":objectN }
jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
fmt.Printf("Key: '%s'\n Value: '%s'\n Type: %s\n", string(key), string(value), dataType)
return nil
}, "person", "name")
// The most efficient way to extract multiple keys is `EachKey`
paths := [][]string{
[]string{"person", "name", "fullName"},
[]string{"person", "avatars", "[0]", "url"},
[]string{"company", "url"},
}
jsonparser.EachKey(data, func(idx int, value []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0: // []string{"person", "name", "fullName"}
{
}
case 1: // []string{"person", "avatars", "[0]", "url"}
{
}
case 2: // []string{"company", "url"},
{
}
}
}, paths...)
}