mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-31 03:46:34 +08:00
refactor: 数据库授权凭证迁移
This commit is contained in:
@@ -5,10 +5,10 @@ export const TagResourceTypeEnum = {
|
|||||||
AuthCert: EnumValue.of(-2, '公共凭证').setExtra({ icon: 'Ticket' }),
|
AuthCert: EnumValue.of(-2, '公共凭证').setExtra({ icon: 'Ticket' }),
|
||||||
Tag: EnumValue.of(-1, '标签').setExtra({ icon: 'CollectionTag' }),
|
Tag: EnumValue.of(-1, '标签').setExtra({ icon: 'CollectionTag' }),
|
||||||
|
|
||||||
Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor' }),
|
Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor' }).tagTypeSuccess(),
|
||||||
Db: EnumValue.of(2, '数据库').setExtra({ icon: 'Coin' }),
|
Db: EnumValue.of(2, '数据库').setExtra({ icon: 'Coin' }).tagTypeWarning(),
|
||||||
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis' }),
|
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis' }).tagTypeInfo(),
|
||||||
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo' }),
|
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo' }).tagTypeDanger(),
|
||||||
|
|
||||||
MachineAuthCert: EnumValue.of(11, '机器-授权凭证').setExtra({ icon: 'Ticket' }),
|
MachineAuthCert: EnumValue.of(11, '机器-授权凭证').setExtra({ icon: 'Ticket' }),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ export const AccountUsernamePattern = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ResourceCodePattern = {
|
export const ResourceCodePattern = {
|
||||||
pattern: /^[a-zA-Z0-9_]{1,32}$/g,
|
pattern: /^[a-zA-Z0-9_.:]{1,32}$/g,
|
||||||
message: '只允许输入1-32位大小写字母、数字、下划线',
|
message: '只允许输入1-32位大小写字母、数字、_.:',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="auth-cert-edit">
|
<div class="auth-cert-edit">
|
||||||
<el-dialog title="凭证保存" v-model="dialogVisible" :show-close="false" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
|
<el-dialog :title="props.title" v-model="dialogVisible" :show-close="false" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
<el-form ref="acForm" :model="state.form" label-width="auto" :rules="rules">
|
<el-form ref="acForm" :model="state.form" label-width="auto" :rules="rules">
|
||||||
<el-form-item prop="type" label="凭证类型" required>
|
<el-form-item prop="type" label="凭证类型" required>
|
||||||
<el-select @change="changeType" v-model="form.type" placeholder="请选择凭证类型">
|
<el-select @change="changeType" v-model="form.type" placeholder="请选择凭证类型">
|
||||||
@@ -118,6 +118,10 @@ import { ResourceCodePattern } from '@/common/pattern';
|
|||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '凭证保存',
|
||||||
|
},
|
||||||
authCert: {
|
authCert: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
@@ -162,7 +166,7 @@ const rules = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const emit = defineEmits(['confirm']);
|
const emit = defineEmits(['confirm', 'cancel']);
|
||||||
|
|
||||||
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
||||||
|
|
||||||
@@ -238,9 +242,10 @@ const getCiphertext = async () => {
|
|||||||
|
|
||||||
const cancelEdit = () => {
|
const cancelEdit = () => {
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
|
emit('cancel');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
state.form = { ...DefaultForm };
|
|
||||||
acForm.value?.resetFields();
|
acForm.value?.resetFields();
|
||||||
|
state.form = { ...DefaultForm };
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,17 @@
|
|||||||
<EnumTag :value="scope.row.type" :enums="AuthCertTypeEnum" />
|
<EnumTag :value="scope.row.type" :enums="AuthCertTypeEnum" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="remark" label="备注" show-overflow-tooltip width="120px"> </el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<ResourceAuthCertEdit v-model:visible="state.dvisible" :auth-cert="state.form" @confirm="btnOk" :disable-type="[AuthCertTypeEnum.Public.value]" />
|
<ResourceAuthCertEdit
|
||||||
|
v-model:visible="state.dvisible"
|
||||||
|
:auth-cert="state.form"
|
||||||
|
@confirm="btnOk"
|
||||||
|
@cancel="cancelEdit"
|
||||||
|
:disable-type="[AuthCertTypeEnum.Public.value]"
|
||||||
|
:disable-ciphertext-type="props.disableCiphertextType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -51,6 +59,9 @@ import { ElMessage } from 'element-plus';
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
resourceType: { type: Number },
|
resourceType: { type: Number },
|
||||||
resourceCode: { type: String },
|
resourceCode: { type: String },
|
||||||
|
disableCiphertextType: {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
testConnBtnLoading: { type: Boolean },
|
testConnBtnLoading: { type: Boolean },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,7 +120,19 @@ const cancelEdit = () => {
|
|||||||
|
|
||||||
const btnOk = async (authCert: any) => {
|
const btnOk = async (authCert: any) => {
|
||||||
const isEdit = authCert.id;
|
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;
|
authCerts.value[state.idx] = authCert;
|
||||||
cancelEdit();
|
cancelEdit();
|
||||||
return;
|
return;
|
||||||
@@ -119,15 +142,6 @@ const btnOk = async (authCert: any) => {
|
|||||||
ElMessage.error('该名称或用户名已存在于该账号列表中');
|
ElMessage.error('该名称或用户名已存在于该账号列表中');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await resourceAuthCertApi.listByQuery.request({
|
|
||||||
name: authCert.name,
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 100,
|
|
||||||
});
|
|
||||||
if (res.total) {
|
|
||||||
ElMessage.error('该授权凭证名称已存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
authCerts.value.push(authCert);
|
authCerts.value.push(authCert);
|
||||||
cancelEdit();
|
cancelEdit();
|
||||||
|
|||||||
@@ -30,13 +30,14 @@
|
|||||||
remote
|
remote
|
||||||
:remote-method="getInstances"
|
:remote-method="getInstances"
|
||||||
@change="changeInstance"
|
@change="changeInstance"
|
||||||
v-model="form.instanceId"
|
v-model="state.selectInstalce"
|
||||||
|
value-key="id"
|
||||||
placeholder="请输入实例名称搜索并选择实例"
|
placeholder="请输入实例名称搜索并选择实例"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
class="w100"
|
class="w100"
|
||||||
>
|
>
|
||||||
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id">
|
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
|
||||||
@@ -47,6 +48,23 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="authCertName" label="授权凭证" required>
|
||||||
|
<el-select @focus="getAuthCerts" @change="changeAuthCert" v-model="form.authCertName" placeholder="请选择授权凭证" filterable>
|
||||||
|
<el-option v-for="item in state.authCerts" :key="item.id" :label="`${item.name}`" :value="item.name">
|
||||||
|
{{ item.name }}
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
{{ item.username }}
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
<EnumTag :value="item.ciphertextType" :enums="AuthCertCiphertextTypeEnum" />
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
{{ item.remark }}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="code" label="编号" required>
|
<el-form-item prop="code" label="编号" required>
|
||||||
<el-input :disabled="form.id" v-model.trim="form.code" placeholder="请输入编号 (数字字母下划线), 不可修改" auto-complete="off"></el-input>
|
<el-input :disabled="form.id" v-model.trim="form.code" placeholder="请输入编号 (数字字母下划线), 不可修改" auto-complete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -100,6 +118,10 @@ import type { CheckboxValueType } from 'element-plus';
|
|||||||
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
|
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
|
||||||
import { DbType } from '@/views/ops/db/dialect';
|
import { DbType } from '@/views/ops/db/dialect';
|
||||||
import { ResourceCodePattern } from '@/common/pattern';
|
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({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -172,6 +194,8 @@ const state = reactive({
|
|||||||
dbNamesSelected: [] as any,
|
dbNamesSelected: [] as any,
|
||||||
dbNamesFiltered: [] as any,
|
dbNamesFiltered: [] as any,
|
||||||
filterString: '',
|
filterString: '',
|
||||||
|
selectInstalce: {} as any,
|
||||||
|
authCerts: [] as any,
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
tagId: [],
|
tagId: [],
|
||||||
@@ -180,6 +204,7 @@ const state = reactive({
|
|||||||
database: '',
|
database: '',
|
||||||
remark: '',
|
remark: '',
|
||||||
instanceId: null as any,
|
instanceId: null as any,
|
||||||
|
authCertName: '',
|
||||||
flowProcdefKey: '',
|
flowProcdefKey: '',
|
||||||
},
|
},
|
||||||
instances: [] as any,
|
instances: [] as any,
|
||||||
@@ -205,14 +230,27 @@ watch(props, async (newValue: any) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeInstance = () => {
|
const changeInstance = async () => {
|
||||||
state.dbNamesSelected = [];
|
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) {
|
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;
|
state.allDatabases = dbs;
|
||||||
|
|
||||||
// 如果是oracle,且没查出数据库列表,则取实例sid
|
// 如果是oracle,且没查出数据库列表,则取实例sid
|
||||||
@@ -238,8 +276,9 @@ const open = async () => {
|
|||||||
if (state.form.instanceId) {
|
if (state.form.instanceId) {
|
||||||
// 根据id获取,因为需要回显实例名称
|
// 根据id获取,因为需要回显实例名称
|
||||||
await getInstances('', state.form.instanceId);
|
await getInstances('', state.form.instanceId);
|
||||||
|
state.selectInstalce = state.instances[0];
|
||||||
|
await getAllDatabase(state.form.authCertName);
|
||||||
}
|
}
|
||||||
await getAllDatabase();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const btnOk = async () => {
|
const btnOk = async () => {
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<db-edit @val-change="search" :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" v-model:db="dbEditDialog.data"></db-edit>
|
<db-edit @val-change="search()" :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" v-model:db="dbEditDialog.data"></db-edit>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +1,111 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
|
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="title" :back="cancel" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
||||||
<el-tabs v-model="tabActiveName">
|
<el-divider content-position="left">基本</el-divider>
|
||||||
<el-tab-pane label="基础信息" name="basic">
|
<el-form-item prop="code" label="编号" required>
|
||||||
<el-form-item prop="name" label="别名" required>
|
<el-input :disabled="form.id" v-model.trim="form.code" placeholder="请输入编号 (数字字母下划线), 不可修改" auto-complete="off"></el-input>
|
||||||
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item prop="name" label="名称" required>
|
||||||
|
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="type" label="类型" required>
|
<el-form-item prop="type" label="类型" required>
|
||||||
<el-select @change="changeDbType" style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
|
<el-select @change="changeDbType" style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(dbTypeAndDialect, key) in getDbDialectMap()"
|
v-for="(dbTypeAndDialect, key) in getDbDialectMap()"
|
||||||
:key="key"
|
:key="key"
|
||||||
:value="dbTypeAndDialect[0]"
|
:value="dbTypeAndDialect[0]"
|
||||||
:label="dbTypeAndDialect[1].getInfo().name"
|
:label="dbTypeAndDialect[1].getInfo().name"
|
||||||
>
|
>
|
||||||
<SvgIcon :name="dbTypeAndDialect[1].getInfo().icon" :size="20" />
|
<SvgIcon :name="dbTypeAndDialect[1].getInfo().icon" :size="20" />
|
||||||
{{ dbTypeAndDialect[1].getInfo().name }}
|
{{ dbTypeAndDialect[1].getInfo().name }}
|
||||||
</el-option>
|
</el-option>
|
||||||
|
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<SvgIcon :name="getDbDialect(form.type).getInfo().icon" :size="20" />
|
<SvgIcon :name="getDbDialect(form.type).getInfo().icon" :size="20" />
|
||||||
|
</template>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="form.type !== DbType.sqlite" prop="host" label="host" required>
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-input :disabled="form.id !== undefined" v-model.trim="form.host" placeholder="请输入主机ip" auto-complete="off"></el-input>
|
||||||
|
</el-col>
|
||||||
|
<el-col style="text-align: center" :span="1">:</el-col>
|
||||||
|
<el-col :span="5">
|
||||||
|
<el-input type="number" v-model.number="form.port" placeholder="端口"></el-input>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="form.type === DbType.sqlite" prop="host" label="sqlite地址">
|
||||||
|
<el-input v-model.trim="form.host" placeholder="请输入sqlite文件在服务器的绝对地址"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="form.type === DbType.oracle" label="SID|服务名">
|
||||||
|
<el-col :span="5">
|
||||||
|
<el-select
|
||||||
|
@change="
|
||||||
|
() => {
|
||||||
|
state.extra.serviceName = '';
|
||||||
|
state.extra.sid = '';
|
||||||
|
}
|
||||||
|
"
|
||||||
|
v-model="state.extra.stype"
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<el-option label="服务名" :value="1" />
|
||||||
|
<el-option label="SID" :value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col style="text-align: center" :span="1">:</el-col>
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-input v-if="state.extra.stype == 1" v-model="state.extra.serviceName" placeholder="请输入服务名"> </el-input>
|
||||||
|
<el-input v-else v-model="state.extra.sid" placeholder="请输入SID"> </el-input>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="remark" label="备注">
|
||||||
|
<el-input v-model="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<template v-if="form.type !== DbType.sqlite">
|
||||||
|
<el-divider content-position="left">账号</el-divider>
|
||||||
|
<div>
|
||||||
|
<ResourceAuthCertTableEdit
|
||||||
|
v-model="form.authCerts"
|
||||||
|
:resource-code="form.code"
|
||||||
|
:resource-type="TagResourceTypeEnum.Db.value"
|
||||||
|
:test-conn-btn-loading="testConnBtnLoading"
|
||||||
|
@test-conn="testConn"
|
||||||
|
:disable-ciphertext-type="[AuthCertCiphertextTypeEnum.PrivateKey.value]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!--
|
||||||
|
<el-form-item v-if="form.type !== DbType.sqlite" prop="username" label="用户名" required>
|
||||||
|
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form.type !== DbType.sqlite" prop="password" label="密码">
|
||||||
|
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password">
|
||||||
|
<template v-if="form.id && form.id != 0" #suffix>
|
||||||
|
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click" :content="pwd">
|
||||||
|
<template #reference>
|
||||||
|
<el-link v-auth="'db:instance:save'" @click="getDbPwd" :underline="false" type="primary" class="mr5">原密码 </el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-select>
|
</el-popover>
|
||||||
</el-form-item>
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item> -->
|
||||||
|
|
||||||
<el-form-item v-if="form.type !== DbType.sqlite" prop="host" label="host" required>
|
<el-divider content-position="left">其他</el-divider>
|
||||||
<el-col :span="18">
|
<el-form-item prop="params" label="连接参数">
|
||||||
<el-input :disabled="form.id !== undefined" v-model.trim="form.host" placeholder="请输入主机ip" auto-complete="off"></el-input>
|
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
|
||||||
</el-col>
|
<!-- <template #suffix>
|
||||||
<el-col style="text-align: center" :span="1">:</el-col>
|
|
||||||
<el-col :span="5">
|
|
||||||
<el-input type="number" v-model.number="form.port" placeholder="端口"></el-input>
|
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item v-if="form.type === DbType.sqlite" prop="host" label="sqlite地址">
|
|
||||||
<el-input v-model.trim="form.host" placeholder="请输入sqlite文件在服务器的绝对地址"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item v-if="form.type === DbType.oracle" label="SID|服务名">
|
|
||||||
<el-col :span="5">
|
|
||||||
<el-select
|
|
||||||
@change="
|
|
||||||
() => {
|
|
||||||
state.extra.serviceName = '';
|
|
||||||
state.extra.sid = '';
|
|
||||||
}
|
|
||||||
"
|
|
||||||
v-model="state.extra.stype"
|
|
||||||
placeholder="请选择"
|
|
||||||
>
|
|
||||||
<el-option label="服务名" :value="1" />
|
|
||||||
<el-option label="SID" :value="2" />
|
|
||||||
</el-select>
|
|
||||||
</el-col>
|
|
||||||
<el-col style="text-align: center" :span="1">:</el-col>
|
|
||||||
<el-col :span="18">
|
|
||||||
<el-input v-if="state.extra.stype == 1" v-model="state.extra.serviceName" placeholder="请输入服务名"> </el-input>
|
|
||||||
<el-input v-else v-model="state.extra.sid" placeholder="请输入SID"> </el-input>
|
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item v-if="form.type !== DbType.sqlite" prop="username" label="用户名" required>
|
|
||||||
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="form.type !== DbType.sqlite" prop="password" label="密码">
|
|
||||||
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password">
|
|
||||||
<template v-if="form.id && form.id != 0" #suffix>
|
|
||||||
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click" :content="pwd">
|
|
||||||
<template #reference>
|
|
||||||
<el-link v-auth="'db:instance:save'" @click="getDbPwd" :underline="false" type="primary" class="mr5"
|
|
||||||
>原密码
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item prop="remark" label="备注">
|
|
||||||
<el-input v-model="form.remark" auto-complete="off" type="textarea"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="其他配置" name="other">
|
|
||||||
<el-form-item prop="params" label="连接参数">
|
|
||||||
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
|
|
||||||
<!-- <template #suffix>
|
|
||||||
<el-link
|
<el-link
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/go-sql-driver/mysql#parameters"
|
href="https://github.com/go-sql-driver/mysql#parameters"
|
||||||
@@ -98,24 +115,21 @@
|
|||||||
>参数参考</el-link
|
>参数参考</el-link
|
||||||
>
|
>
|
||||||
</template> -->
|
</template> -->
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="sshTunnelMachineId" label="SSH隧道">
|
<el-form-item prop="sshTunnelMachineId" label="SSH隧道">
|
||||||
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
|
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="testConn" :loading="testConnBtnLoading" type="success">测试连接</el-button>
|
|
||||||
<el-button @click="cancel()">取 消</el-button>
|
<el-button @click="cancel()">取 消</el-button>
|
||||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
|
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -123,11 +137,14 @@
|
|||||||
import { reactive, ref, toRefs, watch } from 'vue';
|
import { reactive, ref, toRefs, watch } from 'vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { notBlank } from '@/common/assert';
|
|
||||||
import { RsaEncrypt } from '@/common/rsa';
|
|
||||||
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||||
import { DbType, getDbDialect, getDbDialectMap } from './dialect';
|
import { DbType, getDbDialect, getDbDialectMap } from './dialect';
|
||||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||||
|
import { ResourceCodePattern } from '@/common/pattern';
|
||||||
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import ResourceAuthCertTableEdit from '../component/ResourceAuthCertTableEdit.vue';
|
||||||
|
import { AuthCertCiphertextTypeEnum } from '../tag/enums';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -145,6 +162,18 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
|
code: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入编码',
|
||||||
|
trigger: ['change', 'blur'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: ResourceCodePattern.pattern,
|
||||||
|
message: ResourceCodePattern.message,
|
||||||
|
trigger: ['blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
name: [
|
name: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -166,13 +195,6 @@ const rules = {
|
|||||||
trigger: ['blur'],
|
trigger: ['blur'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
username: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入用户名',
|
|
||||||
trigger: ['change', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sid: [
|
sid: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -186,29 +208,24 @@ const dbForm: any = ref(null);
|
|||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
tabActiveName: 'basic',
|
|
||||||
extra: {} as any, // 连接需要的额外参数(json)
|
extra: {} as any, // 连接需要的额外参数(json)
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
type: '',
|
type: '',
|
||||||
|
code: '',
|
||||||
name: null,
|
name: null,
|
||||||
host: '',
|
host: '',
|
||||||
port: null,
|
port: null,
|
||||||
username: null,
|
authCerts: [],
|
||||||
extra: '', // 连接需要的额外参数(json字符串)
|
extra: '', // 连接需要的额外参数(json字符串)
|
||||||
password: null,
|
|
||||||
params: null,
|
params: null,
|
||||||
remark: '',
|
remark: '',
|
||||||
sshTunnelMachineId: null as any,
|
sshTunnelMachineId: null as any,
|
||||||
},
|
},
|
||||||
submitForm: {},
|
submitForm: {} as any,
|
||||||
// 原密码
|
|
||||||
pwd: '',
|
|
||||||
// 原用户名
|
|
||||||
oldUserName: null,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { dialogVisible, tabActiveName, form, submitForm, pwd } = toRefs(state);
|
const { dialogVisible, form, submitForm } = toRefs(state);
|
||||||
|
|
||||||
const { isFetching: saveBtnLoading, execute: saveInstanceExec } = dbApi.saveInstance.useApi(submitForm);
|
const { isFetching: saveBtnLoading, execute: saveInstanceExec } = dbApi.saveInstance.useApi(submitForm);
|
||||||
const { isFetching: testConnBtnLoading, execute: testConnExec } = dbApi.testConn.useApi(submitForm);
|
const { isFetching: testConnBtnLoading, execute: testConnExec } = dbApi.testConn.useApi(submitForm);
|
||||||
@@ -218,14 +235,12 @@ watch(props, (newValue: any) => {
|
|||||||
if (!state.dialogVisible) {
|
if (!state.dialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.tabActiveName = 'basic';
|
|
||||||
if (newValue.data) {
|
if (newValue.data) {
|
||||||
state.form = { ...newValue.data };
|
state.form = { ...newValue.data };
|
||||||
state.oldUserName = state.form.username;
|
|
||||||
state.extra = JSON.parse(state.form.extra);
|
state.extra = JSON.parse(state.form.extra);
|
||||||
} else {
|
} else {
|
||||||
state.form = { port: null, type: DbType.mysql } as any;
|
state.form = { port: null, type: DbType.mysql } as any;
|
||||||
state.oldUserName = null;
|
state.form.authCerts = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -236,13 +251,8 @@ const changeDbType = (val: string) => {
|
|||||||
state.extra = {};
|
state.extra = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDbPwd = async () => {
|
|
||||||
state.pwd = await dbApi.getInstancePwd.request({ id: state.form.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
const getReqForm = async () => {
|
const getReqForm = async () => {
|
||||||
const reqForm = { ...state.form };
|
const reqForm = { ...state.form };
|
||||||
reqForm.password = await RsaEncrypt(reqForm.password);
|
|
||||||
if (!state.form.sshTunnelMachineId) {
|
if (!state.form.sshTunnelMachineId) {
|
||||||
reqForm.sshTunnelMachineId = -1;
|
reqForm.sshTunnelMachineId = -1;
|
||||||
}
|
}
|
||||||
@@ -252,7 +262,7 @@ const getReqForm = async () => {
|
|||||||
return reqForm;
|
return reqForm;
|
||||||
};
|
};
|
||||||
|
|
||||||
const testConn = async () => {
|
const testConn = async (authCert: any) => {
|
||||||
dbForm.value.validate(async (valid: boolean) => {
|
dbForm.value.validate(async (valid: boolean) => {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
ElMessage.error('请正确填写信息');
|
ElMessage.error('请正确填写信息');
|
||||||
@@ -260,20 +270,13 @@ const testConn = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.submitForm = await getReqForm();
|
state.submitForm = await getReqForm();
|
||||||
|
state.submitForm.authCerts = [authCert];
|
||||||
await testConnExec();
|
await testConnExec();
|
||||||
ElMessage.success('连接成功');
|
ElMessage.success('连接成功');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const btnOk = async () => {
|
const btnOk = async () => {
|
||||||
if (state.form.type !== DbType.sqlite) {
|
|
||||||
if (!state.form.id) {
|
|
||||||
notBlank(state.form.password, '新增操作,密码不可为空');
|
|
||||||
} else if (state.form.username != state.oldUserName) {
|
|
||||||
notBlank(state.form.password, '已修改用户名,请输入密码');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dbForm.value.validate(async (valid: boolean) => {
|
dbForm.value.validate(async (valid: boolean) => {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
ElMessage.error('请正确填写信息');
|
ElMessage.error('请正确填写信息');
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<page-table
|
<page-table
|
||||||
ref="pageTableRef"
|
ref="pageTableRef"
|
||||||
:page-api="dbApi.instances"
|
:page-api="dbApi.instances"
|
||||||
|
:data-handler-fn="handleData"
|
||||||
:searchItems="searchItems"
|
:searchItems="searchItems"
|
||||||
v-model:query-form="query"
|
v-model:query-form="query"
|
||||||
:show-selection="true"
|
:show-selection="true"
|
||||||
@@ -16,6 +17,10 @@
|
|||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #authCert="{ data }">
|
||||||
|
<ResourceAuthCert v-model:select-auth-cert="data.selectAuthCert" :auth-certs="data.authCerts" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #type="{ data }">
|
<template #type="{ data }">
|
||||||
<el-tooltip :content="getDbDialect(data.type).getInfo().name" placement="top">
|
<el-tooltip :content="getDbDialect(data.type).getInfo().name" placement="top">
|
||||||
<SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
|
<SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
|
||||||
@@ -35,7 +40,6 @@
|
|||||||
<el-descriptions-item :span="2" label="主机">{{ infoDialog.data.host }}</el-descriptions-item>
|
<el-descriptions-item :span="2" label="主机">{{ infoDialog.data.host }}</el-descriptions-item>
|
||||||
<el-descriptions-item :span="1" label="端口">{{ infoDialog.data.port }}</el-descriptions-item>
|
<el-descriptions-item :span="1" label="端口">{{ infoDialog.data.port }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="用户名">{{ infoDialog.data.username }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item :span="1" label="类型">{{ infoDialog.data.type }}</el-descriptions-item>
|
<el-descriptions-item :span="1" label="类型">{{ infoDialog.data.type }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="3" label="连接参数">{{ infoDialog.data.params }}</el-descriptions-item>
|
<el-descriptions-item :span="3" label="连接参数">{{ infoDialog.data.params }}</el-descriptions-item>
|
||||||
@@ -71,6 +75,7 @@ import { hasPerms } from '@/components/auth/auth';
|
|||||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||||
import { getDbDialect } from './dialect';
|
import { getDbDialect } from './dialect';
|
||||||
import { SearchItem } from '@/components/SearchForm';
|
import { SearchItem } from '@/components/SearchForm';
|
||||||
|
import ResourceAuthCert from '../component/ResourceAuthCert.vue';
|
||||||
|
|
||||||
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
|
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
|
||||||
|
|
||||||
@@ -79,13 +84,14 @@ const perms = {
|
|||||||
delInstance: 'db:instance:del',
|
delInstance: 'db:instance:del',
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchItems = [SearchItem.input('name', '名称')];
|
const searchItems = [SearchItem.input('code', '编号'), SearchItem.input('name', '名称')];
|
||||||
|
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
|
TableColumn.new('code', '编号'),
|
||||||
TableColumn.new('name', '名称'),
|
TableColumn.new('name', '名称'),
|
||||||
TableColumn.new('type', '类型').isSlot().setAddWidth(-15).alignCenter(),
|
TableColumn.new('type', '类型').isSlot().setAddWidth(-15).alignCenter(),
|
||||||
TableColumn.new('host', 'host:port').setFormatFunc((data: any) => `${data.host}:${data.port}`),
|
TableColumn.new('host', 'host:port').setFormatFunc((data: any) => `${data.host}:${data.port}`),
|
||||||
TableColumn.new('username', '用户名'),
|
TableColumn.new('authCerts[0].username', '授权凭证').isSlot('authCert').setAddWidth(10),
|
||||||
TableColumn.new('params', '连接参数'),
|
TableColumn.new('params', '连接参数'),
|
||||||
TableColumn.new('remark', '备注'),
|
TableColumn.new('remark', '备注'),
|
||||||
]);
|
]);
|
||||||
@@ -134,6 +140,15 @@ const search = () => {
|
|||||||
pageTableRef.value.search();
|
pageTableRef.value.search();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleData = (res: any) => {
|
||||||
|
const dataList = res.list;
|
||||||
|
// 赋值授权凭证
|
||||||
|
for (let x of dataList) {
|
||||||
|
x.selectAuthCert = x.authCerts[0];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
const showInfo = (info: any) => {
|
const showInfo = (info: any) => {
|
||||||
state.infoDialog.data = info;
|
state.infoDialog.data = info;
|
||||||
state.infoDialog.visible = true;
|
state.infoDialog.visible = true;
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export const dbApi = {
|
|||||||
getInstanceServerInfo: Api.newGet('/instances/{instanceId}/server-info'),
|
getInstanceServerInfo: Api.newGet('/instances/{instanceId}/server-info'),
|
||||||
testConn: Api.newPost('/instances/test-conn'),
|
testConn: Api.newPost('/instances/test-conn'),
|
||||||
saveInstance: Api.newPost('/instances'),
|
saveInstance: Api.newPost('/instances'),
|
||||||
getInstancePwd: Api.newGet('/instances/{id}/pwd'),
|
|
||||||
deleteInstance: Api.newDelete('/instances/{id}'),
|
deleteInstance: Api.newDelete('/instances/{id}'),
|
||||||
|
|
||||||
// 获取数据库备份列表
|
// 获取数据库备份列表
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ const columns = [
|
|||||||
TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50),
|
TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50),
|
||||||
TableColumn.new('stat', '运行状态').isSlot().setAddWidth(55),
|
TableColumn.new('stat', '运行状态').isSlot().setAddWidth(55),
|
||||||
TableColumn.new('fs', '磁盘(挂载点=>可用/总)').isSlot().setAddWidth(25),
|
TableColumn.new('fs', '磁盘(挂载点=>可用/总)').isSlot().setAddWidth(25),
|
||||||
TableColumn.new('authCerts[0].username', '授权凭证').isSlot('authCert').setAddWidth(20),
|
TableColumn.new('authCerts[0].username', '授权凭证').isSlot('authCert').setAddWidth(10),
|
||||||
TableColumn.new('status', '状态').isSlot().setMinWidth(85),
|
TableColumn.new('status', '状态').isSlot().setMinWidth(85),
|
||||||
TableColumn.new('remark', '备注'),
|
TableColumn.new('remark', '备注'),
|
||||||
TableColumn.new('action', '操作').isSlot().setMinWidth(238).fixedRight().alignCenter(),
|
TableColumn.new('action', '操作').isSlot().setMinWidth(238).fixedRight().alignCenter(),
|
||||||
|
|||||||
@@ -19,9 +19,11 @@
|
|||||||
</page-table>
|
</page-table>
|
||||||
|
|
||||||
<ResourceAuthCertEdit
|
<ResourceAuthCertEdit
|
||||||
|
:title="editor.title"
|
||||||
v-model:visible="editor.visible"
|
v-model:visible="editor.visible"
|
||||||
:auth-cert="editor.authcert"
|
:auth-cert="editor.authcert"
|
||||||
@confirm="confirmSave"
|
@confirm="confirmSave"
|
||||||
|
@cancel="editor.authcert = {}"
|
||||||
:disable-type="state.disableAuthCertType"
|
:disable-type="state.disableAuthCertType"
|
||||||
:disable-ciphertext-type="state.disableAuthCertCiphertextType"
|
:disable-ciphertext-type="state.disableAuthCertCiphertextType"
|
||||||
:resource-edit="false"
|
:resource-edit="false"
|
||||||
@@ -73,7 +75,7 @@ const state = reactive({
|
|||||||
paramsFormItem: [] as any,
|
paramsFormItem: [] as any,
|
||||||
},
|
},
|
||||||
editor: {
|
editor: {
|
||||||
title: '授权凭证保存',
|
title: '添加授权凭证',
|
||||||
visible: false,
|
visible: false,
|
||||||
authcert: {},
|
authcert: {},
|
||||||
},
|
},
|
||||||
@@ -93,6 +95,7 @@ const edit = (data: any) => {
|
|||||||
state.disableAuthCertType = [];
|
state.disableAuthCertType = [];
|
||||||
state.disableAuthCertCiphertextType = [];
|
state.disableAuthCertCiphertextType = [];
|
||||||
if (data) {
|
if (data) {
|
||||||
|
state.editor.title = `编辑授权凭证-[${data.name}]`;
|
||||||
state.editor.authcert = data;
|
state.editor.authcert = data;
|
||||||
// 如果数据为公共授权凭证,则不允许修改凭证类型
|
// 如果数据为公共授权凭证,则不允许修改凭证类型
|
||||||
if (data.type == AuthCertTypeEnum.Public.value) {
|
if (data.type == AuthCertTypeEnum.Public.value) {
|
||||||
@@ -103,6 +106,7 @@ const edit = (data: any) => {
|
|||||||
state.disableAuthCertType = [AuthCertTypeEnum.Public.value];
|
state.disableAuthCertType = [AuthCertTypeEnum.Public.value];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
state.editor.title = '添加授权凭证';
|
||||||
state.editor.authcert = {
|
state.editor.authcert = {
|
||||||
type: AuthCertTypeEnum.Public.value,
|
type: AuthCertTypeEnum.Public.value,
|
||||||
ciphertextType: AuthCertCiphertextTypeEnum.Password.value,
|
ciphertextType: AuthCertCiphertextTypeEnum.Password.value,
|
||||||
|
|||||||
@@ -65,9 +65,16 @@
|
|||||||
<el-descriptions-item label="类型">
|
<el-descriptions-item label="类型">
|
||||||
<EnumTag :enums="TagResourceTypeEnum" :value="currentTag.type" />
|
<EnumTag :enums="TagResourceTypeEnum" :value="currentTag.type" />
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
|
<el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="code路径">{{ currentTag.codePath }}</el-descriptions-item>
|
|
||||||
|
<el-descriptions-item label="路径" :span="2">
|
||||||
|
<span v-for="item in parseTagPath(currentTag.codePath)" :key="item.code">
|
||||||
|
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon" class="mr2" />
|
||||||
|
<span> {{ item.code }}</span>
|
||||||
|
<SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
|
<el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="备注">{{ currentTag.remark }}</el-descriptions-item>
|
<el-descriptions-item label="备注">{{ currentTag.remark }}</el-descriptions-item>
|
||||||
|
|
||||||
@@ -255,6 +262,38 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const parseTagPath = (tagPath: string) => {
|
||||||
|
if (!tagPath) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const res = [] as any;
|
||||||
|
const codes = tagPath.split('/');
|
||||||
|
for (let code of codes) {
|
||||||
|
const typeAndCode = code.split('|');
|
||||||
|
|
||||||
|
if (typeAndCode.length == 1) {
|
||||||
|
const tagCode = typeAndCode[0];
|
||||||
|
if (!tagCode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
type: TagResourceTypeEnum.Tag.value,
|
||||||
|
code: typeAndCode[0],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
type: typeAndCode[0],
|
||||||
|
code: typeAndCode[1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res[res.length - 1].isEnd = true;
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
const tabChange = () => {
|
const tabChange = () => {
|
||||||
setNowTabData();
|
setNowTabData();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,9 +28,15 @@
|
|||||||
</template>
|
</template>
|
||||||
</page-table>
|
</page-table>
|
||||||
|
|
||||||
<el-drawer title="团队编辑" v-model="addTeamDialog.visible" :before-close="cancelSaveTeam" :destroy-on-close="true" :close-on-click-modal="false">
|
<el-drawer
|
||||||
|
:title="addTeamDialog.form.id ? '编辑团队' : '添加团队'"
|
||||||
|
v-model="addTeamDialog.visible"
|
||||||
|
:before-close="cancelSaveTeam"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<DrawerHeader header="团队编辑" :back="cancelSaveTeam" />
|
<DrawerHeader :header="addTeamDialog.form.id ? '编辑团队' : '添加团队'" :back="cancelSaveTeam" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-form ref="teamForm" :model="addTeamDialog.form" label-width="auto">
|
<el-form ref="teamForm" :model="addTeamDialog.form" label-width="auto">
|
||||||
@@ -58,6 +64,7 @@
|
|||||||
value: 'id',
|
value: 'id',
|
||||||
label: 'codePath',
|
label: 'codePath',
|
||||||
children: 'children',
|
children: 'children',
|
||||||
|
disabled: 'disabled',
|
||||||
}"
|
}"
|
||||||
@check="tagTreeNodeCheck"
|
@check="tagTreeNodeCheck"
|
||||||
>
|
>
|
||||||
@@ -152,7 +159,6 @@ const state = reactive({
|
|||||||
currentEditPermissions: false,
|
currentEditPermissions: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
addTeamDialog: {
|
addTeamDialog: {
|
||||||
title: '新增团队',
|
|
||||||
visible: false,
|
visible: false,
|
||||||
form: { id: 0, name: '', remark: '', tags: [] },
|
form: { id: 0, name: '', remark: '', tags: [] },
|
||||||
},
|
},
|
||||||
@@ -201,16 +207,22 @@ const search = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showSaveTeamDialog = async (data: any) => {
|
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) {
|
if (data) {
|
||||||
state.addTeamDialog.form.id = data.id;
|
state.addTeamDialog.form.id = data.id;
|
||||||
state.addTeamDialog.form.name = data.name;
|
state.addTeamDialog.form.name = data.name;
|
||||||
state.addTeamDialog.form.remark = data.remark;
|
state.addTeamDialog.form.remark = data.remark;
|
||||||
state.addTeamDialog.title = `修改 [${data.codePath}] 信息`;
|
|
||||||
state.addTeamDialog.form.tags = await tagApi.getTeamTagIds.request({ teamId: data.id });
|
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;
|
state.addTeamDialog.visible = true;
|
||||||
@@ -231,8 +243,10 @@ const saveTeam = async () => {
|
|||||||
|
|
||||||
const cancelSaveTeam = () => {
|
const cancelSaveTeam = () => {
|
||||||
state.addTeamDialog.visible = false;
|
state.addTeamDialog.visible = false;
|
||||||
state.addTeamDialog.form = {} as any;
|
|
||||||
teamForm.value.resetFields();
|
teamForm.value.resetFields();
|
||||||
|
setTimeout(() => {
|
||||||
|
state.addTeamDialog.form = {} as any;
|
||||||
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteTeam = () => {
|
const deleteTeam = () => {
|
||||||
@@ -289,34 +303,47 @@ const cancelAddMember = () => {
|
|||||||
state.showMemDialog.addVisible = false;
|
state.showMemDialog.addVisible = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagTreeNodeCheck = () => {
|
const tagTreeNodeCheck = (data: any) => {
|
||||||
// const node = tagTreeRef.value.getNode(data.id);
|
const node = tagTreeRef.value.getNode(data.id);
|
||||||
// console.log(node);
|
console.log('check node: ', node);
|
||||||
// // state.showTagDialog.tagTreeTeams = [16]
|
|
||||||
// if (node.checked) {
|
if (node.checked) {
|
||||||
// if (node.parent) {
|
// 如果选中了子节点,则需要将父节点全部取消选中,并禁用父节点
|
||||||
// console.log(node.parent);
|
unCheckParentNodes(node.parent);
|
||||||
// // removeCheckedTagId(node.parent.key);
|
disableParentNodes(node.parent);
|
||||||
// tagTreeRef.value.setChecked(node.parent, false, false);
|
} else {
|
||||||
// }
|
// 如果取消了选中,则需要根据条件恢复父节点的选中状态
|
||||||
// // // parentNode = node.parent
|
disableParentNodes(node.parent, false);
|
||||||
// // for (let parentNode of node.parent) {
|
}
|
||||||
// // parentNode.setChecked(false);
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// console.log(data);
|
|
||||||
// console.log(checkInfo);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// function removeCheckedTagId(id: any) {
|
const unCheckParentNodes = (node: any) => {
|
||||||
// console.log(state.showTagDialog.tagTreeTeams);
|
if (!node) {
|
||||||
// for (let i = 0; i < state.showTagDialog.tagTreeTeams.length; i++) {
|
return;
|
||||||
// if (state.showTagDialog.tagTreeTeams[i] == id) {
|
}
|
||||||
// console.log('has id', id);
|
tagTreeRef.value.setChecked(node, false, false);
|
||||||
// state.showTagDialog.tagTreeTeams.splice(i, 1);
|
unCheckParentNodes(node.parent);
|
||||||
// }
|
};
|
||||||
// }
|
|
||||||
// console.log(state.showTagDialog.tagTreeTeams);
|
/**
|
||||||
// }
|
* 禁用该节点以及所有父节点
|
||||||
|
* @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);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { EnumValue } from '@/common/Enum';
|
|||||||
|
|
||||||
// 授权凭证类型
|
// 授权凭证类型
|
||||||
export const AuthCertTypeEnum = {
|
export const AuthCertTypeEnum = {
|
||||||
Public: EnumValue.of(2, '公共凭证').tagTypeSuccess(),
|
Public: EnumValue.of(2, '公共凭证').tagTypeSuccess().tagTypeSuccess(),
|
||||||
Private: EnumValue.of(1, '普通账号').tagTypeSuccess(),
|
Private: EnumValue.of(1, '普通账号'),
|
||||||
Privileged: EnumValue.of(11, '特权账号').tagTypeSuccess(),
|
Privileged: EnumValue.of(11, '特权账号').tagTypeDanger(),
|
||||||
PrivateDefault: EnumValue.of(12, '默认账号').tagTypeSuccess(),
|
PrivateDefault: EnumValue.of(12, '默认账号').tagTypeWarning(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 授权凭证密文类型
|
// 授权凭证密文类型
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ const (
|
|||||||
// RedisConnExpireTime = 2 * time.Minute
|
// RedisConnExpireTime = 2 * time.Minute
|
||||||
// MongoConnExpireTime = 2 * time.Minute
|
// MongoConnExpireTime = 2 * time.Minute
|
||||||
|
|
||||||
TagResourceTypeMachine int8 = 1
|
ResourceTypeMachine int8 = 1
|
||||||
TagResourceTypeDb int8 = 2
|
ResourceTypeDb int8 = 2
|
||||||
TagResourceTypeRedis int8 = 3
|
ResourceTypeRedis int8 = 3
|
||||||
TagResourceTypeMongo int8 = 4
|
ResourceTypeMongo int8 = 4
|
||||||
|
|
||||||
// 删除机器的事件主题名
|
// 删除机器的事件主题名
|
||||||
DeleteMachineEventTopic = "machine:delete"
|
DeleteMachineEventTopic = "machine:delete"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Dashbord struct {
|
|||||||
|
|
||||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||||
accountId := rc.GetLoginAccount().Id
|
accountId := rc.GetLoginAccount().Id
|
||||||
dbNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, ""))
|
dbNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeDb, ""))
|
||||||
|
|
||||||
rc.ResData = collx.M{
|
rc.ResData = collx.M{
|
||||||
"dbNum": dbNum,
|
"dbNum": dbNum,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
|||||||
queryCond, page := req.BindQueryAndPage[*entity.DbQuery](rc, new(entity.DbQuery))
|
queryCond, page := req.BindQueryAndPage[*entity.DbQuery](rc, new(entity.DbQuery))
|
||||||
|
|
||||||
// 不存在可访问标签id,即没有可操作数据
|
// 不存在可访问标签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 {
|
if len(codes) == 0 {
|
||||||
rc.ResData = model.EmptyPageResult[any]()
|
rc.ResData = model.EmptyPageResult[any]()
|
||||||
return
|
return
|
||||||
@@ -56,7 +56,7 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
|||||||
biz.ErrIsNil(err)
|
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
|
return dbvo
|
||||||
})...)
|
})...)
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
|||||||
la := rc.GetLoginAccount()
|
la := rc.GetLoginAccount()
|
||||||
db, err := d.DbApp.GetById(new(entity.Db), dbId)
|
db, err := d.DbApp.GetById(new(entity.Db), dbId)
|
||||||
biz.ErrIsNil(err, "该数据库不存在")
|
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()
|
now := time.Now()
|
||||||
filename := fmt.Sprintf("%s-%s.%s.sql%s", db.Name, dbName, now.Format("20060102150405"), extName)
|
filename := fmt.Sprintf("%s-%s.%s.sql%s", db.Name, dbName, now.Format("20060102150405"), extName)
|
||||||
|
|||||||
@@ -1,28 +1,41 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mayfly-go/internal/common/consts"
|
||||||
"mayfly-go/internal/db/api/form"
|
"mayfly-go/internal/db/api/form"
|
||||||
"mayfly-go/internal/db/api/vo"
|
"mayfly-go/internal/db/api/vo"
|
||||||
"mayfly-go/internal/db/application"
|
"mayfly-go/internal/db/application"
|
||||||
"mayfly-go/internal/db/domain/entity"
|
"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/biz"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"mayfly-go/pkg/utils/cryptox"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
InstanceApp application.Instance `inject:"DbInstanceApp"`
|
InstanceApp application.Instance `inject:"DbInstanceApp"`
|
||||||
DbApp application.Db `inject:""`
|
DbApp application.Db `inject:""`
|
||||||
|
ResourceAuthCertApp tagapp.ResourceAuthCert `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instances 获取数据库实例信息
|
// Instances 获取数据库实例信息
|
||||||
// @router /api/instances [get]
|
// @router /api/instances [get]
|
||||||
func (d *Instance) Instances(rc *req.Ctx) {
|
func (d *Instance) Instances(rc *req.Ctx) {
|
||||||
queryCond, page := req.BindQueryAndPage[*entity.InstanceQuery](rc, new(entity.InstanceQuery))
|
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)
|
biz.ErrIsNil(err)
|
||||||
|
|
||||||
|
// 填充授权凭证信息
|
||||||
|
d.ResourceAuthCertApp.FillAuthCert(consts.ResourceTypeDb, collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert {
|
||||||
|
return vos
|
||||||
|
})...)
|
||||||
|
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,12 +43,7 @@ func (d *Instance) TestConn(rc *req.Ctx) {
|
|||||||
form := &form.InstanceForm{}
|
form := &form.InstanceForm{}
|
||||||
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
|
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
|
||||||
|
|
||||||
// 密码解密,并使用解密后的赋值
|
biz.ErrIsNil(d.InstanceApp.TestConn(instance, form.AuthCerts[0]))
|
||||||
originPwd, err := cryptox.DefaultRsaDecrypt(form.Password, true)
|
|
||||||
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
|
|
||||||
instance.Password = originPwd
|
|
||||||
|
|
||||||
biz.ErrIsNil(d.InstanceApp.TestConn(instance))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveInstance 保存数据库实例信息
|
// SaveInstance 保存数据库实例信息
|
||||||
@@ -44,15 +52,11 @@ func (d *Instance) SaveInstance(rc *req.Ctx) {
|
|||||||
form := &form.InstanceForm{}
|
form := &form.InstanceForm{}
|
||||||
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
|
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
|
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 获取数据库实例密码,由于数据库是加密存储,故提供该接口展示原文密码
|
// GetInstance 获取数据库实例密码,由于数据库是加密存储,故提供该接口展示原文密码
|
||||||
@@ -61,20 +65,9 @@ func (d *Instance) GetInstance(rc *req.Ctx) {
|
|||||||
dbId := getInstanceId(rc)
|
dbId := getInstanceId(rc)
|
||||||
dbEntity, err := d.InstanceApp.GetById(new(entity.DbInstance), dbId)
|
dbEntity, err := d.InstanceApp.GetById(new(entity.DbInstance), dbId)
|
||||||
biz.ErrIsNil(err, "获取数据库实例错误")
|
biz.ErrIsNil(err, "获取数据库实例错误")
|
||||||
dbEntity.Password = ""
|
|
||||||
rc.ResData = dbEntity
|
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 删除数据库实例信息
|
// DeleteInstance 删除数据库实例信息
|
||||||
// @router /api/instances/:instance [DELETE]
|
// @router /api/instances/:instance [DELETE]
|
||||||
func (d *Instance) DeleteInstance(rc *req.Ctx) {
|
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) {
|
func (d *Instance) GetDatabaseNames(rc *req.Ctx) {
|
||||||
instanceId := getInstanceId(rc)
|
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(err, "获取数据库实例错误")
|
||||||
biz.ErrIsNil(instance.PwdDecrypt())
|
|
||||||
res, err := d.InstanceApp.GetDatabases(instance)
|
res, err := d.InstanceApp.GetDatabases(instance, authCertName)
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package form
|
package form
|
||||||
|
|
||||||
|
import tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
|
|
||||||
type InstanceForm struct {
|
type InstanceForm struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
|
Code string `binding:"pattern=resource_code" json:"code"`
|
||||||
Name string `binding:"required" json:"name"`
|
Name string `binding:"required" json:"name"`
|
||||||
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
||||||
Host string `binding:"required" json:"host"`
|
Host string `binding:"required" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
Extra string `json:"extra"`
|
Extra string `json:"extra"`
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
Params string `json:"params"`
|
Params string `json:"params"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||||
|
|
||||||
|
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ type DbListVO struct {
|
|||||||
Remark *string `json:"remark"`
|
Remark *string `json:"remark"`
|
||||||
|
|
||||||
InstanceId *int64 `json:"instanceId"`
|
InstanceId *int64 `json:"instanceId"`
|
||||||
|
AuthCertName string `json:"authCertName"`
|
||||||
|
Username string `json:"username"`
|
||||||
InstanceName *string `json:"instanceName"`
|
InstanceName *string `json:"instanceName"`
|
||||||
InstanceType *string `json:"type"`
|
InstanceType *string `json:"type"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
Username string `json:"username"`
|
|
||||||
|
|
||||||
FlowProcdefKey string `json:"flowProcdefKey"`
|
FlowProcdefKey string `json:"flowProcdefKey"`
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
package vo
|
package vo
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type InstanceListVO struct {
|
type InstanceListVO struct {
|
||||||
|
tagentity.AuthCerts // 授权凭证信息
|
||||||
|
|
||||||
Id *int64 `json:"id"`
|
Id *int64 `json:"id"`
|
||||||
|
Code string `json:"code"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Host *string `json:"host"`
|
Host *string `json:"host"`
|
||||||
Port *int `json:"port"`
|
Port *int `json:"port"`
|
||||||
Type *string `json:"type"`
|
Type *string `json:"type"`
|
||||||
Params string `json:"params"`
|
Params string `json:"params"`
|
||||||
Extra string `json:"extra"`
|
Extra string `json:"extra"`
|
||||||
Username *string `json:"username"`
|
|
||||||
Remark *string `json:"remark"`
|
Remark *string `json:"remark"`
|
||||||
CreateTime *time.Time `json:"createTime"`
|
CreateTime *time.Time `json:"createTime"`
|
||||||
Creator *string `json:"creator"`
|
Creator *string `json:"creator"`
|
||||||
@@ -22,3 +27,7 @@ type InstanceListVO struct {
|
|||||||
|
|
||||||
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *InstanceListVO) GetCode() string {
|
||||||
|
return i.Code
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/structx"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return d.Insert(ctx, dbEntity)
|
return d.Insert(ctx, dbEntity)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceCode: dbEntity.Code,
|
Code: dbEntity.Code,
|
||||||
ResourceType: tagentity.TagTypeDb,
|
Type: tagentity.TagTypeDb,
|
||||||
TagIds: tagIds,
|
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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return d.UpdateById(ctx, dbEntity)
|
return d.UpdateById(ctx, dbEntity)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceCode: old.Code,
|
Code: old.Code,
|
||||||
ResourceType: tagentity.TagTypeDb,
|
Type: tagentity.TagTypeDb,
|
||||||
TagIds: tagIds,
|
ParentTagIds: tagIds,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -153,9 +152,9 @@ func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
// 删除该库下用户保存的所有sql信息
|
// 删除该库下用户保存的所有sql信息
|
||||||
return d.dbSqlRepo.DeleteByCond(ctx, &entity.DbSql{DbId: id})
|
return d.dbSqlRepo.DeleteByCond(ctx, &entity.DbSql{DbId: id})
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceCode: db.Code,
|
Code: db.Code,
|
||||||
ResourceType: tagentity.TagTypeDb,
|
Type: tagentity.TagTypeDb,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -172,11 +171,13 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
|||||||
return nil, errorx.NewBiz("数据库实例不存在")
|
return nil, errorx.NewBiz("数据库实例不存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 密码解密
|
di, err := d.dbInstanceApp.ToDbInfo(instance, db.AuthCertName, dbName)
|
||||||
if err := instance.PwdDecrypt(); err != nil {
|
if err != nil {
|
||||||
return nil, errorx.NewBiz(err.Error())
|
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 {
|
if db.FlowProcdefKey != nil {
|
||||||
di.FlowProcdefKey = *db.FlowProcdefKey
|
di.FlowProcdefKey = *db.FlowProcdefKey
|
||||||
}
|
}
|
||||||
@@ -323,14 +324,3 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *DumpDbReq) error {
|
|||||||
|
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,18 +3,27 @@ package application
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"mayfly-go/internal/common/consts"
|
||||||
"mayfly-go/internal/db/dbm"
|
"mayfly-go/internal/db/dbm"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/internal/db/domain/entity"
|
"mayfly-go/internal/db/domain/entity"
|
||||||
"mayfly-go/internal/db/domain/repository"
|
"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/base"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
|
"mayfly-go/pkg/utils/structx"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SaveDbInstanceParam struct {
|
||||||
|
DbInstance *entity.DbInstance
|
||||||
|
AuthCerts []*tagentity.ResourceAuthCert
|
||||||
|
}
|
||||||
|
|
||||||
type Instance interface {
|
type Instance interface {
|
||||||
base.App[*entity.DbInstance]
|
base.App[*entity.DbInstance]
|
||||||
|
|
||||||
@@ -23,23 +32,27 @@ type Instance interface {
|
|||||||
|
|
||||||
Count(condition *entity.InstanceQuery) int64
|
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 删除数据库信息
|
||||||
Delete(ctx context.Context, id uint64) error
|
Delete(ctx context.Context, id uint64) error
|
||||||
|
|
||||||
// GetDatabases 获取数据库实例的所有数据库列表
|
// 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 {
|
type instanceAppImpl struct {
|
||||||
base.AppImpl[*entity.DbInstance, repository.Instance]
|
base.AppImpl[*entity.DbInstance, repository.Instance]
|
||||||
|
|
||||||
dbApp Db `inject:"DbApp"`
|
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||||
backupApp *DbBackupApp `inject:"DbBackupApp"`
|
dbApp Db `inject:"DbApp"`
|
||||||
restoreApp *DbRestoreApp `inject:"DbRestoreApp"`
|
backupApp *DbBackupApp `inject:"DbBackupApp"`
|
||||||
|
restoreApp *DbRestoreApp `inject:"DbRestoreApp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注入DbInstanceRepo
|
// 注入DbInstanceRepo
|
||||||
@@ -56,9 +69,23 @@ func (app *instanceAppImpl) Count(condition *entity.InstanceQuery) int64 {
|
|||||||
return app.CountByCond(condition)
|
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()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -66,47 +93,65 @@ func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance) error {
|
|||||||
return nil
|
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连接
|
// 默认tcp连接
|
||||||
instanceEntity.Network = instanceEntity.GetNetwork()
|
instanceEntity.Network = instanceEntity.GetNetwork()
|
||||||
|
resourceType := consts.ResourceTypeDb
|
||||||
|
authCerts := instance.AuthCerts
|
||||||
|
|
||||||
|
if len(authCerts) == 0 {
|
||||||
|
return errorx.NewBiz("授权凭证信息不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
// 查找是否存在该库
|
// 查找是否存在该库
|
||||||
oldInstance := &entity.DbInstance{
|
oldInstance := &entity.DbInstance{
|
||||||
Host: instanceEntity.Host,
|
Host: instanceEntity.Host,
|
||||||
Port: instanceEntity.Port,
|
Port: instanceEntity.Port,
|
||||||
Username: instanceEntity.Username,
|
|
||||||
SshTunnelMachineId: instanceEntity.SshTunnelMachineId,
|
SshTunnelMachineId: instanceEntity.SshTunnelMachineId,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := app.GetBy(oldInstance)
|
err := app.GetBy(oldInstance)
|
||||||
if instanceEntity.Id == 0 {
|
if instanceEntity.Id == 0 {
|
||||||
|
|
||||||
if instanceEntity.Type != string(dbi.DbTypeSqlite) && instanceEntity.Password == "" {
|
|
||||||
return errorx.NewBiz("密码不能为空")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return errorx.NewBiz("该数据库实例已存在")
|
return errorx.NewBiz("该数据库实例已存在")
|
||||||
}
|
}
|
||||||
if err := instanceEntity.PwdEncrypt(); err != nil {
|
if app.CountByCond(&entity.DbInstance{Code: instanceEntity.Code}) > 0 {
|
||||||
return errorx.NewBiz(err.Error())
|
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 {
|
if err == nil && oldInstance.Id != instanceEntity.Id {
|
||||||
return errorx.NewBiz("该数据库实例已存在")
|
return errorx.NewBiz("该数据库实例已存在")
|
||||||
}
|
}
|
||||||
if err := instanceEntity.PwdEncrypt(); err != nil {
|
return app.Tx(ctx, func(ctx context.Context) error {
|
||||||
return errorx.NewBiz(err.Error())
|
return app.UpdateById(ctx, instanceEntity)
|
||||||
}
|
}, func(ctx context.Context) error {
|
||||||
return app.UpdateById(ctx, instanceEntity)
|
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 {
|
func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error {
|
||||||
instance, err := app.GetById(new(entity.DbInstance), instanceId, "name")
|
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{
|
restore := &entity.DbRestore{
|
||||||
DbInstanceId: instanceId,
|
DbInstanceId: instanceId,
|
||||||
@@ -147,13 +192,25 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error
|
|||||||
biz.ErrIsNil(err, "删除数据库实例失败: %v", err)
|
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()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -161,3 +218,23 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error
|
|||||||
|
|
||||||
return dbConn.GetMetaData().GetDbNames()
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ type Db struct {
|
|||||||
Database string `orm:"column(database)" json:"database"`
|
Database string `orm:"column(database)" json:"database"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
InstanceId uint64
|
InstanceId uint64
|
||||||
|
AuthCertName string `json:"authCertName"`
|
||||||
FlowProcdefKey *string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
FlowProcdefKey *string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/common/utils"
|
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbInstance struct {
|
type DbInstance struct {
|
||||||
model.Model
|
model.Model
|
||||||
|
|
||||||
|
Code string `json:"code"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"` // 类型,mysql oracle等
|
Type string `json:"type"` // 类型,mysql oracle等
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
Extra *string `json:"extra"` // 连接需要的其他额外参数(json格式), 如oracle需要sid等
|
Extra *string `json:"extra"` // 连接需要的其他额外参数(json格式), 如oracle需要sid等
|
||||||
Username string `json:"username"`
|
Params *string `json:"params"` // 使用指针类型,可更新为零值(空字符串)
|
||||||
Password string `json:"-"`
|
|
||||||
Params *string `json:"params"`
|
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
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)
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package entity
|
|||||||
type InstanceQuery struct {
|
type InstanceQuery struct {
|
||||||
Id uint64 `json:"id" form:"id"`
|
Id uint64 `json:"id" form:"id"`
|
||||||
Name string `json:"name" form:"name"`
|
Name string `json:"name" form:"name"`
|
||||||
|
Code string `json:"code" form:"code"`
|
||||||
Host string `json:"host" form:"host"`
|
Host string `json:"host" form:"host"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||||
qd := gormx.NewQueryWithTableName("t_db db").
|
qd := gormx.NewQueryWithTableName("t_db db").
|
||||||
Select("db.*, inst.name instance_name, inst.type instance_type, inst.host, inst.port, inst.username ").
|
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").
|
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.instance_id", condition.InstanceId).
|
||||||
Eq("db.id", condition.Id).
|
Eq("db.id", condition.Id).
|
||||||
Like("db.database", condition.Database).
|
Like("db.database", condition.Database).
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, page
|
|||||||
qd := gormx.NewQuery(new(entity.DbInstance)).
|
qd := gormx.NewQuery(new(entity.DbInstance)).
|
||||||
Eq("id", condition.Id).
|
Eq("id", condition.Id).
|
||||||
Eq("host", condition.Host).
|
Eq("host", condition.Host).
|
||||||
Like("name", condition.Name)
|
Like("name", condition.Name).
|
||||||
|
Like("code", condition.Code)
|
||||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ func InitInstanceRouter(router *gin.RouterGroup) {
|
|||||||
|
|
||||||
req.NewGet(":instanceId", d.GetInstance),
|
req.NewGet(":instanceId", d.GetInstance),
|
||||||
|
|
||||||
req.NewGet(":instanceId/pwd", d.GetInstancePwd),
|
|
||||||
|
|
||||||
// 获取数据库实例的所有数据库名
|
// 获取数据库实例的所有数据库名
|
||||||
req.NewGet(":instanceId/databases", d.GetDatabaseNames),
|
req.NewGet(":instanceId/databases", d.GetDatabaseNames),
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import tagentity "mayfly-go/internal/tag/domain/entity"
|
|||||||
type MachineForm struct {
|
type MachineForm struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Protocol int `json:"protocol" binding:"required"`
|
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"`
|
Name string `json:"name" binding:"required"`
|
||||||
Ip string `json:"ip" binding:"required"` // IP地址
|
Ip string `json:"ip" binding:"required"` // IP地址
|
||||||
Port int `json:"port" binding:"required"` // 端口号
|
Port int `json:"port" binding:"required"` // 端口号
|
||||||
|
|
||||||
TagId []uint64 `json:"tagId" binding:"required"`
|
TagId []uint64 `json:"tagId" binding:"required"`
|
||||||
AuthCerts []*tagentity.ResourceAuthCert // 资产授权凭证信息列表
|
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表
|
||||||
|
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mayfly-go/internal/common/consts"
|
||||||
"mayfly-go/internal/machine/api/form"
|
"mayfly-go/internal/machine/api/form"
|
||||||
"mayfly-go/internal/machine/api/vo"
|
"mayfly-go/internal/machine/api/vo"
|
||||||
"mayfly-go/internal/machine/application"
|
"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
|
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
|
return mvo
|
||||||
})...)
|
})...)
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/scheduler"
|
"mayfly-go/pkg/scheduler"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SaveMachineParam struct {
|
type SaveMachineParam struct {
|
||||||
@@ -66,7 +65,6 @@ type Machine interface {
|
|||||||
type machineAppImpl struct {
|
type machineAppImpl struct {
|
||||||
base.AppImpl[*entity.Machine, repository.Machine]
|
base.AppImpl[*entity.Machine, repository.Machine]
|
||||||
|
|
||||||
// authCertApp AuthCert `inject:"AuthCertApp"`
|
|
||||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||||
}
|
}
|
||||||
@@ -87,6 +85,10 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
|||||||
authCerts := param.AuthCerts
|
authCerts := param.AuthCerts
|
||||||
resourceType := tagentity.TagTypeMachine
|
resourceType := tagentity.TagTypeMachine
|
||||||
|
|
||||||
|
if len(authCerts) == 0 {
|
||||||
|
return errorx.NewBiz("授权凭证信息不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
oldMachine := &entity.Machine{
|
oldMachine := &entity.Machine{
|
||||||
Ip: me.Ip,
|
Ip: me.Ip,
|
||||||
Port: me.Port,
|
Port: me.Port,
|
||||||
@@ -94,7 +96,6 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := m.GetBy(oldMachine)
|
err := m.GetBy(oldMachine)
|
||||||
|
|
||||||
if me.Id == 0 {
|
if me.Id == 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return errorx.NewBiz("该机器信息已存在")
|
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 {
|
if err := m.Tx(ctx, func(ctx context.Context) error {
|
||||||
return m.Insert(ctx, me)
|
return m.Insert(ctx, me)
|
||||||
}, func(ctx context.Context) error {
|
}, 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,
|
ResourceCode: me.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
TagIds: tagIds,
|
AuthCerts: authCerts,
|
||||||
})
|
})
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
// 事务问题,导致可能删除或新增的标签数据不可见,造成数据错误,故单独执行。若万一失败,可授权凭证管理处添加
|
||||||
|
return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{
|
||||||
ResourceCode: me.Code,
|
ResourceCode: me.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
AuthCerts: authCerts,
|
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 {
|
if err := m.Tx(ctx, func(ctx context.Context) error {
|
||||||
return m.UpdateById(ctx, me)
|
return m.UpdateById(ctx, me)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceCode: oldMachine.Code,
|
Code: oldMachine.Code,
|
||||||
ResourceType: resourceType,
|
Type: resourceType,
|
||||||
TagIds: tagIds,
|
ParentTagIds: tagIds,
|
||||||
})
|
})
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return m.Tx(ctx, func(ctx context.Context) error {
|
||||||
ResourceCode: oldMachine.Code,
|
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||||
ResourceType: resourceType,
|
ResourceCode: oldMachine.Code,
|
||||||
AuthCerts: authCerts,
|
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 {
|
func (m *machineAppImpl) TestConn(me *entity.Machine, authCert *tagentity.ResourceAuthCert) error {
|
||||||
me.Id = 0
|
me.Id = 0
|
||||||
|
|
||||||
if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic {
|
if authCert.Id != 0 {
|
||||||
publicAuthCert, err := m.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext)
|
// 密文可能被清除,故需要重新获取
|
||||||
if err != nil {
|
authCert, _ = m.resourceAuthCertApp.GetAuthCert(authCert.Name)
|
||||||
return err
|
} 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)
|
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 {
|
func(ctx context.Context) error {
|
||||||
return m.DeleteById(ctx, id)
|
return m.DeleteById(ctx, id)
|
||||||
}, func(ctx context.Context) error {
|
}, 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,
|
ResourceCode: machine.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
})
|
})
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{
|
||||||
ResourceCode: machine.Code,
|
ResourceCode: machine.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
})
|
})
|
||||||
@@ -267,13 +293,9 @@ func (m *machineAppImpl) TimerUpdateStats() {
|
|||||||
}()
|
}()
|
||||||
logx.Debugf("定时获取机器[id=%d]状态信息开始", mid)
|
logx.Debugf("定时获取机器[id=%d]状态信息开始", mid)
|
||||||
cli, err := m.GetCli(mid)
|
cli, err := m.GetCli(mid)
|
||||||
// ssh获取客户端失败,则更新机器状态为禁用
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
updateMachine := &entity.Machine{Status: entity.MachineStatusDisable}
|
logx.Errorf("定时获取机器[id=%d]状态信息失败, 获取机器cli失败: %s", mid, err.Error())
|
||||||
updateMachine.Id = mid
|
return
|
||||||
now := time.Now()
|
|
||||||
updateMachine.UpdateTime = &now
|
|
||||||
m.UpdateById(context.TODO(), updateMachine)
|
|
||||||
}
|
}
|
||||||
cache.SaveMachineStats(mid, cli.GetAllStats())
|
cache.SaveMachineStats(mid, cli.GetAllStats())
|
||||||
logx.Debugf("定时获取机器[id=%d]状态信息结束", mid)
|
logx.Debugf("定时获取机器[id=%d]状态信息结束", mid)
|
||||||
@@ -336,7 +358,7 @@ func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAu
|
|||||||
mi.Name = me.Name
|
mi.Name = me.Name
|
||||||
mi.Ip = me.Ip
|
mi.Ip = me.Ip
|
||||||
mi.Port = me.Port
|
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.EnableRecorder = me.EnableRecorder
|
||||||
mi.Protocol = me.Protocol
|
mi.Protocol = me.Protocol
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
|
|||||||
Like("ip", condition.Ip).
|
Like("ip", condition.Ip).
|
||||||
Like("name", condition.Name).
|
Like("name", condition.Name).
|
||||||
In("code", condition.Codes).
|
In("code", condition.Codes).
|
||||||
Eq("code", condition.Code)
|
Like("code", condition.Code)
|
||||||
|
|
||||||
// 只查询ssh服务器
|
// 只查询ssh服务器
|
||||||
if condition.Ssh == entity.MachineProtocolSsh {
|
if condition.Ssh == entity.MachineProtocolSsh {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type Dashbord struct {
|
|||||||
|
|
||||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||||
accountId := rc.GetLoginAccount().Id
|
accountId := rc.GetLoginAccount().Id
|
||||||
mongoNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, ""))
|
mongoNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, ""))
|
||||||
|
|
||||||
rc.ResData = collx.M{
|
rc.ResData = collx.M{
|
||||||
"mongoNum": mongoNum,
|
"mongoNum": mongoNum,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (m *Mongo) Mongos(rc *req.Ctx) {
|
|||||||
queryCond, page := req.BindQueryAndPage[*entity.MongoQuery](rc, new(entity.MongoQuery))
|
queryCond, page := req.BindQueryAndPage[*entity.MongoQuery](rc, new(entity.MongoQuery))
|
||||||
|
|
||||||
// 不存在可访问标签id,即没有可操作数据
|
// 不存在可访问标签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 {
|
if len(codes) == 0 {
|
||||||
rc.ResData = model.EmptyPageResult[any]()
|
rc.ResData = model.EmptyPageResult[any]()
|
||||||
return
|
return
|
||||||
@@ -43,7 +43,7 @@ func (m *Mongo) Mongos(rc *req.Ctx) {
|
|||||||
biz.ErrIsNil(err)
|
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
|
return mvo
|
||||||
})...)
|
})...)
|
||||||
|
|
||||||
|
|||||||
@@ -59,9 +59,9 @@ func (d *mongoAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
return d.DeleteById(ctx, id)
|
return d.DeleteById(ctx, id)
|
||||||
},
|
},
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceType: tagentity.TagTypeMongo,
|
Type: tagentity.TagTypeMongo,
|
||||||
ResourceCode: mongoEntity.Code,
|
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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return d.Insert(ctx, m)
|
return d.Insert(ctx, m)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceType: tagentity.TagTypeMongo,
|
Type: tagentity.TagTypeMongo,
|
||||||
ResourceCode: m.Code,
|
Code: m.Code,
|
||||||
TagIds: tagIds,
|
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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return d.UpdateById(ctx, m)
|
return d.UpdateById(ctx, m)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceType: tagentity.TagTypeMongo,
|
Type: tagentity.TagTypeMongo,
|
||||||
ResourceCode: oldMongo.Code,
|
Code: oldMongo.Code,
|
||||||
TagIds: tagIds,
|
ParentTagIds: tagIds,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -127,6 +127,6 @@ func (d *mongoAppImpl) GetMongoConn(id uint64) (*mgm.MongoConn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorx.NewBiz("mongo信息不存在")
|
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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type Dashbord struct {
|
|||||||
|
|
||||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||||
accountId := rc.GetLoginAccount().Id
|
accountId := rc.GetLoginAccount().Id
|
||||||
redisNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, ""))
|
redisNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, ""))
|
||||||
|
|
||||||
rc.ResData = collx.M{
|
rc.ResData = collx.M{
|
||||||
"redisNum": redisNum,
|
"redisNum": redisNum,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (r *Redis) RedisList(rc *req.Ctx) {
|
|||||||
queryCond, page := req.BindQueryAndPage[*entity.RedisQuery](rc, new(entity.RedisQuery))
|
queryCond, page := req.BindQueryAndPage[*entity.RedisQuery](rc, new(entity.RedisQuery))
|
||||||
|
|
||||||
// 不存在可访问标签id,即没有可操作数据
|
// 不存在可访问标签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 {
|
if len(codes) == 0 {
|
||||||
rc.ResData = model.EmptyPageResult[any]()
|
rc.ResData = model.EmptyPageResult[any]()
|
||||||
return
|
return
|
||||||
@@ -43,7 +43,7 @@ func (r *Redis) RedisList(rc *req.Ctx) {
|
|||||||
biz.ErrIsNil(err)
|
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
|
return rvo
|
||||||
})...)
|
})...)
|
||||||
|
|
||||||
|
|||||||
@@ -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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return r.Insert(ctx, re)
|
return r.Insert(ctx, re)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceType: tagenttiy.TagTypeRedis,
|
Type: tagenttiy.TagTypeRedis,
|
||||||
ResourceCode: re.Code,
|
Code: re.Code,
|
||||||
TagIds: tagIds,
|
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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return r.UpdateById(ctx, re)
|
return r.UpdateById(ctx, re)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceType: tagenttiy.TagTypeRedis,
|
Type: tagenttiy.TagTypeRedis,
|
||||||
ResourceCode: oldRedis.Code,
|
Code: oldRedis.Code,
|
||||||
TagIds: tagIds,
|
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.Tx(ctx, func(ctx context.Context) error {
|
||||||
return r.DeleteById(ctx, id)
|
return r.DeleteById(ctx, id)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||||
ResourceType: tagenttiy.TagTypeRedis,
|
Type: tagenttiy.TagTypeRedis,
|
||||||
ResourceCode: re.Code,
|
Code: re.Code,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) {
|
|||||||
if err := re.PwdDecrypt(); err != nil {
|
if err := re.PwdDecrypt(); err != nil {
|
||||||
return nil, errorx.NewBiz(err.Error())
|
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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,10 +87,10 @@ func (p *TagTree) DelTagTree(rc *req.Ctx) {
|
|||||||
func (p *TagTree) TagResources(rc *req.Ctx) {
|
func (p *TagTree) TagResources(rc *req.Ctx) {
|
||||||
resourceType := int8(rc.PathParamInt("rtype"))
|
resourceType := int8(rc.PathParamInt("rtype"))
|
||||||
accountId := rc.GetLoginAccount().Id
|
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 {
|
tagPath2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(tagResource *entity.TagTree) string {
|
||||||
return tagResource.GetParentPath(1)
|
return tagResource.GetTagPath()
|
||||||
})
|
})
|
||||||
|
|
||||||
tagPaths := collx.MapKeys(tagPath2Resource)
|
tagPaths := collx.MapKeys(tagPath2Resource)
|
||||||
@@ -110,8 +110,8 @@ func (p *TagTree) CountTagResource(rc *req.Ctx) {
|
|||||||
|
|
||||||
rc.ResData = collx.M{
|
rc.ResData = collx.M{
|
||||||
"machine": len(collx.ArrayDeduplicate(machineCodes)),
|
"machine": len(collx.ArrayDeduplicate(machineCodes)),
|
||||||
"db": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, tagPath)),
|
"db": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeDb, tagPath)),
|
||||||
"redis": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, tagPath)),
|
"redis": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, tagPath)),
|
||||||
"mongo": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, tagPath)),
|
"mongo": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ type RelateAuthCertParam struct {
|
|||||||
type ResourceAuthCert interface {
|
type ResourceAuthCert interface {
|
||||||
base.App[*entity.ResourceAuthCert]
|
base.App[*entity.ResourceAuthCert]
|
||||||
|
|
||||||
// RelateAuthCert 保存资源授权凭证信息,不可放于事务中
|
// RelateAuthCert 关联资源授权凭证信息
|
||||||
RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error
|
RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error
|
||||||
|
|
||||||
|
// RelateAuthCert2ResourceTag 关联授权凭证标签至指定资源标签下
|
||||||
|
RelateAuthCert2ResourceTag(ctx context.Context, param *RelateAuthCertParam) error
|
||||||
|
|
||||||
// SaveAuthCert 保存授权凭证信息
|
// SaveAuthCert 保存授权凭证信息
|
||||||
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
||||||
|
|
||||||
@@ -40,9 +43,15 @@ type ResourceAuthCert interface {
|
|||||||
// GetAccountAuthCert 获取账号有权限操作的授权凭证信息
|
// GetAccountAuthCert 获取账号有权限操作的授权凭证信息
|
||||||
GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert
|
GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert
|
||||||
|
|
||||||
// FillAuthCert 填充资源的授权凭证信息
|
// FillAuthCertByAcs 根据授权凭证列表填充资源的授权凭证信息
|
||||||
|
// @param authCerts 授权凭证列表
|
||||||
// @param resources 实现了entity.IAuthCert接口的资源信息
|
// @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 {
|
type resourceAuthCertAppImpl struct {
|
||||||
@@ -60,11 +69,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
resourceCode := params.ResourceCode
|
resourceCode := params.ResourceCode
|
||||||
resourceType := int8(params.ResourceType)
|
resourceType := int8(params.ResourceType)
|
||||||
resourceAuthCerts := params.AuthCerts
|
resourceAuthCerts := params.AuthCerts
|
||||||
authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType))
|
|
||||||
|
|
||||||
if authCertTagType == 0 {
|
|
||||||
return errorx.NewBiz("资源授权凭证所属标签类型不能为空")
|
|
||||||
}
|
|
||||||
if resourceCode == "" {
|
if resourceCode == "" {
|
||||||
return errorx.NewBiz("资源授权凭证的资源编号不能为空")
|
return errorx.NewBiz("资源授权凭证的资源编号不能为空")
|
||||||
}
|
}
|
||||||
@@ -79,15 +84,6 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除该资源下的所有授权凭证资源标签
|
|
||||||
if err := r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
|
|
||||||
ResourceCode: resourceCode,
|
|
||||||
ResourceType: params.ResourceType,
|
|
||||||
ChildType: authCertTagType,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +94,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
name2AuthCert[resourceAuthCert.Name] = resourceAuthCert
|
name2AuthCert[resourceAuthCert.Name] = resourceAuthCert
|
||||||
|
|
||||||
existNameAc := &entity.ResourceAuthCert{Name: resourceAuthCert.Name}
|
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)
|
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 {
|
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
|
||||||
return err
|
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 {
|
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 {
|
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 删除对应授权凭证资源标签
|
|
||||||
if err := r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
|
|
||||||
ResourceCode: del,
|
|
||||||
ResourceType: authCertTagType,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(unmodifys) > 0 {
|
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 {
|
oldName2AuthCert := collx.ArrayToMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||||
return ac.Name
|
return ac.Name
|
||||||
})
|
})
|
||||||
|
acTagType := GetResourceAuthCertTagType(params.ResourceType)
|
||||||
for _, unmodify := range unmodifys {
|
for _, unmodify := range unmodifys {
|
||||||
unmodifyAc := name2AuthCert[unmodify]
|
unmodifyAc := name2AuthCert[unmodify]
|
||||||
if unmodifyAc.Id == 0 {
|
if unmodifyAc.Id == 0 {
|
||||||
@@ -197,13 +164,11 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
// 如果修改了用户名,且该凭证关联至标签,则需要更新对应的标签名(资源授权凭证类型的标签名为username)
|
||||||
if oldAuthCert.Username != unmodifyAc.Username {
|
if oldAuthCert.Username != unmodifyAc.Username && acTagType != 0 {
|
||||||
if err := r.updateAuthCertTagName(ctx, unmodify, authCertTagType, unmodifyAc.Username); err != nil {
|
r.updateAuthCertTagName(ctx, unmodify, acTagType, unmodifyAc.Username)
|
||||||
logx.WarnfContext(ctx, "授权凭证[%s]修改了用户名-同步更新授权凭证标签名失败", unmodify)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
||||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -213,6 +178,17 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
return nil
|
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 {
|
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||||
if rac.Id == 0 {
|
if rac.Id == 0 {
|
||||||
return r.addAuthCert(ctx, rac)
|
return r.addAuthCert(ctx, rac)
|
||||||
@@ -242,7 +218,7 @@ func (r *resourceAuthCertAppImpl) DeleteAuthCert(ctx context.Context, id uint64)
|
|||||||
return r.Tx(ctx,
|
return r.Tx(ctx,
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
// 删除对应授权凭证标签
|
// 删除对应授权凭证标签
|
||||||
return r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
|
return r.tagTreeApp.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||||
ResourceCode: rac.Name,
|
ResourceCode: rac.Name,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -288,7 +264,7 @@ func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertT
|
|||||||
Type: authCertTagType,
|
Type: authCertTagType,
|
||||||
CodePathLikes: tagPath,
|
CodePathLikes: tagPath,
|
||||||
}
|
}
|
||||||
authCertTags := r.tagTreeApp.GetAccountTagResources(accountId, tagQuery)
|
authCertTags := r.tagTreeApp.GetAccountTags(accountId, tagQuery)
|
||||||
|
|
||||||
// 获取所有授权凭证名称
|
// 获取所有授权凭证名称
|
||||||
authCertNames := collx.ArrayMap(authCertTags, func(tag *entity.TagTree) string {
|
authCertNames := collx.ArrayMap(authCertTags, func(tag *entity.TagTree) string {
|
||||||
@@ -303,7 +279,7 @@ func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertT
|
|||||||
return authCerts
|
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 {
|
if len(resources) == 0 || len(authCerts) == 0 {
|
||||||
return
|
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 添加授权凭证
|
// addAuthCert 添加授权凭证
|
||||||
func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||||
if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 {
|
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
|
resourceCode := rac.ResourceCode
|
||||||
resourceType := rac.ResourceType
|
resourceType := rac.ResourceType
|
||||||
|
// 资源对应的授权凭证标签类型,若为0则说明该资源不需要关联至资源tagTree
|
||||||
|
authCertTagType := GetResourceAuthCertTagType(entity.TagType(resourceType))
|
||||||
|
|
||||||
// 获取资源编号对应的资源标签信息
|
var resourceTagIds []uint64
|
||||||
var resourceTags []*entity.TagTree
|
// 如果该资源存在对应的授权凭证标签类型,则说明需要关联至tagTree,否则直接从授权凭证库中验证资源编号是否正确即可(一个资源最少有一个授权凭证)
|
||||||
r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags)
|
if authCertTagType != 0 {
|
||||||
// 资源标签id(相当于父tag id)
|
// 获取资源编号对应的资源标签信息
|
||||||
resourceTagIds := collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 {
|
var resourceTags []*entity.TagTree
|
||||||
return tag.Id
|
r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags)
|
||||||
})
|
// 资源标签id(相当于父tag id)
|
||||||
if len(resourceTagIds) == 0 {
|
resourceTagIds = collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 {
|
||||||
return errorx.NewBiz("资源标签不存在[%s], 请检查资源编号是否正确", resourceCode)
|
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 {
|
if rac.CiphertextType != entity.AuthCertCiphertextTypePublic {
|
||||||
rac.CiphertextEncrypt()
|
rac.CiphertextEncrypt()
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.Tx(ctx, func(ctx context.Context) error {
|
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{
|
if len(resourceTagIds) > 0 {
|
||||||
ResourceCode: rac.Name,
|
logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagIds)
|
||||||
ResourceType: authCertTagType,
|
return r.tagTreeApp.SaveResourceTag(ctx, &SaveResourceTagParam{
|
||||||
ResourceName: rac.Username,
|
Code: rac.Name,
|
||||||
TagIds: resourceTagIds,
|
Type: GetResourceAuthCertTagType(entity.TagType(resourceType)),
|
||||||
})
|
Name: rac.Username,
|
||||||
|
ParentTagIds: resourceTagIds,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.Insert(ctx, rac)
|
return r.Insert(ctx, rac)
|
||||||
})
|
})
|
||||||
@@ -401,9 +403,12 @@ func (r *resourceAuthCertAppImpl) updateAuthCert(ctx context.Context, rac *entit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 修改了用户名,则需要同步更新对应授权凭证标签里的名称
|
// 修改了用户名,则需要同步更新对应授权凭证标签里的名称
|
||||||
if rac.Username != oldRac.Username {
|
if rac.Username != oldRac.Username && rac.ResourceType == int8(entity.TagTypeMachine) {
|
||||||
if err := r.updateAuthCertTagName(ctx, oldRac.Name, getResourceAuthCertTagType(entity.TagType(oldRac.ResourceType)), rac.Username); err != nil {
|
authCertTagType := GetResourceAuthCertTagType(entity.TagType(oldRac.ResourceType))
|
||||||
return errorx.NewBiz("同步更新授权凭证标签名称失败")
|
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})
|
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) {
|
func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
|
||||||
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||||
@@ -457,3 +450,13 @@ func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthC
|
|||||||
}
|
}
|
||||||
return authCert, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,26 +2,46 @@ package application
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"mayfly-go/internal/common/consts"
|
"mayfly-go/internal/common/consts"
|
||||||
"mayfly-go/internal/tag/domain/entity"
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/internal/tag/domain/repository"
|
"mayfly-go/internal/tag/domain/repository"
|
||||||
"mayfly-go/pkg/base"
|
"mayfly-go/pkg/base"
|
||||||
"mayfly-go/pkg/contextx"
|
"mayfly-go/pkg/contextx"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 保存资源标签参数
|
// 标签接口,实现了该接口的结构体默认都可以当成标签树的一种标签
|
||||||
type SaveResourceTagParam struct {
|
type ITag interface {
|
||||||
ResourceCode string
|
// 资源标签code
|
||||||
ResourceName string
|
GetCode() string
|
||||||
ResourceType entity.TagType
|
|
||||||
|
|
||||||
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 {
|
type DelResourceTagParam struct {
|
||||||
|
Id uint64
|
||||||
ResourceCode string
|
ResourceCode string
|
||||||
ResourceType entity.TagType
|
ResourceType entity.TagType
|
||||||
|
|
||||||
@@ -40,23 +60,25 @@ type TagTree interface {
|
|||||||
|
|
||||||
Delete(ctx context.Context, id uint64) error
|
Delete(ctx context.Context, id uint64) error
|
||||||
|
|
||||||
// 获取指定账号有权限操作的资源信息列表
|
// 获取指定账号有权限操作的标签列表
|
||||||
// @param accountId 账号id
|
// @param accountId 账号id
|
||||||
// @param resourceType 资源类型
|
// @param query 查询条件
|
||||||
// @param tagPath 访问指定的标签路径下关联的资源
|
GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree
|
||||||
GetAccountTagResources(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree
|
|
||||||
|
|
||||||
// 获取指定账号有权限操作的资源codes
|
// 获取指定账号有权限操作的标签codes
|
||||||
GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string
|
GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string
|
||||||
|
|
||||||
// SaveResource 保存资源标签
|
// SaveResourceTag 保存资源类型标签
|
||||||
SaveResource(ctx context.Context, req *SaveResourceTagParam) error
|
SaveResourceTag(ctx context.Context, param *SaveResourceTagParam) error
|
||||||
|
|
||||||
// DeleteResource 删除资源标签,会删除该资源下所有子节点信息
|
// RelateTagsByCodeAndType 将指定标签数组关联至满足指定标签类型和标签code的标签下
|
||||||
DeleteResource(ctx context.Context, param *DelResourceTagParam) error
|
RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error
|
||||||
|
|
||||||
// 根据资源信息获取对应的标签路径列表
|
// DeleteTagByParam 删除标签,会删除该标签下所有子标签信息以及团队关联的标签信息
|
||||||
ListTagPathByResource(resourceType int8, resourceCode string) []string
|
DeleteTagByParam(ctx context.Context, param *DelResourceTagParam) error
|
||||||
|
|
||||||
|
// 根据标签类型和标签code获取对应的标签路径列表
|
||||||
|
ListTagPathByTypeAndCode(resourceType int8, resourceCode string) []string
|
||||||
|
|
||||||
// 根据账号id获取其可访问标签信息
|
// 根据账号id获取其可访问标签信息
|
||||||
ListTagByAccountId(accountId uint64) []string
|
ListTagByAccountId(accountId uint64) []string
|
||||||
@@ -65,7 +87,7 @@ type TagTree interface {
|
|||||||
CanAccess(accountId uint64, tagPath ...string) error
|
CanAccess(accountId uint64, tagPath ...string) error
|
||||||
|
|
||||||
// 填充资源的标签信息
|
// 填充资源的标签信息
|
||||||
FillTagInfo(resources ...entity.ITagResource)
|
FillTagInfo(resourceTagType entity.TagType, resources ...entity.ITagResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tagTreeAppImpl struct {
|
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)
|
return p.Insert(ctx, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +150,7 @@ func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity an
|
|||||||
p.GetRepo().SelectByCondition(condition, toEntity)
|
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{
|
tagResourceQuery := &entity.TagTreeQuery{
|
||||||
Type: query.Type,
|
Type: query.Type,
|
||||||
}
|
}
|
||||||
@@ -182,8 +204,8 @@ func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, query *entity.
|
|||||||
return tagResources
|
return tagResources
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string {
|
func (p *tagTreeAppImpl) GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string {
|
||||||
tagResources := p.GetAccountTagResources(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType), CodePathLikes: []string{tagPath}})
|
tagResources := p.GetAccountTags(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType), CodePathLikes: []string{tagPath}})
|
||||||
// resouce code去重
|
// resouce code去重
|
||||||
code2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(val *entity.TagTree) string {
|
code2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(val *entity.TagTree) string {
|
||||||
return val.Code
|
return val.Code
|
||||||
@@ -192,34 +214,34 @@ func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType
|
|||||||
return collx.MapKeys(code2Resource)
|
return collx.MapKeys(code2Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagParam) error {
|
func (p *tagTreeAppImpl) SaveResourceTag(ctx context.Context, param *SaveResourceTagParam) error {
|
||||||
resourceCode := req.ResourceCode
|
code := param.Code
|
||||||
resourceType := entity.TagType(req.ResourceType)
|
tagType := entity.TagType(param.Type)
|
||||||
resourceName := req.ResourceName
|
name := param.Name
|
||||||
tagIds := req.TagIds
|
tagIds := param.ParentTagIds
|
||||||
|
|
||||||
if resourceCode == "" {
|
if code == "" {
|
||||||
return errorx.NewBiz("资源编号不能为空")
|
return errorx.NewBiz("资源编号不能为空")
|
||||||
}
|
}
|
||||||
if resourceType == 0 {
|
if tagType == 0 {
|
||||||
return errorx.NewBiz("资源类型不能为空")
|
return errorx.NewBiz("资源类型不能为空")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果tagIds为空数组,则为删除该资源标签
|
// 如果tagIds为空数组,则为删除该资源标签
|
||||||
if len(tagIds) == 0 {
|
if len(tagIds) == 0 {
|
||||||
return p.DeleteResource(ctx, &DelResourceTagParam{
|
return p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||||
ResourceType: resourceType,
|
ResourceType: tagType,
|
||||||
ResourceCode: resourceCode,
|
ResourceCode: code,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if resourceName == "" {
|
if name == "" {
|
||||||
resourceName = resourceCode
|
name = code
|
||||||
}
|
}
|
||||||
|
|
||||||
// 该资源对应的旧资源标签信息
|
// 该资源对应的旧资源标签信息
|
||||||
var oldTagTree []*entity.TagTree
|
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
|
var addTagIds, delTagIds []uint64
|
||||||
if len(oldTagTree) == 0 {
|
if len(oldTagTree) == 0 {
|
||||||
@@ -240,11 +262,12 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP
|
|||||||
}
|
}
|
||||||
addTagResource = append(addTagResource, &entity.TagTree{
|
addTagResource = append(addTagResource, &entity.TagTree{
|
||||||
Pid: tagId,
|
Pid: tagId,
|
||||||
Code: resourceCode,
|
Code: code,
|
||||||
Type: resourceType,
|
Type: tagType,
|
||||||
Name: resourceName,
|
Name: name,
|
||||||
CodePath: tag.CodePath + resourceCode + entity.CodePathSeparator,
|
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 {
|
if err := p.BatchInsert(ctx, addTagResource); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -253,9 +276,9 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP
|
|||||||
|
|
||||||
if len(delTagIds) > 0 {
|
if len(delTagIds) > 0 {
|
||||||
for _, tagId := range delTagIds {
|
for _, tagId := range delTagIds {
|
||||||
if err := p.DeleteResource(ctx, &DelResourceTagParam{
|
if err := p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||||
ResourceType: resourceType,
|
ResourceType: tagType,
|
||||||
ResourceCode: resourceCode,
|
ResourceCode: code,
|
||||||
Pid: tagId,
|
Pid: tagId,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -267,29 +290,136 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeAppImpl) DeleteResource(ctx context.Context, param *DelResourceTagParam) error {
|
func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error {
|
||||||
// 获取资源编号对应的资源标签信息
|
parentTagCode := param.ParentTagCode
|
||||||
var resourceTags []*entity.TagTree
|
parentTagType := param.ParentTagType
|
||||||
p.ListByCond(&entity.TagTree{Type: param.ResourceType, Code: param.ResourceCode, Pid: param.Pid}, &resourceTags)
|
tagType := param.TagType
|
||||||
if len(resourceTags) == 0 {
|
|
||||||
return nil
|
// 如果资源为,则表示清楚关联
|
||||||
|
if len(param.Tags) == 0 {
|
||||||
|
// 删除该资源下的所有指定类型的资源
|
||||||
|
return p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||||
|
ResourceCode: parentTagCode,
|
||||||
|
ResourceType: param.ParentTagType,
|
||||||
|
ChildType: tagType,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
delTagType := param.ChildType
|
// 获取满足指定编号与类型的所有标签信息
|
||||||
for _, resourceTag := range resourceTags {
|
var parentTags []*entity.TagTree
|
||||||
// 删除所有code_path下的子标签
|
p.ListByCond(&entity.TagTree{Type: parentTagType, Code: parentTagCode}, &parentTags)
|
||||||
if err := p.DeleteByWheres(ctx, collx.M{
|
// 标签id(相当于需要关联的标签数组的父tag id)
|
||||||
"code_path LIKE ?": resourceTag.CodePath + "%",
|
parentTagIds := collx.ArrayMap(parentTags, func(tag *entity.TagTree) uint64 {
|
||||||
"type = ?": delTagType,
|
return tag.Id
|
||||||
}); err != nil {
|
})
|
||||||
|
|
||||||
|
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
|
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
|
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
|
var trs []*entity.TagTree
|
||||||
p.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &trs)
|
p.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &trs)
|
||||||
return collx.ArrayMap(trs, func(tr *entity.TagTree) string {
|
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("您无权操作该资源")
|
return errorx.NewBiz("您无权操作该资源")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeAppImpl) FillTagInfo(resources ...entity.ITagResource) {
|
func (p *tagTreeAppImpl) FillTagInfo(resourceTagType entity.TagType, resources ...entity.ITagResource) {
|
||||||
if len(resources) == 0 {
|
if len(resources) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -330,11 +460,11 @@ func (p *tagTreeAppImpl) FillTagInfo(resources ...entity.ITagResource) {
|
|||||||
|
|
||||||
// 获取所有资源code关联的标签列表信息
|
// 获取所有资源code关联的标签列表信息
|
||||||
var tagResources []*entity.TagTree
|
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 {
|
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()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,16 @@ func (m *ResourceAuthCert) GetExtraString(key string) string {
|
|||||||
return cast.ToString(m.Extra[key])
|
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 与指定授权凭证比较是否有变更
|
// HasChanged 与指定授权凭证比较是否有变更
|
||||||
func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool {
|
func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool {
|
||||||
if rac == nil {
|
if rac == nil {
|
||||||
|
|||||||
@@ -23,17 +23,18 @@ type TagType int8
|
|||||||
const (
|
const (
|
||||||
// 标识路径分隔符
|
// 标识路径分隔符
|
||||||
CodePathSeparator = "/"
|
CodePathSeparator = "/"
|
||||||
|
// 标签路径资源段分隔符
|
||||||
|
CodePathResourceSeparator = "|"
|
||||||
|
|
||||||
TagTypeTag TagType = -1
|
TagTypeTag TagType = -1
|
||||||
TagTypeMachine TagType = TagType(consts.TagResourceTypeMachine)
|
TagTypeMachine TagType = TagType(consts.ResourceTypeMachine)
|
||||||
TagTypeDb TagType = TagType(consts.TagResourceTypeDb)
|
TagTypeDb TagType = TagType(consts.ResourceTypeDb)
|
||||||
TagTypeRedis TagType = TagType(consts.TagResourceTypeRedis)
|
TagTypeRedis TagType = TagType(consts.ResourceTypeRedis)
|
||||||
TagTypeMongo TagType = TagType(consts.TagResourceTypeMongo)
|
TagTypeMongo TagType = TagType(consts.ResourceTypeMongo)
|
||||||
|
|
||||||
// ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证)
|
// ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证)
|
||||||
|
|
||||||
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
|
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
|
||||||
TagTypeDbAuthCert TagType = 21 // DB-授权凭证
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRootCode 获取根路径信息
|
// GetRootCode 获取根路径信息
|
||||||
@@ -62,6 +63,30 @@ func (pt *TagTree) GetParentPath(index int) string {
|
|||||||
return parentPath + "/"
|
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 {
|
type ITagResource interface {
|
||||||
// 获取资源code
|
// 获取资源code
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ var (
|
|||||||
func RegisterCustomPatterns() {
|
func RegisterCustomPatterns() {
|
||||||
// 账号用户名校验
|
// 账号用户名校验
|
||||||
RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线")
|
RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线")
|
||||||
|
RegisterPattern("resource_code", "^[a-zA-Z0-9_.:]{1,32}$", "只允许输入1-32位大小写字母、数字、_.:")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册自定义正则表达式
|
// 注册自定义正则表达式
|
||||||
|
|||||||
@@ -873,7 +873,7 @@ CREATE TABLE `t_tag_tree` (
|
|||||||
`pid` bigint(20) NOT NULL DEFAULT '0',
|
`pid` bigint(20) NOT NULL DEFAULT '0',
|
||||||
`type` tinyint NOT NULL DEFAULT '-1' COMMENT '类型: -1.普通标签; 其他值则为对应的资源类型',
|
`type` tinyint NOT NULL DEFAULT '-1' COMMENT '类型: -1.普通标签; 其他值则为对应的资源类型',
|
||||||
`code` varchar(36) NOT NULL COMMENT '标识符',
|
`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 '名称',
|
`name` varchar(36) DEFAULT NULL COMMENT '名称',
|
||||||
`remark` varchar(255) DEFAULT NULL,
|
`remark` varchar(255) DEFAULT NULL,
|
||||||
`create_time` datetime NOT NULL,
|
`create_time` datetime NOT NULL,
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ INSERT
|
|||||||
select
|
select
|
||||||
tag_id,
|
tag_id,
|
||||||
resource_code,
|
resource_code,
|
||||||
CONCAT(tag_path , resource_code, '/'),
|
CONCAT(tag_path ,resource_type , '|', resource_code, '/'),
|
||||||
resource_type,
|
resource_type,
|
||||||
resource_code,
|
resource_code,
|
||||||
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),
|
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),
|
||||||
@@ -175,7 +175,7 @@ INSERT
|
|||||||
SELECT
|
SELECT
|
||||||
tt.id,
|
tt.id,
|
||||||
rac.`name`,
|
rac.`name`,
|
||||||
CONCAT(tt.code_path, rac.`name`, '/'),
|
CONCAT(tt.code_path, '11|' ,rac.`name`, '/'),
|
||||||
11,
|
11,
|
||||||
rac.`username`,
|
rac.`username`,
|
||||||
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),
|
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),
|
||||||
@@ -188,9 +188,9 @@ SELECT
|
|||||||
FROM
|
FROM
|
||||||
`t_tag_tree` tt
|
`t_tag_tree` tt
|
||||||
JOIN `t_resource_auth_cert` rac ON tt.`code` = rac.`resource_code`
|
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
|
WHERE
|
||||||
tt.`is_deleted` = 0
|
tt.`is_deleted` = 0;
|
||||||
|
|
||||||
-- 删除机器表 账号相关字段
|
-- 删除机器表 账号相关字段
|
||||||
ALTER TABLE t_machine DROP COLUMN username;
|
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);
|
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;
|
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;
|
||||||
Reference in New Issue
Block a user