Files
mayfly-go/server/internal/db/application/db_instance.go

311 lines
9.7 KiB
Go
Raw Normal View History

2023-08-27 11:07:29 +08:00
package application
import (
"context"
"errors"
2024-04-12 13:24:20 +08:00
"mayfly-go/internal/common/consts"
"mayfly-go/internal/db/application/dto"
"mayfly-go/internal/db/dbm"
"mayfly-go/internal/db/dbm/dbi"
2023-08-27 11:07:29 +08:00
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
2024-04-12 13:24:20 +08:00
tagapp "mayfly-go/internal/tag/application"
tagdto "mayfly-go/internal/tag/application/dto"
2024-04-12 13:24:20 +08:00
tagentity "mayfly-go/internal/tag/domain/entity"
"mayfly-go/pkg/base"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
2023-08-27 11:07:29 +08:00
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils/collx"
2024-04-12 13:24:20 +08:00
"mayfly-go/pkg/utils/structx"
2024-03-11 20:04:20 +08:00
"gorm.io/gorm"
2023-08-27 11:07:29 +08:00
)
type Instance interface {
base.App[*entity.DbInstance]
// GetPageList 分页获取数据库实例
GetPageList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
2023-08-27 11:07:29 +08:00
2024-04-12 13:24:20 +08:00
TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error
SaveDbInstance(ctx context.Context, instance *dto.SaveDbInstance) (uint64, error)
2023-08-27 11:07:29 +08:00
// Delete 删除数据库信息
Delete(ctx context.Context, id uint64) error
// GetDatabases 获取数据库实例的所有数据库列表
GetDatabases(entity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) ([]string, error)
2024-04-12 13:24:20 +08:00
// ToDbInfo 根据实例与授权凭证返回对应的DbInfo
ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error)
2023-08-27 11:07:29 +08:00
}
type instanceAppImpl struct {
base.AppImpl[*entity.DbInstance, repository.Instance]
tagApp tagapp.TagTree `inject:"TagTreeApp"`
2024-04-12 13:24:20 +08:00
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
dbApp Db `inject:"DbApp"`
backupApp *DbBackupApp `inject:"DbBackupApp"`
restoreApp *DbRestoreApp `inject:"DbRestoreApp"`
2023-08-27 11:07:29 +08:00
}
2024-01-21 22:52:20 +08:00
// 注入DbInstanceRepo
func (app *instanceAppImpl) InjectDbInstanceRepo(repo repository.Instance) {
app.Repo = repo
2024-01-21 22:52:20 +08:00
}
// GetPageList 分页获取数据库实例
func (app *instanceAppImpl) GetPageList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.GetRepo().GetInstanceList(condition, pageParam, toEntity, orderBy...)
2023-08-27 11:07:29 +08:00
}
2024-04-12 13:24:20 +08:00
func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error {
instanceEntity.Network = instanceEntity.GetNetwork()
2024-04-12 13:24:20 +08:00
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
}
}
dbConn, err := dbm.Conn(app.toDbInfoByAc(instanceEntity, authCert, ""))
if err != nil {
return err
}
dbConn.Close()
return nil
}
func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.SaveDbInstance) (uint64, error) {
2024-04-12 13:24:20 +08:00
instanceEntity := instance.DbInstance
2023-08-27 11:07:29 +08:00
// 默认tcp连接
instanceEntity.Network = instanceEntity.GetNetwork()
2024-04-12 13:24:20 +08:00
resourceType := consts.ResourceTypeDb
authCerts := instance.AuthCerts
tagCodePaths := instance.TagCodePaths
2024-04-12 13:24:20 +08:00
if len(authCerts) == 0 {
return 0, errorx.NewBiz("授权凭证信息不能为空")
2024-04-12 13:24:20 +08:00
}
2023-08-27 11:07:29 +08:00
// 查找是否存在该库
oldInstance := &entity.DbInstance{
Host: instanceEntity.Host,
Port: instanceEntity.Port,
SshTunnelMachineId: instanceEntity.SshTunnelMachineId,
2023-08-27 11:07:29 +08:00
}
err := app.GetByCond(oldInstance)
2023-08-27 11:07:29 +08:00
if instanceEntity.Id == 0 {
if err == nil {
return 0, errorx.NewBiz("该数据库实例已存在")
2023-08-27 11:07:29 +08:00
}
2024-04-12 13:24:20 +08:00
if app.CountByCond(&entity.DbInstance{Code: instanceEntity.Code}) > 0 {
return 0, errorx.NewBiz("该编码已存在")
}
2024-04-12 13:24:20 +08:00
return instanceEntity.Id, app.Tx(ctx, func(ctx context.Context) error {
2024-04-12 13:24:20 +08:00
return app.Insert(ctx, instanceEntity)
}, func(ctx context.Context) error {
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: instanceEntity.Code,
ResourceType: tagentity.TagType(resourceType),
AuthCerts: authCerts,
})
}, func(ctx context.Context) error {
return app.tagApp.SaveResourceTag(ctx, &tagdto.SaveResourceTag{
ResourceTag: app.genDbInstanceResourceTag(instanceEntity, authCerts),
ParentTagCodePaths: tagCodePaths,
})
2024-04-12 13:24:20 +08:00
})
}
// 如果存在该库,则校验修改的库是否为该库
2024-04-23 11:35:45 +08:00
if err == nil {
if oldInstance.Id != instanceEntity.Id {
return 0, errorx.NewBiz("该数据库实例已存在")
}
} else {
// 根据host等未查到旧数据则需要根据id重新获取因为后续需要使用到code
2024-05-05 14:53:30 +08:00
oldInstance, err = app.GetById(instanceEntity.Id)
2024-04-23 11:35:45 +08:00
if err != nil {
return 0, errorx.NewBiz("该数据库实例不存在")
}
}
2024-04-23 11:35:45 +08:00
return oldInstance.Id, app.Tx(ctx, func(ctx context.Context) error {
2024-04-12 13:24:20 +08:00
return app.UpdateById(ctx, instanceEntity)
}, func(ctx context.Context) error {
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: oldInstance.Code,
ResourceType: tagentity.TagType(resourceType),
AuthCerts: authCerts,
})
}, func(ctx context.Context) error {
if instanceEntity.Name != oldInstance.Name {
if err := app.tagApp.UpdateTagName(ctx, tagentity.TagTypeDb, oldInstance.Code, instanceEntity.Name); err != nil {
return err
}
}
return app.tagApp.SaveResourceTag(ctx, &tagdto.SaveResourceTag{
ResourceTag: app.genDbInstanceResourceTag(instanceEntity, authCerts),
ParentTagCodePaths: tagCodePaths,
})
2024-04-12 13:24:20 +08:00
})
}
func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error {
2024-05-05 14:53:30 +08:00
instance, err := app.GetById(instanceId, "name")
2024-04-12 13:24:20 +08:00
if err != nil {
return errorx.NewBiz("获取数据库实例错误数据库实例ID为: %d", instance.Id)
}
restore := &entity.DbRestore{
DbInstanceId: instanceId,
}
err = app.restoreApp.restoreRepo.GetByCond(restore)
switch {
case err == nil:
biz.ErrNotNil(err, "不能删除数据库实例【%s】请先删除关联的数据库恢复任务。", instance.Name)
case errors.Is(err, gorm.ErrRecordNotFound):
break
default:
biz.ErrIsNil(err, "删除数据库实例失败: %v", err)
}
backup := &entity.DbBackup{
DbInstanceId: instanceId,
}
err = app.backupApp.backupRepo.GetByCond(backup)
switch {
case err == nil:
biz.ErrNotNil(err, "不能删除数据库实例【%s】请先删除关联的数据库备份任务。", instance.Name)
case errors.Is(err, gorm.ErrRecordNotFound):
break
default:
biz.ErrIsNil(err, "删除数据库实例失败: %v", err)
}
2024-05-05 14:53:30 +08:00
dbs, _ := app.dbApp.ListByCond(&entity.Db{
InstanceId: instanceId,
2024-05-05 14:53:30 +08:00
})
2024-04-12 13:24:20 +08:00
return app.Tx(ctx, func(ctx context.Context) error {
return app.DeleteById(ctx, instanceId)
}, func(ctx context.Context) error {
// 删除该实例关联的授权凭证信息
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: instance.Code,
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
})
}, func(ctx context.Context) error {
return app.tagApp.DeleteTagByParam(ctx, &tagdto.DelResourceTag{
ResourceCode: instance.Code,
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
})
2024-05-05 14:53:30 +08:00
}, func(ctx context.Context) error {
// 删除所有库配置
for _, db := range dbs {
if err := app.dbApp.Delete(ctx, db.Id); err != nil {
return err
}
}
return nil
2024-04-12 13:24:20 +08:00
})
}
func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance, authCert *tagentity.ResourceAuthCert) ([]string, error) {
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 nil, err
}
authCert = publicAuthCert
}
2024-04-12 13:24:20 +08:00
}
ed.Network = ed.GetNetwork()
dbi := app.toDbInfoByAc(ed, authCert, "")
2024-04-12 13:24:20 +08:00
dbConn, err := dbm.Conn(dbi)
if err != nil {
return nil, err
}
defer dbConn.Close()
2024-03-15 13:31:53 +08:00
return dbConn.GetMetaData().GetDbNames()
2023-08-27 11:07:29 +08:00
}
2024-04-12 13:24:20 +08:00
func (app *instanceAppImpl) ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error) {
ac, err := app.resourceAuthCertApp.GetAuthCert(authCertName)
if err != nil {
return nil, err
}
return app.toDbInfoByAc(instance, ac, database), nil
}
func (app *instanceAppImpl) toDbInfoByAc(instance *entity.DbInstance, ac *tagentity.ResourceAuthCert, database string) *dbi.DbInfo {
di := new(dbi.DbInfo)
di.InstanceId = instance.Id
di.Database = database
structx.Copy(di, instance)
di.Username = ac.Username
di.Password = ac.Ciphertext
return di
}
func (m *instanceAppImpl) genDbInstanceResourceTag(me *entity.DbInstance, authCerts []*tagentity.ResourceAuthCert) *tagdto.ResourceTag {
authCertTags := collx.ArrayMap[*tagentity.ResourceAuthCert, *tagdto.ResourceTag](authCerts, func(val *tagentity.ResourceAuthCert) *tagdto.ResourceTag {
return &tagdto.ResourceTag{
Code: val.Name,
Name: val.Username,
Type: tagentity.TagTypeDbAuthCert,
}
})
2024-05-05 14:53:30 +08:00
dbs, err := m.dbApp.ListByCond(&entity.Db{
InstanceId: me.Id,
2024-05-05 14:53:30 +08:00
})
if err != nil {
logx.Errorf("获取实例关联的数据库失败: %v", err)
}
authCertName2DbTags := make(map[string][]*tagdto.ResourceTag)
for _, db := range dbs {
authCertName2DbTags[db.AuthCertName] = append(authCertName2DbTags[db.AuthCertName], &tagdto.ResourceTag{
Code: db.Code,
Name: db.Name,
Type: tagentity.TagTypeDbName,
})
}
// 将数据库挂至授权凭证下
for _, ac := range authCertTags {
ac.Children = authCertName2DbTags[ac.Code]
}
return &tagdto.ResourceTag{
Code: me.Code,
Type: tagentity.TagTypeDb,
Name: me.Name,
Children: authCertTags,
}
}