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