mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
reafctor: 团队管理与授权凭证优化
This commit is contained in:
@@ -89,13 +89,21 @@ type RouterConvCallbackFunc = (router: any) => void;
|
||||
* @param meta.link ==> 外链地址
|
||||
* */
|
||||
export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCallbackFunc = null as any, parentPath: string = '/') {
|
||||
if (!routes) return [];
|
||||
return routes.map((item: any) => {
|
||||
if (!routes) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const routeItems = [];
|
||||
for (let item of routes) {
|
||||
if (!item.meta) {
|
||||
return item;
|
||||
}
|
||||
// 将json字符串的meta转为对象
|
||||
item.meta = JSON.parse(item.meta);
|
||||
if (item.meta.isHide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 将meta.comoponet 解析为route.component
|
||||
if (item.meta.component) {
|
||||
item.component = dynamicImport(dynamicViewsModules, item.meta.component);
|
||||
@@ -126,8 +134,10 @@ export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCall
|
||||
// 存在回调,则执行回调
|
||||
callbackFunc && callbackFunc(item);
|
||||
item.children && backEndRouterConverter(item.children, callbackFunc, item.path);
|
||||
return item;
|
||||
});
|
||||
routeItems.push(item);
|
||||
}
|
||||
|
||||
return routeItems;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,59 +1,87 @@
|
||||
<template>
|
||||
<div class="auth-cert-manage">
|
||||
<el-table :data="authCerts" max-height="180" stripe style="width: 100%" size="small">
|
||||
<el-table-column min-wdith="120px">
|
||||
<template #header>
|
||||
<el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="edit(null)"> </el-button>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-link @click="edit(scope.row)" type="primary" icon="edit"></el-link>
|
||||
<el-link class="ml5" v-auth="'machine:file:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete"></el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="name" label="名称" min-width="100px"> </el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="120px" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="ciphertextType" label="密文类型" width="100px">
|
||||
<template #default="scope">
|
||||
<EnumTag :value="scope.row.ciphertextType" :enums="AuthCertCiphertextTypeEnum" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="凭证类型" width="100px">
|
||||
<template #default="scope">
|
||||
<EnumTag :value="scope.row.type" :enums="AuthCertTypeEnum" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="凭证保存" v-model="state.dvisible" :show-close="false" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
|
||||
<el-form ref="acForm" :model="state.form" label-width="auto">
|
||||
<el-form-item prop="type" label="凭证类型" required>
|
||||
<el-select style="width: 100%" v-model="form.type" placeholder="请选择凭证类型">
|
||||
<el-option v-for="item in AuthCertTypeEnum" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
<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-form ref="acForm" :model="state.form" label-width="auto" :rules="rules">
|
||||
<el-form-item prop="ciphertextType" label="密文类型" required>
|
||||
<el-select
|
||||
:disabled="form.id && props.resourceEdit"
|
||||
v-model="form.ciphertextType"
|
||||
placeholder="请选择密文类型"
|
||||
@change="changeCiphertextType"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in AuthCertCiphertextTypeEnum"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-show="!props.disableCiphertextType?.includes(item.value)"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="ciphertextType" label="密文类型" required>
|
||||
<el-select style="width: 100%" v-model="form.ciphertextType" placeholder="请选择密文类型">
|
||||
<el-option v-for="item in AuthCertCiphertextTypeEnum" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
|
||||
<el-form-item prop="type" label="凭证类型" required>
|
||||
<el-select style="width: 100%" v-model="form.type" placeholder="请选择凭证类型">
|
||||
<el-option
|
||||
v-for="item in AuthCertTypeEnum"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-show="!props.disableType?.includes(item.value)"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="name" label="名称" required>
|
||||
<el-input :disabled="form.id" v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username" label="用户名">
|
||||
<el-input v-model="form.username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.Password.value" prop="ciphertext" label="密码">
|
||||
<el-input type="password" show-password clearable v-model.trim="form.ciphertext" placeholder="请输入密码" autocomplete="new-password">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="ciphertext" label="秘钥">
|
||||
<el-input type="textarea" :rows="5" v-model="form.ciphertext" placeholder="请将私钥文件内容拷贝至此"> </el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="passphrase" label="秘钥密码">
|
||||
<el-input type="password" v-model="form.extra.passphrase"> </el-input>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="form.ciphertextType != AuthCertCiphertextTypeEnum.Public.value">
|
||||
<el-form-item prop="username" label="用户名">
|
||||
<el-input :disabled="form.id && props.resourceEdit" v-model="form.username"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.Password.value" prop="ciphertext" label="密码">
|
||||
<el-input type="password" show-password clearable v-model.trim="form.ciphertext" placeholder="请输入密码" autocomplete="new-password">
|
||||
<template #suffix>
|
||||
<SvgIcon v-if="form.id" v-auth="'authcert:showciphertext'" @click="getCiphertext" name="search" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="ciphertext" label="秘钥">
|
||||
<div class="w100" style="position: relative">
|
||||
<SvgIcon
|
||||
v-if="form.id"
|
||||
v-auth="'authcert:showciphertext'"
|
||||
@click="getCiphertext"
|
||||
name="search"
|
||||
style="position: absolute; top: 5px; right: 5px; cursor: pointer; z-index: 1"
|
||||
/>
|
||||
<el-input type="textarea" :rows="5" v-model="form.ciphertext" placeholder="请将私钥文件内容拷贝至此"> </el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="passphrase" label="秘钥密码">
|
||||
<el-input type="password" show-password v-model="form.extra.passphrase"> </el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item label="公共凭证">
|
||||
<el-select default-first-option filterable v-model="form.ciphertext" @change="changePublicAuthCert">
|
||||
<el-option v-for="item in state.publicAuthCerts" :key="item.name" :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-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||
</el-form-item>
|
||||
@@ -69,21 +97,29 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref, toRefs } from 'vue';
|
||||
import { reactive, ref, toRefs, onMounted, watch } from 'vue';
|
||||
import { AuthCertTypeEnum, AuthCertCiphertextTypeEnum } from '../tag/enums';
|
||||
import { resourceAuthCertApi } from '../tag/api';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { resourceAuthCertApi } from '../tag/api';
|
||||
import { ResourceCodePattern } from '@/common/pattern';
|
||||
|
||||
const props = defineProps({
|
||||
resourceType: { type: Number },
|
||||
resourceCode: { type: String },
|
||||
authCert: {
|
||||
type: Object,
|
||||
},
|
||||
disableCiphertextType: {
|
||||
type: Array,
|
||||
},
|
||||
disableType: {
|
||||
type: Array,
|
||||
},
|
||||
// 是否为资源编辑该授权凭证
|
||||
resourceEdit: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const authCerts = defineModel<any>('modelValue', { required: true, default: [] });
|
||||
|
||||
const acForm: any = ref(null);
|
||||
|
||||
const DefaultForm = {
|
||||
id: null,
|
||||
name: '',
|
||||
@@ -94,47 +130,85 @@ const DefaultForm = {
|
||||
extra: {} as any,
|
||||
remark: '',
|
||||
};
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入凭证名',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
{
|
||||
pattern: ResourceCodePattern.pattern,
|
||||
message: ResourceCodePattern.message,
|
||||
trigger: ['blur'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const emit = defineEmits(['confirm']);
|
||||
|
||||
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
||||
|
||||
const acForm: any = ref(null);
|
||||
|
||||
const state = reactive({
|
||||
dvisible: false,
|
||||
params: [] as any,
|
||||
form: { ...DefaultForm },
|
||||
btnLoading: false,
|
||||
edit: false,
|
||||
publicAuthCerts: [] as any,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setForm(props.authCert);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.authCert,
|
||||
(val: any) => {
|
||||
setForm(val);
|
||||
}
|
||||
);
|
||||
|
||||
const setForm = (val: any) => {
|
||||
if (!val.extra) {
|
||||
val.extra = {};
|
||||
}
|
||||
state.form = val;
|
||||
if (state.form.ciphertextType == AuthCertCiphertextTypeEnum.Public.value) {
|
||||
getPublicAuthCerts();
|
||||
}
|
||||
};
|
||||
|
||||
const { form, btnLoading } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
getAuthCerts();
|
||||
});
|
||||
|
||||
const getAuthCerts = async () => {
|
||||
if (!props.resourceCode || !props.resourceType) {
|
||||
return;
|
||||
const changeCiphertextType = (val: any) => {
|
||||
if (val == AuthCertCiphertextTypeEnum.Public.value) {
|
||||
getPublicAuthCerts();
|
||||
}
|
||||
};
|
||||
|
||||
const changePublicAuthCert = (val: string) => {
|
||||
// 使用公共授权凭证名称赋值username
|
||||
state.form.username = val;
|
||||
};
|
||||
|
||||
const getPublicAuthCerts = async () => {
|
||||
const res = await resourceAuthCertApi.listByQuery.request({
|
||||
resourceCode: props.resourceCode,
|
||||
resourceType: props.resourceType,
|
||||
type: AuthCertTypeEnum.Public.value,
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
});
|
||||
authCerts.value = res.list?.reverse() || [];
|
||||
state.publicAuthCerts = res.list;
|
||||
};
|
||||
|
||||
const edit = (form: any) => {
|
||||
if (form) {
|
||||
state.form = form;
|
||||
state.edit = true;
|
||||
}
|
||||
state.dvisible = true;
|
||||
};
|
||||
|
||||
const deleteRow = (idx: any) => {
|
||||
authCerts.value.splice(idx, 1);
|
||||
const getCiphertext = async () => {
|
||||
const res = await resourceAuthCertApi.detail.request({ name: state.form.name });
|
||||
state.form.ciphertext = res.ciphertext;
|
||||
state.form.extra.passphrase = res.extra?.passphrase;
|
||||
};
|
||||
|
||||
const cancelEdit = () => {
|
||||
state.dvisible = false;
|
||||
dialogVisible.value = false;
|
||||
setTimeout(() => {
|
||||
state.form = { ...DefaultForm };
|
||||
}, 300);
|
||||
@@ -143,28 +217,7 @@ const cancelEdit = () => {
|
||||
const btnOk = async () => {
|
||||
acForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
const isEdit = state.form.id;
|
||||
if (isEdit || state.edit) {
|
||||
cancelEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (authCerts.value?.filter((x: any) => x.username == state.form.username || x.name == state.form.name).length > 0) {
|
||||
ElMessage.error('该名称或用户名已存在于该账号列表中');
|
||||
return;
|
||||
}
|
||||
const res = await resourceAuthCertApi.listByQuery.request({
|
||||
name: state.form.name,
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
});
|
||||
if (res.total) {
|
||||
ElMessage.error('该授权凭证名称已存在');
|
||||
return;
|
||||
}
|
||||
|
||||
authCerts.value.push(state.form);
|
||||
cancelEdit();
|
||||
emit('confirm', state.form);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div class="auth-cert-manage">
|
||||
<el-table :data="authCerts" max-height="180" stripe size="small">
|
||||
<el-table-column min-wdith="120px">
|
||||
<template #header>
|
||||
<el-button v-auth="'authcert:save'" class="ml0" type="primary" circle size="small" icon="Plus" @click="edit(null)"> </el-button>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button v-auth="'authcert:save'" @click="edit({ ...scope.row }, scope.$index)" type="primary" icon="edit" link></el-button>
|
||||
<el-button class="ml1" v-auth="'authcert:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete" link></el-button>
|
||||
|
||||
<el-button
|
||||
title="测试连接"
|
||||
:loading="props.testConnBtnLoading && scope.$index == state.idx"
|
||||
:disabled="props.testConnBtnLoading"
|
||||
class="ml1"
|
||||
type="success"
|
||||
@click="testConn(scope.row, scope.$index)"
|
||||
icon="Link"
|
||||
link
|
||||
></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="name" label="名称" show-overflow-tooltip min-width="100px"> </el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="120px" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="ciphertextType" label="密文类型" width="100px">
|
||||
<template #default="scope">
|
||||
<EnumTag :value="scope.row.ciphertextType" :enums="AuthCertCiphertextTypeEnum" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="凭证类型" width="100px">
|
||||
<template #default="scope">
|
||||
<EnumTag :value="scope.row.type" :enums="AuthCertTypeEnum" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<ResourceAuthCertEdit v-model:visible="state.dvisible" :auth-cert="state.form" @confirm="btnOk" :disable-type="[AuthCertTypeEnum.Public.value]" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { AuthCertTypeEnum, AuthCertCiphertextTypeEnum } from '../tag/enums';
|
||||
import { resourceAuthCertApi } from '../tag/api';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import ResourceAuthCertEdit from './ResourceAuthCertEdit.vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
resourceType: { type: Number },
|
||||
resourceCode: { type: String },
|
||||
testConnBtnLoading: { type: Boolean },
|
||||
});
|
||||
|
||||
const authCerts = defineModel<any>('modelValue', { required: true, default: [] });
|
||||
const emit = defineEmits(['testConn']);
|
||||
|
||||
const state = reactive({
|
||||
dvisible: false,
|
||||
params: [] as any,
|
||||
form: {},
|
||||
idx: -1,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getAuthCerts();
|
||||
});
|
||||
|
||||
const getAuthCerts = async () => {
|
||||
if (!props.resourceCode || !props.resourceType) {
|
||||
return;
|
||||
}
|
||||
const res = await resourceAuthCertApi.listByQuery.request({
|
||||
resourceCode: props.resourceCode,
|
||||
resourceType: props.resourceType,
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
});
|
||||
authCerts.value = res.list?.reverse() || [];
|
||||
};
|
||||
|
||||
const testConn = async (row: any, idx: number) => {
|
||||
state.idx = idx;
|
||||
emit('testConn', row);
|
||||
};
|
||||
|
||||
const edit = (form: any, idx = -1) => {
|
||||
state.idx = idx;
|
||||
if (form) {
|
||||
state.form = form;
|
||||
} else {
|
||||
state.form = { ciphertextType: AuthCertCiphertextTypeEnum.Password.value, type: AuthCertTypeEnum.Private.value, extra: {} };
|
||||
}
|
||||
state.dvisible = true;
|
||||
};
|
||||
|
||||
const deleteRow = (idx: any) => {
|
||||
authCerts.value.splice(idx, 1);
|
||||
};
|
||||
|
||||
const cancelEdit = () => {
|
||||
state.dvisible = false;
|
||||
setTimeout(() => {
|
||||
state.form = {};
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const btnOk = async (authCert: any) => {
|
||||
const isEdit = authCert.id;
|
||||
if (isEdit || state.idx > 0) {
|
||||
authCerts.value[state.idx] = authCert;
|
||||
cancelEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (authCerts.value?.filter((x: any) => x.username == authCert.username || x.name == authCert.name).length > 0) {
|
||||
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();
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -48,10 +48,15 @@
|
||||
|
||||
<el-divider content-position="left">账号</el-divider>
|
||||
<div>
|
||||
<ResourceAuthCertEdit v-model="form.authCerts" :resource-code="form.code" :resource-type="TagResourceTypeEnum.Machine.value" />
|
||||
<ResourceAuthCertTableEdit
|
||||
v-model="form.authCerts"
|
||||
:resource-code="form.code"
|
||||
:resource-type="TagResourceTypeEnum.Machine.value"
|
||||
:test-conn-btn-loading="testConnBtnLoading"
|
||||
@test-conn="testConn"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- <el-tab-pane label="其他配置" name="other"> -->
|
||||
<el-divider content-position="left">其他</el-divider>
|
||||
<el-form-item prop="enableRecorder" label="终端回放">
|
||||
<el-checkbox v-model="form.enableRecorder" :true-value="1" :false-value="-1"></el-checkbox>
|
||||
@@ -64,7 +69,6 @@
|
||||
|
||||
<template #footer>
|
||||
<div>
|
||||
<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>
|
||||
@@ -78,7 +82,7 @@ import { reactive, ref, toRefs, watch } from 'vue';
|
||||
import { machineApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import TagTreeSelect from '../component/TagTreeSelect.vue';
|
||||
import ResourceAuthCertEdit from '../component/ResourceAuthCertEdit.vue';
|
||||
import ResourceAuthCertTableEdit from '../component/ResourceAuthCertTableEdit.vue';
|
||||
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||
import { MachineProtocolEnum } from './enums';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
@@ -165,7 +169,7 @@ const state = reactive({
|
||||
dialogVisible: false,
|
||||
sshTunnelMachineList: [] as any,
|
||||
form: defaultForm,
|
||||
submitForm: {},
|
||||
submitForm: {} as any,
|
||||
pwd: '',
|
||||
});
|
||||
|
||||
@@ -187,7 +191,7 @@ watch(props, async (newValue: any) => {
|
||||
}
|
||||
});
|
||||
|
||||
const testConn = async () => {
|
||||
const testConn = async (authCert: any) => {
|
||||
machineForm.value.validate(async (valid: boolean) => {
|
||||
if (!valid) {
|
||||
ElMessage.error('请正确填写信息');
|
||||
@@ -195,6 +199,7 @@ const testConn = async () => {
|
||||
}
|
||||
|
||||
state.submitForm = getReqForm();
|
||||
state.submitForm.authCerts = [authCert];
|
||||
await testConnExec();
|
||||
ElMessage.success('连接成功');
|
||||
});
|
||||
|
||||
@@ -106,7 +106,9 @@
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
|
||||
<el-button :disabled="data.status == -1" type="warning" @click="serviceManager(data)" link>脚本</el-button>
|
||||
<el-button v-if="data.protocol == MachineProtocolEnum.Ssh.value" :disabled="data.status == -1" type="warning" @click="serviceManager(data)" link
|
||||
>脚本</el-button
|
||||
>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-dropdown @command="handleCommand">
|
||||
@@ -120,11 +122,19 @@
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ type: 'detail', data }"> 详情 </el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'rdp-blank', data }" v-if="data.protocol == 2"> RDP(新窗口) </el-dropdown-item>
|
||||
<el-dropdown-item :command="{ type: 'rdp-blank', data }" v-if="data.protocol == MachineProtocolEnum.Rdp.value">
|
||||
RDP(新窗口)
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'edit', data }" v-if="actionBtns[perms.updateMachine]"> 编辑 </el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'process', data }" :disabled="data.status == -1"> 进程 </el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="data.protocol == MachineProtocolEnum.Ssh.value"
|
||||
:command="{ type: 'process', data }"
|
||||
:disabled="data.status == -1"
|
||||
>
|
||||
进程
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'terminalRec', data }" v-if="actionBtns[perms.updateMachine] && data.enableRecorder == 1">
|
||||
终端回放
|
||||
@@ -177,7 +187,12 @@
|
||||
|
||||
<process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
|
||||
|
||||
<script-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
|
||||
<script-manage
|
||||
:title="serviceDialog.title"
|
||||
v-model:visible="serviceDialog.visible"
|
||||
v-model:machineId="serviceDialog.machineId"
|
||||
:auth-cert-name="serviceDialog.authCertName"
|
||||
/>
|
||||
|
||||
<file-conf-list
|
||||
:title="fileDialog.title"
|
||||
@@ -298,6 +313,7 @@ const state = reactive({
|
||||
serviceDialog: {
|
||||
visible: false,
|
||||
machineId: 0,
|
||||
authCertName: '',
|
||||
title: '',
|
||||
},
|
||||
processDialog: {
|
||||
@@ -468,9 +484,11 @@ const deleteMachine = async () => {
|
||||
};
|
||||
|
||||
const serviceManager = (row: any) => {
|
||||
const authCert = row.selectAuthCert;
|
||||
state.serviceDialog.machineId = row.id;
|
||||
state.serviceDialog.authCertName = authCert.name;
|
||||
state.serviceDialog.visible = true;
|
||||
state.serviceDialog.title = `${row.name} => ${row.ip}`;
|
||||
state.serviceDialog.title = `${row.name} => ${authCert.username}@${row.ip}`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,10 +111,16 @@
|
||||
|
||||
<process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
|
||||
|
||||
<script-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
|
||||
<script-manage
|
||||
:title="serviceDialog.title"
|
||||
v-model:visible="serviceDialog.visible"
|
||||
v-model:machineId="serviceDialog.machineId"
|
||||
:auth-cert-name="serviceDialog.authCertName"
|
||||
/>
|
||||
|
||||
<file-conf-list
|
||||
:title="fileDialog.title"
|
||||
:auth-cert-name="fileDialog.authCertName"
|
||||
v-model:visible="fileDialog.visible"
|
||||
v-model:machineId="fileDialog.machineId"
|
||||
:protocol="fileDialog.protocol"
|
||||
@@ -129,6 +135,7 @@
|
||||
>
|
||||
<machine-file
|
||||
:machine-id="state.filesystemDialog.machineId"
|
||||
:auth-cert-name="state.filesystemDialog.authCertName"
|
||||
:protocol="state.filesystemDialog.protocol"
|
||||
:file-id="state.filesystemDialog.fileId"
|
||||
:path="state.filesystemDialog.path"
|
||||
@@ -202,6 +209,7 @@ const state = reactive({
|
||||
serviceDialog: {
|
||||
visible: false,
|
||||
machineId: 0,
|
||||
authCertName: '',
|
||||
title: '',
|
||||
},
|
||||
processDialog: {
|
||||
@@ -213,10 +221,12 @@ const state = reactive({
|
||||
machineId: 0,
|
||||
protocol: 1,
|
||||
title: '',
|
||||
authCertName: '',
|
||||
},
|
||||
filesystemDialog: {
|
||||
visible: false,
|
||||
machineId: 0,
|
||||
authCertName: '',
|
||||
protocol: 1,
|
||||
title: '',
|
||||
fileId: 0,
|
||||
@@ -280,8 +290,17 @@ const NodeTypeMachine = new NodeType(MachineNodeType.Machine)
|
||||
})
|
||||
.withContextMenuItems([
|
||||
new ContextmenuItem('detail', '详情').withIcon('More').withOnClick((node: any) => showInfo(node.params)),
|
||||
new ContextmenuItem('status', '状态').withIcon('Compass').withOnClick((node: any) => showMachineStats(node.params)),
|
||||
new ContextmenuItem('process', '进程').withIcon('DataLine').withOnClick((node: any) => showProcess(node.params)),
|
||||
|
||||
new ContextmenuItem('status', '状态')
|
||||
.withIcon('Compass')
|
||||
.withHideFunc((node: any) => node.params.protocol != MachineProtocolEnum.Ssh.value)
|
||||
.withOnClick((node: any) => showMachineStats(node.params)),
|
||||
|
||||
new ContextmenuItem('process', '进程')
|
||||
.withIcon('DataLine')
|
||||
.withHideFunc((node: any) => node.params.protocol != MachineProtocolEnum.Ssh.value)
|
||||
.withOnClick((node: any) => showProcess(node.params)),
|
||||
|
||||
new ContextmenuItem('edit', '终端回放')
|
||||
.withIcon('Compass')
|
||||
.withOnClick((node: any) => showRec(node.params))
|
||||
@@ -296,7 +315,11 @@ const NodeTypeAuthCert = new NodeType(MachineNodeType.AuthCert)
|
||||
new ContextmenuItem('term', '打开终端').withIcon('Monitor').withOnClick((node: any) => openTerminal(node.params)),
|
||||
new ContextmenuItem('term-ex', '打开终端(新窗口)').withIcon('Monitor').withOnClick((node: any) => openTerminal(node.params, true)),
|
||||
new ContextmenuItem('files', '文件管理').withIcon('FolderOpened').withOnClick((node: any) => showFileManage(node.params)),
|
||||
new ContextmenuItem('scripts', '脚本管理').withIcon('Files').withOnClick((node: any) => serviceManager(node.params)),
|
||||
|
||||
new ContextmenuItem('scripts', '脚本管理')
|
||||
.withIcon('Files')
|
||||
.withHideFunc((node: any) => node.params.protocol != MachineProtocolEnum.Ssh.value)
|
||||
.withOnClick((node: any) => serviceManager(node.params)),
|
||||
]);
|
||||
|
||||
const openTerminal = (machine: any, ex?: boolean) => {
|
||||
@@ -356,9 +379,11 @@ const openTerminal = (machine: any, ex?: boolean) => {
|
||||
};
|
||||
|
||||
const serviceManager = (row: any) => {
|
||||
const authCert = row.selectAuthCert;
|
||||
state.serviceDialog.machineId = row.id;
|
||||
state.serviceDialog.visible = true;
|
||||
state.serviceDialog.title = `${row.name} => ${row.ip}`;
|
||||
state.serviceDialog.authCertName = authCert.name;
|
||||
state.serviceDialog.title = `${row.name} => ${authCert.username}@${row.ip}`;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -376,16 +401,19 @@ const search = async () => {
|
||||
};
|
||||
|
||||
const showFileManage = (selectionData: any) => {
|
||||
const authCert = selectionData.selectAuthCert;
|
||||
if (selectionData.protocol == 1) {
|
||||
state.fileDialog.visible = true;
|
||||
state.fileDialog.protocol = selectionData.protocol;
|
||||
state.fileDialog.machineId = selectionData.id;
|
||||
state.fileDialog.title = `${selectionData.name} => ${selectionData.ip}`;
|
||||
state.fileDialog.authCertName = authCert.name;
|
||||
state.fileDialog.title = `${selectionData.name} => ${authCert.username}@${selectionData.ip}`;
|
||||
}
|
||||
|
||||
if (selectionData.protocol == 2) {
|
||||
state.filesystemDialog.protocol = 2;
|
||||
state.filesystemDialog.machineId = selectionData.id;
|
||||
state.filesystemDialog.authCertName = authCert.name;
|
||||
state.filesystemDialog.fileId = selectionData.id;
|
||||
state.filesystemDialog.path = '/';
|
||||
state.filesystemDialog.title = `远程桌面文件管理`;
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
draggable
|
||||
append-to-body
|
||||
>
|
||||
<TerminalBody ref="terminal" :cmd="terminalDialog.cmd" :socket-url="getMachineTerminalSocketUrl(terminalDialog.machineId)" height="560px" />
|
||||
<TerminalBody ref="terminal" :cmd="terminalDialog.cmd" :socket-url="getMachineTerminalSocketUrl(props.authCertName)" height="560px" />
|
||||
</el-dialog>
|
||||
|
||||
<script-edit
|
||||
@@ -100,6 +100,7 @@ import { SearchItem } from '@/components/SearchForm';
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean },
|
||||
machineId: { type: Number },
|
||||
authCertName: { type: String },
|
||||
title: { type: String },
|
||||
});
|
||||
|
||||
@@ -143,7 +144,6 @@ const state = reactive({
|
||||
terminalDialog: {
|
||||
visible: false,
|
||||
cmd: '',
|
||||
machineId: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -195,6 +195,7 @@ const run = async (script: any) => {
|
||||
if (script.type == ScriptResultEnum.Result.value || noResult) {
|
||||
const res = await machineApi.runScript.request({
|
||||
machineId: props.machineId,
|
||||
ac: props.authCertName,
|
||||
scriptId: script.id,
|
||||
params: JSON.stringify(state.scriptParamsDialog.params),
|
||||
});
|
||||
@@ -215,7 +216,6 @@ const run = async (script: any) => {
|
||||
}
|
||||
state.terminalDialog.cmd = script;
|
||||
state.terminalDialog.visible = true;
|
||||
state.terminalDialog.machineId = props.machineId as any;
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -236,7 +236,6 @@ function templateResolve(template: string, param: any) {
|
||||
|
||||
const closeTermnial = () => {
|
||||
state.terminalDialog.visible = false;
|
||||
state.terminalDialog.machineId = 0;
|
||||
};
|
||||
|
||||
const editScript = (data: any) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ export const machineApi = {
|
||||
// 删除机器
|
||||
del: Api.newDelete('/machines/{id}'),
|
||||
scripts: Api.newGet('/machines/{machineId}/scripts'),
|
||||
runScript: Api.newGet('/machines/{machineId}/scripts/{scriptId}/run'),
|
||||
runScript: Api.newGet('/machines/scripts/{scriptId}/{ac}/run'),
|
||||
saveScript: Api.newPost('/machines/{machineId}/scripts'),
|
||||
deleteScript: Api.newDelete('/machines/{machineId}/scripts/{scriptId}'),
|
||||
// 获取配置文件列表
|
||||
@@ -48,13 +48,6 @@ export const machineApi = {
|
||||
termOpRec: Api.newGet('/machines/{id}/term-recs/{recId}'),
|
||||
};
|
||||
|
||||
export const authCertApi = {
|
||||
baseList: Api.newGet('/sys/authcerts/base'),
|
||||
list: Api.newGet('/sys/authcerts'),
|
||||
save: Api.newPost('/sys/authcerts'),
|
||||
delete: Api.newDelete('/sys/authcerts/{id}'),
|
||||
};
|
||||
|
||||
export const cronJobApi = {
|
||||
list: Api.newGet('/machine-cronjobs'),
|
||||
relateMachineIds: Api.newGet('/machine-cronjobs/machine-ids'),
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="500px" :destroy-on-close="true">
|
||||
<el-form ref="acForm" :rules="rules" :model="form" label-width="auto">
|
||||
<el-form-item prop="name" label="名称" required>
|
||||
<el-input v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="authMethod" label="认证方式" required>
|
||||
<el-select style="width: 100%" v-model="form.authMethod" placeholder="请选择认证方式">
|
||||
<el-option key="1" label="密码" :value="1"> </el-option>
|
||||
<el-option key="2" label="密钥" :value="2"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.authMethod == 1" prop="password" label="密码">
|
||||
<el-input type="password" show-password clearable v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.authMethod == 2" prop="password" label="秘钥">
|
||||
<el-input type="textarea" :rows="5" v-model="form.password" placeholder="请将私钥文件内容拷贝至此"> </el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.authMethod == 2" prop="passphrase" label="秘钥密码">
|
||||
<el-input type="password" v-model="form.passphrase"> </el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel()">取 消</el-button>
|
||||
<el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, watch } from 'vue';
|
||||
import { authCertApi } from '../api';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
data: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
|
||||
const acForm: any = ref(null);
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '授权凭证名称不能为空',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
dvisible: false,
|
||||
params: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
name: '',
|
||||
authMethod: 1,
|
||||
password: '',
|
||||
passphrase: '',
|
||||
remark: '',
|
||||
},
|
||||
btnLoading: false,
|
||||
});
|
||||
|
||||
const { dvisible, form, btnLoading } = toRefs(state);
|
||||
|
||||
watch(props, (newValue: any) => {
|
||||
state.dvisible = newValue.visible;
|
||||
if (newValue.data) {
|
||||
state.form = { ...newValue.data };
|
||||
} else {
|
||||
state.form = { authMethod: 1 } as any;
|
||||
state.params = [];
|
||||
}
|
||||
});
|
||||
|
||||
const cancel = () => {
|
||||
// 更新父组件visible prop对应的值为false
|
||||
emit('update:visible', false);
|
||||
// 若父组件有取消事件,则调用
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
acForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
state.btnLoading = true;
|
||||
try {
|
||||
await authCertApi.save.request(state.form);
|
||||
emit('val-change', state.form);
|
||||
cancel();
|
||||
} finally {
|
||||
state.btnLoading = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -1,106 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="authCertApi.list"
|
||||
:search-items="state.searchItems"
|
||||
v-model:query-form="query"
|
||||
:show-selection="true"
|
||||
v-model:selection-data="selectionData"
|
||||
:columns="state.columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button type="primary" icon="plus" @click="edit(false)">添加</el-button>
|
||||
<el-button :disabled="selectionData.length < 1" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button @click="edit(data)" type="primary" link>编辑 </el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<auth-cert-edit :title="editor.title" v-model:visible="editor.visible" :data="editor.authcert" @val-change="editChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, onMounted, ref, Ref } from 'vue';
|
||||
import AuthCertEdit from './AuthCertEdit.vue';
|
||||
import { authCertApi } from '../api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { AuthMethodEnum } from '../enums';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
|
||||
const pageTableRef: Ref<any> = ref(null);
|
||||
const state = reactive({
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 0,
|
||||
name: null,
|
||||
},
|
||||
searchItems: [SearchItem.input('name', '凭证名称')],
|
||||
columns: [
|
||||
TableColumn.new('name', '名称'),
|
||||
TableColumn.new('authMethod', '认证方式').typeTag(AuthMethodEnum),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('creator', '创建人'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
TableColumn.new('creator', '修改者'),
|
||||
TableColumn.new('createTime', '修改时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
|
||||
],
|
||||
selectionData: [],
|
||||
paramsDialog: {
|
||||
visible: false,
|
||||
config: null as any,
|
||||
params: {},
|
||||
paramsFormItem: [] as any,
|
||||
},
|
||||
editor: {
|
||||
title: '授权凭证保存',
|
||||
visible: false,
|
||||
authcert: {},
|
||||
},
|
||||
});
|
||||
|
||||
const { query, selectionData, editor } = toRefs(state);
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const editChange = () => {
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
};
|
||||
|
||||
const edit = (data: any) => {
|
||||
if (data) {
|
||||
state.editor.authcert = data;
|
||||
} else {
|
||||
state.editor.authcert = false;
|
||||
}
|
||||
|
||||
state.editor.visible = true;
|
||||
};
|
||||
|
||||
const deleteAc = async (data: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除该【${data.map((x: any) => x.name).join(', ')}授权凭证?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await authCertApi.delete.request({ id: data.map((x: any) => x.id).join(',') });
|
||||
ElMessage.success('删除成功');
|
||||
search();
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-select @change="changeValue" v-model="id" filterable placeholder="请选择授权凭证,可前往[机器管理->授权凭证]添加" style="width: 100%">
|
||||
<el-option v-for="ac in acs" :key="ac.id" :value="ac.id" :label="ac.name">
|
||||
<el-tag v-if="ac.authMethod == 1" type="success" size="small">密码</el-tag>
|
||||
<el-tag v-if="ac.authMethod == 2" size="small">密钥</el-tag>
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
{{ ac.name }}
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
{{ ac.remark }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, toRefs, onMounted } from 'vue';
|
||||
import { authCertApi } from '../api';
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Number],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
acs: [] as any,
|
||||
id: null as any,
|
||||
});
|
||||
|
||||
const { acs, id } = toRefs(state);
|
||||
|
||||
onMounted(async () => {
|
||||
await getAcs();
|
||||
if (props.modelValue) {
|
||||
state.id = props.modelValue;
|
||||
}
|
||||
});
|
||||
|
||||
const changeValue = (val: any) => {
|
||||
emit('update:modelValue', val);
|
||||
emit('change', val);
|
||||
};
|
||||
|
||||
const getAcs = async () => {
|
||||
const acs = await authCertApi.baseList.request({ pageSize: 100, type: 2 });
|
||||
state.acs = acs.list;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
132
mayfly_go_web/src/views/ops/tag/AuthCertList.vue
Executable file
132
mayfly_go_web/src/views/ops/tag/AuthCertList.vue
Executable file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="resourceAuthCertApi.listByQuery"
|
||||
:search-items="state.searchItems"
|
||||
v-model:query-form="query"
|
||||
:show-selection="true"
|
||||
v-model:selection-data="selectionData"
|
||||
:columns="state.columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="edit(false)">添加</el-button>
|
||||
<el-button v-auth="'authcert:del'" :disabled="disabledDelBtn" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button v-auth="'authcert:save'" v-if="data.type == AuthCertTypeEnum.Public.value" @click="edit(data)" type="primary" link>编辑 </el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<ResourceAuthCertEdit
|
||||
v-model:visible="editor.visible"
|
||||
:auth-cert="editor.authcert"
|
||||
@confirm="confirmSave"
|
||||
:resource-edit="false"
|
||||
:disable-ciphertext-type="[AuthCertCiphertextTypeEnum.Public.value]"
|
||||
:disable-type="[AuthCertTypeEnum.Private.value, AuthCertTypeEnum.PrivateDefault.value, AuthCertTypeEnum.Privileged.value]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, onMounted, ref, Ref, computed } from 'vue';
|
||||
import { resourceAuthCertApi } from './api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import { AuthCertCiphertextTypeEnum, AuthCertTypeEnum } from './enums';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import ResourceAuthCertEdit from '../component/ResourceAuthCertEdit.vue';
|
||||
|
||||
const pageTableRef: Ref<any> = ref(null);
|
||||
const state = reactive({
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 0,
|
||||
name: null,
|
||||
},
|
||||
searchItems: [
|
||||
SearchItem.input('name', '凭证名称'),
|
||||
SearchItem.select('type', '凭证类型').withEnum(AuthCertTypeEnum),
|
||||
SearchItem.select('ciphertextType', '密文类型').withEnum(AuthCertCiphertextTypeEnum),
|
||||
],
|
||||
columns: [
|
||||
TableColumn.new('name', '名称'),
|
||||
TableColumn.new('username', '用户名'),
|
||||
TableColumn.new('ciphertextType', '密文类型').typeTag(AuthCertCiphertextTypeEnum),
|
||||
TableColumn.new('type', '凭证类型').typeTag(AuthCertTypeEnum),
|
||||
TableColumn.new('resourceType', '资源类型').typeTag(TagResourceTypeEnum),
|
||||
TableColumn.new('resourceCode', '资源编号'),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('creator', '创建人'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
TableColumn.new('creator', '修改者'),
|
||||
TableColumn.new('createTime', '修改时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
|
||||
],
|
||||
selectionData: [],
|
||||
paramsDialog: {
|
||||
visible: false,
|
||||
config: null as any,
|
||||
params: {},
|
||||
paramsFormItem: [] as any,
|
||||
},
|
||||
editor: {
|
||||
title: '授权凭证保存',
|
||||
visible: false,
|
||||
authcert: {},
|
||||
},
|
||||
});
|
||||
|
||||
const { query, selectionData, editor } = toRefs(state);
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const disabledDelBtn = computed(() => {
|
||||
return state.selectionData.length < 1 || state.selectionData.find((item: any) => item.type != AuthCertTypeEnum.Public.value);
|
||||
});
|
||||
|
||||
const edit = (data: any) => {
|
||||
if (data) {
|
||||
state.editor.authcert = data;
|
||||
} else {
|
||||
state.editor.authcert = {
|
||||
type: AuthCertTypeEnum.Public.value,
|
||||
ciphertextType: AuthCertCiphertextTypeEnum.Password.value,
|
||||
extra: {},
|
||||
};
|
||||
}
|
||||
|
||||
state.editor.visible = true;
|
||||
};
|
||||
|
||||
const confirmSave = async (authCert: any) => {
|
||||
await resourceAuthCertApi.save.request(authCert);
|
||||
ElMessage.success('保存成功');
|
||||
state.editor.visible = false;
|
||||
search();
|
||||
};
|
||||
|
||||
const deleteAc = async (data: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除该【${data.map((x: any) => x.name).join(', ')}授权凭证?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await resourceAuthCertApi.delete.request({ id: data.map((x: any) => x.id).join(',') });
|
||||
ElMessage.success('删除成功');
|
||||
search();
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -24,20 +24,59 @@
|
||||
<template #action="{ data }">
|
||||
<el-button @click.prevent="showMembers(data)" link type="primary">成员</el-button>
|
||||
|
||||
<el-button @click.prevent="showTags(data)" link type="success">标签</el-button>
|
||||
|
||||
<el-button v-auth="'team:save'" @click.prevent="showSaveTeamDialog(data)" link type="warning">编辑</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<el-dialog width="400px" title="团队编辑" :before-close="cancelSaveTeam" v-model="addTeamDialog.visible">
|
||||
<el-drawer title="团队编辑" v-model="addTeamDialog.visible" :before-close="cancelSaveTeam" :destroy-on-close="true" :close-on-click-modal="false">
|
||||
<template #header>
|
||||
<DrawerHeader header="团队编辑" :back="cancelSaveTeam" />
|
||||
</template>
|
||||
|
||||
<el-form ref="teamForm" :model="addTeamDialog.form" label-width="auto">
|
||||
<el-form-item prop="name" label="团队名" required>
|
||||
<el-input v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
|
||||
<el-input :disabled="addTeamDialog.form.id" v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="addTeamDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="tag" label="标签">
|
||||
<el-scrollbar style="height: calc(100vh - 300px); border: 1px solid var(--el-border-color)">
|
||||
<el-tree
|
||||
ref="tagTreeRef"
|
||||
style="width: 100%"
|
||||
:data="state.tags"
|
||||
:default-expanded-keys="state.addTeamDialog.form.tags"
|
||||
:default-checked-keys="state.addTeamDialog.form.tags"
|
||||
multiple
|
||||
:render-after-expand="true"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'codePath',
|
||||
children: 'children',
|
||||
}"
|
||||
@check="tagTreeNodeCheck"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" />
|
||||
|
||||
<span class="font13 ml5">
|
||||
{{ data.code }}
|
||||
<span style="color: #3c8dbc">【</span>
|
||||
{{ data.name }}
|
||||
<span style="color: #3c8dbc">】</span>
|
||||
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }} </el-tag>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -45,48 +84,7 @@
|
||||
<el-button @click="saveTeam" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog" v-model="showTagDialog.visible">
|
||||
<el-form label-width="auto">
|
||||
<el-form-item prop="tag" label="标签">
|
||||
<el-tree-select
|
||||
ref="tagTreeRef"
|
||||
style="width: 100%"
|
||||
v-model="showTagDialog.tagTreeTeams"
|
||||
:data="showTagDialog.tags"
|
||||
:default-expanded-keys="showTagDialog.tagTreeTeams"
|
||||
multiple
|
||||
:render-after-expand="true"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
:props="showTagDialog.props"
|
||||
@check="tagTreeNodeCheck"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" />
|
||||
|
||||
<span class="font13 ml5">
|
||||
{{ data.code }}
|
||||
<span style="color: #3c8dbc">【</span>
|
||||
{{ data.name }}
|
||||
<span style="color: #3c8dbc">】</span>
|
||||
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }} </el-tag>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeTagDialog()">取 消</el-button>
|
||||
<el-button v-auth="'team:tag:save'" @click="saveTags()" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog @open="setMemebers" width="50%" :title="showMemDialog.title" v-model="showMemDialog.visible">
|
||||
<page-table
|
||||
@@ -132,6 +130,7 @@ import { SearchItem } from '@/components/SearchForm';
|
||||
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import EnumValue from '@/common/Enum';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
|
||||
const teamForm: any = ref(null);
|
||||
const tagTreeRef: any = ref(null);
|
||||
@@ -142,17 +141,20 @@ const searchItems = [SearchItem.input('name', '团队名称')];
|
||||
const columns = [
|
||||
TableColumn.new('name', '团队名称'),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('creator', '创建者'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
TableColumn.new('creator', '创建人'),
|
||||
TableColumn.new('modifier', '修改者'),
|
||||
TableColumn.new('updateTime', '修改时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(120).fixedRight().alignCenter(),
|
||||
];
|
||||
|
||||
const state = reactive({
|
||||
currentEditPermissions: false,
|
||||
tags: [],
|
||||
addTeamDialog: {
|
||||
title: '新增团队',
|
||||
visible: false,
|
||||
form: { id: 0, name: '', remark: '' },
|
||||
form: { id: 0, name: '', remark: '', tags: [] },
|
||||
},
|
||||
query: {
|
||||
pageNum: 1,
|
||||
@@ -188,21 +190,9 @@ const state = reactive({
|
||||
},
|
||||
accounts: Array(),
|
||||
},
|
||||
showTagDialog: {
|
||||
title: '项目信息',
|
||||
visible: false,
|
||||
tags: [],
|
||||
teamId: 0,
|
||||
tagTreeTeams: [] as any,
|
||||
props: {
|
||||
value: 'id',
|
||||
label: 'codePath',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { query, addTeamDialog, selectionData, showMemDialog, showTagDialog } = toRefs(state);
|
||||
const { query, addTeamDialog, selectionData, showMemDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
@@ -210,13 +200,19 @@ const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const showSaveTeamDialog = (data: any) => {
|
||||
const showSaveTeamDialog = async (data: any) => {
|
||||
if (state.tags.length == 0) {
|
||||
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 });
|
||||
}
|
||||
|
||||
state.addTeamDialog.visible = true;
|
||||
};
|
||||
|
||||
@@ -224,6 +220,7 @@ const saveTeam = async () => {
|
||||
teamForm.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
const form = state.addTeamDialog.form;
|
||||
form.tags = tagTreeRef.value.getCheckedKeys(false);
|
||||
await tagApi.saveTeam.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
@@ -292,32 +289,6 @@ const cancelAddMember = () => {
|
||||
state.showMemDialog.addVisible = false;
|
||||
};
|
||||
|
||||
/********** 标签相关 ***********/
|
||||
|
||||
const showTags = async (team: any) => {
|
||||
state.showTagDialog.tags = await tagApi.getTagTrees.request(null);
|
||||
state.showTagDialog.tagTreeTeams = await tagApi.getTeamTagIds.request({ teamId: team.id });
|
||||
state.showTagDialog.title = `[${team.name}] 团队标签信息`;
|
||||
state.showTagDialog.teamId = team.id;
|
||||
state.showTagDialog.visible = true;
|
||||
};
|
||||
|
||||
const closeTagDialog = () => {
|
||||
state.showTagDialog.visible = false;
|
||||
setTimeout(() => {
|
||||
state.showTagDialog.tagTreeTeams = [];
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const saveTags = async () => {
|
||||
await tagApi.saveTeamTags.request({
|
||||
teamId: state.showTagDialog.teamId,
|
||||
tagIds: state.showTagDialog.tagTreeTeams,
|
||||
});
|
||||
ElMessage.success('保存成功');
|
||||
closeTagDialog();
|
||||
};
|
||||
|
||||
const tagTreeNodeCheck = () => {
|
||||
// const node = tagTreeRef.value.getNode(data.id);
|
||||
// console.log(node);
|
||||
@@ -348,4 +319,4 @@ const tagTreeNodeCheck = () => {
|
||||
// console.log(state.showTagDialog.tagTreeTeams);
|
||||
// }
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -18,9 +18,11 @@ export const tagApi = {
|
||||
delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'),
|
||||
|
||||
getTeamTagIds: Api.newGet('/teams/{teamId}/tags'),
|
||||
saveTeamTags: Api.newPost('/teams/{teamId}/tags'),
|
||||
};
|
||||
|
||||
export const resourceAuthCertApi = {
|
||||
detail: Api.newGet('/auth-certs/detail'),
|
||||
listByQuery: Api.newGet('/auth-certs'),
|
||||
save: Api.newPost('/auth-certs'),
|
||||
delete: Api.newDelete('/auth-certs/{id}'),
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ 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(),
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/api/form"
|
||||
"mayfly-go/internal/machine/api/vo"
|
||||
"mayfly-go/internal/machine/application"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AuthCert struct {
|
||||
AuthCertApp application.AuthCert `inject:""`
|
||||
}
|
||||
|
||||
func (ac *AuthCert) BaseAuthCerts(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage(rc, new(entity.AuthCertQuery))
|
||||
res, err := ac.AuthCertApp.GetPageList(queryCond, page, new([]vo.AuthCertBaseVO))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (ac *AuthCert) AuthCerts(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage(rc, new(entity.AuthCertQuery))
|
||||
|
||||
res := new([]*entity.AuthCert)
|
||||
pageRes, err := ac.AuthCertApp.GetPageList(queryCond, page, res)
|
||||
biz.ErrIsNil(err)
|
||||
for _, r := range *res {
|
||||
r.PwdDecrypt()
|
||||
}
|
||||
rc.ResData = pageRes
|
||||
}
|
||||
|
||||
func (c *AuthCert) SaveAuthCert(rc *req.Ctx) {
|
||||
acForm := &form.AuthCertForm{}
|
||||
ac := req.BindJsonAndCopyTo(rc, acForm, new(entity.AuthCert))
|
||||
|
||||
// 脱敏记录日志
|
||||
acForm.Passphrase = "***"
|
||||
acForm.Password = "***"
|
||||
rc.ReqParam = acForm
|
||||
|
||||
biz.ErrIsNil(c.AuthCertApp.Save(rc.MetaCtx, ac))
|
||||
}
|
||||
|
||||
func (c *AuthCert) Delete(rc *req.Ctx) {
|
||||
idsStr := rc.PathParam("id")
|
||||
rc.ReqParam = idsStr
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
c.AuthCertApp.DeleteById(rc.MetaCtx, uint64(value))
|
||||
}
|
||||
}
|
||||
@@ -33,17 +33,6 @@ type MachineScriptForm struct {
|
||||
Script string `json:"script" binding:"required"`
|
||||
}
|
||||
|
||||
// 授权凭证
|
||||
type AuthCertForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
AuthMethod int8 `json:"authMethod" binding:"required"` // 1.密码 2.秘钥
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"` // 密码or私钥
|
||||
Passphrase string `json:"passphrase"` // 私钥口令
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// 机器记录任务
|
||||
type MachineCronJobForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
|
||||
@@ -50,10 +50,9 @@ func (m *MachineScript) DeleteMachineScript(rc *req.Ctx) {
|
||||
|
||||
func (m *MachineScript) RunMachineScript(rc *req.Ctx) {
|
||||
scriptId := GetMachineScriptId(rc)
|
||||
machineId := GetMachineId(rc)
|
||||
ac := GetMachineAc(rc)
|
||||
ms, err := m.MachineScriptApp.GetById(new(entity.MachineScript), scriptId, "MachineId", "Name", "Script")
|
||||
biz.ErrIsNil(err, "该脚本不存在")
|
||||
biz.IsTrue(ms.MachineId == application.Common_Script_Machine_Id || ms.MachineId == machineId, "该脚本不属于该机器")
|
||||
|
||||
script := ms.Script
|
||||
// 如果有脚本参数,则用脚本参数替换脚本中的模板占位符参数
|
||||
@@ -61,7 +60,7 @@ func (m *MachineScript) RunMachineScript(rc *req.Ctx) {
|
||||
script, err = stringx.TemplateParse(ms.Script, jsonx.ToMap(params))
|
||||
biz.ErrIsNilAppendErr(err, "脚本模板参数解析失败: %s")
|
||||
}
|
||||
cli, err := m.MachineApp.GetCli(machineId)
|
||||
cli, err := m.MachineApp.GetCliByAc(ac)
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
|
||||
|
||||
@@ -5,13 +5,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 授权凭证基础信息
|
||||
type AuthCertBaseVO struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AuthMethod int8 `json:"authMethod"`
|
||||
}
|
||||
|
||||
type MachineVO struct {
|
||||
tagentity.ResourceTags // 标签信息
|
||||
tagentity.AuthCerts // 授权凭证信息
|
||||
|
||||
@@ -8,7 +8,6 @@ func InitIoc() {
|
||||
ioc.Register(new(machineAppImpl), ioc.WithComponentName("MachineApp"))
|
||||
ioc.Register(new(machineFileAppImpl), ioc.WithComponentName("MachineFileApp"))
|
||||
ioc.Register(new(machineScriptAppImpl), ioc.WithComponentName("MachineScriptApp"))
|
||||
ioc.Register(new(authCertAppImpl), ioc.WithComponentName("AuthCertApp"))
|
||||
ioc.Register(new(machineCronJobAppImpl), ioc.WithComponentName("MachineCronJobApp"))
|
||||
ioc.Register(new(machineTermOpAppImpl), ioc.WithComponentName("MachineTermOpApp"))
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type AuthCert interface {
|
||||
base.App[*entity.AuthCert]
|
||||
|
||||
GetPageList(condition *entity.AuthCertQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
Save(ctx context.Context, ac *entity.AuthCert) error
|
||||
|
||||
GetByIds(ids ...uint64) []*entity.AuthCert
|
||||
}
|
||||
|
||||
type authCertAppImpl struct {
|
||||
base.AppImpl[*entity.AuthCert, repository.AuthCert]
|
||||
}
|
||||
|
||||
// 注入AuthCertRepo
|
||||
func (a *authCertAppImpl) InjectAuthCertRepo(repo repository.AuthCert) {
|
||||
a.Repo = repo
|
||||
}
|
||||
|
||||
func (a *authCertAppImpl) GetPageList(condition *entity.AuthCertQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
return a.GetRepo().GetPageList(condition, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (a *authCertAppImpl) Save(ctx context.Context, ac *entity.AuthCert) error {
|
||||
oldAc := &entity.AuthCert{Name: ac.Name}
|
||||
err := a.GetBy(oldAc, "Id", "Name")
|
||||
|
||||
ac.PwdEncrypt()
|
||||
if ac.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该凭证名已存在")
|
||||
}
|
||||
return a.Insert(ctx, ac)
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldAc.Id != ac.Id {
|
||||
return errorx.NewBiz("该凭证名已存在")
|
||||
}
|
||||
return a.UpdateById(ctx, ac)
|
||||
}
|
||||
|
||||
func (a *authCertAppImpl) GetByIds(ids ...uint64) []*entity.AuthCert {
|
||||
acs := new([]*entity.AuthCert)
|
||||
a.GetByIdIn(acs, ids)
|
||||
return *acs
|
||||
}
|
||||
@@ -18,8 +18,6 @@ import (
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
"time"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type SaveMachineParam struct {
|
||||
@@ -164,6 +162,15 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
||||
|
||||
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
|
||||
}
|
||||
authCert = publicAuthCert
|
||||
}
|
||||
|
||||
mi, err := m.toMi(me, authCert)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -338,7 +345,7 @@ func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAu
|
||||
|
||||
mi.Username = authCert.Username
|
||||
mi.Password = authCert.Ciphertext
|
||||
mi.Passphrase = cast.ToString(authCert.Extra["passphrase"])
|
||||
mi.Passphrase = authCert.GetExtraString(tagentity.ExtraKeyPassphrase)
|
||||
mi.AuthMethod = int8(authCert.CiphertextType)
|
||||
|
||||
// 使用了ssh隧道,则将隧道机器信息也附上
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mayfly-go/internal/common/utils"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
// 授权凭证
|
||||
type AuthCert struct {
|
||||
model.Model
|
||||
|
||||
Name string `json:"name"`
|
||||
AuthMethod int8 `json:"authMethod"` // 1.密码 2.秘钥
|
||||
Password string `json:"password" gorm:"column:password;type:varchar(4200)"` // 密码or私钥
|
||||
Passphrase string `json:"passphrase"` // 私钥口令
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func (ac *AuthCert) TableName() string {
|
||||
return "t_auth_cert"
|
||||
}
|
||||
|
||||
const (
|
||||
AuthCertAuthMethodPassword int8 = 1 // 密码
|
||||
MachineAuthMethodPublicKey int8 = 2 // 密钥
|
||||
|
||||
AuthCertTypePrivate int8 = 1
|
||||
AuthCertTypePublic int8 = 2
|
||||
)
|
||||
|
||||
// PwdEncrypt 密码加密
|
||||
func (ac *AuthCert) PwdEncrypt() error {
|
||||
password, err := utils.PwdAesEncrypt(ac.Password)
|
||||
if err != nil {
|
||||
return errors.New("加密授权凭证密码失败")
|
||||
}
|
||||
passphrase, err := utils.PwdAesEncrypt(ac.Passphrase)
|
||||
if err != nil {
|
||||
return errors.New("加密授权凭证私钥失败")
|
||||
}
|
||||
ac.Password = password
|
||||
ac.Passphrase = passphrase
|
||||
return nil
|
||||
}
|
||||
|
||||
// PwdDecrypt 密码解密
|
||||
func (ac *AuthCert) PwdDecrypt() error {
|
||||
password, err := utils.PwdAesDecrypt(ac.Password)
|
||||
if err != nil {
|
||||
return errors.New("解密授权凭证密码失败")
|
||||
}
|
||||
passphrase, err := utils.PwdAesDecrypt(ac.Passphrase)
|
||||
if err != nil {
|
||||
return errors.New("解密授权凭证私钥失败")
|
||||
}
|
||||
ac.Password = password
|
||||
ac.Passphrase = passphrase
|
||||
return nil
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type AuthCert interface {
|
||||
base.Repo[*entity.AuthCert]
|
||||
|
||||
GetPageList(condition *entity.AuthCertQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
// GetByIds(ids ...uint64) []*entity.AuthCert
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type authCertRepoImpl struct {
|
||||
base.RepoImpl[*entity.AuthCert]
|
||||
}
|
||||
|
||||
func newAuthCertRepo() repository.AuthCert {
|
||||
return &authCertRepoImpl{base.RepoImpl[*entity.AuthCert]{M: new(entity.AuthCert)}}
|
||||
}
|
||||
|
||||
func (m *authCertRepoImpl) GetPageList(condition *entity.AuthCertQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(new(entity.AuthCert)).WithCondModel(condition).WithOrderBy(orderBy...)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
// func (m *authCertRepoImpl) GetByIds(ids ...uint64) []*entity.AuthCert {
|
||||
// acs := new([]*entity.AuthCert)
|
||||
// gormx.GetByIdIn(new(entity.AuthCert), acs, ids)
|
||||
// return *acs
|
||||
// }
|
||||
@@ -8,7 +8,6 @@ func InitIoc() {
|
||||
ioc.Register(newMachineRepo(), ioc.WithComponentName("MachineRepo"))
|
||||
ioc.Register(newMachineFileRepo(), ioc.WithComponentName("MachineFileRepo"))
|
||||
ioc.Register(newMachineScriptRepo(), ioc.WithComponentName("MachineScriptRepo"))
|
||||
ioc.Register(newAuthCertRepo(), ioc.WithComponentName("AuthCertRepo"))
|
||||
ioc.Register(newMachineCronJobRepo(), ioc.WithComponentName("MachineCronJobRepo"))
|
||||
ioc.Register(newMachineCronJobExecRepo(), ioc.WithComponentName("MachineCronJobExecRepo"))
|
||||
ioc.Register(newMachineCronJobRelateRepo(), ioc.WithComponentName("MachineCronJobRelateRepo"))
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitAuthCertRouter(router *gin.RouterGroup) {
|
||||
ag := router.Group("sys/authcerts")
|
||||
|
||||
r := new(api.AuthCert)
|
||||
biz.ErrIsNil(ioc.Inject(r))
|
||||
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("", r.AuthCerts).RequiredPermissionCode("authcert"),
|
||||
|
||||
// 基础授权凭证信息,不包含密码等
|
||||
req.NewGet("base", r.BaseAuthCerts),
|
||||
|
||||
req.NewPost("", r.SaveAuthCert).Log(req.NewLogSave("保存授权凭证")).RequiredPermissionCode("authcert:save"),
|
||||
|
||||
req.NewDelete(":id", r.Delete).Log(req.NewLogSave("删除授权凭证")).RequiredPermissionCode("authcert:del"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(ag, reqs[:])
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func InitMachineScriptRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewDelete(":machineId/scripts/:scriptId", ms.DeleteMachineScript).Log(req.NewLogSave("机器-删除脚本")).RequiredPermissionCode("machine:script:del"),
|
||||
|
||||
req.NewGet(":machineId/scripts/:scriptId/run", ms.RunMachineScript).Log(req.NewLogSave("机器-执行脚本")).RequiredPermissionCode("machine:script:run"),
|
||||
req.NewGet("scripts/:scriptId/:ac/run", ms.RunMachineScript).Log(req.NewLogSave("机器-执行脚本")).RequiredPermissionCode("machine:script:run"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(machines, reqs[:])
|
||||
|
||||
@@ -6,6 +6,5 @@ func Init(router *gin.RouterGroup) {
|
||||
InitMachineRouter(router)
|
||||
InitMachineFileRouter(router)
|
||||
InitMachineScriptRouter(router)
|
||||
InitAuthCertRouter(router)
|
||||
InitMachineCronJobRouter(router)
|
||||
}
|
||||
|
||||
20
server/internal/tag/api/form/auth_cert.go
Normal file
20
server/internal/tag/api/form/auth_cert.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
// 授权凭证
|
||||
type AuthCertForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required"` // 名称
|
||||
ResourceCode string `json:"resourceCode"` // 资源编号
|
||||
ResourceType int8 `json:"resourceType"` // 资源类型
|
||||
Username string `json:"username"` // 用户名
|
||||
Ciphertext string `json:"ciphertext"` // 密文
|
||||
CiphertextType entity.AuthCertCiphertextType `json:"ciphertextType" binding:"required"` // 密文类型
|
||||
Extra model.Map[string, any] `json:"extra"` // 账号需要的其他额外信息(如秘钥口令等)
|
||||
Type entity.AuthCertType `json:"type" binding:"required"` // 凭证类型
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package form
|
||||
|
||||
type TagTreeTeam struct {
|
||||
TeamId uint64 `json:"teamId"`
|
||||
TagIds []uint64 `json:"tagIds"`
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package form
|
||||
|
||||
type TeamMember struct {
|
||||
TeamId uint64 `json:"teamId"`
|
||||
TeamId uint64 `json:"teamId" binding:"required"`
|
||||
AccountIds []uint64 `json:"accountIds"`
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/api/form"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type ResourceAuthCert struct {
|
||||
@@ -23,7 +28,48 @@ func (r *ResourceAuthCert) ListByQuery(rc *req.Ctx) {
|
||||
res, err := r.ResourceAuthCertApp.PageQuery(cond, rc.GetPageParam(), &racs)
|
||||
biz.ErrIsNil(err)
|
||||
for _, rac := range racs {
|
||||
rac.CiphertextDecrypt()
|
||||
rac.CiphertextClear()
|
||||
}
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *ResourceAuthCert) GetCompleteAuthCert(rc *req.Ctx) {
|
||||
acName := rc.Query("name")
|
||||
biz.NotEmpty(acName, "授权凭证名不能为空")
|
||||
res := &entity.ResourceAuthCert{Name: acName}
|
||||
err := r.ResourceAuthCertApp.GetBy(res)
|
||||
biz.ErrIsNil(err)
|
||||
res.CiphertextDecrypt()
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (c *ResourceAuthCert) SaveAuthCert(rc *req.Ctx) {
|
||||
acForm := &form.AuthCertForm{}
|
||||
ac := req.BindJsonAndCopyTo(rc, acForm, new(entity.ResourceAuthCert))
|
||||
|
||||
// 脱敏记录日志
|
||||
acForm.Ciphertext = "***"
|
||||
rc.ReqParam = acForm
|
||||
|
||||
biz.ErrIsNil(c.ResourceAuthCertApp.SavePulbicAuthCert(rc.MetaCtx, ac))
|
||||
}
|
||||
|
||||
func (c *ResourceAuthCert) Delete(rc *req.Ctx) {
|
||||
idsStr := rc.PathParam("id")
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
acIds := make([]uint64, 0)
|
||||
acNames := make([]string, 0)
|
||||
for _, v := range ids {
|
||||
id := cast.ToUint64(v)
|
||||
rac, err := c.ResourceAuthCertApp.GetById(new(entity.ResourceAuthCert), id)
|
||||
biz.ErrIsNil(err, "存在错误授权凭证id")
|
||||
biz.IsTrue(rac.Type == entity.AuthCertTypePublic, "只允许删除公共授权凭证")
|
||||
biz.IsTrue(c.ResourceAuthCertApp.CountByCond(&entity.ResourceAuthCert{Ciphertext: rac.Name}) == 0, "[%s]该授权凭证已被关联", rac.Name)
|
||||
acIds = append(acIds, id)
|
||||
acNames = append(acNames, rac.Name)
|
||||
}
|
||||
|
||||
rc.ReqParam = acNames
|
||||
biz.ErrIsNil(c.ResourceAuthCertApp.DeleteByWheres(rc.MetaCtx, collx.M{"id in ?": acIds}))
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type Team struct {
|
||||
@@ -30,22 +30,9 @@ func (p *Team) GetTeams(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (p *Team) SaveTeam(rc *req.Ctx) {
|
||||
team := req.BindJsonAndValid(rc, new(entity.Team))
|
||||
team := req.BindJsonAndValid(rc, new(application.SaveTeamParam))
|
||||
rc.ReqParam = team
|
||||
isAdd := team.Id == 0
|
||||
|
||||
loginAccount := rc.GetLoginAccount()
|
||||
p.TeamApp.Save(rc.MetaCtx, team)
|
||||
|
||||
// 如果是新增团队则默认将自己加入该团队
|
||||
if isAdd {
|
||||
teamMem := &entity.TeamMember{}
|
||||
teamMem.AccountId = loginAccount.Id
|
||||
teamMem.Username = loginAccount.Username
|
||||
teamMem.TeamId = team.Id
|
||||
|
||||
p.TeamApp.SaveMember(rc.MetaCtx, teamMem)
|
||||
}
|
||||
biz.ErrIsNil(p.TeamApp.Save(rc.MetaCtx, team))
|
||||
}
|
||||
|
||||
func (p *Team) DelTeam(rc *req.Ctx) {
|
||||
@@ -54,9 +41,7 @@ func (p *Team) DelTeam(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
p.TeamApp.Delete(rc.MetaCtx, uint64(value))
|
||||
p.TeamApp.Delete(rc.MetaCtx, cast.ToUint64(v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,28 +94,3 @@ func (p *Team) DelTeamMember(rc *req.Ctx) {
|
||||
func (p *Team) GetTagIds(rc *req.Ctx) {
|
||||
rc.ResData = p.TeamApp.ListTagIds(uint64(rc.PathParamInt("id")))
|
||||
}
|
||||
|
||||
// 保存团队关联标签信息
|
||||
func (p *Team) SaveTags(rc *req.Ctx) {
|
||||
form := req.BindJsonAndValid(rc, new(form.TagTreeTeam))
|
||||
teamId := form.TeamId
|
||||
|
||||
// 将[]uint64转为[]any
|
||||
oIds := p.TeamApp.ListTagIds(teamId)
|
||||
// 比较新旧两合集
|
||||
addIds, delIds, _ := collx.ArrayCompare(form.TagIds, oIds)
|
||||
|
||||
for _, v := range addIds {
|
||||
tagId := v
|
||||
tag, err := p.TagTreeApp.GetById(new(entity.TagTree), tagId)
|
||||
biz.ErrIsNil(err, "存在非法标签id")
|
||||
|
||||
ptt := &entity.TagTreeTeam{TeamId: teamId, TagId: tagId, TagPath: tag.CodePath}
|
||||
p.TeamApp.SaveTag(rc.MetaCtx, ptt)
|
||||
}
|
||||
for _, v := range delIds {
|
||||
p.TeamApp.DeleteTag(rc.MetaCtx, teamId, v)
|
||||
}
|
||||
|
||||
rc.ReqParam = form
|
||||
}
|
||||
|
||||
@@ -20,3 +20,16 @@ type ResourceAuthCert struct {
|
||||
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
}
|
||||
|
||||
// 授权凭证基础信息
|
||||
type AuthCertBaseVO struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"` // 名称(全局唯一)
|
||||
|
||||
ResourceCode string `json:"resourceCode"` // 资源编号
|
||||
ResourceType int8 `json:"resourceType"` // 资源类型
|
||||
Type entity.AuthCertType `json:"type"` // 凭证类型
|
||||
Username string `json:"username"` // 用户名
|
||||
CiphertextType entity.AuthCertCiphertextType `json:"ciphertextType"` // 密文类型
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
@@ -27,6 +28,9 @@ type ResourceAuthCert interface {
|
||||
// SaveAuthCert 保存资源授权凭证信息,不可放于事务中
|
||||
SaveAuthCert(ctx context.Context, param *SaveAuthCertParam) error
|
||||
|
||||
// SavePublicAuthCert 保存公共授权凭证信息
|
||||
SavePulbicAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
||||
|
||||
// GetAuthCert 根据授权凭证名称获取授权凭证
|
||||
GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error)
|
||||
|
||||
@@ -68,6 +72,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
|
||||
// 删除授权信息
|
||||
if len(resourceAuthCerts) == 0 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-删除所有关联的授权凭证信息", resourceType, resourceCode)
|
||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,6 +116,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
|
||||
var adds, dels, unmodifys []string
|
||||
if len(oldAuthCert) == 0 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
|
||||
adds = collx.MapKeys(name2AuthCert)
|
||||
} else {
|
||||
oldNames := collx.ArrayMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||
@@ -128,6 +134,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
|
||||
// 处理新增的授权凭证
|
||||
if len(addAuthCerts) > 0 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, adds)
|
||||
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -140,20 +147,24 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
return tag.Id
|
||||
})
|
||||
|
||||
// 保存授权凭证类型的资源标签
|
||||
for _, authCert := range addAuthCerts {
|
||||
if err := r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{
|
||||
ResourceCode: authCert.Name,
|
||||
ResourceType: authCertTagType,
|
||||
ResourceName: authCert.Username,
|
||||
TagIds: resourceTagIds,
|
||||
}); err != nil {
|
||||
return err
|
||||
if len(resourceTagIds) > 0 {
|
||||
// 保存授权凭证类型的资源标签
|
||||
for _, authCert := range addAuthCerts {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%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 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, del)
|
||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -171,6 +182,12 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
if unmodifyAc.Id == 0 {
|
||||
continue
|
||||
}
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
||||
// 置空用户名,不允许修改(TagTree的name关联了该值,修改了会造成数据不一致,懒得处理该同步。要修改用户名可通过重新删除旧凭证后添加一个授权凭证或通过关联公共凭证(公共凭证可修改用户名))
|
||||
if unmodifyAc.CiphertextType != entity.AuthCertCiphertextTypePublic {
|
||||
unmodifyAc.Username = ""
|
||||
}
|
||||
|
||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -179,6 +196,22 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) SavePulbicAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||
rac.Type = entity.AuthCertTypePublic
|
||||
rac.CiphertextEncrypt()
|
||||
rac.ResourceType = 0
|
||||
rac.ResourceCode = "-"
|
||||
if rac.Id == 0 {
|
||||
if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 {
|
||||
return errorx.NewBiz("授权凭证的名称不能重复[%s]", rac.Name)
|
||||
}
|
||||
return r.Insert(ctx, rac)
|
||||
}
|
||||
// 名称置空,防止被更新
|
||||
rac.Name = ""
|
||||
return r.UpdateById(ctx, rac)
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error) {
|
||||
authCert := &entity.ResourceAuthCert{Name: authCertName}
|
||||
if err := r.GetBy(authCert); err != nil {
|
||||
@@ -232,17 +265,23 @@ func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertT
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) {
|
||||
if len(resources) == 0 {
|
||||
if len(resources) == 0 || len(authCerts) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 资源编号 -> 资源
|
||||
resourceCode2Resouce := collx.ArrayToMap(resources, func(ac entity.IAuthCert) string {
|
||||
resourceCode2Resource := collx.ArrayToMap(resources, func(ac entity.IAuthCert) string {
|
||||
return ac.GetCode()
|
||||
})
|
||||
|
||||
for _, authCert := range authCerts {
|
||||
resourceCode2Resouce[authCert.ResourceCode].SetAuthCert(entity.AuthCert{
|
||||
resource := resourceCode2Resource[authCert.ResourceCode]
|
||||
if resource == nil {
|
||||
logx.Debugf("FillAuthCert-授权凭证[%s]未匹配到对应的资源[%s]", authCert.Name, authCert.ResourceCode)
|
||||
continue
|
||||
}
|
||||
|
||||
resource.SetAuthCert(entity.AuthCert{
|
||||
Name: authCert.Name,
|
||||
Username: authCert.Username,
|
||||
Type: authCert.Type,
|
||||
@@ -254,11 +293,21 @@ func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthC
|
||||
// 解密授权凭证信息
|
||||
func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
|
||||
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||
// 需要维持资源关联信息
|
||||
resourceCode := authCert.ResourceCode
|
||||
resourceType := authCert.ResourceType
|
||||
authCertType := authCert.Type
|
||||
|
||||
// 如果是公共授权凭证,则密文为公共授权凭证名称,需要使用该名称再去获取对应的授权凭证
|
||||
authCert = &entity.ResourceAuthCert{Name: authCert.Ciphertext}
|
||||
if err := r.GetBy(authCert); err != nil {
|
||||
return nil, errorx.NewBiz("该公共授权凭证[%s]不存在", authCert.Ciphertext)
|
||||
}
|
||||
|
||||
// 使用资源关联的凭证类型
|
||||
authCert.ResourceCode = resourceCode
|
||||
authCert.ResourceType = resourceType
|
||||
authCert.Type = authCertType
|
||||
}
|
||||
|
||||
if err := authCert.CiphertextDecrypt(); err != nil {
|
||||
|
||||
@@ -5,17 +5,30 @@ import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SaveTeamParam struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required"` // 名称
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
|
||||
Tags []uint64 `json:"tags"` // 关联标签信息
|
||||
}
|
||||
|
||||
type Team interface {
|
||||
// 分页获取项目团队信息列表
|
||||
GetPageList(condition *entity.TeamQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
Save(ctx context.Context, team *entity.Team) error
|
||||
// Save 保存团队信息
|
||||
Save(ctx context.Context, team *SaveTeamParam) error
|
||||
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
|
||||
@@ -33,8 +46,6 @@ type Team interface {
|
||||
|
||||
ListTagIds(teamId uint64) []uint64
|
||||
|
||||
SaveTag(ctx context.Context, tagTeam *entity.TagTreeTeam) error
|
||||
|
||||
DeleteTag(tx context.Context, teamId, tagId uint64) error
|
||||
}
|
||||
|
||||
@@ -42,17 +53,78 @@ type teamAppImpl struct {
|
||||
teamRepo repository.Team `inject:"TeamRepo"`
|
||||
teamMemberRepo repository.TeamMember `inject:"TeamMemberRepo"`
|
||||
tagTreeTeamRepo repository.TagTreeTeam `inject:"TagTreeTeamRepo"`
|
||||
tagTreeApp TagTree `inject:"TagTreeApp"`
|
||||
}
|
||||
|
||||
func (p *teamAppImpl) GetPageList(condition *entity.TeamQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
return p.teamRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *teamAppImpl) Save(ctx context.Context, team *entity.Team) error {
|
||||
func (p *teamAppImpl) Save(ctx context.Context, saveParam *SaveTeamParam) error {
|
||||
team := &entity.Team{Name: saveParam.Name, Remark: saveParam.Remark}
|
||||
team.Id = saveParam.Id
|
||||
|
||||
if team.Id == 0 {
|
||||
return p.teamRepo.Insert(ctx, team)
|
||||
if p.teamRepo.CountByCond(&entity.Team{Name: saveParam.Name}) > 0 {
|
||||
return errorx.NewBiz("团队名[%s]已存在", saveParam.Name)
|
||||
}
|
||||
|
||||
if err := p.teamRepo.Insert(ctx, team); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginAccount := contextx.GetLoginAccount(ctx)
|
||||
logx.DebugfContext(ctx, "将[%s]默认加入至[%s]团队", loginAccount.Username, team.Name)
|
||||
|
||||
teamMem := &entity.TeamMember{}
|
||||
teamMem.AccountId = loginAccount.Id
|
||||
teamMem.Username = loginAccount.Username
|
||||
teamMem.TeamId = team.Id
|
||||
p.SaveMember(ctx, teamMem)
|
||||
} else {
|
||||
// 置空名称,防止变更
|
||||
team.Name = ""
|
||||
if err := p.teamRepo.UpdateById(ctx, team); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return p.teamRepo.UpdateById(ctx, team)
|
||||
|
||||
// 保存团队关联的标签信息
|
||||
teamId := team.Id
|
||||
var addIds, delIds []uint64
|
||||
if saveParam.Id == 0 {
|
||||
addIds = saveParam.Tags
|
||||
} else {
|
||||
// 将[]uint64转为[]any
|
||||
oIds := p.ListTagIds(team.Id)
|
||||
// 比较新旧两合集
|
||||
addIds, delIds, _ = collx.ArrayCompare(saveParam.Tags, oIds)
|
||||
}
|
||||
|
||||
addTeamTags := make([]*entity.TagTreeTeam, 0)
|
||||
for _, v := range addIds {
|
||||
tagId := v
|
||||
tag, err := p.tagTreeApp.GetById(new(entity.TagTree), tagId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("存在非法标签id")
|
||||
}
|
||||
|
||||
ptt := &entity.TagTreeTeam{TeamId: teamId, TagId: tagId, TagPath: tag.CodePath}
|
||||
addTeamTags = append(addTeamTags, ptt)
|
||||
}
|
||||
if len(addTeamTags) > 0 {
|
||||
logx.DebugfContext(ctx, "团队[%s]新增关联的标签信息: [%v]", team.Name, addTeamTags)
|
||||
p.tagTreeTeamRepo.BatchInsert(ctx, addTeamTags)
|
||||
}
|
||||
|
||||
for _, v := range delIds {
|
||||
p.DeleteTag(ctx, teamId, v)
|
||||
}
|
||||
if len(delIds) > 0 {
|
||||
logx.DebugfContext(ctx, "团队[%s]删除关联的标签信息: [%v]", team.Name, delIds)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
@@ -91,7 +163,7 @@ func (p *teamAppImpl) IsExistMember(teamId, accounId uint64) bool {
|
||||
return p.teamMemberRepo.IsExist(teamId, accounId)
|
||||
}
|
||||
|
||||
//--------------- 关联标签相关接口 ---------------
|
||||
//--------------- 标签相关接口 ---------------
|
||||
|
||||
func (p *teamAppImpl) ListTagIds(teamId uint64) []uint64 {
|
||||
tags := &[]entity.TagTreeTeam{}
|
||||
@@ -103,12 +175,6 @@ func (p *teamAppImpl) ListTagIds(teamId uint64) []uint64 {
|
||||
return ids
|
||||
}
|
||||
|
||||
// 保存关联项目信息
|
||||
func (p *teamAppImpl) SaveTag(ctx context.Context, tagTreeTeam *entity.TagTreeTeam) error {
|
||||
tagTreeTeam.Id = 0
|
||||
return p.tagTreeTeamRepo.Insert(ctx, tagTreeTeam)
|
||||
}
|
||||
|
||||
// 删除关联项目信息
|
||||
func (p *teamAppImpl) DeleteTag(ctx context.Context, teamId, tagId uint64) error {
|
||||
return p.tagTreeTeamRepo.DeleteByCond(ctx, &entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
|
||||
|
||||
@@ -8,6 +8,10 @@ import (
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
const (
|
||||
ExtraKeyPassphrase = "passphrase"
|
||||
)
|
||||
|
||||
// 资源授权凭证
|
||||
type ResourceAuthCert struct {
|
||||
model.Model
|
||||
@@ -16,14 +20,15 @@ type ResourceAuthCert struct {
|
||||
|
||||
ResourceCode string `json:"resourceCode"` // 资源编号
|
||||
ResourceType int8 `json:"resourceType"` // 资源类型
|
||||
Type AuthCertType `json:"type"` // 凭证类型
|
||||
Username string `json:"username"` // 用户名
|
||||
Ciphertext string `json:"ciphertext"` // 密文
|
||||
CiphertextType AuthCertCiphertextType `json:"ciphertextType"` // 密文类型
|
||||
Extra model.Map[string, any] `json:"extra"` // 账号需要的其他额外信息(如秘钥口令等)
|
||||
Type AuthCertType `json:"type"` // 凭证类型
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
// CiphertextEncrypt 密文加密
|
||||
func (m *ResourceAuthCert) CiphertextEncrypt() error {
|
||||
// 密码替换为加密后的密码
|
||||
password, err := utils.PwdAesEncrypt(m.Ciphertext)
|
||||
@@ -34,19 +39,20 @@ func (m *ResourceAuthCert) CiphertextEncrypt() error {
|
||||
|
||||
// 加密秘钥口令
|
||||
if m.CiphertextType == AuthCertCiphertextTypePrivateKey {
|
||||
passphrase := cast.ToString(m.Extra["passphrase"])
|
||||
passphrase := m.GetExtraString(ExtraKeyPassphrase)
|
||||
if passphrase != "" {
|
||||
passphrase, err := utils.PwdAesEncrypt(passphrase)
|
||||
if err != nil {
|
||||
return errors.New("加密秘钥口令失败")
|
||||
}
|
||||
m.Extra["passphrase"] = passphrase
|
||||
m.SetExtra(ExtraKeyPassphrase, passphrase)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CiphertextDecrypt 密文解密
|
||||
func (m *ResourceAuthCert) CiphertextDecrypt() error {
|
||||
// 密码替换为解密后的密码
|
||||
password, err := utils.PwdAesDecrypt(m.Ciphertext)
|
||||
@@ -57,18 +63,40 @@ func (m *ResourceAuthCert) CiphertextDecrypt() error {
|
||||
|
||||
// 加密秘钥口令
|
||||
if m.CiphertextType == AuthCertCiphertextTypePrivateKey {
|
||||
passphrase := cast.ToString(m.Extra["passphrase"])
|
||||
passphrase := m.GetExtraString(ExtraKeyPassphrase)
|
||||
if passphrase != "" {
|
||||
passphrase, err := utils.PwdAesDecrypt(passphrase)
|
||||
if err != nil {
|
||||
return errors.New("解密秘钥口令失败")
|
||||
}
|
||||
m.Extra["passphrase"] = passphrase
|
||||
m.SetExtra(ExtraKeyPassphrase, passphrase)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CiphertextClear 密文清楚
|
||||
func (m *ResourceAuthCert) CiphertextClear() {
|
||||
// 如果密文类型非公共授权凭证,则清空
|
||||
if m.CiphertextType != AuthCertCiphertextTypePublic {
|
||||
m.Ciphertext = ""
|
||||
}
|
||||
m.SetExtra(ExtraKeyPassphrase, "")
|
||||
}
|
||||
|
||||
func (m *ResourceAuthCert) SetExtra(key string, val any) {
|
||||
if m.Extra != nil {
|
||||
m.Extra[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ResourceAuthCert) GetExtraString(key string) string {
|
||||
if m.Extra == nil {
|
||||
return ""
|
||||
}
|
||||
return cast.ToString(m.Extra[key])
|
||||
}
|
||||
|
||||
// 密文类型
|
||||
type AuthCertCiphertextType int8
|
||||
|
||||
@@ -76,14 +104,14 @@ type AuthCertCiphertextType int8
|
||||
type AuthCertType int8
|
||||
|
||||
const (
|
||||
AuthCertCiphertextTypePublic AuthCertCiphertextType = -1 // 公共授权凭证
|
||||
AuthCertCiphertextTypePublic AuthCertCiphertextType = -1 // 公共授权凭证密文
|
||||
AuthCertCiphertextTypePassword AuthCertCiphertextType = 1 // 密码
|
||||
AuthCertCiphertextTypePrivateKey AuthCertCiphertextType = 2 // 私钥
|
||||
|
||||
AuthCertTypePublic AuthCertType = 2 // 公共凭证(可多个资源共享该授权凭证)
|
||||
AuthCertTypePrivate AuthCertType = 1 // 普通私有凭证
|
||||
AuthCertTypePrivileged AuthCertType = 11 // 特权私有凭证
|
||||
AuthCertTypePrivateDefault AuthCertType = 12 // 默认私有凭证
|
||||
AuthCertTypePublic AuthCertType = 2 // 公共凭证(可多个资源共享该授权凭证)
|
||||
)
|
||||
|
||||
// 授权凭证接口,填充资源授权凭证信息
|
||||
|
||||
@@ -17,6 +17,12 @@ func InitResourceAuthCertRouter(router *gin.RouterGroup) {
|
||||
{
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("", m.ListByQuery),
|
||||
|
||||
req.NewGet("/detail", m.GetCompleteAuthCert).Log(req.NewLogSave("授权凭证-查看密文")).RequiredPermissionCode("authcert:showciphertext"),
|
||||
|
||||
req.NewPost("", m.SaveAuthCert).Log(req.NewLogSave("授权凭证-保存")).RequiredPermissionCode("authcert:save"),
|
||||
|
||||
req.NewDelete(":id", m.Delete).Log(req.NewLogSave("授权凭证-删除")).RequiredPermissionCode("authcert:del"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(resourceAuthCert, reqs[:])
|
||||
|
||||
@@ -32,8 +32,6 @@ func InitTeamRouter(router *gin.RouterGroup) {
|
||||
|
||||
// 获取团队关联的标签id列表
|
||||
req.NewGet("/:id/tags", m.GetTagIds),
|
||||
|
||||
req.NewPost("/:id/tags", m.SaveTags).Log(req.NewLogSave("团队-保存标签关联信息")).RequiredPermissionCode("team:tag:save"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(team, reqs[:])
|
||||
|
||||
@@ -18,9 +18,6 @@ func T2022() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "2022",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&entity.AuthCert{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&entity.Machine{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ CREATE TABLE `t_db_instance` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库实例信息表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库实例信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db
|
||||
@@ -51,7 +51,7 @@ CREATE TABLE `t_db` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_code` (`code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库资源信息表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库资源信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_sql
|
||||
@@ -73,7 +73,7 @@ CREATE TABLE `t_db_sql` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库sql信息';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库sql信息';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_sql_exec
|
||||
@@ -132,7 +132,7 @@ CREATE TABLE `t_db_backup` (
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_db_name` (`db_name`) USING BTREE,
|
||||
KEY `idx_db_instance_id` (`db_instance_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_backup_history
|
||||
@@ -157,7 +157,7 @@ CREATE TABLE `t_db_backup_history` (
|
||||
KEY `idx_db_backup_id` (`db_backup_id`) USING BTREE,
|
||||
KEY `idx_db_instance_id` (`db_instance_id`) USING BTREE,
|
||||
KEY `idx_db_name` (`db_name`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_restore
|
||||
@@ -190,7 +190,7 @@ CREATE TABLE `t_db_restore` (
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_db_instane_id` (`db_instance_id`) USING BTREE,
|
||||
KEY `idx_db_name` (`db_name`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_restore_history
|
||||
@@ -204,7 +204,7 @@ CREATE TABLE `t_db_restore_history` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_db_restore_id` (`db_restore_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_binlog
|
||||
@@ -226,7 +226,7 @@ CREATE TABLE `t_db_binlog` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_db_instance_id` (`db_instance_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_binlog_history
|
||||
@@ -245,7 +245,7 @@ CREATE TABLE `t_db_binlog_history` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_db_instance_id` (`db_instance_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_db_data_sync_task
|
||||
@@ -322,7 +322,7 @@ CREATE TABLE `t_auth_cert` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='授权凭证';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='授权凭证';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_machine
|
||||
@@ -349,7 +349,7 @@ CREATE TABLE `t_machine` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器信息';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器信息';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_machine_file
|
||||
@@ -370,7 +370,7 @@ CREATE TABLE `t_machine_file` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器文件';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器文件';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_machine_file
|
||||
@@ -416,7 +416,7 @@ CREATE TABLE `t_machine_script` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器脚本';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器脚本';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_machine_script
|
||||
@@ -426,7 +426,7 @@ INSERT INTO `t_machine_script` VALUES (1, 'sys_info', 9999999, '# 获取系统cp
|
||||
INSERT INTO `t_machine_script` VALUES (2, 'get_process_by_name', 9999999, '#! /bin/bash\n# Function: 根据输入的程序的名字过滤出所对应的PID,并显示出详细信息,如果有几个PID,则全部显示\nNAME={{.processName}}\nN=`ps -aux | grep $NAME | grep -v grep | wc -l` ##统计进程总数\nif [ $N -le 0 ];then\n echo \"无该进程!\"\nfi\ni=1\nwhile [ $N -gt 0 ]\ndo\n echo \"进程PID: `ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $2}\'`\"\n echo \"进程命令:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $11}\'`\"\n echo \"进程所属用户: `ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $1}\'`\"\n echo \"CPU占用率:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $3}\'`%\"\n echo \"内存占用率:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $4}\'`%\"\n echo \"进程开始运行的时刻:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $9}\'`\"\n echo \"进程运行的时间:` ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $11}\'`\"\n echo \"进程状态:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $8}\'`\"\n echo \"进程虚拟内存:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $5}\'`\"\n echo \"进程共享内存:`ps -aux | grep $NAME | grep -v grep | awk \'NR==\'$i\'{print $0}\'| awk \'{print $6}\'`\"\n echo \"***************************************************************\"\n let N-- i++\ndone', '[{\"name\": \"进程名\",\"model\": \"processName\", \"placeholder\": \"请输入进程名\"}]', '获取进程运行状态', 1, NULL, NULL, 1, 'admin', NULL, '2021-07-12 15:33:41', 0, NULL);
|
||||
INSERT INTO `t_machine_script` VALUES (3, 'sys_run_info', 9999999, '#!/bin/bash\n# 获取要监控的本地服务器IP地址\nIP=`ifconfig | grep inet | grep -vE \'inet6|127.0.0.1\' | awk \'{print $2}\'`\necho \"IP地址:\"$IP\n \n# 获取cpu总核数\ncpu_num=`grep -c \"model name\" /proc/cpuinfo`\necho \"cpu总核数:\"$cpu_num\n \n# 1、获取CPU利用率\n################################################\n#us 用户空间占用CPU百分比\n#sy 内核空间占用CPU百分比\n#ni 用户进程空间内改变过优先级的进程占用CPU百分比\n#id 空闲CPU百分比\n#wa 等待输入输出的CPU时间百分比\n#hi 硬件中断\n#si 软件中断\n#################################################\n# 获取用户空间占用CPU百分比\ncpu_user=`top -b -n 1 | grep Cpu | awk \'{print $2}\' | cut -f 1 -d \"%\"`\necho \"用户空间占用CPU百分比:\"$cpu_user\n \n# 获取内核空间占用CPU百分比\ncpu_system=`top -b -n 1 | grep Cpu | awk \'{print $4}\' | cut -f 1 -d \"%\"`\necho \"内核空间占用CPU百分比:\"$cpu_system\n \n# 获取空闲CPU百分比\ncpu_idle=`top -b -n 1 | grep Cpu | awk \'{print $8}\' | cut -f 1 -d \"%\"`\necho \"空闲CPU百分比:\"$cpu_idle\n \n# 获取等待输入输出占CPU百分比\ncpu_iowait=`top -b -n 1 | grep Cpu | awk \'{print $10}\' | cut -f 1 -d \"%\"`\necho \"等待输入输出占CPU百分比:\"$cpu_iowait\n \n#2、获取CPU上下文切换和中断次数\n# 获取CPU中断次数\ncpu_interrupt=`vmstat -n 1 1 | sed -n 3p | awk \'{print $11}\'`\necho \"CPU中断次数:\"$cpu_interrupt\n \n# 获取CPU上下文切换次数\ncpu_context_switch=`vmstat -n 1 1 | sed -n 3p | awk \'{print $12}\'`\necho \"CPU上下文切换次数:\"$cpu_context_switch\n \n#3、获取CPU负载信息\n# 获取CPU15分钟前到现在的负载平均值\ncpu_load_15min=`uptime | awk \'{print $11}\' | cut -f 1 -d \',\'`\necho \"CPU 15分钟前到现在的负载平均值:\"$cpu_load_15min\n \n# 获取CPU5分钟前到现在的负载平均值\ncpu_load_5min=`uptime | awk \'{print $10}\' | cut -f 1 -d \',\'`\necho \"CPU 5分钟前到现在的负载平均值:\"$cpu_load_5min\n \n# 获取CPU1分钟前到现在的负载平均值\ncpu_load_1min=`uptime | awk \'{print $9}\' | cut -f 1 -d \',\'`\necho \"CPU 1分钟前到现在的负载平均值:\"$cpu_load_1min\n \n# 获取任务队列(就绪状态等待的进程数)\ncpu_task_length=`vmstat -n 1 1 | sed -n 3p | awk \'{print $1}\'`\necho \"CPU任务队列长度:\"$cpu_task_length\n \n#4、获取内存信息\n# 获取物理内存总量\nmem_total=`free -h | grep Mem | awk \'{print $2}\'`\necho \"物理内存总量:\"$mem_total\n \n# 获取操作系统已使用内存总量\nmem_sys_used=`free -h | grep Mem | awk \'{print $3}\'`\necho \"已使用内存总量(操作系统):\"$mem_sys_used\n \n# 获取操作系统未使用内存总量\nmem_sys_free=`free -h | grep Mem | awk \'{print $4}\'`\necho \"剩余内存总量(操作系统):\"$mem_sys_free\n \n# 获取应用程序已使用的内存总量\nmem_user_used=`free | sed -n 3p | awk \'{print $3}\'`\necho \"已使用内存总量(应用程序):\"$mem_user_used\n \n# 获取应用程序未使用内存总量\nmem_user_free=`free | sed -n 3p | awk \'{print $4}\'`\necho \"剩余内存总量(应用程序):\"$mem_user_free\n \n# 获取交换分区总大小\nmem_swap_total=`free | grep Swap | awk \'{print $2}\'`\necho \"交换分区总大小:\"$mem_swap_total\n \n# 获取已使用交换分区大小\nmem_swap_used=`free | grep Swap | awk \'{print $3}\'`\necho \"已使用交换分区大小:\"$mem_swap_used\n \n# 获取剩余交换分区大小\nmem_swap_free=`free | grep Swap | awk \'{print $4}\'`\necho \"剩余交换分区大小:\"$mem_swap_free', NULL, '获取cpu、内存等系统运行状态', 1, NULL, NULL, NULL, NULL, NULL, '2021-04-25 15:07:16', 0, NULL);
|
||||
INSERT INTO `t_machine_script` VALUES (4, 'top', 9999999, 'top', NULL, '实时获取系统运行状态', 3, NULL, NULL, 1, 'admin', NULL, '2021-05-24 15:58:20', 0, NULL);
|
||||
INSERT INTO `t_machine_script` VALUES (18, 'disk-mem', 9999999, 'df -h', '', '磁盘空间查看', 1, 1, 'admin', 1, 'admin', '2021-07-16 10:49:53', '2021-07-16 10:49:53', 0, NULL);
|
||||
INSERT INTO `t_machine_script` VALUES (5, 'disk-mem', 9999999, 'df -h', '', '磁盘空间查看', 1, 1, 'admin', 1, 'admin', '2021-07-16 10:49:53', '2021-07-16 10:49:53', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_cron_job`;
|
||||
@@ -449,7 +449,7 @@ CREATE TABLE `t_machine_cron_job` (
|
||||
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务';
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_cron_job_exec`;
|
||||
CREATE TABLE `t_machine_cron_job_exec` (
|
||||
@@ -462,7 +462,7 @@ CREATE TABLE `t_machine_cron_job_exec` (
|
||||
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务执行记录';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务执行记录';
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_cron_job_relate`;
|
||||
CREATE TABLE `t_machine_cron_job_relate` (
|
||||
@@ -475,7 +475,7 @@ CREATE TABLE `t_machine_cron_job_relate` (
|
||||
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务关联表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务关联表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_term_op`;
|
||||
CREATE TABLE `t_machine_term_op` (
|
||||
@@ -490,7 +490,7 @@ CREATE TABLE `t_machine_term_op` (
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_mongo
|
||||
@@ -511,7 +511,7 @@ CREATE TABLE `t_mongo` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_redis
|
||||
@@ -538,7 +538,7 @@ CREATE TABLE `t_redis` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='redis信息';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='redis信息';
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_oauth2_account`;
|
||||
@@ -551,7 +551,7 @@ CREATE TABLE `t_oauth2_account` (
|
||||
`is_deleted` tinyint DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='oauth2关联账号';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='oauth2关联账号';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_sys_account
|
||||
@@ -575,7 +575,7 @@ CREATE TABLE `t_sys_account` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='账号信息表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='账号信息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_sys_account
|
||||
@@ -598,7 +598,7 @@ CREATE TABLE `t_sys_account_role` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='账号角色关联表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='账号角色关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_sys_config
|
||||
@@ -621,7 +621,7 @@ CREATE TABLE `t_sys_config` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_sys_config
|
||||
@@ -655,7 +655,7 @@ CREATE TABLE `t_sys_log` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_creator_id` (`creator_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=58 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='系统操作日志';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='系统操作日志';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_sys_msg
|
||||
@@ -672,7 +672,7 @@ CREATE TABLE `t_sys_msg` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='系统消息表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='系统消息表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_sys_resource
|
||||
@@ -698,7 +698,7 @@ CREATE TABLE `t_sys_resource` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `t_sys_resource_ui_path_IDX` (`ui_path`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_sys_resource
|
||||
@@ -778,7 +778,7 @@ 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(100, 95, 'Tag3fhad/Bjlag32x/Lgidsq32/', 2, 1, '新增团队成员', 'team:member:save', 30000000, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:27', '2022-10-26 13:59:27', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(101, 95, 'Tag3fhad/Bjlag32x/Lixaue3G/', 2, 1, '移除团队成员', 'team:member:del', 40000000, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:43', '2022-10-26 13:59:43', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(102, 95, 'Tag3fhad/Bjlag32x/Oygsq3xg/', 2, 1, '保存团队标签', 'team:tag:save', 50000000, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:57', '2022-10-26 13:59:57', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(103, 2, '12sSjal1/exahgl32/', 1, 1, '授权凭证', 'authcerts', 60000000, '{"component":"ops/machine/authcert/AuthCertList","icon":"Unlock","isKeepAlive":true,"routeName":"AuthCertList"}', 1, 'admin', 1, 'admin', '2023-02-23 11:36:26', '2023-03-14 14:33:28', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(103, 93, '12sSjal1/exahgl32/', 1, 1, '授权凭证', 'authcerts', 19999999, '{"component":"ops/tag/AuthCertList","icon":"Ticket","isKeepAlive":true,"routeName":"AuthCertList"}', 1, 'admin', 1, 'admin', '2023-02-23 11:36:26', '2023-03-14 14:33:28', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(104, 103, '12sSjal1/exahgl32/egxahg24/', 2, 1, '基本权限', 'authcert', 10000000, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:37:24', '2023-02-23 11:37:24', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(105, 103, '12sSjal1/exahgl32/yglxahg2/', 2, 1, '保存权限', 'authcert:save', 20000000, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:37:54', '2023-02-23 11:37:54', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(106, 103, '12sSjal1/exahgl32/Glxag234/', 2, 1, '删除权限', 'authcert:del', 30000000, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:38:09', '2023-02-23 11:38:09', 0, NULL);
|
||||
@@ -807,7 +807,8 @@ 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(1709045735, 1708910975, '6egfEVYr/3r3hHEub/', 1, 1, '我的任务', 'procinst-tasks', 1708911263, '{"component":"flow/ProcinstTaskList","icon":"Tickets","isKeepAlive":true,"routeName":"ProcinstTaskList"}', 1, 'admin', 1, 'admin', '2024-02-27 22:55:35', '2024-02-27 22:56:35', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1708911264, 1708910975, '6egfEVYr/fw0Hhvye/', 1, 1, '流程定义', 'procdefs', 1708911264, '{"component":"flow/ProcdefList","icon":"List","isKeepAlive":true,"routeName":"ProcdefList"}', 1, 'admin', 1, 'admin', '2024-02-26 09:34:24', '2024-02-27 22:54:32', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1708910975, 0, '6egfEVYr/', 1, 1, '工单流程', '/flow', 60000000, '{"icon":"List","isKeepAlive":true,"routeName":"flow"}', 1, 'admin', 1, 'admin', '2024-02-26 09:29:36', '2024-02-26 15:37:52', 0, NULL);
|
||||
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1712717290, 0, 'tLb8TKLB/', 1, 1, '无页面权限', 'empty', 1712717290, '{"component":"empty","icon":"Menu","isHide":true,"isKeepAlive":true,"routeName":"empty"}', 1, 'admin', 1, 'admin', '2024-04-10 10:48:10', '2024-04-10 10:48:10', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1712717337, 1712717290, 'tLb8TKLB/m2abQkA8/', 2, 1, '授权凭证密文查看', 'authcert:showciphertext', 1712717337, 'null', 1, 'admin', 1, 'admin', '2024-04-10 10:48:58', '2024-04-10 10:48:58', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -830,14 +831,13 @@ CREATE TABLE `t_sys_role` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='角色表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='角色表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_sys_role
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `t_sys_role` VALUES (7, '公共角色', 'COMMON', 1, '所有账号基础角色', 1, '2021-07-06 15:05:47', 1, 'admin', '2021-07-06 15:05:47', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_role` VALUES (8, '开发', 'DEV', 1, '研发人员', 0, '2021-07-09 10:46:10', 1, 'admin', '2021-07-09 10:46:10', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -854,32 +854,14 @@ CREATE TABLE `t_sys_role_resource` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=526 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='角色资源关联表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='角色资源关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_sys_role_resource
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `t_sys_role_resource` (role_id,resource_id,creator_id,creator,create_time,is_deleted,delete_time) VALUES
|
||||
(7,1,1,'admin','2021-07-06 15:07:09', 0, NULL),
|
||||
(8,57,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,12,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,15,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,38,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,2,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,3,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,36,1,'admin','2021-07-09 10:49:46', 0, NULL),
|
||||
(8,59,1,'admin','2021-07-09 10:50:32', 0, NULL),
|
||||
(7,39,1,'admin','2021-09-09 10:10:30', 0, NULL),
|
||||
(8,42,1,'admin','2021-11-05 15:59:16', 0, NULL),
|
||||
(8,43,1,'admin','2021-11-05 15:59:16', 0, NULL),
|
||||
(8,47,1,'admin','2021-11-05 15:59:16', 0, NULL),
|
||||
(8,60,1,'admin','2021-11-05 15:59:16', 0, NULL),
|
||||
(8,61,1,'admin','2021-11-05 15:59:16', 0, NULL),
|
||||
(8,62,1,'admin','2021-11-05 15:59:16', 0, NULL),
|
||||
(8,80,1,'admin','2022-10-08 10:54:34', 0, NULL),
|
||||
(8,81,1,'admin','2022-10-08 10:54:34', 0, NULL),
|
||||
(8,79,1,'admin','2022-10-08 10:54:34', 0, NULL);
|
||||
(7,1,1,'admin','2021-07-06 15:07:09', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -904,13 +886,13 @@ CREATE TABLE `t_tag_tree` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_code_path` (`code_path`(100)) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_tag_tree
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `t_tag_tree` VALUES (33, 0, 'default', 'default/', '默认', '默认标签', '2022-10-26 20:04:19', 1, 'admin', '2022-10-26 20:04:19', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_tag_tree` VALUES (1, 0, -1, 'default', 'default/', '默认', '默认标签', '2022-10-26 20:04:19', 1, 'admin', '2022-10-26 20:04:19', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -932,13 +914,13 @@ CREATE TABLE `t_tag_tree_team` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tag_id` (`tag_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树团队关联信息';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树团队关联信息';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_tag_tree_team
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `t_tag_tree_team` VALUES (31, 33, 'default/', 3, '2022-10-26 20:04:45', 1, 'admin', '2022-10-26 20:04:45', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_tag_tree_team` VALUES (1, 1, 'default/', 1, '2022-10-26 20:04:45', 1, 'admin', '2022-10-26 20:04:45', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -958,13 +940,13 @@ CREATE TABLE `t_team` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='团队信息';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='团队信息';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_team
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `t_team` VALUES (3, '默认团队', '默认团队', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_team` VALUES (1, 'default_team', '默认团队', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -985,13 +967,13 @@ CREATE TABLE `t_team_member` (
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='团队成员表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='团队成员表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_team_member
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `t_team_member` VALUES (7, 3, 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_team_member` VALUES (1, 1, 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE IF EXISTS `t_resource_auth_cert`;
|
||||
@@ -1017,7 +999,7 @@ CREATE TABLE `t_resource_auth_cert` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源授权凭证表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源授权凭证表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_flow_procdef`;
|
||||
-- 工单流程相关表
|
||||
@@ -1037,7 +1019,7 @@ CREATE TABLE `t_flow_procdef` (
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流程-流程定义';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流程-流程定义';
|
||||
|
||||
DROP TABLE IF EXISTS `t_flow_procinst`;
|
||||
CREATE TABLE `t_flow_procinst` (
|
||||
@@ -1064,7 +1046,7 @@ CREATE TABLE `t_flow_procinst` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_procdef_id` (`procdef_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流程-流程实例(根据流程定义开启一个流程)';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流程-流程实例(根据流程定义开启一个流程)';
|
||||
|
||||
DROP TABLE IF EXISTS `t_flow_procinst_task`;
|
||||
CREATE TABLE `t_flow_procinst_task` (
|
||||
@@ -1087,6 +1069,6 @@ CREATE TABLE `t_flow_procinst_task` (
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_procinst_id` (`procinst_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流程-流程实例任务';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流程-流程实例任务';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
@@ -118,7 +118,90 @@ CREATE TABLE `t_resource_auth_cert` (
|
||||
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源授权凭证表';
|
||||
|
||||
|
||||
Begin;
|
||||
-- 迁移机器表账号
|
||||
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('machine_', code, '_' username),
|
||||
code,
|
||||
1,
|
||||
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_machine
|
||||
WHERE
|
||||
is_deleted = 0;
|
||||
|
||||
-- 关联机器账号到tag_tree
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_tree (pid,
|
||||
code,
|
||||
code_path,
|
||||
type,
|
||||
name,
|
||||
create_time,
|
||||
creator_id,
|
||||
creator,
|
||||
update_time,
|
||||
modifier_id,
|
||||
modifier,
|
||||
is_deleted)
|
||||
SELECT
|
||||
tt.id,
|
||||
rac.`name`,
|
||||
CONCAT(tt.code_path, rac.`name`, '/'),
|
||||
11,
|
||||
rac.`username`,
|
||||
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_tag_tree` tt
|
||||
JOIN `t_resource_auth_cert` rac ON tt.`code` = rac.`resource_code`
|
||||
AND tt.`type` = rac.`resource_type`
|
||||
WHERE
|
||||
tt.`is_deleted` = 0
|
||||
|
||||
-- 删除机器表 账号相关字段
|
||||
ALTER TABLE t_machine DROP COLUMN username;
|
||||
ALTER TABLE t_machine DROP COLUMN password;
|
||||
ALTER TABLE t_machine DROP COLUMN auth_cert_id;
|
||||
|
||||
UPDATE t_sys_resource SET pid=93, ui_path='Tag3fhad/exahgl32/', weight=19999999, meta='{"component":"ops/tag/AuthCertList","icon":"Ticket","isKeepAlive":true,"routeName":"AuthCertList"}' WHERE id=103;
|
||||
UPDATE t_sys_resource SET ui_path='Tag3fhad/exahgl32/egxahg24/', weight=10000000 WHERE id=104;
|
||||
UPDATE t_sys_resource SET ui_path='Tag3fhad/exahgl32/yglxahg2/', weight=20000000 WHERE id=105;
|
||||
UPDATE t_sys_resource SET ui_path='Tag3fhad/exahgl32/Glxag234/', weight=30000000 WHERE id=106;
|
||||
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(1712717290, 0, 'tLb8TKLB/', 1, 1, '无页面权限', 'empty', 1712717290, '{"component":"empty","icon":"Menu","isHide":true,"isKeepAlive":true,"routeName":"empty"}', 1, 'admin', 1, 'admin', '2024-04-10 10:48:10', '2024-04-10 10:48:10', 0, NULL);
|
||||
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1712717337, 1712717290, 'tLb8TKLB/m2abQkA8/', 2, 1, '授权凭证密文查看', 'authcert:showciphertext', 1712717337, 'null', 1, 'admin', 1, 'admin', '2024-04-10 10:48:58', '2024-04-10 10:48:58', 0, NULL);
|
||||
commit;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user