From abc015aec00081a5bba51b406c9684a41918500a Mon Sep 17 00:00:00 2001 From: "meilin.huang" <954537473@qq.com> Date: Fri, 12 Apr 2024 13:24:20 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E5=87=AD=E8=AF=81=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mayfly_go_web/src/common/commonEnum.ts | 8 +- mayfly_go_web/src/common/pattern.ts | 4 +- .../ops/component/ResourceAuthCertEdit.vue | 11 +- .../component/ResourceAuthCertTableEdit.vue | 36 ++- mayfly_go_web/src/views/ops/db/DbEdit.vue | 53 +++- mayfly_go_web/src/views/ops/db/DbList.vue | 2 +- .../src/views/ops/db/InstanceEdit.vue | 259 +++++++++--------- .../src/views/ops/db/InstanceList.vue | 21 +- mayfly_go_web/src/views/ops/db/api.ts | 1 - .../src/views/ops/machine/MachineList.vue | 2 +- .../src/views/ops/tag/AuthCertList.vue | 6 +- .../src/views/ops/tag/TagTreeList.vue | 43 ++- mayfly_go_web/src/views/ops/tag/TeamList.vue | 97 ++++--- mayfly_go_web/src/views/ops/tag/enums.ts | 8 +- server/internal/common/consts/consts.go | 8 +- server/internal/db/api/dashbord.go | 2 +- server/internal/db/api/db.go | 6 +- server/internal/db/api/db_instance.go | 60 ++-- server/internal/db/api/form/instance.go | 7 +- server/internal/db/api/vo/db.go | 3 +- server/internal/db/api/vo/instance.go | 13 +- server/internal/db/application/db.go | 44 ++- server/internal/db/application/db_instance.go | 129 +++++++-- server/internal/db/domain/entity/db.go | 1 + .../internal/db/domain/entity/db_instance.go | 29 +- server/internal/db/domain/entity/query.go | 1 + .../db/infrastructure/persistence/db.go | 4 +- .../db/infrastructure/persistence/instance.go | 3 +- server/internal/db/router/instance.go | 2 - server/internal/machine/api/form/form.go | 4 +- server/internal/machine/api/machine.go | 5 +- .../internal/machine/application/machine.go | 78 ++++-- .../infrastructure/persistence/machine.go | 2 +- server/internal/mongo/api/dashbord.go | 2 +- server/internal/mongo/api/mongo.go | 4 +- server/internal/mongo/application/mongo.go | 24 +- server/internal/redis/api/dashbord.go | 2 +- server/internal/redis/api/redis.go | 4 +- server/internal/redis/application/redis.go | 24 +- server/internal/tag/api/tag_tree.go | 10 +- .../tag/application/resouce_auth_cert.go | 179 ++++++------ server/internal/tag/application/tag_tree.go | 250 +++++++++++++---- .../tag/domain/entity/resource_auth_cert.go | 10 + server/internal/tag/domain/entity/tag_tree.go | 35 ++- server/pkg/validatorx/pattern.go | 1 + server/resources/script/sql/mayfly-go.sql | 2 +- server/resources/script/sql/v1.7/v1.7.5.sql | 70 ++++- 47 files changed, 1011 insertions(+), 558 deletions(-) diff --git a/mayfly_go_web/src/common/commonEnum.ts b/mayfly_go_web/src/common/commonEnum.ts index f4824cba..d450a529 100644 --- a/mayfly_go_web/src/common/commonEnum.ts +++ b/mayfly_go_web/src/common/commonEnum.ts @@ -5,10 +5,10 @@ export const TagResourceTypeEnum = { AuthCert: EnumValue.of(-2, '公共凭证').setExtra({ icon: 'Ticket' }), Tag: EnumValue.of(-1, '标签').setExtra({ icon: 'CollectionTag' }), - Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor' }), - Db: EnumValue.of(2, '数据库').setExtra({ icon: 'Coin' }), - Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis' }), - Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo' }), + Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor' }).tagTypeSuccess(), + Db: EnumValue.of(2, '数据库').setExtra({ icon: 'Coin' }).tagTypeWarning(), + Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis' }).tagTypeInfo(), + Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo' }).tagTypeDanger(), MachineAuthCert: EnumValue.of(11, '机器-授权凭证').setExtra({ icon: 'Ticket' }), }; diff --git a/mayfly_go_web/src/common/pattern.ts b/mayfly_go_web/src/common/pattern.ts index a27d17cb..274a7763 100644 --- a/mayfly_go_web/src/common/pattern.ts +++ b/mayfly_go_web/src/common/pattern.ts @@ -4,6 +4,6 @@ export const AccountUsernamePattern = { }; export const ResourceCodePattern = { - pattern: /^[a-zA-Z0-9_]{1,32}$/g, - message: '只允许输入1-32位大小写字母、数字、下划线', + pattern: /^[a-zA-Z0-9_.:]{1,32}$/g, + message: '只允许输入1-32位大小写字母、数字、_.:', }; diff --git a/mayfly_go_web/src/views/ops/component/ResourceAuthCertEdit.vue b/mayfly_go_web/src/views/ops/component/ResourceAuthCertEdit.vue index 3080e898..115a1ea9 100644 --- a/mayfly_go_web/src/views/ops/component/ResourceAuthCertEdit.vue +++ b/mayfly_go_web/src/views/ops/component/ResourceAuthCertEdit.vue @@ -1,6 +1,6 @@ + - + @@ -51,6 +59,9 @@ import { ElMessage } from 'element-plus'; const props = defineProps({ resourceType: { type: Number }, resourceCode: { type: String }, + disableCiphertextType: { + type: Array, + }, testConnBtnLoading: { type: Boolean }, }); @@ -109,7 +120,19 @@ const cancelEdit = () => { const btnOk = async (authCert: any) => { const isEdit = authCert.id; - if (isEdit || state.idx > 0) { + if (!isEdit) { + const res = await resourceAuthCertApi.listByQuery.request({ + name: authCert.name, + pageNum: 1, + pageSize: 100, + }); + if (res.total) { + ElMessage.error('该授权凭证名称已存在'); + return; + } + } + + if (isEdit || state.idx >= 0) { authCerts.value[state.idx] = authCert; cancelEdit(); return; @@ -119,15 +142,6 @@ const btnOk = async (authCert: any) => { ElMessage.error('该名称或用户名已存在于该账号列表中'); return; } - const res = await resourceAuthCertApi.listByQuery.request({ - name: authCert.name, - pageNum: 1, - pageSize: 100, - }); - if (res.total) { - ElMessage.error('该授权凭证名称已存在'); - return; - } authCerts.value.push(authCert); cancelEdit(); diff --git a/mayfly_go_web/src/views/ops/db/DbEdit.vue b/mayfly_go_web/src/views/ops/db/DbEdit.vue index 7751eef4..d4a2ac8f 100644 --- a/mayfly_go_web/src/views/ops/db/DbEdit.vue +++ b/mayfly_go_web/src/views/ops/db/DbEdit.vue @@ -30,13 +30,14 @@ remote :remote-method="getInstances" @change="changeInstance" - v-model="form.instanceId" + v-model="state.selectInstalce" + value-key="id" placeholder="请输入实例名称搜索并选择实例" filterable clearable class="w100" > - + {{ item.name }} @@ -47,6 +48,23 @@ + + + + {{ item.name }} + + + {{ item.username }} + + + + + + {{ item.remark }} + + + + @@ -100,6 +118,10 @@ import type { CheckboxValueType } from 'element-plus'; import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue'; import { DbType } from '@/views/ops/db/dialect'; import { ResourceCodePattern } from '@/common/pattern'; +import { resourceAuthCertApi } from '../tag/api'; +import { TagResourceTypeEnum } from '@/common/commonEnum'; +import EnumTag from '@/components/enumtag/EnumTag.vue'; +import { AuthCertCiphertextTypeEnum } from '../tag/enums'; const props = defineProps({ visible: { @@ -172,6 +194,8 @@ const state = reactive({ dbNamesSelected: [] as any, dbNamesFiltered: [] as any, filterString: '', + selectInstalce: {} as any, + authCerts: [] as any, form: { id: null, tagId: [], @@ -180,6 +204,7 @@ const state = reactive({ database: '', remark: '', instanceId: null as any, + authCertName: '', flowProcdefKey: '', }, instances: [] as any, @@ -205,14 +230,27 @@ watch(props, async (newValue: any) => { } }); -const changeInstance = () => { +const changeInstance = async () => { state.dbNamesSelected = []; - getAllDatabase(); + state.form.instanceId = state.selectInstalce.id; }; -const getAllDatabase = async () => { +const getAuthCerts = async () => { + const res = await resourceAuthCertApi.listByQuery.request({ + resourceCode: state.selectInstalce.code, + resourceType: TagResourceTypeEnum.Db.value, + pageSize: 100, + }); + state.authCerts = res.list || []; +}; + +const changeAuthCert = (val: string) => { + getAllDatabase(val); +}; + +const getAllDatabase = async (authCertName: string) => { if (state.form.instanceId > 0) { - let dbs = await dbApi.getAllDatabase.request({ instanceId: state.form.instanceId }); + let dbs = await dbApi.getAllDatabase.request({ instanceId: state.form.instanceId, authCertName }); state.allDatabases = dbs; // 如果是oracle,且没查出数据库列表,则取实例sid @@ -238,8 +276,9 @@ const open = async () => { if (state.form.instanceId) { // 根据id获取,因为需要回显实例名称 await getInstances('', state.form.instanceId); + state.selectInstalce = state.instances[0]; + await getAllDatabase(state.form.authCertName); } - await getAllDatabase(); }; const btnOk = async () => { diff --git a/mayfly_go_web/src/views/ops/db/DbList.vue b/mayfly_go_web/src/views/ops/db/DbList.vue index eeac1cd6..2bfaaa01 100644 --- a/mayfly_go_web/src/views/ops/db/DbList.vue +++ b/mayfly_go_web/src/views/ops/db/DbList.vue @@ -194,7 +194,7 @@ - + diff --git a/mayfly_go_web/src/views/ops/db/InstanceEdit.vue b/mayfly_go_web/src/views/ops/db/InstanceEdit.vue index 46fffe0a..0ebd5ff3 100644 --- a/mayfly_go_web/src/views/ops/db/InstanceEdit.vue +++ b/mayfly_go_web/src/views/ops/db/InstanceEdit.vue @@ -1,94 +1,111 @@ + + - + @@ -58,6 +64,7 @@ value: 'id', label: 'codePath', children: 'children', + disabled: 'disabled', }" @check="tagTreeNodeCheck" > @@ -152,7 +159,6 @@ const state = reactive({ currentEditPermissions: false, tags: [], addTeamDialog: { - title: '新增团队', visible: false, form: { id: 0, name: '', remark: '', tags: [] }, }, @@ -201,16 +207,22 @@ const search = async () => { }; const showSaveTeamDialog = async (data: any) => { - if (state.tags.length == 0) { - state.tags = await tagApi.getTagTrees.request(null); - } + state.tags = await tagApi.getTagTrees.request(null); if (data) { state.addTeamDialog.form.id = data.id; state.addTeamDialog.form.name = data.name; state.addTeamDialog.form.remark = data.remark; - state.addTeamDialog.title = `修改 [${data.codePath}] 信息`; state.addTeamDialog.form.tags = await tagApi.getTeamTagIds.request({ teamId: data.id }); + + setTimeout(() => { + const checkedNodes = tagTreeRef.value.getCheckedNodes(); + console.log('check nodes: ', checkedNodes); + // 禁用选中节点的所有父节点,不可选中 + for (let checkNodeData of checkedNodes) { + disableParentNodes(tagTreeRef.value.getNode(checkNodeData.id).parent); + } + }, 200); } state.addTeamDialog.visible = true; @@ -231,8 +243,10 @@ const saveTeam = async () => { const cancelSaveTeam = () => { state.addTeamDialog.visible = false; - state.addTeamDialog.form = {} as any; teamForm.value.resetFields(); + setTimeout(() => { + state.addTeamDialog.form = {} as any; + }, 500); }; const deleteTeam = () => { @@ -289,34 +303,47 @@ const cancelAddMember = () => { state.showMemDialog.addVisible = false; }; -const tagTreeNodeCheck = () => { - // const node = tagTreeRef.value.getNode(data.id); - // console.log(node); - // // state.showTagDialog.tagTreeTeams = [16] - // if (node.checked) { - // if (node.parent) { - // console.log(node.parent); - // // removeCheckedTagId(node.parent.key); - // tagTreeRef.value.setChecked(node.parent, false, false); - // } - // // // parentNode = node.parent - // // for (let parentNode of node.parent) { - // // parentNode.setChecked(false); - // // } - // } - // console.log(data); - // console.log(checkInfo); +const tagTreeNodeCheck = (data: any) => { + const node = tagTreeRef.value.getNode(data.id); + console.log('check node: ', node); + + if (node.checked) { + // 如果选中了子节点,则需要将父节点全部取消选中,并禁用父节点 + unCheckParentNodes(node.parent); + disableParentNodes(node.parent); + } else { + // 如果取消了选中,则需要根据条件恢复父节点的选中状态 + disableParentNodes(node.parent, false); + } }; -// function removeCheckedTagId(id: any) { -// console.log(state.showTagDialog.tagTreeTeams); -// for (let i = 0; i < state.showTagDialog.tagTreeTeams.length; i++) { -// if (state.showTagDialog.tagTreeTeams[i] == id) { -// console.log('has id', id); -// state.showTagDialog.tagTreeTeams.splice(i, 1); -// } -// } -// console.log(state.showTagDialog.tagTreeTeams); -// } +const unCheckParentNodes = (node: any) => { + if (!node) { + return; + } + tagTreeRef.value.setChecked(node, false, false); + unCheckParentNodes(node.parent); +}; + +/** + * 禁用该节点以及所有父节点 + * @param node 节点 + * @param disable 是否禁用 + */ +const disableParentNodes = (node: any, disable = true) => { + if (!node) { + return; + } + if (!disable) { + // 恢复为非禁用状态时,若同层级存在一个选中状态或者禁用状态,则继续禁用 不恢复非禁用状态。 + for (let oneLevelNodes of node.childNodes) { + if (oneLevelNodes.checked || oneLevelNodes.data.disabled) { + return; + } + } + } + node.data.disabled = disable; + disableParentNodes(node.parent, disable); +}; diff --git a/mayfly_go_web/src/views/ops/tag/enums.ts b/mayfly_go_web/src/views/ops/tag/enums.ts index de061f7f..acd7a518 100644 --- a/mayfly_go_web/src/views/ops/tag/enums.ts +++ b/mayfly_go_web/src/views/ops/tag/enums.ts @@ -2,10 +2,10 @@ import { EnumValue } from '@/common/Enum'; // 授权凭证类型 export const AuthCertTypeEnum = { - Public: EnumValue.of(2, '公共凭证').tagTypeSuccess(), - Private: EnumValue.of(1, '普通账号').tagTypeSuccess(), - Privileged: EnumValue.of(11, '特权账号').tagTypeSuccess(), - PrivateDefault: EnumValue.of(12, '默认账号').tagTypeSuccess(), + Public: EnumValue.of(2, '公共凭证').tagTypeSuccess().tagTypeSuccess(), + Private: EnumValue.of(1, '普通账号'), + Privileged: EnumValue.of(11, '特权账号').tagTypeDanger(), + PrivateDefault: EnumValue.of(12, '默认账号').tagTypeWarning(), }; // 授权凭证密文类型 diff --git a/server/internal/common/consts/consts.go b/server/internal/common/consts/consts.go index 0eba158b..0d7899df 100644 --- a/server/internal/common/consts/consts.go +++ b/server/internal/common/consts/consts.go @@ -16,10 +16,10 @@ const ( // RedisConnExpireTime = 2 * time.Minute // MongoConnExpireTime = 2 * time.Minute - TagResourceTypeMachine int8 = 1 - TagResourceTypeDb int8 = 2 - TagResourceTypeRedis int8 = 3 - TagResourceTypeMongo int8 = 4 + ResourceTypeMachine int8 = 1 + ResourceTypeDb int8 = 2 + ResourceTypeRedis int8 = 3 + ResourceTypeMongo int8 = 4 // 删除机器的事件主题名 DeleteMachineEventTopic = "machine:delete" diff --git a/server/internal/db/api/dashbord.go b/server/internal/db/api/dashbord.go index a65e9cbe..331f54f8 100644 --- a/server/internal/db/api/dashbord.go +++ b/server/internal/db/api/dashbord.go @@ -15,7 +15,7 @@ type Dashbord struct { func (m *Dashbord) Dashbord(rc *req.Ctx) { accountId := rc.GetLoginAccount().Id - dbNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, "")) + dbNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeDb, "")) rc.ResData = collx.M{ "dbNum": dbNum, diff --git a/server/internal/db/api/db.go b/server/internal/db/api/db.go index aacb70e7..03852a67 100644 --- a/server/internal/db/api/db.go +++ b/server/internal/db/api/db.go @@ -44,7 +44,7 @@ func (d *Db) Dbs(rc *req.Ctx) { queryCond, page := req.BindQueryAndPage[*entity.DbQuery](rc, new(entity.DbQuery)) // 不存在可访问标签id,即没有可操作数据 - codes := d.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeDb, queryCond.TagPath) + codes := d.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, consts.ResourceTypeDb, queryCond.TagPath) if len(codes) == 0 { rc.ResData = model.EmptyPageResult[any]() return @@ -56,7 +56,7 @@ func (d *Db) Dbs(rc *req.Ctx) { biz.ErrIsNil(err) // 填充标签信息 - d.TagApp.FillTagInfo(collx.ArrayMap(dbvos, func(dbvo *vo.DbListVO) tagentity.ITagResource { + d.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeDb), collx.ArrayMap(dbvos, func(dbvo *vo.DbListVO) tagentity.ITagResource { return dbvo })...) @@ -280,7 +280,7 @@ func (d *Db) DumpSql(rc *req.Ctx) { la := rc.GetLoginAccount() db, err := d.DbApp.GetById(new(entity.Db), dbId) biz.ErrIsNil(err, "该数据库不存在") - biz.ErrIsNilAppendErr(d.TagApp.CanAccess(la.Id, d.TagApp.ListTagPathByResource(consts.TagResourceTypeDb, db.Code)...), "%s") + biz.ErrIsNilAppendErr(d.TagApp.CanAccess(la.Id, d.TagApp.ListTagPathByTypeAndCode(consts.ResourceTypeDb, db.Code)...), "%s") now := time.Now() filename := fmt.Sprintf("%s-%s.%s.sql%s", db.Name, dbName, now.Format("20060102150405"), extName) diff --git a/server/internal/db/api/db_instance.go b/server/internal/db/api/db_instance.go index ade2dcc7..626e22c6 100644 --- a/server/internal/db/api/db_instance.go +++ b/server/internal/db/api/db_instance.go @@ -1,28 +1,41 @@ package api import ( + "mayfly-go/internal/common/consts" "mayfly-go/internal/db/api/form" "mayfly-go/internal/db/api/vo" "mayfly-go/internal/db/application" "mayfly-go/internal/db/domain/entity" + + tagapp "mayfly-go/internal/tag/application" + tagentity "mayfly-go/internal/tag/domain/entity" "mayfly-go/pkg/biz" "mayfly-go/pkg/req" - "mayfly-go/pkg/utils/cryptox" + "mayfly-go/pkg/utils/collx" "strconv" "strings" ) type Instance struct { - InstanceApp application.Instance `inject:"DbInstanceApp"` - DbApp application.Db `inject:""` + InstanceApp application.Instance `inject:"DbInstanceApp"` + DbApp application.Db `inject:""` + ResourceAuthCertApp tagapp.ResourceAuthCert `inject:""` } // Instances 获取数据库实例信息 // @router /api/instances [get] func (d *Instance) Instances(rc *req.Ctx) { queryCond, page := req.BindQueryAndPage[*entity.InstanceQuery](rc, new(entity.InstanceQuery)) - res, err := d.InstanceApp.GetPageList(queryCond, page, new([]vo.InstanceListVO)) + + var instvos []*vo.InstanceListVO + res, err := d.InstanceApp.GetPageList(queryCond, page, &instvos) biz.ErrIsNil(err) + + // 填充授权凭证信息 + d.ResourceAuthCertApp.FillAuthCert(consts.ResourceTypeDb, collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert { + return vos + })...) + rc.ResData = res } @@ -30,12 +43,7 @@ func (d *Instance) TestConn(rc *req.Ctx) { form := &form.InstanceForm{} instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance)) - // 密码解密,并使用解密后的赋值 - originPwd, err := cryptox.DefaultRsaDecrypt(form.Password, true) - biz.ErrIsNilAppendErr(err, "解密密码错误: %s") - instance.Password = originPwd - - biz.ErrIsNil(d.InstanceApp.TestConn(instance)) + biz.ErrIsNil(d.InstanceApp.TestConn(instance, form.AuthCerts[0])) } // SaveInstance 保存数据库实例信息 @@ -44,15 +52,11 @@ func (d *Instance) SaveInstance(rc *req.Ctx) { form := &form.InstanceForm{} instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance)) - // 密码解密,并使用解密后的赋值 - originPwd, err := cryptox.DefaultRsaDecrypt(form.Password, true) - biz.ErrIsNilAppendErr(err, "解密密码错误: %s") - instance.Password = originPwd - - // 密码脱敏记录日志 - form.Password = "****" rc.ReqParam = form - biz.ErrIsNil(d.InstanceApp.Save(rc.MetaCtx, instance)) + biz.ErrIsNil(d.InstanceApp.SaveDbInstance(rc.MetaCtx, &application.SaveDbInstanceParam{ + DbInstance: instance, + AuthCerts: form.AuthCerts, + })) } // GetInstance 获取数据库实例密码,由于数据库是加密存储,故提供该接口展示原文密码 @@ -61,20 +65,9 @@ func (d *Instance) GetInstance(rc *req.Ctx) { dbId := getInstanceId(rc) dbEntity, err := d.InstanceApp.GetById(new(entity.DbInstance), dbId) biz.ErrIsNil(err, "获取数据库实例错误") - dbEntity.Password = "" rc.ResData = dbEntity } -// GetInstancePwd 获取数据库实例密码,由于数据库是加密存储,故提供该接口展示原文密码 -// @router /api/instances/:instance/pwd [GET] -func (d *Instance) GetInstancePwd(rc *req.Ctx) { - instanceId := getInstanceId(rc) - instanceEntity, err := d.InstanceApp.GetById(new(entity.DbInstance), instanceId, "Password") - biz.ErrIsNil(err, "获取数据库实例错误") - biz.ErrIsNil(instanceEntity.PwdDecrypt()) - rc.ResData = instanceEntity.Password -} - // DeleteInstance 删除数据库实例信息 // @router /api/instances/:instance [DELETE] func (d *Instance) DeleteInstance(rc *req.Ctx) { @@ -94,10 +87,13 @@ func (d *Instance) DeleteInstance(rc *req.Ctx) { // 获取数据库实例的所有数据库名 func (d *Instance) GetDatabaseNames(rc *req.Ctx) { instanceId := getInstanceId(rc) - instance, err := d.InstanceApp.GetById(new(entity.DbInstance), instanceId, "Password") + authCertName := rc.Query("authCertName") + biz.NotEmpty(authCertName, "授权凭证名不能为空") + + instance, err := d.InstanceApp.GetById(new(entity.DbInstance), instanceId) biz.ErrIsNil(err, "获取数据库实例错误") - biz.ErrIsNil(instance.PwdDecrypt()) - res, err := d.InstanceApp.GetDatabases(instance) + + res, err := d.InstanceApp.GetDatabases(instance, authCertName) biz.ErrIsNil(err) rc.ResData = res } diff --git a/server/internal/db/api/form/instance.go b/server/internal/db/api/form/instance.go index 89b50fa9..254f8828 100644 --- a/server/internal/db/api/form/instance.go +++ b/server/internal/db/api/form/instance.go @@ -1,15 +1,18 @@ package form +import tagentity "mayfly-go/internal/tag/domain/entity" + type InstanceForm struct { Id uint64 `json:"id"` + Code string `binding:"pattern=resource_code" json:"code"` Name string `binding:"required" json:"name"` Type string `binding:"required" json:"type"` // 类型,mysql oracle等 Host string `binding:"required" json:"host"` Port int `json:"port"` Extra string `json:"extra"` - Username string `json:"username"` - Password string `json:"password"` Params string `json:"params"` Remark string `json:"remark"` SshTunnelMachineId int `json:"sshTunnelMachineId"` + + AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表 } diff --git a/server/internal/db/api/vo/db.go b/server/internal/db/api/vo/db.go index ec6dd73a..d433905d 100644 --- a/server/internal/db/api/vo/db.go +++ b/server/internal/db/api/vo/db.go @@ -15,11 +15,12 @@ type DbListVO struct { Remark *string `json:"remark"` InstanceId *int64 `json:"instanceId"` + AuthCertName string `json:"authCertName"` + Username string `json:"username"` InstanceName *string `json:"instanceName"` InstanceType *string `json:"type"` Host string `json:"host"` Port int `json:"port"` - Username string `json:"username"` FlowProcdefKey string `json:"flowProcdefKey"` diff --git a/server/internal/db/api/vo/instance.go b/server/internal/db/api/vo/instance.go index b593e761..7de47aff 100644 --- a/server/internal/db/api/vo/instance.go +++ b/server/internal/db/api/vo/instance.go @@ -1,16 +1,21 @@ package vo -import "time" +import ( + tagentity "mayfly-go/internal/tag/domain/entity" + "time" +) type InstanceListVO struct { + tagentity.AuthCerts // 授权凭证信息 + Id *int64 `json:"id"` + Code string `json:"code"` Name *string `json:"name"` Host *string `json:"host"` Port *int `json:"port"` Type *string `json:"type"` Params string `json:"params"` Extra string `json:"extra"` - Username *string `json:"username"` Remark *string `json:"remark"` CreateTime *time.Time `json:"createTime"` Creator *string `json:"creator"` @@ -22,3 +27,7 @@ type InstanceListVO struct { SshTunnelMachineId int `json:"sshTunnelMachineId"` } + +func (i *InstanceListVO) GetCode() string { + return i.Code +} diff --git a/server/internal/db/application/db.go b/server/internal/db/application/db.go index eaa45d6b..012e0c4e 100644 --- a/server/internal/db/application/db.go +++ b/server/internal/db/application/db.go @@ -15,7 +15,6 @@ import ( "mayfly-go/pkg/errorx" "mayfly-go/pkg/model" "mayfly-go/pkg/utils/collx" - "mayfly-go/pkg/utils/structx" "sort" "strings" "time" @@ -86,10 +85,10 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...u return d.Tx(ctx, func(ctx context.Context) error { return d.Insert(ctx, dbEntity) }, func(ctx context.Context) error { - return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceCode: dbEntity.Code, - ResourceType: tagentity.TagTypeDb, - TagIds: tagIds, + return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Code: dbEntity.Code, + Type: tagentity.TagTypeDb, + ParentTagIds: tagIds, }) }) } @@ -126,10 +125,10 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...u return d.Tx(ctx, func(ctx context.Context) error { return d.UpdateById(ctx, dbEntity) }, func(ctx context.Context) error { - return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceCode: old.Code, - ResourceType: tagentity.TagTypeDb, - TagIds: tagIds, + return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Code: old.Code, + Type: tagentity.TagTypeDb, + ParentTagIds: tagIds, }) }) } @@ -153,9 +152,9 @@ func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error { // 删除该库下用户保存的所有sql信息 return d.dbSqlRepo.DeleteByCond(ctx, &entity.DbSql{DbId: id}) }, func(ctx context.Context) error { - return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceCode: db.Code, - ResourceType: tagentity.TagTypeDb, + return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Code: db.Code, + Type: tagentity.TagTypeDb, }) }) } @@ -172,11 +171,13 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) { return nil, errorx.NewBiz("数据库实例不存在") } - // 密码解密 - if err := instance.PwdDecrypt(); err != nil { - return nil, errorx.NewBiz(err.Error()) + di, err := d.dbInstanceApp.ToDbInfo(instance, db.AuthCertName, dbName) + if err != nil { + return nil, err } - di := toDbInfo(instance, dbId, dbName, d.tagApp.ListTagPathByResource(consts.TagResourceTypeDb, db.Code)...) + di.TagPath = d.tagApp.ListTagPathByTypeAndCode(consts.ResourceTypeDb, db.Code) + di.Id = db.Id + if db.FlowProcdefKey != nil { di.FlowProcdefKey = *db.FlowProcdefKey } @@ -323,14 +324,3 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *DumpDbReq) error { return nil } - -func toDbInfo(instance *entity.DbInstance, dbId uint64, database string, tagPath ...string) *dbi.DbInfo { - di := new(dbi.DbInfo) - di.InstanceId = instance.Id - di.Id = dbId - di.Database = database - di.TagPath = tagPath - - structx.Copy(di, instance) - return di -} diff --git a/server/internal/db/application/db_instance.go b/server/internal/db/application/db_instance.go index a1025295..5590cdf2 100644 --- a/server/internal/db/application/db_instance.go +++ b/server/internal/db/application/db_instance.go @@ -3,18 +3,27 @@ package application import ( "context" "errors" + "mayfly-go/internal/common/consts" "mayfly-go/internal/db/dbm" "mayfly-go/internal/db/dbm/dbi" "mayfly-go/internal/db/domain/entity" "mayfly-go/internal/db/domain/repository" + tagapp "mayfly-go/internal/tag/application" + tagentity "mayfly-go/internal/tag/domain/entity" "mayfly-go/pkg/base" "mayfly-go/pkg/biz" "mayfly-go/pkg/errorx" "mayfly-go/pkg/model" + "mayfly-go/pkg/utils/structx" "gorm.io/gorm" ) +type SaveDbInstanceParam struct { + DbInstance *entity.DbInstance + AuthCerts []*tagentity.ResourceAuthCert +} + type Instance interface { base.App[*entity.DbInstance] @@ -23,23 +32,27 @@ type Instance interface { Count(condition *entity.InstanceQuery) int64 - TestConn(instanceEntity *entity.DbInstance) error + TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error - Save(ctx context.Context, instanceEntity *entity.DbInstance) error + SaveDbInstance(ctx context.Context, instance *SaveDbInstanceParam) error // Delete 删除数据库信息 Delete(ctx context.Context, id uint64) error // GetDatabases 获取数据库实例的所有数据库列表 - GetDatabases(entity *entity.DbInstance) ([]string, error) + GetDatabases(entity *entity.DbInstance, authCertName string) ([]string, error) + + // ToDbInfo 根据实例与授权凭证返回对应的DbInfo + ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error) } type instanceAppImpl struct { base.AppImpl[*entity.DbInstance, repository.Instance] - dbApp Db `inject:"DbApp"` - backupApp *DbBackupApp `inject:"DbBackupApp"` - restoreApp *DbRestoreApp `inject:"DbRestoreApp"` + resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"` + dbApp Db `inject:"DbApp"` + backupApp *DbBackupApp `inject:"DbBackupApp"` + restoreApp *DbRestoreApp `inject:"DbRestoreApp"` } // 注入DbInstanceRepo @@ -56,9 +69,23 @@ func (app *instanceAppImpl) Count(condition *entity.InstanceQuery) int64 { return app.CountByCond(condition) } -func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance) error { +func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error { instanceEntity.Network = instanceEntity.GetNetwork() - dbConn, err := dbm.Conn(toDbInfo(instanceEntity, 0, "", "")) + + 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 } @@ -66,47 +93,65 @@ func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance) error { return nil } -func (app *instanceAppImpl) Save(ctx context.Context, instanceEntity *entity.DbInstance) error { +func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *SaveDbInstanceParam) error { + instanceEntity := instance.DbInstance // 默认tcp连接 instanceEntity.Network = instanceEntity.GetNetwork() + resourceType := consts.ResourceTypeDb + authCerts := instance.AuthCerts + + if len(authCerts) == 0 { + return errorx.NewBiz("授权凭证信息不能为空") + } // 查找是否存在该库 oldInstance := &entity.DbInstance{ Host: instanceEntity.Host, Port: instanceEntity.Port, - Username: instanceEntity.Username, SshTunnelMachineId: instanceEntity.SshTunnelMachineId, } err := app.GetBy(oldInstance) if instanceEntity.Id == 0 { - - if instanceEntity.Type != string(dbi.DbTypeSqlite) && instanceEntity.Password == "" { - return errorx.NewBiz("密码不能为空") - } - if err == nil { return errorx.NewBiz("该数据库实例已存在") } - if err := instanceEntity.PwdEncrypt(); err != nil { - return errorx.NewBiz(err.Error()) + if app.CountByCond(&entity.DbInstance{Code: instanceEntity.Code}) > 0 { + return errorx.NewBiz("该编码已存在") } - return app.Insert(ctx, instanceEntity) + + return app.Tx(ctx, func(ctx context.Context) error { + 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, + }) + }) + } // 如果存在该库,则校验修改的库是否为该库 if err == nil && oldInstance.Id != instanceEntity.Id { return errorx.NewBiz("该数据库实例已存在") } - if err := instanceEntity.PwdEncrypt(); err != nil { - return errorx.NewBiz(err.Error()) - } - return app.UpdateById(ctx, instanceEntity) + return app.Tx(ctx, func(ctx context.Context) error { + 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 (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error { instance, err := app.GetById(new(entity.DbInstance), instanceId, "name") - biz.ErrIsNil(err, "获取数据库实例错误,数据库实例ID为: %d", instance.Id) + if err != nil { + return errorx.NewBiz("获取数据库实例错误,数据库实例ID为: %d", instance.Id) + } restore := &entity.DbRestore{ DbInstanceId: instanceId, @@ -147,13 +192,25 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error biz.ErrIsNil(err, "删除数据库实例失败: %v", err) } - return app.DeleteById(ctx, instanceId) + 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 (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error) { +func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance, authCertName string) ([]string, error) { ed.Network = ed.GetNetwork() + dbi, err := app.ToDbInfo(ed, authCertName, "") + if err != nil { + return nil, err + } - dbConn, err := dbm.Conn(toDbInfo(ed, 0, "", "")) + dbConn, err := dbm.Conn(dbi) if err != nil { return nil, err } @@ -161,3 +218,23 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error return dbConn.GetMetaData().GetDbNames() } + +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 +} diff --git a/server/internal/db/domain/entity/db.go b/server/internal/db/domain/entity/db.go index 94b7e326..ce2770ec 100644 --- a/server/internal/db/domain/entity/db.go +++ b/server/internal/db/domain/entity/db.go @@ -12,5 +12,6 @@ type Db struct { Database string `orm:"column(database)" json:"database"` Remark string `json:"remark"` InstanceId uint64 + AuthCertName string `json:"authCertName"` FlowProcdefKey *string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批) } diff --git a/server/internal/db/domain/entity/db_instance.go b/server/internal/db/domain/entity/db_instance.go index 10a3e073..2426e61b 100644 --- a/server/internal/db/domain/entity/db_instance.go +++ b/server/internal/db/domain/entity/db_instance.go @@ -1,24 +1,21 @@ package entity import ( - "errors" "fmt" - "mayfly-go/internal/common/utils" "mayfly-go/pkg/model" ) type DbInstance struct { model.Model + Code string `json:"code"` Name string `json:"name"` Type string `json:"type"` // 类型,mysql oracle等 Host string `json:"host"` Port int `json:"port"` Network string `json:"network"` - Extra *string `json:"extra"` // 连接需要的其他额外参数(json格式), 如oracle需要sid等 - Username string `json:"username"` - Password string `json:"-"` - Params *string `json:"params"` + Extra *string `json:"extra"` // 连接需要的其他额外参数(json格式), 如oracle需要sid等 + Params *string `json:"params"` // 使用指针类型,可更新为零值(空字符串) Remark string `json:"remark"` SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id } @@ -39,23 +36,3 @@ func (d *DbInstance) GetNetwork() string { } return fmt.Sprintf("%s+ssh:%d", d.Type, d.SshTunnelMachineId) } - -func (d *DbInstance) PwdEncrypt() error { - // 密码替换为加密后的密码 - password, err := utils.PwdAesEncrypt(d.Password) - if err != nil { - return errors.New("加密数据库密码失败") - } - d.Password = password - return nil -} - -func (d *DbInstance) PwdDecrypt() error { - // 密码替换为解密后的密码 - password, err := utils.PwdAesDecrypt(d.Password) - if err != nil { - return errors.New("解密数据库密码失败") - } - d.Password = password - return nil -} diff --git a/server/internal/db/domain/entity/query.go b/server/internal/db/domain/entity/query.go index 8e30b69c..d12625ab 100644 --- a/server/internal/db/domain/entity/query.go +++ b/server/internal/db/domain/entity/query.go @@ -4,6 +4,7 @@ package entity type InstanceQuery struct { Id uint64 `json:"id" form:"id"` Name string `json:"name" form:"name"` + Code string `json:"code" form:"code"` Host string `json:"host" form:"host"` } diff --git a/server/internal/db/infrastructure/persistence/db.go b/server/internal/db/infrastructure/persistence/db.go index ae4ecc90..44d0ccd5 100644 --- a/server/internal/db/infrastructure/persistence/db.go +++ b/server/internal/db/infrastructure/persistence/db.go @@ -19,8 +19,8 @@ func newDbRepo() repository.Db { // 分页获取数据库信息列表 func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { qd := gormx.NewQueryWithTableName("t_db db"). - Select("db.*, inst.name instance_name, inst.type instance_type, inst.host, inst.port, inst.username "). - Joins("JOIN t_db_instance inst ON db.instance_id = inst.id"). + Select("db.*, inst.name instance_name, inst.type instance_type, inst.host, inst.port, rac.username "). + Joins("JOIN t_db_instance inst ON db.instance_id = inst.id JOIN t_resource_auth_cert rac ON inst.code = rac.resource_code AND rac.resource_type = 2"). Eq("db.instance_id", condition.InstanceId). Eq("db.id", condition.Id). Like("db.database", condition.Database). diff --git a/server/internal/db/infrastructure/persistence/instance.go b/server/internal/db/infrastructure/persistence/instance.go index f4652088..3e3b4a5c 100644 --- a/server/internal/db/infrastructure/persistence/instance.go +++ b/server/internal/db/infrastructure/persistence/instance.go @@ -21,6 +21,7 @@ func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, page qd := gormx.NewQuery(new(entity.DbInstance)). Eq("id", condition.Id). Eq("host", condition.Host). - Like("name", condition.Name) + Like("name", condition.Name). + Like("code", condition.Code) return gormx.PageQuery(qd, pageParam, toEntity) } diff --git a/server/internal/db/router/instance.go b/server/internal/db/router/instance.go index 81cbdeae..14335262 100644 --- a/server/internal/db/router/instance.go +++ b/server/internal/db/router/instance.go @@ -25,8 +25,6 @@ func InitInstanceRouter(router *gin.RouterGroup) { req.NewGet(":instanceId", d.GetInstance), - req.NewGet(":instanceId/pwd", d.GetInstancePwd), - // 获取数据库实例的所有数据库名 req.NewGet(":instanceId/databases", d.GetDatabaseNames), diff --git a/server/internal/machine/api/form/form.go b/server/internal/machine/api/form/form.go index 4db25605..5592465e 100644 --- a/server/internal/machine/api/form/form.go +++ b/server/internal/machine/api/form/form.go @@ -5,13 +5,13 @@ import tagentity "mayfly-go/internal/tag/domain/entity" type MachineForm struct { Id uint64 `json:"id"` Protocol int `json:"protocol" binding:"required"` - Code string `json:"code" binding:"required"` + Code string `json:"code" binding:"pattern=resource_code"` Name string `json:"name" binding:"required"` Ip string `json:"ip" binding:"required"` // IP地址 Port int `json:"port" binding:"required"` // 端口号 TagId []uint64 `json:"tagId" binding:"required"` - AuthCerts []*tagentity.ResourceAuthCert // 资产授权凭证信息列表 + AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表 Remark string `json:"remark"` SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id diff --git a/server/internal/machine/api/machine.go b/server/internal/machine/api/machine.go index 38cc721c..44adb098 100644 --- a/server/internal/machine/api/machine.go +++ b/server/internal/machine/api/machine.go @@ -3,6 +3,7 @@ package api import ( "encoding/base64" "fmt" + "mayfly-go/internal/common/consts" "mayfly-go/internal/machine/api/form" "mayfly-go/internal/machine/api/vo" "mayfly-go/internal/machine/application" @@ -62,12 +63,12 @@ func (m *Machine) Machines(rc *req.Ctx) { } // 填充标签信息 - m.TagApp.FillTagInfo(collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.ITagResource { + m.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeMachine), collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.ITagResource { return mvo })...) // 填充授权凭证信息 - m.ResourceAuthCertApp.FillAuthCert(authCerts, collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert { + m.ResourceAuthCertApp.FillAuthCertByAcs(authCerts, collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert { return mvo })...) diff --git a/server/internal/machine/application/machine.go b/server/internal/machine/application/machine.go index 3a62afd8..49138e88 100644 --- a/server/internal/machine/application/machine.go +++ b/server/internal/machine/application/machine.go @@ -17,7 +17,6 @@ import ( "mayfly-go/pkg/logx" "mayfly-go/pkg/model" "mayfly-go/pkg/scheduler" - "time" ) type SaveMachineParam struct { @@ -66,7 +65,6 @@ type Machine interface { type machineAppImpl struct { base.AppImpl[*entity.Machine, repository.Machine] - // authCertApp AuthCert `inject:"AuthCertApp"` tagApp tagapp.TagTree `inject:"TagTreeApp"` resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"` } @@ -87,6 +85,10 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara authCerts := param.AuthCerts resourceType := tagentity.TagTypeMachine + if len(authCerts) == 0 { + return errorx.NewBiz("授权凭证信息不能为空") + } + oldMachine := &entity.Machine{ Ip: me.Ip, Port: me.Port, @@ -94,7 +96,6 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara } err := m.GetBy(oldMachine) - if me.Id == 0 { if err == nil { return errorx.NewBiz("该机器信息已存在") @@ -109,16 +110,23 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara if err := m.Tx(ctx, func(ctx context.Context) error { return m.Insert(ctx, me) }, func(ctx context.Context) error { - return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ + return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Code: me.Code, + Type: resourceType, + ParentTagIds: tagIds, + }) + }, func(ctx context.Context) error { + return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{ ResourceCode: me.Code, ResourceType: resourceType, - TagIds: tagIds, + AuthCerts: authCerts, }) }); err != nil { return err } - return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{ + // 事务问题,导致可能删除或新增的标签数据不可见,造成数据错误,故单独执行。若万一失败,可授权凭证管理处添加 + return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{ ResourceCode: me.Code, ResourceType: resourceType, AuthCerts: authCerts, @@ -141,31 +149,44 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara if err := m.Tx(ctx, func(ctx context.Context) error { return m.UpdateById(ctx, me) }, func(ctx context.Context) error { - return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceCode: oldMachine.Code, - ResourceType: resourceType, - TagIds: tagIds, + return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Code: oldMachine.Code, + Type: resourceType, + ParentTagIds: tagIds, }) }); err != nil { return err } - return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{ - ResourceCode: oldMachine.Code, - ResourceType: resourceType, - AuthCerts: authCerts, + return m.Tx(ctx, func(ctx context.Context) error { + return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{ + ResourceCode: oldMachine.Code, + ResourceType: resourceType, + AuthCerts: authCerts, + }) + }, func(ctx context.Context) error { + return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{ + ResourceCode: oldMachine.Code, + ResourceType: resourceType, + AuthCerts: authCerts, + }) }) } func (m *machineAppImpl) TestConn(me *entity.Machine, authCert *tagentity.ResourceAuthCert) error { me.Id = 0 - if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic { - publicAuthCert, err := m.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext) - if err != nil { - return err + 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 = publicAuthCert } mi, err := m.toMi(me, authCert) @@ -208,12 +229,17 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error { func(ctx context.Context) error { return m.DeleteById(ctx, id) }, func(ctx context.Context) error { - return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ + return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Code: machine.Code, + Type: resourceType, + }) + }, func(ctx context.Context) error { + return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{ ResourceCode: machine.Code, ResourceType: resourceType, }) }, func(ctx context.Context) error { - return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{ + return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{ ResourceCode: machine.Code, ResourceType: resourceType, }) @@ -267,13 +293,9 @@ func (m *machineAppImpl) TimerUpdateStats() { }() logx.Debugf("定时获取机器[id=%d]状态信息开始", mid) cli, err := m.GetCli(mid) - // ssh获取客户端失败,则更新机器状态为禁用 if err != nil { - updateMachine := &entity.Machine{Status: entity.MachineStatusDisable} - updateMachine.Id = mid - now := time.Now() - updateMachine.UpdateTime = &now - m.UpdateById(context.TODO(), updateMachine) + logx.Errorf("定时获取机器[id=%d]状态信息失败, 获取机器cli失败: %s", mid, err.Error()) + return } cache.SaveMachineStats(mid, cli.GetAllStats()) logx.Debugf("定时获取机器[id=%d]状态信息结束", mid) @@ -336,7 +358,7 @@ func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAu mi.Name = me.Name mi.Ip = me.Ip mi.Port = me.Port - mi.TagPath = m.tagApp.ListTagPathByResource(int8(tagentity.TagTypeMachineAuthCert), authCert.Name) + mi.TagPath = m.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeMachineAuthCert), authCert.Name) mi.EnableRecorder = me.EnableRecorder mi.Protocol = me.Protocol diff --git a/server/internal/machine/infrastructure/persistence/machine.go b/server/internal/machine/infrastructure/persistence/machine.go index 64fdbaf1..7bd54cfa 100644 --- a/server/internal/machine/infrastructure/persistence/machine.go +++ b/server/internal/machine/infrastructure/persistence/machine.go @@ -28,7 +28,7 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar Like("ip", condition.Ip). Like("name", condition.Name). In("code", condition.Codes). - Eq("code", condition.Code) + Like("code", condition.Code) // 只查询ssh服务器 if condition.Ssh == entity.MachineProtocolSsh { diff --git a/server/internal/mongo/api/dashbord.go b/server/internal/mongo/api/dashbord.go index 2adc4d91..00926096 100644 --- a/server/internal/mongo/api/dashbord.go +++ b/server/internal/mongo/api/dashbord.go @@ -13,7 +13,7 @@ type Dashbord struct { func (m *Dashbord) Dashbord(rc *req.Ctx) { accountId := rc.GetLoginAccount().Id - mongoNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, "")) + mongoNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, "")) rc.ResData = collx.M{ "mongoNum": mongoNum, diff --git a/server/internal/mongo/api/mongo.go b/server/internal/mongo/api/mongo.go index 0f4dc6d7..0bc2c334 100644 --- a/server/internal/mongo/api/mongo.go +++ b/server/internal/mongo/api/mongo.go @@ -31,7 +31,7 @@ func (m *Mongo) Mongos(rc *req.Ctx) { queryCond, page := req.BindQueryAndPage[*entity.MongoQuery](rc, new(entity.MongoQuery)) // 不存在可访问标签id,即没有可操作数据 - codes := m.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeMongo, queryCond.TagPath) + codes := m.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, consts.ResourceTypeMongo, queryCond.TagPath) if len(codes) == 0 { rc.ResData = model.EmptyPageResult[any]() return @@ -43,7 +43,7 @@ func (m *Mongo) Mongos(rc *req.Ctx) { biz.ErrIsNil(err) // 填充标签信息 - m.TagApp.FillTagInfo(collx.ArrayMap(mongovos, func(mvo *vo.Mongo) tagentity.ITagResource { + m.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeMongo), collx.ArrayMap(mongovos, func(mvo *vo.Mongo) tagentity.ITagResource { return mvo })...) diff --git a/server/internal/mongo/application/mongo.go b/server/internal/mongo/application/mongo.go index 7046cf0e..77f9e372 100644 --- a/server/internal/mongo/application/mongo.go +++ b/server/internal/mongo/application/mongo.go @@ -59,9 +59,9 @@ func (d *mongoAppImpl) Delete(ctx context.Context, id uint64) error { return d.DeleteById(ctx, id) }, func(ctx context.Context) error { - return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceType: tagentity.TagTypeMongo, - ResourceCode: mongoEntity.Code, + return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Type: tagentity.TagTypeMongo, + Code: mongoEntity.Code, }) }) } @@ -90,10 +90,10 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds .. return d.Tx(ctx, func(ctx context.Context) error { return d.Insert(ctx, m) }, func(ctx context.Context) error { - return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceType: tagentity.TagTypeMongo, - ResourceCode: m.Code, - TagIds: tagIds, + return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Type: tagentity.TagTypeMongo, + Code: m.Code, + ParentTagIds: tagIds, }) }) } @@ -113,10 +113,10 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds .. return d.Tx(ctx, func(ctx context.Context) error { return d.UpdateById(ctx, m) }, func(ctx context.Context) error { - return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceType: tagentity.TagTypeMongo, - ResourceCode: oldMongo.Code, - TagIds: tagIds, + return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Type: tagentity.TagTypeMongo, + Code: oldMongo.Code, + ParentTagIds: tagIds, }) }) } @@ -127,6 +127,6 @@ func (d *mongoAppImpl) GetMongoConn(id uint64) (*mgm.MongoConn, error) { if err != nil { return nil, errorx.NewBiz("mongo信息不存在") } - return me.ToMongoInfo(d.tagApp.ListTagPathByResource(consts.TagResourceTypeMongo, me.Code)...), nil + return me.ToMongoInfo(d.tagApp.ListTagPathByTypeAndCode(consts.ResourceTypeMongo, me.Code)...), nil }) } diff --git a/server/internal/redis/api/dashbord.go b/server/internal/redis/api/dashbord.go index 4047369d..ea319668 100644 --- a/server/internal/redis/api/dashbord.go +++ b/server/internal/redis/api/dashbord.go @@ -13,7 +13,7 @@ type Dashbord struct { func (m *Dashbord) Dashbord(rc *req.Ctx) { accountId := rc.GetLoginAccount().Id - redisNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, "")) + redisNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, "")) rc.ResData = collx.M{ "redisNum": redisNum, diff --git a/server/internal/redis/api/redis.go b/server/internal/redis/api/redis.go index 0b117615..8eccad74 100644 --- a/server/internal/redis/api/redis.go +++ b/server/internal/redis/api/redis.go @@ -31,7 +31,7 @@ func (r *Redis) RedisList(rc *req.Ctx) { queryCond, page := req.BindQueryAndPage[*entity.RedisQuery](rc, new(entity.RedisQuery)) // 不存在可访问标签id,即没有可操作数据 - codes := r.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeRedis, queryCond.TagPath) + codes := r.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, consts.ResourceTypeRedis, queryCond.TagPath) if len(codes) == 0 { rc.ResData = model.EmptyPageResult[any]() return @@ -43,7 +43,7 @@ func (r *Redis) RedisList(rc *req.Ctx) { biz.ErrIsNil(err) // 填充标签信息 - r.TagApp.FillTagInfo(collx.ArrayMap(redisvos, func(rvo *vo.Redis) tagentity.ITagResource { + r.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeRedis), collx.ArrayMap(redisvos, func(rvo *vo.Redis) tagentity.ITagResource { return rvo })...) diff --git a/server/internal/redis/application/redis.go b/server/internal/redis/application/redis.go index cd166c5d..02577026 100644 --- a/server/internal/redis/application/redis.go +++ b/server/internal/redis/application/redis.go @@ -107,10 +107,10 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, re *entity.Redis, tagIds . return r.Tx(ctx, func(ctx context.Context) error { return r.Insert(ctx, re) }, func(ctx context.Context) error { - return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceType: tagenttiy.TagTypeRedis, - ResourceCode: re.Code, - TagIds: tagIds, + return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Type: tagenttiy.TagTypeRedis, + Code: re.Code, + ParentTagIds: tagIds, }) }) } @@ -138,10 +138,10 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, re *entity.Redis, tagIds . return r.Tx(ctx, func(ctx context.Context) error { return r.UpdateById(ctx, re) }, func(ctx context.Context) error { - return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceType: tagenttiy.TagTypeRedis, - ResourceCode: oldRedis.Code, - TagIds: tagIds, + return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Type: tagenttiy.TagTypeRedis, + Code: oldRedis.Code, + ParentTagIds: tagIds, }) }) } @@ -161,9 +161,9 @@ func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error { return r.Tx(ctx, func(ctx context.Context) error { return r.DeleteById(ctx, id) }, func(ctx context.Context) error { - return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{ - ResourceType: tagenttiy.TagTypeRedis, - ResourceCode: re.Code, + return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ + Type: tagenttiy.TagTypeRedis, + Code: re.Code, }) }) } @@ -179,7 +179,7 @@ func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) { if err := re.PwdDecrypt(); err != nil { return nil, errorx.NewBiz(err.Error()) } - return re.ToRedisInfo(db, r.tagApp.ListTagPathByResource(consts.TagResourceTypeRedis, re.Code)...), nil + return re.ToRedisInfo(db, r.tagApp.ListTagPathByTypeAndCode(consts.ResourceTypeRedis, re.Code)...), nil }) } diff --git a/server/internal/tag/api/tag_tree.go b/server/internal/tag/api/tag_tree.go index cfa3e3ca..8aa8f5ec 100644 --- a/server/internal/tag/api/tag_tree.go +++ b/server/internal/tag/api/tag_tree.go @@ -87,10 +87,10 @@ func (p *TagTree) DelTagTree(rc *req.Ctx) { func (p *TagTree) TagResources(rc *req.Ctx) { resourceType := int8(rc.PathParamInt("rtype")) accountId := rc.GetLoginAccount().Id - tagResources := p.TagTreeApp.GetAccountTagResources(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType)}) + tagResources := p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType)}) tagPath2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(tagResource *entity.TagTree) string { - return tagResource.GetParentPath(1) + return tagResource.GetTagPath() }) tagPaths := collx.MapKeys(tagPath2Resource) @@ -110,8 +110,8 @@ func (p *TagTree) CountTagResource(rc *req.Ctx) { rc.ResData = collx.M{ "machine": len(collx.ArrayDeduplicate(machineCodes)), - "db": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, tagPath)), - "redis": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, tagPath)), - "mongo": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, tagPath)), + "db": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeDb, tagPath)), + "redis": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, tagPath)), + "mongo": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)), } } diff --git a/server/internal/tag/application/resouce_auth_cert.go b/server/internal/tag/application/resouce_auth_cert.go index 6defe04a..caf67f20 100644 --- a/server/internal/tag/application/resouce_auth_cert.go +++ b/server/internal/tag/application/resouce_auth_cert.go @@ -23,9 +23,12 @@ type RelateAuthCertParam struct { type ResourceAuthCert interface { base.App[*entity.ResourceAuthCert] - // RelateAuthCert 保存资源授权凭证信息,不可放于事务中 + // RelateAuthCert 关联资源授权凭证信息 RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error + // RelateAuthCert2ResourceTag 关联授权凭证标签至指定资源标签下 + RelateAuthCert2ResourceTag(ctx context.Context, param *RelateAuthCertParam) error + // SaveAuthCert 保存授权凭证信息 SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error @@ -40,9 +43,15 @@ type ResourceAuthCert interface { // GetAccountAuthCert 获取账号有权限操作的授权凭证信息 GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert - // FillAuthCert 填充资源的授权凭证信息 + // FillAuthCertByAcs 根据授权凭证列表填充资源的授权凭证信息 + // @param authCerts 授权凭证列表 // @param resources 实现了entity.IAuthCert接口的资源信息 - FillAuthCert(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) + FillAuthCertByAcs(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) + + // FillAuthCert 填充资源对应的授权凭证信息 + // @param resourceType 资源类型 + // @param resources 实现了entity.IAuthCert接口的资源信息 + FillAuthCert(resourceType int8, resources ...entity.IAuthCert) } type resourceAuthCertAppImpl struct { @@ -60,11 +69,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re resourceCode := params.ResourceCode resourceType := int8(params.ResourceType) resourceAuthCerts := params.AuthCerts - authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType)) - if authCertTagType == 0 { - return errorx.NewBiz("资源授权凭证所属标签类型不能为空") - } if resourceCode == "" { return errorx.NewBiz("资源授权凭证的资源编号不能为空") } @@ -79,15 +84,6 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re return err } - // 删除该资源下的所有授权凭证资源标签 - if err := r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{ - ResourceCode: resourceCode, - ResourceType: params.ResourceType, - ChildType: authCertTagType, - }); err != nil { - return err - } - return nil } @@ -98,7 +94,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re name2AuthCert[resourceAuthCert.Name] = resourceAuthCert existNameAc := &entity.ResourceAuthCert{Name: resourceAuthCert.Name} - if r.GetBy(existNameAc) == nil && existNameAc.ResourceCode != resourceCode { + if resourceAuthCert.Id == 0 && r.GetBy(existNameAc) == nil && existNameAc.ResourceCode != resourceCode { return errorx.NewBiz("授权凭证的名称不能重复[%s]", resourceAuthCert.Name) } @@ -141,29 +137,6 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re if err := r.BatchInsert(ctx, addAuthCerts); err != nil { return err } - - // 获取资源编号对应的资源标签信息 - var resourceTags []*entity.TagTree - r.tagTreeApp.ListByCond(&entity.TagTree{Type: params.ResourceType, Code: resourceCode}, &resourceTags) - // 资源标签id(相当于父tag id) - resourceTagIds := collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 { - return tag.Id - }) - - if len(resourceTagIds) > 0 { - // 保存授权凭证类型的资源标签 - for _, authCert := range addAuthCerts { - logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, authCert.Name, resourceTagIds) - if err := r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{ - ResourceCode: authCert.Name, - ResourceType: authCertTagType, - ResourceName: authCert.Username, - TagIds: resourceTagIds, - }); err != nil { - return err - } - } - } } for _, del := range dels { @@ -171,13 +144,6 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil { return err } - // 删除对应授权凭证资源标签 - if err := r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{ - ResourceCode: del, - ResourceType: authCertTagType, - }); err != nil { - return err - } } if len(unmodifys) > 0 { @@ -185,6 +151,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re oldName2AuthCert := collx.ArrayToMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string { return ac.Name }) + acTagType := GetResourceAuthCertTagType(params.ResourceType) for _, unmodify := range unmodifys { unmodifyAc := name2AuthCert[unmodify] if unmodifyAc.Id == 0 { @@ -197,13 +164,11 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re continue } - logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify) - if oldAuthCert.Username != unmodifyAc.Username { - if err := r.updateAuthCertTagName(ctx, unmodify, authCertTagType, unmodifyAc.Username); err != nil { - logx.WarnfContext(ctx, "授权凭证[%s]修改了用户名-同步更新授权凭证标签名失败", unmodify) - } + // 如果修改了用户名,且该凭证关联至标签,则需要更新对应的标签名(资源授权凭证类型的标签名为username) + if oldAuthCert.Username != unmodifyAc.Username && acTagType != 0 { + r.updateAuthCertTagName(ctx, unmodify, acTagType, unmodifyAc.Username) } - + logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify) if err := r.UpdateById(ctx, unmodifyAc); err != nil { return err } @@ -213,6 +178,17 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re return nil } +func (r *resourceAuthCertAppImpl) RelateAuthCert2ResourceTag(ctx context.Context, param *RelateAuthCertParam) error { + return r.tagTreeApp.RelateTagsByCodeAndType(ctx, &RelateTagsByCodeAndTypeParam{ + ParentTagCode: param.ResourceCode, + ParentTagType: param.ResourceType, + TagType: GetResourceAuthCertTagType(param.ResourceType), + Tags: collx.ArrayMap(param.AuthCerts, func(rac *entity.ResourceAuthCert) ITag { + return rac + }), + }) +} + func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error { if rac.Id == 0 { return r.addAuthCert(ctx, rac) @@ -242,7 +218,7 @@ func (r *resourceAuthCertAppImpl) DeleteAuthCert(ctx context.Context, id uint64) return r.Tx(ctx, func(ctx context.Context) error { // 删除对应授权凭证标签 - return r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{ + return r.tagTreeApp.DeleteTagByParam(ctx, &DelResourceTagParam{ ResourceCode: rac.Name, }) }, @@ -288,7 +264,7 @@ func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertT Type: authCertTagType, CodePathLikes: tagPath, } - authCertTags := r.tagTreeApp.GetAccountTagResources(accountId, tagQuery) + authCertTags := r.tagTreeApp.GetAccountTags(accountId, tagQuery) // 获取所有授权凭证名称 authCertNames := collx.ArrayMap(authCertTags, func(tag *entity.TagTree) string { @@ -303,7 +279,7 @@ func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertT return authCerts } -func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) { +func (r *resourceAuthCertAppImpl) FillAuthCertByAcs(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) { if len(resources) == 0 || len(authCerts) == 0 { return } @@ -329,6 +305,19 @@ func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthC } } +func (r *resourceAuthCertAppImpl) FillAuthCert(resourceType int8, resources ...entity.IAuthCert) { + if len(resources) == 0 { + return + } + + resourceCodes := collx.ArrayMap(resources, func(ac entity.IAuthCert) string { + return ac.GetCode() + }) + var acs []*entity.ResourceAuthCert + r.Repo.ListByWheres(collx.M{"resource_code in ?": resourceCodes, "resource_type = ?": resourceType}, &acs) + r.FillAuthCertByAcs(acs, resources...) +} + // addAuthCert 添加授权凭证 func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error { if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 { @@ -344,32 +333,45 @@ func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.R resourceCode := rac.ResourceCode resourceType := rac.ResourceType + // 资源对应的授权凭证标签类型,若为0则说明该资源不需要关联至资源tagTree + authCertTagType := GetResourceAuthCertTagType(entity.TagType(resourceType)) - // 获取资源编号对应的资源标签信息 - var resourceTags []*entity.TagTree - r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags) - // 资源标签id(相当于父tag id) - resourceTagIds := collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 { - return tag.Id - }) - if len(resourceTagIds) == 0 { - return errorx.NewBiz("资源标签不存在[%s], 请检查资源编号是否正确", resourceCode) + var resourceTagIds []uint64 + // 如果该资源存在对应的授权凭证标签类型,则说明需要关联至tagTree,否则直接从授权凭证库中验证资源编号是否正确即可(一个资源最少有一个授权凭证) + if authCertTagType != 0 { + // 获取资源编号对应的资源标签信息 + var resourceTags []*entity.TagTree + r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags) + // 资源标签id(相当于父tag id) + resourceTagIds = collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 { + return tag.Id + }) + if len(resourceTagIds) == 0 { + return errorx.NewBiz("资源标签不存在[%s], 请检查资源编号是否正确", resourceCode) + } + } else { + if r.CountByCond(&entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}) == 0 { + return errorx.NewBiz("该授权凭证关联的资源信息不存在, 请检查资源编号") + } } - authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType)) // 如果密文类型不为公共凭证,则进行加密。公共凭证密文内容存的是明文的公共凭证名 if rac.CiphertextType != entity.AuthCertCiphertextTypePublic { rac.CiphertextEncrypt() } return r.Tx(ctx, func(ctx context.Context) error { - logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagIds) - return r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{ - ResourceCode: rac.Name, - ResourceType: authCertTagType, - ResourceName: rac.Username, - TagIds: resourceTagIds, - }) + // 若存在需要关联到的资源标签,则关联到对应的资源标签下 + if len(resourceTagIds) > 0 { + logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagIds) + return r.tagTreeApp.SaveResourceTag(ctx, &SaveResourceTagParam{ + Code: rac.Name, + Type: GetResourceAuthCertTagType(entity.TagType(resourceType)), + Name: rac.Username, + ParentTagIds: resourceTagIds, + }) + } + return nil }, func(ctx context.Context) error { return r.Insert(ctx, rac) }) @@ -401,9 +403,12 @@ func (r *resourceAuthCertAppImpl) updateAuthCert(ctx context.Context, rac *entit } // 修改了用户名,则需要同步更新对应授权凭证标签里的名称 - if rac.Username != oldRac.Username { - if err := r.updateAuthCertTagName(ctx, oldRac.Name, getResourceAuthCertTagType(entity.TagType(oldRac.ResourceType)), rac.Username); err != nil { - return errorx.NewBiz("同步更新授权凭证标签名称失败") + if rac.Username != oldRac.Username && rac.ResourceType == int8(entity.TagTypeMachine) { + authCertTagType := GetResourceAuthCertTagType(entity.TagType(oldRac.ResourceType)) + if authCertTagType != 0 { + if err := r.updateAuthCertTagName(ctx, oldRac.Name, authCertTagType, rac.Username); err != nil { + return errorx.NewBiz("同步更新授权凭证标签名称失败") + } } } } @@ -420,18 +425,6 @@ func (r *resourceAuthCertAppImpl) updateAuthCertTagName(ctx context.Context, aut return r.tagTreeApp.UpdateByWheres(ctx, &entity.TagTree{Name: newTagName}, collx.M{"code = ?": authCertName, "type = ?": autyCertTagType}) } -// getResourceAuthCertTagType 根据资源类型,获取对应的授权凭证标签类型 -func getResourceAuthCertTagType(resourceType entity.TagType) entity.TagType { - if resourceType == entity.TagTypeMachine { - return entity.TagTypeMachineAuthCert - } - if resourceType == entity.TagTypeDb { - return entity.TagTypeDbAuthCert - } - - return -2 -} - // 解密授权凭证信息 func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) { if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic { @@ -457,3 +450,13 @@ func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthC } return authCert, nil } + +// GetResourceAuthCertTagType 根据资源类型,获取对应的授权凭证标签类型,return 0 说明该资源授权凭证不关联至tagTree +func GetResourceAuthCertTagType(resourceType entity.TagType) entity.TagType { + if resourceType == entity.TagTypeMachine { + return entity.TagTypeMachineAuthCert + } + + // 该资源不存在对应的授权凭证标签,即tag_tree不关联该资源的授权凭证 + return 0 +} diff --git a/server/internal/tag/application/tag_tree.go b/server/internal/tag/application/tag_tree.go index e56a5025..eacdf73d 100644 --- a/server/internal/tag/application/tag_tree.go +++ b/server/internal/tag/application/tag_tree.go @@ -2,26 +2,46 @@ package application import ( "context" + "fmt" "mayfly-go/internal/common/consts" "mayfly-go/internal/tag/domain/entity" "mayfly-go/internal/tag/domain/repository" "mayfly-go/pkg/base" "mayfly-go/pkg/contextx" "mayfly-go/pkg/errorx" + "mayfly-go/pkg/logx" "mayfly-go/pkg/utils/collx" "strings" ) -// 保存资源标签参数 -type SaveResourceTagParam struct { - ResourceCode string - ResourceName string - ResourceType entity.TagType +// 标签接口,实现了该接口的结构体默认都可以当成标签树的一种标签 +type ITag interface { + // 资源标签code + GetCode() string - TagIds []uint64 // 关联标签,相当于父标签 pid,空数组则为删除该资源绑定的标签 + // 资源标签名 + GetName() string +} + +// 保存标签参数 +type SaveResourceTagParam struct { + Code string + Name string + Type entity.TagType + + ParentTagIds []uint64 // 关联标签,空数组则为删除该资源绑定的标签 +} + +type RelateTagsByCodeAndTypeParam struct { + ParentTagCode string // 父标签编号 + ParentTagType entity.TagType // 父标签类型 + + TagType entity.TagType // 要关联的标签类型 + Tags []ITag // 要关联的标签数组 } type DelResourceTagParam struct { + Id uint64 ResourceCode string ResourceType entity.TagType @@ -40,23 +60,25 @@ type TagTree interface { Delete(ctx context.Context, id uint64) error - // 获取指定账号有权限操作的资源信息列表 + // 获取指定账号有权限操作的标签列表 // @param accountId 账号id - // @param resourceType 资源类型 - // @param tagPath 访问指定的标签路径下关联的资源 - GetAccountTagResources(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree + // @param query 查询条件 + GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree - // 获取指定账号有权限操作的资源codes - GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string + // 获取指定账号有权限操作的标签codes + GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string - // SaveResource 保存资源标签 - SaveResource(ctx context.Context, req *SaveResourceTagParam) error + // SaveResourceTag 保存资源类型标签 + SaveResourceTag(ctx context.Context, param *SaveResourceTagParam) error - // DeleteResource 删除资源标签,会删除该资源下所有子节点信息 - DeleteResource(ctx context.Context, param *DelResourceTagParam) error + // RelateTagsByCodeAndType 将指定标签数组关联至满足指定标签类型和标签code的标签下 + RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error - // 根据资源信息获取对应的标签路径列表 - ListTagPathByResource(resourceType int8, resourceCode string) []string + // DeleteTagByParam 删除标签,会删除该标签下所有子标签信息以及团队关联的标签信息 + DeleteTagByParam(ctx context.Context, param *DelResourceTagParam) error + + // 根据标签类型和标签code获取对应的标签路径列表 + ListTagPathByTypeAndCode(resourceType int8, resourceCode string) []string // 根据账号id获取其可访问标签信息 ListTagByAccountId(accountId uint64) []string @@ -65,7 +87,7 @@ type TagTree interface { CanAccess(accountId uint64, tagPath ...string) error // 填充资源的标签信息 - FillTagInfo(resources ...entity.ITagResource) + FillTagInfo(resourceTagType entity.TagType, resources ...entity.ITagResource) } type tagTreeAppImpl struct { @@ -114,7 +136,7 @@ func (p *tagTreeAppImpl) Save(ctx context.Context, tag *entity.TagTree) error { } // 普通标签类型 - tag.Type = -1 + tag.Type = entity.TagTypeTag return p.Insert(ctx, tag) } @@ -128,7 +150,7 @@ func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity an p.GetRepo().SelectByCondition(condition, toEntity) } -func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree { +func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree { tagResourceQuery := &entity.TagTreeQuery{ Type: query.Type, } @@ -182,8 +204,8 @@ func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, query *entity. return tagResources } -func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string { - tagResources := p.GetAccountTagResources(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType), CodePathLikes: []string{tagPath}}) +func (p *tagTreeAppImpl) GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string { + tagResources := p.GetAccountTags(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType), CodePathLikes: []string{tagPath}}) // resouce code去重 code2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(val *entity.TagTree) string { return val.Code @@ -192,34 +214,34 @@ func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType return collx.MapKeys(code2Resource) } -func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagParam) error { - resourceCode := req.ResourceCode - resourceType := entity.TagType(req.ResourceType) - resourceName := req.ResourceName - tagIds := req.TagIds +func (p *tagTreeAppImpl) SaveResourceTag(ctx context.Context, param *SaveResourceTagParam) error { + code := param.Code + tagType := entity.TagType(param.Type) + name := param.Name + tagIds := param.ParentTagIds - if resourceCode == "" { + if code == "" { return errorx.NewBiz("资源编号不能为空") } - if resourceType == 0 { + if tagType == 0 { return errorx.NewBiz("资源类型不能为空") } // 如果tagIds为空数组,则为删除该资源标签 if len(tagIds) == 0 { - return p.DeleteResource(ctx, &DelResourceTagParam{ - ResourceType: resourceType, - ResourceCode: resourceCode, + return p.DeleteTagByParam(ctx, &DelResourceTagParam{ + ResourceType: tagType, + ResourceCode: code, }) } - if resourceName == "" { - resourceName = resourceCode + if name == "" { + name = code } // 该资源对应的旧资源标签信息 var oldTagTree []*entity.TagTree - p.ListByCond(&entity.TagTree{Type: resourceType, Code: resourceCode}, &oldTagTree) + p.ListByCond(&entity.TagTree{Type: tagType, Code: code}, &oldTagTree) var addTagIds, delTagIds []uint64 if len(oldTagTree) == 0 { @@ -240,11 +262,12 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP } addTagResource = append(addTagResource, &entity.TagTree{ Pid: tagId, - Code: resourceCode, - Type: resourceType, - Name: resourceName, - CodePath: tag.CodePath + resourceCode + entity.CodePathSeparator, + Code: code, + Type: tagType, + Name: name, + CodePath: fmt.Sprintf("%s%d%s%s%s", tag.CodePath, tagType, entity.CodePathResourceSeparator, code, entity.CodePathSeparator), // tag1/tag2/1|resourceCode1/11|resourceCode2 }) + } if err := p.BatchInsert(ctx, addTagResource); err != nil { return err @@ -253,9 +276,9 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP if len(delTagIds) > 0 { for _, tagId := range delTagIds { - if err := p.DeleteResource(ctx, &DelResourceTagParam{ - ResourceType: resourceType, - ResourceCode: resourceCode, + if err := p.DeleteTagByParam(ctx, &DelResourceTagParam{ + ResourceType: tagType, + ResourceCode: code, Pid: tagId, }); err != nil { return err @@ -267,29 +290,136 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP return nil } -func (p *tagTreeAppImpl) DeleteResource(ctx context.Context, param *DelResourceTagParam) error { - // 获取资源编号对应的资源标签信息 - var resourceTags []*entity.TagTree - p.ListByCond(&entity.TagTree{Type: param.ResourceType, Code: param.ResourceCode, Pid: param.Pid}, &resourceTags) - if len(resourceTags) == 0 { - return nil +func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error { + parentTagCode := param.ParentTagCode + parentTagType := param.ParentTagType + tagType := param.TagType + + // 如果资源为,则表示清楚关联 + if len(param.Tags) == 0 { + // 删除该资源下的所有指定类型的资源 + return p.DeleteTagByParam(ctx, &DelResourceTagParam{ + ResourceCode: parentTagCode, + ResourceType: param.ParentTagType, + ChildType: tagType, + }) } - delTagType := param.ChildType - for _, resourceTag := range resourceTags { - // 删除所有code_path下的子标签 - if err := p.DeleteByWheres(ctx, collx.M{ - "code_path LIKE ?": resourceTag.CodePath + "%", - "type = ?": delTagType, - }); err != nil { + // 获取满足指定编号与类型的所有标签信息 + var parentTags []*entity.TagTree + p.ListByCond(&entity.TagTree{Type: parentTagType, Code: parentTagCode}, &parentTags) + // 标签id(相当于需要关联的标签数组的父tag id) + parentTagIds := collx.ArrayMap(parentTags, func(tag *entity.TagTree) uint64 { + return tag.Id + }) + + if len(parentTagIds) == 0 { + return errorx.NewBiz("不存在满足[type=%d, code=%s]的标签", parentTagType, parentTagCode) + } + + var oldChildrenTags []*entity.TagTree + // 获取该资源的所有旧的该类型的子标签 + p.ListByQuery(&entity.TagTreeQuery{ + CodePathLikes: collx.ArrayMap[*entity.TagTree, string](parentTags, func(val *entity.TagTree) string { return val.CodePath }), + Type: tagType, + }, &oldChildrenTags) + + // 组合新的授权凭证资源标签 + newTags := make([]*entity.TagTree, 0) + for _, resourceTag := range parentTags { + for _, resource := range param.Tags { + tagCode := resource.GetCode() + newTags = append(newTags, &entity.TagTree{ + Pid: resourceTag.Id, + Type: tagType, + Code: tagCode, + CodePath: fmt.Sprintf("%s%d%s%s%s", resourceTag.CodePath, tagType, entity.CodePathResourceSeparator, tagCode, entity.CodePathSeparator), + Name: resource.GetName(), + }) + } + } + + // 旧的codePath -> tag + oldCodePath2Tag := collx.ArrayToMap[*entity.TagTree, string](oldChildrenTags, func(val *entity.TagTree) string { return val.CodePath }) + // 新的codePath -> tag + newCodePath2Tag := collx.ArrayToMap[*entity.TagTree, string](newTags, func(val *entity.TagTree) string { return val.CodePath }) + + var addCodePaths, delCodePaths []string + addCodePaths, delCodePaths, _ = collx.ArrayCompare(collx.MapKeys(newCodePath2Tag), collx.MapKeys(oldCodePath2Tag)) + + if len(addCodePaths) > 0 { + logx.DebugfContext(ctx, "RelateTags2CodeAndType[%d-%s]-新增标签[%v]", parentTagType, parentTagCode, addCodePaths) + + addTags := make([]*entity.TagTree, 0) + for _, addCodePath := range addCodePaths { + addTags = append(addTags, newCodePath2Tag[addCodePath]) + } + if err := p.BatchInsert(ctx, addTags); err != nil { return err } } + if len(delCodePaths) > 0 { + logx.DebugfContext(ctx, "RelateTags2CodeAndType[%d-%s]-删除标签[%v]", parentTagType, parentTagCode, delCodePaths) + + for _, delCodePath := range delCodePaths { + oldTag := oldCodePath2Tag[delCodePath] + if oldTag == nil { + continue + } + if err := p.DeleteTagByParam(ctx, &DelResourceTagParam{ + Id: oldTag.Id, + }); err != nil { + return err + } + } + } + return nil } -func (p *tagTreeAppImpl) ListTagPathByResource(resourceType int8, resourceCode string) []string { +func (p *tagTreeAppImpl) DeleteTagByParam(ctx context.Context, param *DelResourceTagParam) error { + // 获取资源编号对应的资源标签信息 + var resourceTags []*entity.TagTree + cond := &entity.TagTree{Type: param.ResourceType, Code: param.ResourceCode, Pid: param.Pid} + cond.Id = param.Id + p.ListByCond(cond, &resourceTags) + + if len(resourceTags) == 0 { + logx.DebugfContext(ctx, "TagTreeApp.DeleteResource[%d-%s]不存在可删除的标签", param.ResourceType, param.ResourceCode) + return nil + } + + delTagType := param.ChildType + for _, resourceTag := range resourceTags { + // 获取所有关联的子标签 + var childrenTag []*entity.TagTree + p.Repo.ListByWheres(collx.M{ + "code_path LIKE ?": resourceTag.CodePath + "%", + "type = ?": delTagType, + }, &childrenTag) + if len(childrenTag) == 0 { + continue + } + + childrenTagIds := collx.ArrayMap(childrenTag, func(item *entity.TagTree) uint64 { + return item.Id + }) + // 删除所有code_path下的子标签 + if err := p.DeleteByWheres(ctx, collx.M{ + "id in ?": childrenTagIds, + }); err != nil { + return err + } + + // 删除team关联的标签 + return p.tagTreeTeamRepo.DeleteByWheres(ctx, collx.M{"tag_id in ?": childrenTagIds}) + } + + return nil +} + +func (p *tagTreeAppImpl) ListTagPathByTypeAndCode(resourceType int8, resourceCode string) []string { var trs []*entity.TagTree p.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &trs) return collx.ArrayMap(trs, func(tr *entity.TagTree) string { @@ -318,7 +448,7 @@ func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error { return errorx.NewBiz("您无权操作该资源") } -func (p *tagTreeAppImpl) FillTagInfo(resources ...entity.ITagResource) { +func (p *tagTreeAppImpl) FillTagInfo(resourceTagType entity.TagType, resources ...entity.ITagResource) { if len(resources) == 0 { return } @@ -330,11 +460,11 @@ func (p *tagTreeAppImpl) FillTagInfo(resources ...entity.ITagResource) { // 获取所有资源code关联的标签列表信息 var tagResources []*entity.TagTree - p.ListByQuery(&entity.TagTreeQuery{Codes: collx.MapKeys(resourceCode2Resouce)}, &tagResources) + p.ListByQuery(&entity.TagTreeQuery{Codes: collx.MapKeys(resourceCode2Resouce), Type: resourceTagType}, &tagResources) for _, tr := range tagResources { // 赋值标签信息 - resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Pid, TagPath: tr.GetParentPath(0)}) + resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Pid, TagPath: tr.GetTagPath()}) } } diff --git a/server/internal/tag/domain/entity/resource_auth_cert.go b/server/internal/tag/domain/entity/resource_auth_cert.go index 6457f679..7c6b08cb 100644 --- a/server/internal/tag/domain/entity/resource_auth_cert.go +++ b/server/internal/tag/domain/entity/resource_auth_cert.go @@ -97,6 +97,16 @@ func (m *ResourceAuthCert) GetExtraString(key string) string { return cast.ToString(m.Extra[key]) } +// IResourceTag接口 授权凭证名 -> 资源标签code +func (m *ResourceAuthCert) GetCode() string { + return m.Name +} + +// IResourceTag接口 授权凭证用户名 -> 资源标签名 +func (m *ResourceAuthCert) GetName() string { + return m.Username +} + // HasChanged 与指定授权凭证比较是否有变更 func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool { if rac == nil { diff --git a/server/internal/tag/domain/entity/tag_tree.go b/server/internal/tag/domain/entity/tag_tree.go index 3eb6ed46..b426ad91 100644 --- a/server/internal/tag/domain/entity/tag_tree.go +++ b/server/internal/tag/domain/entity/tag_tree.go @@ -23,17 +23,18 @@ type TagType int8 const ( // 标识路径分隔符 CodePathSeparator = "/" + // 标签路径资源段分隔符 + CodePathResourceSeparator = "|" TagTypeTag TagType = -1 - TagTypeMachine TagType = TagType(consts.TagResourceTypeMachine) - TagTypeDb TagType = TagType(consts.TagResourceTypeDb) - TagTypeRedis TagType = TagType(consts.TagResourceTypeRedis) - TagTypeMongo TagType = TagType(consts.TagResourceTypeMongo) + TagTypeMachine TagType = TagType(consts.ResourceTypeMachine) + TagTypeDb TagType = TagType(consts.ResourceTypeDb) + TagTypeRedis TagType = TagType(consts.ResourceTypeRedis) + TagTypeMongo TagType = TagType(consts.ResourceTypeMongo) // ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证) TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证 - TagTypeDbAuthCert TagType = 21 // DB-授权凭证 ) // GetRootCode 获取根路径信息 @@ -62,6 +63,30 @@ func (pt *TagTree) GetParentPath(index int) string { return parentPath + "/" } +// GetTagPath 获取标签段路径,不获取对应资源相关路径 +func (pt *TagTree) GetTagPath() string { + codePath := pt.CodePath + + // 以 资源分隔符"|" 符号对字符串进行分割 + parts := strings.Split(codePath, CodePathResourceSeparator) + if len(parts) < 2 { + return codePath + } + + // 从分割后的第一个子串中提取所需部分 + substringBeforeNumber := parts[0] + + // 找到最后一个 "/" 的位置 + lastSlashIndex := strings.LastIndex(substringBeforeNumber, CodePathSeparator) + + // 如果找到最后一个 "/" 符号,则截取子串 + if lastSlashIndex != -1 { + return substringBeforeNumber[:lastSlashIndex+1] + } + + return codePath +} + // 标签接口资源,如果要实现资源结构体填充标签信息,则资源结构体需要实现该接口 type ITagResource interface { // 获取资源code diff --git a/server/pkg/validatorx/pattern.go b/server/pkg/validatorx/pattern.go index 4f8c3acf..a329707b 100644 --- a/server/pkg/validatorx/pattern.go +++ b/server/pkg/validatorx/pattern.go @@ -18,6 +18,7 @@ var ( func RegisterCustomPatterns() { // 账号用户名校验 RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线") + RegisterPattern("resource_code", "^[a-zA-Z0-9_.:]{1,32}$", "只允许输入1-32位大小写字母、数字、_.:") } // 注册自定义正则表达式 diff --git a/server/resources/script/sql/mayfly-go.sql b/server/resources/script/sql/mayfly-go.sql index 5489eb1c..d3ec3a0c 100644 --- a/server/resources/script/sql/mayfly-go.sql +++ b/server/resources/script/sql/mayfly-go.sql @@ -873,7 +873,7 @@ CREATE TABLE `t_tag_tree` ( `pid` bigint(20) NOT NULL DEFAULT '0', `type` tinyint NOT NULL DEFAULT '-1' COMMENT '类型: -1.普通标签; 其他值则为对应的资源类型', `code` varchar(36) NOT NULL COMMENT '标识符', - `code_path` varchar(255) NOT NULL COMMENT '标识符路径', + `code_path` varchar(555) NOT NULL COMMENT '标识符路径', `name` varchar(36) DEFAULT NULL COMMENT '名称', `remark` varchar(255) DEFAULT NULL, `create_time` datetime NOT NULL, diff --git a/server/resources/script/sql/v1.7/v1.7.5.sql b/server/resources/script/sql/v1.7/v1.7.5.sql index 6e0d9533..297aa70e 100644 --- a/server/resources/script/sql/v1.7/v1.7.5.sql +++ b/server/resources/script/sql/v1.7/v1.7.5.sql @@ -76,7 +76,7 @@ INSERT select tag_id, resource_code, - CONCAT(tag_path , resource_code, '/'), + CONCAT(tag_path ,resource_type , '|', resource_code, '/'), resource_type, resource_code, DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'), @@ -175,7 +175,7 @@ INSERT SELECT tt.id, rac.`name`, - CONCAT(tt.code_path, rac.`name`, '/'), + CONCAT(tt.code_path, '11|' ,rac.`name`, '/'), 11, rac.`username`, DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'), @@ -188,9 +188,9 @@ SELECT FROM `t_tag_tree` tt JOIN `t_resource_auth_cert` rac ON tt.`code` = rac.`resource_code` - AND tt.`type` = rac.`resource_type` + AND tt.`type` = rac.`resource_type` AND rac.type = 1 WHERE - tt.`is_deleted` = 0 + tt.`is_deleted` = 0; -- 删除机器表 账号相关字段 ALTER TABLE t_machine DROP COLUMN username; @@ -205,3 +205,65 @@ INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1712717337, 1712717290, 'tLb8TKLB/m2abQkA8/', 2, 1, '授权凭证密文查看', 'authcert:showciphertext', 1712717337, 'null', 1, 'admin', 1, 'admin', '2024-04-10 10:48:58', '2024-04-10 10:48:58', 0, NULL); commit; +-- 关联数据库账号至授权凭证表 +begin; +ALTER TABLE t_db_instance ADD code varchar(36) NULL COMMENT '唯一编号'; +ALTER TABLE t_db_instance CHANGE code code varchar(36) NULL COMMENT '唯一编号' AFTER id; + +UPDATE t_db_instance SET code = CONCAT('db_code_', id); + +INSERT + INTO + t_resource_auth_cert (name, + resource_code, + resource_type, + type, + username, + ciphertext, + ciphertext_type, + create_time, + creator_id, + creator, + update_time, + modifier_id, + modifier, + is_deleted) +select + CONCAT(code, '_', username), + code, + 2, + 1, + username, + password, + 1, + DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'), + 1, + 'admin', + DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'), + 1, + 'admin', + 0 +from + t_db_instance +WHERE + is_deleted = 0; + +ALTER TABLE t_db ADD auth_cert_name varchar(36) NULL COMMENT '授权凭证名'; +ALTER TABLE t_db CHANGE auth_cert_name auth_cert_name varchar(36) NULL COMMENT '授权凭证名' AFTER instance_id; + +UPDATE + t_db d +SET + d.auth_cert_name = ( + SELECT + rac.name + FROM + t_resource_auth_cert rac + join t_db_instance di on + rac.resource_code = di.code + and rac.resource_type = 2 + WHERE + di.id = d.instance_id); + +ALTER TABLE t_tag_tree MODIFY COLUMN code_path varchar(555) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '标识符路径'; +commit; \ No newline at end of file