mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-14 21:30:26 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d85bbff270 | ||
|
|
bb1522f4dc | ||
|
|
a7632fbf58 | ||
|
|
c4cb4234fd |
@@ -14,7 +14,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
server:
|
server:
|
||||||
image: ccr.ccs.tencentyun.com/mayfly/mayfly-go:v1.8.3
|
image: ccr.ccs.tencentyun.com/mayfly/mayfly-go:v1.8.5
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"element-plus": "^2.7.2",
|
"element-plus": "^2.7.3",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-plugin-vue": "^9.25.0",
|
"eslint-plugin-vue": "^9.25.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"sass": "^1.76.0",
|
"sass": "^1.77.1",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"vite": "^5.2.11",
|
"vite": "^5.2.11",
|
||||||
"vue-eslint-parser": "^9.4.2"
|
"vue-eslint-parser": "^9.4.2"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const config = {
|
|||||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||||
|
|
||||||
// 系统版本
|
// 系统版本
|
||||||
version: 'v1.8.4',
|
version: 'v1.8.6',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const state = reactive({
|
|||||||
search: null as any,
|
search: null as any,
|
||||||
weblinks: null as any,
|
weblinks: null as any,
|
||||||
},
|
},
|
||||||
status: TerminalStatus.NoConnected,
|
status: -11,
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -96,6 +96,7 @@ onBeforeUnmount(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
state.status = TerminalStatus.NoConnected;
|
||||||
if (term) {
|
if (term) {
|
||||||
console.log('重新连接...');
|
console.log('重新连接...');
|
||||||
close();
|
close();
|
||||||
@@ -105,7 +106,7 @@ function init() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTerm() {
|
async function initTerm() {
|
||||||
term = new Terminal({
|
term = new Terminal({
|
||||||
fontSize: themeConfig.value.terminalFontSize || 15,
|
fontSize: themeConfig.value.terminalFontSize || 15,
|
||||||
fontWeight: themeConfig.value.terminalFontWeight || 'normal',
|
fontWeight: themeConfig.value.terminalFontWeight || 'normal',
|
||||||
@@ -155,6 +156,7 @@ function initSocket() {
|
|||||||
state.status = TerminalStatus.Connected;
|
state.status = TerminalStatus.Connected;
|
||||||
|
|
||||||
focus();
|
focus();
|
||||||
|
fitTerminal();
|
||||||
|
|
||||||
// 如果有初始要执行的命令,则发送执行命令
|
// 如果有初始要执行的命令,则发送执行命令
|
||||||
if (props.cmd) {
|
if (props.cmd) {
|
||||||
@@ -209,7 +211,6 @@ function loadAddon() {
|
|||||||
// tell trzsz the terminal columns has been changed
|
// tell trzsz the terminal columns has been changed
|
||||||
trzsz.setTerminalColumns(size.cols);
|
trzsz.setTerminalColumns(size.cols);
|
||||||
});
|
});
|
||||||
window.addEventListener('resize', () => state.addon.fit.fit());
|
|
||||||
// enable drag files or directories to upload
|
// enable drag files or directories to upload
|
||||||
terminalRef.value.addEventListener('dragover', (event: Event) => event.preventDefault());
|
terminalRef.value.addEventListener('dragover', (event: Event) => event.preventDefault());
|
||||||
terminalRef.value.addEventListener('drop', (event: any) => {
|
terminalRef.value.addEventListener('drop', (event: any) => {
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
|
import EnumValue from '@/common/Enum';
|
||||||
|
|
||||||
export enum TerminalStatus {
|
export enum TerminalStatus {
|
||||||
Error = -1,
|
Error = -1,
|
||||||
NoConnected = 0,
|
NoConnected = 0,
|
||||||
Connected = 1,
|
Connected = 1,
|
||||||
Disconnected = 2,
|
Disconnected = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TerminalStatusEnum = {
|
||||||
|
Error: EnumValue.of(TerminalStatus.Error, '连接出错').setExtra({ iconColor: 'var(--el-color-error)' }),
|
||||||
|
NoConnected: EnumValue.of(TerminalStatus.NoConnected, '未连接').setExtra({ iconColor: 'var(--el-color-primary)' }),
|
||||||
|
Connected: EnumValue.of(TerminalStatus.Connected, '连接成功').setExtra({ iconColor: 'var(--el-color-success)' }),
|
||||||
|
Disconnected: EnumValue.of(TerminalStatus.Disconnected, '连接失败').setExtra({ iconColor: 'var(--el-color-error)' }),
|
||||||
|
};
|
||||||
|
|||||||
@@ -39,6 +39,11 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<el-option :key="TagResourceTypeEnum.Db.value" :label="TagResourceTypeEnum.Db.label" :value="TagResourceTypeEnum.Db.value" />
|
<el-option :key="TagResourceTypeEnum.Db.value" :label="TagResourceTypeEnum.Db.label" :value="TagResourceTypeEnum.Db.value" />
|
||||||
|
<el-option
|
||||||
|
:key="TagResourceTypeEnum.Redis.value"
|
||||||
|
:label="TagResourceTypeEnum.Redis.label"
|
||||||
|
:value="TagResourceTypeEnum.Redis.value"
|
||||||
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="resourceCode" label="资源编号" required>
|
<el-form-item prop="resourceCode" label="资源编号" required>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
:default-expanded-keys="props.defaultExpandedKeys"
|
:default-expanded-keys="props.defaultExpandedKeys"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<span @dblclick="treeNodeDblclick(data)" :class="data.type.nodeDblclickFunc ? 'none-select' : ''">
|
<span :id="node.key" @dblclick="treeNodeDblclick(data)" :class="data.type.nodeDblclickFunc ? 'none-select' : ''">
|
||||||
<span v-if="data.type.value == TagTreeNode.TagPath">
|
<span v-if="data.type.value == TagTreeNode.TagPath">
|
||||||
<tag-info :tag-path="data.label" />
|
<tag-info :tag-path="data.label" />
|
||||||
</span>
|
</span>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, reactive, ref, watch, toRefs } from 'vue';
|
import { onMounted, reactive, ref, watch, toRefs, nextTick } from 'vue';
|
||||||
import { NodeType, TagTreeNode } from './tag';
|
import { NodeType, TagTreeNode } from './tag';
|
||||||
import TagInfo from './TagInfo.vue';
|
import TagInfo from './TagInfo.vue';
|
||||||
import { Contextmenu } from '@/components/contextmenu';
|
import { Contextmenu } from '@/components/contextmenu';
|
||||||
@@ -126,7 +126,7 @@ const loadTags = async () => {
|
|||||||
* @param { Object } node
|
* @param { Object } node
|
||||||
* @param { Object } resolve
|
* @param { Object } resolve
|
||||||
*/
|
*/
|
||||||
const loadNode = async (node: any, resolve: any) => {
|
const loadNode = async (node: any, resolve: (data: any) => void, reject: () => void) => {
|
||||||
if (typeof resolve !== 'function') {
|
if (typeof resolve !== 'function') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -141,6 +141,8 @@ const loadNode = async (node: any, resolve: any) => {
|
|||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
// 调用 reject 以保持节点状态,并允许远程加载继续。
|
||||||
|
return reject();
|
||||||
}
|
}
|
||||||
return resolve(nodes);
|
return resolve(nodes);
|
||||||
};
|
};
|
||||||
@@ -207,6 +209,17 @@ const getNode = (nodeKey: any) => {
|
|||||||
|
|
||||||
const setCurrentKey = (nodeKey: any) => {
|
const setCurrentKey = (nodeKey: any) => {
|
||||||
treeRef.value.setCurrentKey(nodeKey);
|
treeRef.value.setCurrentKey(nodeKey);
|
||||||
|
|
||||||
|
// 通过Id获取到对应的dom元素
|
||||||
|
const node = document.getElementById(nodeKey);
|
||||||
|
if (node) {
|
||||||
|
setTimeout(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
// 通过scrollIntoView方法将对应的dom元素定位到可见区域 【block: 'center'】这个属性是在垂直方向居中显示
|
||||||
|
node.scrollIntoView({ block: 'center' });
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
ref="tagTreeRef"
|
ref="tagTreeRef"
|
||||||
:data="state.tags"
|
:data="state.tags"
|
||||||
:default-expanded-keys="checkedTags"
|
:default-expanded-keys="state.defaultExpandedKeys"
|
||||||
:default-checked-keys="checkedTags"
|
:default-checked-keys="checkedTags"
|
||||||
multiple
|
multiple
|
||||||
:render-after-expand="true"
|
:render-after-expand="true"
|
||||||
@@ -74,10 +74,12 @@ const tagTreeRef: any = ref(null);
|
|||||||
const filterTag = ref('');
|
const filterTag = ref('');
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
defaultExpandedKeys: [] as any,
|
||||||
tags: [],
|
tags: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
state.defaultExpandedKeys = checkedTags.value;
|
||||||
search();
|
search();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="authCertName" label="授权凭证" required>
|
<el-form-item prop="authCertName" label="授权凭证" required>
|
||||||
<el-select @change="changeAuthCert" v-model="form.authCertName" placeholder="请选择授权凭证" filterable>
|
<el-select v-model="form.authCertName" placeholder="请选择授权凭证" filterable>
|
||||||
<el-option v-for="item in state.authCerts" :key="item.id" :label="`${item.name}`" :value="item.name">
|
<el-option v-for="item in state.authCerts" :key="item.id" :label="`${item.name}`" :value="item.name">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
|
|
||||||
@@ -39,8 +39,15 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="getDatabaseMode" label="获库方式" required>
|
||||||
|
<el-select v-model="form.getDatabaseMode" @change="onChangeGetDatabaseMode" placeholder="请选择库名获取方式">
|
||||||
|
<el-option v-for="item in DbGetDbNamesMode" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="database" label="数据库名">
|
<el-form-item prop="database" label="数据库名">
|
||||||
<el-select
|
<el-select
|
||||||
|
:disabled="form.getDatabaseMode == DbGetDbNamesMode.Auto.value || !form.authCertName"
|
||||||
v-model="dbNamesSelected"
|
v-model="dbNamesSelected"
|
||||||
multiple
|
multiple
|
||||||
clearable
|
clearable
|
||||||
@@ -49,8 +56,9 @@
|
|||||||
filterable
|
filterable
|
||||||
:filter-method="filterDbNames"
|
:filter-method="filterDbNames"
|
||||||
allow-create
|
allow-create
|
||||||
placeholder="请确保数据库实例信息填写完整后获取库名"
|
placeholder="获库方式为‘指定库名’时,可选择"
|
||||||
style="width: 100%"
|
@focus="getAllDatabase(form.authCertName)"
|
||||||
|
:loading="state.loadingDbNames"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-checkbox v-model="checkAllDbNames" :indeterminate="indeterminateDbNames" @change="handleCheckAll"> 全选 </el-checkbox>
|
<el-checkbox v-model="checkAllDbNames" :indeterminate="indeterminateDbNames" @change="handleCheckAll"> 全选 </el-checkbox>
|
||||||
@@ -75,7 +83,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, watch, ref, watchEffect } from 'vue';
|
import { toRefs, reactive, watch, ref } from 'vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import type { CheckboxValueType } from 'element-plus';
|
import type { CheckboxValueType } from 'element-plus';
|
||||||
@@ -86,6 +94,7 @@ import EnumTag from '@/components/enumtag/EnumTag.vue';
|
|||||||
import { AuthCertCiphertextTypeEnum } from '../tag/enums';
|
import { AuthCertCiphertextTypeEnum } from '../tag/enums';
|
||||||
import { resourceAuthCertApi } from '../tag/api';
|
import { resourceAuthCertApi } from '../tag/api';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import { DbGetDbNamesMode } from './enums';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -147,10 +156,10 @@ const rules = {
|
|||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
database: [
|
getDatabaseMode: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请添加数据库',
|
message: '请选择库名获取方式',
|
||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -172,20 +181,23 @@ const state = reactive({
|
|||||||
authCerts: [] as any,
|
authCerts: [] as any,
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
// tagId: [],
|
|
||||||
name: null,
|
name: null,
|
||||||
code: '',
|
code: '',
|
||||||
|
getDatabaseMode: DbGetDbNamesMode.Auto.value,
|
||||||
database: '',
|
database: '',
|
||||||
remark: '',
|
remark: '',
|
||||||
instanceId: null as any,
|
instanceId: null as any,
|
||||||
authCertName: '',
|
authCertName: '',
|
||||||
},
|
},
|
||||||
instances: [] as any,
|
instances: [] as any,
|
||||||
|
loadingDbNames: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { dialogVisible, allDatabases, form, dbNamesSelected } = toRefs(state);
|
const { dialogVisible, allDatabases, form, dbNamesSelected } = toRefs(state);
|
||||||
|
|
||||||
watchEffect(() => {
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
() => {
|
||||||
state.dialogVisible = props.visible;
|
state.dialogVisible = props.visible;
|
||||||
if (!state.dialogVisible) {
|
if (!state.dialogVisible) {
|
||||||
return;
|
return;
|
||||||
@@ -193,17 +205,21 @@ watchEffect(() => {
|
|||||||
const db: any = props.db;
|
const db: any = props.db;
|
||||||
if (db.code) {
|
if (db.code) {
|
||||||
state.form = { ...db };
|
state.form = { ...db };
|
||||||
// state.form.tagId = newValue.db.tags.map((t: any) => t.tagId);
|
if (db.getDatabaseMode == DbGetDbNamesMode.Assign.value) {
|
||||||
// 将数据库名使用空格切割,获取所有数据库列表
|
// 将数据库名使用空格切割,获取所有数据库列表
|
||||||
state.dbNamesSelected = db.database.split(' ');
|
state.dbNamesSelected = db.database.split(' ');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
state.form = {} as any;
|
state.form = { getDatabaseMode: DbGetDbNamesMode.Auto.value } as any;
|
||||||
state.dbNamesSelected = [];
|
state.dbNamesSelected = [];
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const changeAuthCert = (val: string) => {
|
const onChangeGetDatabaseMode = (val: any) => {
|
||||||
getAllDatabase(val);
|
if (val == DbGetDbNamesMode.Auto.value) {
|
||||||
|
state.dbNamesSelected = [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAuthCerts = async () => {
|
const getAuthCerts = async () => {
|
||||||
@@ -217,6 +233,8 @@ const getAuthCerts = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAllDatabase = async (authCertName: string) => {
|
const getAllDatabase = async (authCertName: string) => {
|
||||||
|
try {
|
||||||
|
state.loadingDbNames = true;
|
||||||
const req = { ...(props.instance as any) };
|
const req = { ...(props.instance as any) };
|
||||||
req.authCert = state.authCerts?.find((x: any) => x.name == authCertName);
|
req.authCert = state.authCerts?.find((x: any) => x.name == authCertName);
|
||||||
let dbs = await dbApi.getAllDatabase.request(req);
|
let dbs = await dbApi.getAllDatabase.request(req);
|
||||||
@@ -227,6 +245,9 @@ const getAllDatabase = async (authCertName: string) => {
|
|||||||
if (instance && instance.type === DbType.oracle && dbs.length === 0) {
|
if (instance && instance.type === DbType.oracle && dbs.length === 0) {
|
||||||
state.allDatabases = [instance.sid];
|
state.allDatabases = [instance.sid];
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
state.loadingDbNames = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const open = async () => {
|
const open = async () => {
|
||||||
|
|||||||
@@ -1,25 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="db-list">
|
<div class="db-list">
|
||||||
|
<el-drawer
|
||||||
|
:title="title"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
@open="search"
|
||||||
|
:before-close="cancel"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
size="60%"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="title" :back="cancel">
|
||||||
|
<template #extra>
|
||||||
|
<div class="mr20">
|
||||||
|
<span>{{ $props.instance?.tags?.[0]?.codePath }}</span>
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
<SvgIcon :name="getDbDialect($props.instance?.type).getInfo()?.icon" :size="20" />
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
<span>{{ $props.instance?.host }}:{{ $props.instance?.port }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</DrawerHeader>
|
||||||
|
</template>
|
||||||
|
|
||||||
<page-table
|
<page-table
|
||||||
ref="pageTableRef"
|
ref="pageTableRef"
|
||||||
:page-api="dbApi.dbs"
|
:page-api="dbApi.dbs"
|
||||||
:before-query-fn="checkRouteTagPath"
|
|
||||||
:search-items="searchItems"
|
|
||||||
v-model:query-form="query"
|
v-model:query-form="query"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
lazy
|
lazy
|
||||||
|
show-selection
|
||||||
|
v-model:selection-data="state.selectionData"
|
||||||
>
|
>
|
||||||
<template #instanceSelect>
|
<template #tableHeader>
|
||||||
<el-select remote :remote-method="getInstances" v-model="query.instanceId" placeholder="输入并选择实例" filterable clearable>
|
<el-button v-auth="perms.saveDb" type="primary" circle icon="Plus" @click="editDb(null)"> </el-button>
|
||||||
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id">
|
<el-button v-auth="perms.delDb" :disabled="state.selectionData.length < 1" @click="deleteDb" type="danger" circle icon="delete"></el-button>
|
||||||
{{ item.name }}
|
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
|
||||||
|
|
||||||
{{ item.type }} / {{ item.host }}:{{ item.port }}
|
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
|
||||||
{{ item.username }}
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #type="{ data }">
|
<template #type="{ data }">
|
||||||
@@ -28,16 +43,12 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #host="{ data }">
|
|
||||||
{{ `${data.host}:${data.port}` }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #database="{ data }">
|
<template #database="{ data }">
|
||||||
<el-popover placement="bottom" :width="200" trigger="click">
|
<el-popover placement="bottom" :width="200" trigger="click">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button @click="state.currentDbs = data.database" type="primary" link>查看库</el-button>
|
<el-button @click="getDbNames(data)" type="primary" link>查看库</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-table :data="filterDbs" size="small">
|
<el-table :data="filterDbs" v-loading="state.loadingDbNames" size="small">
|
||||||
<el-table-column prop="dbName" label="数据库">
|
<el-table-column prop="dbName" label="数据库">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-input v-model="state.dbNameSearch" size="small" placeholder="库名: 输入可过滤" clearable />
|
<el-input v-model="state.dbNameSearch" size="small" placeholder="库名: 输入可过滤" clearable />
|
||||||
@@ -52,7 +63,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #action="{ data }">
|
<template #action="{ data }">
|
||||||
|
<el-button v-auth="perms.saveDb" @click="editDb(data)" type="primary" link>编辑</el-button>
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
|
||||||
<el-button type="primary" @click="onShowSqlExec(data)" link>SQL记录</el-button>
|
<el-button type="primary" @click="onShowSqlExec(data)" link>SQL记录</el-button>
|
||||||
|
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
|
||||||
<el-dropdown @command="handleMoreActionCommand">
|
<el-dropdown @command="handleMoreActionCommand">
|
||||||
@@ -64,9 +80,11 @@
|
|||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item :command="{ type: 'detail', data }"> 详情 </el-dropdown-item>
|
|
||||||
<el-dropdown-item :command="{ type: 'dumpDb', data }"> 导出 </el-dropdown-item>
|
<el-dropdown-item :command="{ type: 'dumpDb', data }"> 导出 </el-dropdown-item>
|
||||||
<el-dropdown-item :command="{ type: 'backupDb', data }" v-if="actionBtns[perms.backupDb] && supportAction('backupDb', data.type)">
|
<el-dropdown-item
|
||||||
|
:command="{ type: 'backupDb', data }"
|
||||||
|
v-if="actionBtns[perms.backupDb] && supportAction('backupDb', data.type)"
|
||||||
|
>
|
||||||
备份任务
|
备份任务
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
@@ -86,8 +104,9 @@
|
|||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</page-table>
|
</page-table>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
<el-dialog width="750px" :title="`${db} 数据库导出`" v-model="exportDialog.visible">
|
<el-dialog width="750px" :title="`${exportDialog.db} 数据库导出`" v-model="exportDialog.visible">
|
||||||
<el-row justify="space-between">
|
<el-row justify="space-between">
|
||||||
<el-col :span="9">
|
<el-col :span="9">
|
||||||
<el-form-item label="导出内容: ">
|
<el-form-item label="导出内容: ">
|
||||||
@@ -168,125 +187,98 @@
|
|||||||
<db-restore-list :dbId="dbRestoreDialog.dbId" :dbNames="dbRestoreDialog.dbs" />
|
<db-restore-list :dbId="dbRestoreDialog.dbId" :dbNames="dbRestoreDialog.dbs" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-if="infoDialog.visible" v-model="infoDialog.visible" :before-close="onBeforeCloseInfoDialog">
|
<db-edit
|
||||||
<el-descriptions title="详情" :column="3" border>
|
@confirm="confirmEditDb"
|
||||||
<el-descriptions-item :span="2" label="名称">{{ infoDialog.data?.name }}</el-descriptions-item>
|
@cancel="cancelEditDb"
|
||||||
<el-descriptions-item :span="1" label="id">{{ infoDialog.data?.id }}</el-descriptions-item>
|
:title="dbEditDialog.title"
|
||||||
|
v-model:visible="dbEditDialog.visible"
|
||||||
<el-descriptions-item :span="3" label="关联标签"><ResourceTags :tags="infoDialog.data.tags" /></el-descriptions-item>
|
:instance="props.instance"
|
||||||
<el-descriptions-item :span="3" label="数据库实例名称">{{ infoDialog.instance?.name }}</el-descriptions-item>
|
v-model:db="dbEditDialog.data"
|
||||||
|
></db-edit>
|
||||||
<el-descriptions-item :span="2" label="主机">{{ infoDialog.instance?.host }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item :span="1" label="端口">{{ infoDialog.instance?.port }}</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="授权凭证">{{ infoDialog.instance.authCertName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item :span="1" label="类型">
|
|
||||||
<SvgIcon :name="getDbDialect(infoDialog.instance?.type).getInfo().icon" :size="20" />{{ infoDialog.instance?.type }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item :span="3" label="数据库">{{ infoDialog.data?.database }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data?.remark }}</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="创建时间">{{ formatDate(infoDialog.data?.createTime) }} </el-descriptions-item>
|
|
||||||
<el-descriptions-item :span="1" label="创建者">{{ infoDialog.data?.creator }}</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="更新时间">{{ formatDate(infoDialog.data?.updateTime) }} </el-descriptions-item>
|
|
||||||
<el-descriptions-item :span="1" label="修改者">{{ infoDialog.data?.modifier }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<db-edit @val-change="search()" :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" v-model:db="dbEditDialog.data"></db-edit>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, onMounted, reactive, ref, Ref, toRefs } from 'vue';
|
import { computed, defineAsyncComponent, reactive, ref, Ref, toRefs } from 'vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import config from '@/common/config';
|
import config from '@/common/config';
|
||||||
import { joinClientParams } from '@/common/request';
|
import { joinClientParams } from '@/common/request';
|
||||||
import { isTrue } from '@/common/assert';
|
import { isTrue } from '@/common/assert';
|
||||||
import { formatDate } from '@/common/utils/format';
|
|
||||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||||
import { TableColumn } from '@/components/pagetable';
|
import { TableColumn } from '@/components/pagetable';
|
||||||
import { hasPerms } from '@/components/auth/auth';
|
import { hasPerms } from '@/components/auth/auth';
|
||||||
import DbSqlExecLog from './DbSqlExecLog.vue';
|
import DbSqlExecLog from './DbSqlExecLog.vue';
|
||||||
import { DbType } from './dialect';
|
import { DbType } from './dialect';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import { getDbDialect } from './dialect/index';
|
import { getDbDialect } from './dialect/index';
|
||||||
import { getTagPathSearchItem } from '../component/tag';
|
|
||||||
import { SearchItem } from '@/components/SearchForm';
|
|
||||||
import DbBackupList from './DbBackupList.vue';
|
import DbBackupList from './DbBackupList.vue';
|
||||||
import DbBackupHistoryList from './DbBackupHistoryList.vue';
|
import DbBackupHistoryList from './DbBackupHistoryList.vue';
|
||||||
import DbRestoreList from './DbRestoreList.vue';
|
import DbRestoreList from './DbRestoreList.vue';
|
||||||
import ResourceTags from '../component/ResourceTags.vue';
|
import ResourceTags from '../component/ResourceTags.vue';
|
||||||
import { sleep } from '@/common/utils/loading';
|
import { sleep } from '@/common/utils/loading';
|
||||||
|
import { DbGetDbNamesMode } from './enums';
|
||||||
|
import { DbInst } from './db';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||||
|
|
||||||
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
|
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
|
||||||
|
|
||||||
const searchItems = [
|
const props = defineProps({
|
||||||
getTagPathSearchItem(TagResourceTypeEnum.DbName.value),
|
instance: {
|
||||||
SearchItem.slot('instanceId', '实例', 'instanceSelect'),
|
type: [Object],
|
||||||
SearchItem.input('code', '编号'),
|
required: true,
|
||||||
];
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogVisible = defineModel<boolean>('visible');
|
||||||
|
|
||||||
|
const emit = defineEmits(['cancel']);
|
||||||
|
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
TableColumn.new('tags[0].tagPath', '关联标签').isSlot('tagPath').setAddWidth(20),
|
|
||||||
TableColumn.new('name', '名称'),
|
TableColumn.new('name', '名称'),
|
||||||
TableColumn.new('type', '类型').isSlot().setAddWidth(-15).alignCenter(),
|
|
||||||
TableColumn.new('instanceName', '实例名'),
|
|
||||||
TableColumn.new('host', 'ip:port').isSlot().setAddWidth(40),
|
|
||||||
TableColumn.new('authCertName', '授权凭证'),
|
TableColumn.new('authCertName', '授权凭证'),
|
||||||
|
TableColumn.new('getDatabaseMode', '获库方式').typeTag(DbGetDbNamesMode),
|
||||||
TableColumn.new('database', '库').isSlot().setMinWidth(80),
|
TableColumn.new('database', '库').isSlot().setMinWidth(80),
|
||||||
TableColumn.new('remark', '备注'),
|
TableColumn.new('remark', '备注'),
|
||||||
TableColumn.new('code', '编号'),
|
TableColumn.new('code', '编号'),
|
||||||
|
TableColumn.new('action', '操作').isSlot().setMinWidth(210).fixedRight().alignCenter(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const perms = {
|
const perms = {
|
||||||
|
base: 'db',
|
||||||
|
saveDb: 'db:save',
|
||||||
|
delDb: 'db:del',
|
||||||
backupDb: 'db:backup',
|
backupDb: 'db:backup',
|
||||||
restoreDb: 'db:restore',
|
restoreDb: 'db:restore',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 该用户拥有的的操作列按钮权限
|
|
||||||
// const actionBtns = hasPerms([perms.base, perms.saveDb]);
|
|
||||||
const actionBtns = hasPerms(Object.values(perms));
|
const actionBtns = hasPerms(Object.values(perms));
|
||||||
const actionColumn = TableColumn.new('action', '操作').isSlot().setMinWidth(180).fixedRight().alignCenter();
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const pageTableRef: Ref<any> = ref(null);
|
const pageTableRef: Ref<any> = ref(null);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
row: {} as any,
|
loadingDbNames: false,
|
||||||
dbId: 0,
|
currentDbNames: [],
|
||||||
db: '',
|
|
||||||
currentDbs: '',
|
|
||||||
dbNameSearch: '',
|
dbNameSearch: '',
|
||||||
instances: [] as any,
|
instances: [] as any,
|
||||||
/**
|
/**
|
||||||
* 选中的数据
|
* 选中的数据
|
||||||
*/
|
*/
|
||||||
selectionData: [],
|
selectionData: [] as any,
|
||||||
/**
|
/**
|
||||||
* 查询条件
|
* 查询条件
|
||||||
*/
|
*/
|
||||||
query: {
|
query: {
|
||||||
tagPath: '',
|
instanceId: 0,
|
||||||
instanceId: null,
|
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 0,
|
pageSize: 0,
|
||||||
},
|
},
|
||||||
infoDialog: {
|
|
||||||
visible: false,
|
|
||||||
data: null as any,
|
|
||||||
instance: null as any,
|
|
||||||
query: {
|
|
||||||
instanceId: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// sql执行记录弹框
|
// sql执行记录弹框
|
||||||
sqlExecLogDialog: {
|
sqlExecLogDialog: {
|
||||||
title: '',
|
title: '',
|
||||||
visible: false,
|
visible: false,
|
||||||
dbs: [],
|
dbs: [] as any,
|
||||||
dbId: 0,
|
dbId: 0,
|
||||||
},
|
},
|
||||||
// 数据库备份弹框
|
// 数据库备份弹框
|
||||||
@@ -317,6 +309,7 @@ const state = reactive({
|
|||||||
exportDialog: {
|
exportDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
dbId: 0,
|
dbId: 0,
|
||||||
|
db: '',
|
||||||
type: 3,
|
type: 3,
|
||||||
data: [] as any,
|
data: [] as any,
|
||||||
value: [],
|
value: [],
|
||||||
@@ -335,64 +328,77 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { db, query, infoDialog, sqlExecLogDialog, exportDialog, dbEditDialog, dbBackupDialog, dbBackupHistoryDialog, dbRestoreDialog } = toRefs(state);
|
const { query, sqlExecLogDialog, exportDialog, dbEditDialog, dbBackupDialog, dbBackupHistoryDialog, dbRestoreDialog } = toRefs(state);
|
||||||
|
|
||||||
onMounted(async () => {
|
const search = async () => {
|
||||||
if (Object.keys(actionBtns).length > 0) {
|
state.query.instanceId = props.instance?.id;
|
||||||
columns.value.push(actionColumn);
|
pageTableRef.value.search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDbNames = async (db: any) => {
|
||||||
|
try {
|
||||||
|
state.loadingDbNames = true;
|
||||||
|
state.currentDbNames = await DbInst.getDbNames(db);
|
||||||
|
} finally {
|
||||||
|
state.loadingDbNames = false;
|
||||||
}
|
}
|
||||||
search();
|
};
|
||||||
});
|
|
||||||
|
|
||||||
const filterDbs = computed(() => {
|
const filterDbs = computed(() => {
|
||||||
const dbsStr = state.currentDbs;
|
const dbNames = state.currentDbNames;
|
||||||
if (!dbsStr) {
|
if (!dbNames) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const dbs = dbsStr.split(' ').map((db: any) => {
|
const dbNameObjs = dbNames.map((x) => {
|
||||||
return { dbName: db };
|
return {
|
||||||
|
dbName: x,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
return dbs.filter((db: any) => {
|
return dbNameObjs.filter((db: any) => {
|
||||||
return db.dbName.includes(state.dbNameSearch);
|
return db.dbName.includes(state.dbNameSearch);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkRouteTagPath = (query: any) => {
|
const editDb = (data: any) => {
|
||||||
if (route.query.tagPath) {
|
|
||||||
query.tagPath = route.query.tagPath as string;
|
|
||||||
}
|
|
||||||
return query;
|
|
||||||
};
|
|
||||||
|
|
||||||
const search = async (tagPath: string = '') => {
|
|
||||||
if (tagPath) {
|
|
||||||
state.query.tagPath = tagPath;
|
|
||||||
}
|
|
||||||
pageTableRef.value.search();
|
|
||||||
};
|
|
||||||
|
|
||||||
const showInfo = async (info: any) => {
|
|
||||||
state.infoDialog.data = info;
|
|
||||||
state.infoDialog.query.instanceId = info.instanceId;
|
|
||||||
const res = await dbApi.getInstance.request(state.infoDialog.query);
|
|
||||||
state.infoDialog.instance = res;
|
|
||||||
state.infoDialog.visible = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBeforeCloseInfoDialog = () => {
|
|
||||||
state.infoDialog.visible = false;
|
|
||||||
state.infoDialog.data = null;
|
|
||||||
state.infoDialog.instance = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInstances = async (instanceName = '') => {
|
|
||||||
if (!instanceName) {
|
|
||||||
state.instances = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = await dbApi.instances.request({ name: instanceName });
|
|
||||||
if (data) {
|
if (data) {
|
||||||
state.instances = data.list;
|
state.dbEditDialog.data = { ...data };
|
||||||
|
} else {
|
||||||
|
state.dbEditDialog.data = {
|
||||||
|
instanceId: props.instance.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
state.dbEditDialog.title = data ? '编辑数据库' : '新增数据库';
|
||||||
|
state.dbEditDialog.visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmEditDb = async (db: any) => {
|
||||||
|
db.instanceId = props.instance.id;
|
||||||
|
await dbApi.saveDb.request(db);
|
||||||
|
ElMessage.success('保存成功');
|
||||||
|
search();
|
||||||
|
cancelEditDb();
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelEditDb = () => {
|
||||||
|
state.dbEditDialog.visible = false;
|
||||||
|
state.dbEditDialog.data = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteDb = async () => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定删除【${state.selectionData.map((x: any) => x.name).join(', ')}】库?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
for (let db of state.selectionData) {
|
||||||
|
await dbApi.deleteDb.request({ id: db.id });
|
||||||
|
}
|
||||||
|
ElMessage.success('删除成功');
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
} finally {
|
||||||
|
search();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -400,10 +406,6 @@ const handleMoreActionCommand = (commond: any) => {
|
|||||||
const data = commond.data;
|
const data = commond.data;
|
||||||
const type = commond.type;
|
const type = commond.type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'detail': {
|
|
||||||
showInfo(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'dumpDb': {
|
case 'dumpDb': {
|
||||||
onDumpDbs(data);
|
onDumpDbs(data);
|
||||||
return;
|
return;
|
||||||
@@ -426,7 +428,9 @@ const handleMoreActionCommand = (commond: any) => {
|
|||||||
const onShowSqlExec = async (row: any) => {
|
const onShowSqlExec = async (row: any) => {
|
||||||
state.sqlExecLogDialog.title = `${row.name}`;
|
state.sqlExecLogDialog.title = `${row.name}`;
|
||||||
state.sqlExecLogDialog.dbId = row.id;
|
state.sqlExecLogDialog.dbId = row.id;
|
||||||
state.sqlExecLogDialog.dbs = row.database.split(' ');
|
DbInst.getDbNames(row).then((res) => {
|
||||||
|
state.sqlExecLogDialog.dbs = res;
|
||||||
|
});
|
||||||
state.sqlExecLogDialog.visible = true;
|
state.sqlExecLogDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -439,26 +443,32 @@ const onBeforeCloseSqlExecDialog = () => {
|
|||||||
const onShowDbBackupDialog = async (row: any) => {
|
const onShowDbBackupDialog = async (row: any) => {
|
||||||
state.dbBackupDialog.title = `${row.name}`;
|
state.dbBackupDialog.title = `${row.name}`;
|
||||||
state.dbBackupDialog.dbId = row.id;
|
state.dbBackupDialog.dbId = row.id;
|
||||||
state.dbBackupDialog.dbs = row.database.split(' ');
|
DbInst.getDbNames(row).then((res) => {
|
||||||
|
state.sqlExecLogDialog.dbs = res;
|
||||||
|
});
|
||||||
state.dbBackupDialog.visible = true;
|
state.dbBackupDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onShowDbBackupHistoryDialog = async (row: any) => {
|
const onShowDbBackupHistoryDialog = async (row: any) => {
|
||||||
state.dbBackupHistoryDialog.title = `${row.name}`;
|
state.dbBackupHistoryDialog.title = `${row.name}`;
|
||||||
state.dbBackupHistoryDialog.dbId = row.id;
|
state.dbBackupHistoryDialog.dbId = row.id;
|
||||||
state.dbBackupHistoryDialog.dbs = row.database.split(' ');
|
DbInst.getDbNames(row).then((res) => {
|
||||||
|
state.sqlExecLogDialog.dbs = res;
|
||||||
|
});
|
||||||
state.dbBackupHistoryDialog.visible = true;
|
state.dbBackupHistoryDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onShowDbRestoreDialog = async (row: any) => {
|
const onShowDbRestoreDialog = async (row: any) => {
|
||||||
state.dbRestoreDialog.title = `${row.name}`;
|
state.dbRestoreDialog.title = `${row.name}`;
|
||||||
state.dbRestoreDialog.dbId = row.id;
|
state.dbRestoreDialog.dbId = row.id;
|
||||||
state.dbRestoreDialog.dbs = row.database.split(' ');
|
DbInst.getDbNames(row).then((res) => {
|
||||||
|
state.sqlExecLogDialog.dbs = res;
|
||||||
|
});
|
||||||
state.dbRestoreDialog.visible = true;
|
state.dbRestoreDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDumpDbs = async (row: any) => {
|
const onDumpDbs = async (row: any) => {
|
||||||
const dbs = row.database.split(' ');
|
const dbs = await DbInst.getDbNames(row);
|
||||||
const data = [];
|
const data = [];
|
||||||
for (let name of dbs) {
|
for (let name of dbs) {
|
||||||
data.push({
|
data.push({
|
||||||
@@ -466,6 +476,7 @@ const onDumpDbs = async (row: any) => {
|
|||||||
label: name,
|
label: name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
state.exportDialog.db = row.name;
|
||||||
state.exportDialog.value = [];
|
state.exportDialog.value = [];
|
||||||
state.exportDialog.data = data;
|
state.exportDialog.data = data;
|
||||||
state.exportDialog.dbId = row.id;
|
state.exportDialog.dbId = row.id;
|
||||||
@@ -509,7 +520,10 @@ const supportAction = (action: string, dbType: string): boolean => {
|
|||||||
return actions.includes(action);
|
return actions.includes(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({ search });
|
const cancel = () => {
|
||||||
|
dialogVisible.value = false;
|
||||||
|
emit('cancel');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.db-list {
|
.db-list {
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
|
||||||
<template #header>
|
|
||||||
<DrawerHeader :header="title" :back="cancel" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<el-table :data="state.dbs" stripe>
|
|
||||||
<el-table-column prop="name" label="名称" show-overflow-tooltip min-width="100"> </el-table-column>
|
|
||||||
<el-table-column prop="authCertName" label="授权凭证" min-width="120" show-overflow-tooltip> </el-table-column>
|
|
||||||
<el-table-column prop="database" label="库" min-width="80">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-popover placement="bottom" :width="200" trigger="click">
|
|
||||||
<template #reference>
|
|
||||||
<el-button @click="state.currentDbs = scope.row.database" type="primary" link>查看库</el-button>
|
|
||||||
</template>
|
|
||||||
<el-table :data="filterDbs" size="small">
|
|
||||||
<el-table-column prop="dbName" label="数据库">
|
|
||||||
<template #header>
|
|
||||||
<el-input v-model="state.dbNameSearch" size="small" placeholder="库名: 输入可过滤" clearable />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-popover>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip min-width="120"> </el-table-column>
|
|
||||||
<el-table-column prop="code" label="编号" show-overflow-tooltip min-width="120"> </el-table-column>
|
|
||||||
<el-table-column min-wdith="120px">
|
|
||||||
<template #header>
|
|
||||||
操作
|
|
||||||
<el-button v-auth="perms.saveDb" type="primary" circle size="small" icon="Plus" @click="editDb(null)"> </el-button>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button v-auth="perms.saveDb" @click="editDb(scope.row)" type="primary" icon="edit" link></el-button>
|
|
||||||
<el-button class="ml1" v-auth="perms.delDb" type="danger" @click="deleteDb(scope.row)" icon="delete" link></el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<db-edit
|
|
||||||
@confirm="confirmEditDb"
|
|
||||||
@cancel="cancelEditDb"
|
|
||||||
:title="dbEditDialog.title"
|
|
||||||
v-model:visible="dbEditDialog.visible"
|
|
||||||
:instance="props.instance"
|
|
||||||
v-model:db="dbEditDialog.data"
|
|
||||||
></db-edit>
|
|
||||||
</el-drawer>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, reactive, toRefs, watchEffect } from 'vue';
|
|
||||||
import { dbApi } from './api';
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
||||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
|
||||||
import DbEdit from './DbEdit.vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
instance: {
|
|
||||||
type: [Object],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const perms = {
|
|
||||||
base: 'db',
|
|
||||||
saveDb: 'db:save',
|
|
||||||
delDb: 'db:del',
|
|
||||||
};
|
|
||||||
|
|
||||||
//定义事件
|
|
||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
dialogVisible: false,
|
|
||||||
dbs: [] as any,
|
|
||||||
currentDbs: '', // 当前数据库名,空格分割库名
|
|
||||||
dbNameSearch: '',
|
|
||||||
dbEditDialog: {
|
|
||||||
visible: false,
|
|
||||||
data: null as any,
|
|
||||||
title: '新增数据库',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { dialogVisible, dbEditDialog } = toRefs(state);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
state.dialogVisible = props.visible;
|
|
||||||
if (!state.dialogVisible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDbs();
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterDbs = computed(() => {
|
|
||||||
const dbsStr = state.currentDbs;
|
|
||||||
if (!dbsStr) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const dbs = dbsStr.split(' ').map((db: any) => {
|
|
||||||
return { dbName: db };
|
|
||||||
});
|
|
||||||
return dbs.filter((db: any) => {
|
|
||||||
return db.dbName.includes(state.dbNameSearch);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
emit('update:visible', false);
|
|
||||||
emit('cancel');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDbs = () => {
|
|
||||||
dbApi.dbs.request({ pageSize: 200, instanceId: props.instance.id }).then((res: any) => {
|
|
||||||
state.dbs = res.list || [];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const editDb = (data: any) => {
|
|
||||||
if (data) {
|
|
||||||
state.dbEditDialog.data = { ...data };
|
|
||||||
} else {
|
|
||||||
state.dbEditDialog.data = {
|
|
||||||
instanceId: props.instance.id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
state.dbEditDialog.title = data ? '编辑数据库' : '新增数据库';
|
|
||||||
state.dbEditDialog.visible = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteDb = async (db: any) => {
|
|
||||||
try {
|
|
||||||
await ElMessageBox.confirm(`确定删除【${db.name}】库?`, '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
await dbApi.deleteDb.request({ id: db.id });
|
|
||||||
ElMessage.success('删除成功');
|
|
||||||
getDbs();
|
|
||||||
} catch (err) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmEditDb = async (db: any) => {
|
|
||||||
db.instanceId = props.instance.id;
|
|
||||||
await dbApi.saveDb.request(db);
|
|
||||||
ElMessage.success('保存成功');
|
|
||||||
getDbs();
|
|
||||||
cancelEditDb();
|
|
||||||
};
|
|
||||||
|
|
||||||
const cancelEditDb = () => {
|
|
||||||
state.dbEditDialog.visible = false;
|
|
||||||
state.dbEditDialog.data = {};
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style lang="scss"></style>
|
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<template #action="{ data }">
|
<template #action="{ data }">
|
||||||
<el-button @click="showInfo(data)" link>详情</el-button>
|
<el-button @click="showInfo(data)" link>详情</el-button>
|
||||||
<el-button v-if="actionBtns[perms.saveInstance]" @click="editInstance(data)" type="primary" link>编辑</el-button>
|
<el-button v-if="actionBtns[perms.saveInstance]" @click="editInstance(data)" type="primary" link>编辑</el-button>
|
||||||
<el-button v-if="actionBtns[perms.saveDb]" @click="editDb(data)" type="primary" link>库配置</el-button>
|
<el-button v-if="actionBtns[perms.saveDb]" @click="editDb(data)" type="primary" link>库管理</el-button>
|
||||||
</template>
|
</template>
|
||||||
</page-table>
|
</page-table>
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
v-model:data="instanceEditDialog.data"
|
v-model:data="instanceEditDialog.data"
|
||||||
></instance-edit>
|
></instance-edit>
|
||||||
|
|
||||||
<instance-db-conf :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" :instance="dbEditDialog.instance" />
|
<DbList :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" :instance="dbEditDialog.instance" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ import { getTagPathSearchItem } from '../component/tag';
|
|||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
|
||||||
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
|
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
|
||||||
const InstanceDbConf = defineAsyncComponent(() => import('./InstanceDbConf.vue'));
|
const DbList = defineAsyncComponent(() => import('./DbList.vue'));
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
lazy: {
|
lazy: {
|
||||||
@@ -215,7 +215,7 @@ const deleteInstance = async () => {
|
|||||||
|
|
||||||
const editDb = (data: any) => {
|
const editDb = (data: any) => {
|
||||||
state.dbEditDialog.instance = data;
|
state.dbEditDialog.instance = data;
|
||||||
state.dbEditDialog.title = `配置 "${data.name}" 数据库`;
|
state.dbEditDialog.title = `管理 "${data.name}" 数据库`;
|
||||||
state.dbEditDialog.visible = true;
|
state.dbEditDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -58,16 +58,61 @@
|
|||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24" v-if="state.db">
|
<el-col :span="24" v-if="state.db">
|
||||||
<el-descriptions :column="4" size="small" border>
|
<el-descriptions :column="4" size="small" border>
|
||||||
<el-descriptions-item label-align="right" label="操作"
|
<el-descriptions-item label-align="right" label="操作">
|
||||||
><el-button
|
<el-button
|
||||||
:disabled="!state.db || !nowDbInst.id"
|
:disabled="!state.db || !nowDbInst.id"
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="Search"
|
icon="Search"
|
||||||
@click="addQueryTab({ id: nowDbInst.id, dbs: nowDbInst.databases }, state.db)"
|
link
|
||||||
size="small"
|
@click="
|
||||||
>新建查询</el-button
|
addQueryTab(
|
||||||
></el-descriptions-item
|
{ id: nowDbInst.id, dbs: nowDbInst.databases, nodeKey: getSqlMenuNodeKey(nowDbInst.id, state.db) },
|
||||||
|
state.db
|
||||||
|
)
|
||||||
|
"
|
||||||
|
title="新建查询"
|
||||||
>
|
>
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<template v-if="!dbConfig.locationTreeNode">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
<el-button @click="locationNowTreeNode(null)" title="定位至左侧树的指定位置" icon="Location" link></el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
<!-- 数据库展示配置 -->
|
||||||
|
<el-popover
|
||||||
|
popper-style="max-height: 550px; overflow: auto; max-width: 450px"
|
||||||
|
placement="bottom"
|
||||||
|
width="auto"
|
||||||
|
title="数据库展示配置"
|
||||||
|
trigger="click"
|
||||||
|
>
|
||||||
|
<el-row>
|
||||||
|
<el-checkbox
|
||||||
|
v-model="dbConfig.showColumnComment"
|
||||||
|
label="显示字段备注"
|
||||||
|
:true-value="1"
|
||||||
|
:false-value="0"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<el-checkbox
|
||||||
|
v-model="dbConfig.locationTreeNode"
|
||||||
|
label="自动定位树节点"
|
||||||
|
:true-value="1"
|
||||||
|
:false-value="0"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<template #reference>
|
||||||
|
<el-link type="primary" icon="setting" :underline="false"></el-link>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label-align="right" label="tag">{{ nowDbInst.tagPath }}</el-descriptions-item>
|
<el-descriptions-item label-align="right" label="tag">{{ nowDbInst.tagPath }}</el-descriptions-item>
|
||||||
|
|
||||||
@@ -103,7 +148,9 @@
|
|||||||
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
||||||
<template #label>
|
<template #label>
|
||||||
<el-popover :show-after="1000" placement="bottom-start" trigger="hover" :width="250">
|
<el-popover :show-after="1000" placement="bottom-start" trigger="hover" :width="250">
|
||||||
<template #reference> {{ dt.label }} </template>
|
<template #reference>
|
||||||
|
<span class="font12">{{ dt.label }}</span>
|
||||||
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-descriptions :column="1" size="small">
|
<el-descriptions :column="1" size="small">
|
||||||
<el-descriptions-item label="tagPath">
|
<el-descriptions-item label="tagPath">
|
||||||
@@ -184,7 +231,7 @@ import { getDbDialect, schemaDbTypes } from './dialect/index';
|
|||||||
import { sleep } from '@/common/utils/loading';
|
import { sleep } from '@/common/utils/loading';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
import { Pane, Splitpanes } from 'splitpanes';
|
import { Pane, Splitpanes } from 'splitpanes';
|
||||||
import { useEventListener } from '@vueuse/core';
|
import { useEventListener, useStorage } from '@vueuse/core';
|
||||||
import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
|
import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
|
||||||
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
@@ -272,7 +319,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath)
|
|||||||
// 数据库实例节点类型
|
// 数据库实例节点类型
|
||||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||||
const params = parentNode.params;
|
const params = parentNode.params;
|
||||||
const dbs = params.database.split(' ')?.sort();
|
const dbs = (await DbInst.getDbNames(params))?.sort();
|
||||||
|
|
||||||
const flowProcdef = await procdefApi.getByResource.request({ resourceType: TagResourceTypeEnum.DbName.value, resourceCode: params.code });
|
const flowProcdef = await procdefApi.getByResource.request({ resourceType: TagResourceTypeEnum.DbName.value, resourceCode: params.code });
|
||||||
return dbs.map((x: any) => {
|
return dbs.map((x: any) => {
|
||||||
@@ -456,6 +503,8 @@ const state = reactive({
|
|||||||
|
|
||||||
const { nowDbInst, tableCreateDialog } = toRefs(state);
|
const { nowDbInst, tableCreateDialog } = toRefs(state);
|
||||||
|
|
||||||
|
const dbConfig = useStorage('dbConfig', { showColumnComment: false, locationTreeNode: false });
|
||||||
|
|
||||||
const serverInfoReqParam = ref({
|
const serverInfoReqParam = ref({
|
||||||
instanceId: 0,
|
instanceId: 0,
|
||||||
});
|
});
|
||||||
@@ -670,6 +719,18 @@ const onTabChange = () => {
|
|||||||
registerDbCompletionItemProvider(nowTab.dbId, nowTab.db, nowTab.params.dbs, nowDbInst.value.type);
|
registerDbCompletionItemProvider(nowTab.dbId, nowTab.db, nowTab.params.dbs, nowDbInst.value.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dbConfig.value.locationTreeNode) {
|
||||||
|
locationNowTreeNode(nowTab);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定位至当前树节点
|
||||||
|
*/
|
||||||
|
const locationNowTreeNode = (nowTab: any = null) => {
|
||||||
|
if (!nowTab) {
|
||||||
|
nowTab = state.tabs.get(state.activeName);
|
||||||
|
}
|
||||||
tagTreeRef.value.setCurrentKey(nowTab?.treeNodeKey);
|
tagTreeRef.value.setCurrentKey(nowTab?.treeNodeKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -854,7 +915,7 @@ const getNowDbInfo = () => {
|
|||||||
margin: 0 0 5px;
|
margin: 0 0 5px;
|
||||||
|
|
||||||
.el-tabs__item {
|
.el-tabs__item {
|
||||||
padding: 0 10px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const dbApi = {
|
|||||||
instances: Api.newGet('/instances'),
|
instances: Api.newGet('/instances'),
|
||||||
getInstance: Api.newGet('/instances/{instanceId}'),
|
getInstance: Api.newGet('/instances/{instanceId}'),
|
||||||
getAllDatabase: Api.newPost('/instances/databases'),
|
getAllDatabase: Api.newPost('/instances/databases'),
|
||||||
|
getDbNamesByAc: Api.newGet('/instances/databases/{authCertName}'),
|
||||||
getInstanceServerInfo: Api.newGet('/instances/{instanceId}/server-info'),
|
getInstanceServerInfo: Api.newGet('/instances/{instanceId}/server-info'),
|
||||||
testConn: Api.newPost('/instances/test-conn'),
|
testConn: Api.newPost('/instances/test-conn'),
|
||||||
saveInstance: Api.newPost('/instances'),
|
saveInstance: Api.newPost('/instances'),
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import SvgIcon from '@/components/svgIcon/index.vue';
|
|||||||
import { getDbDialect, noSchemaTypes } from '@/views/ops/db/dialect';
|
import { getDbDialect, noSchemaTypes } from '@/views/ops/db/dialect';
|
||||||
import TagTreeResourceSelect from '../../component/TagTreeResourceSelect.vue';
|
import TagTreeResourceSelect from '../../component/TagTreeResourceSelect.vue';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import { DbInst } from '../db';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dbId: {
|
dbId: {
|
||||||
@@ -101,9 +102,9 @@ const noSchemaType = (type: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 数据库实例节点类型
|
// 数据库实例节点类型
|
||||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
|
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||||
const params = parentNode.params;
|
const params = parentNode.params;
|
||||||
const dbs = params.database.split(' ')?.sort();
|
const dbs = (await DbInst.getDbNames(params))?.sort();
|
||||||
let fn: NodeType;
|
let fn: NodeType;
|
||||||
if (noSchemaType(params.type)) {
|
if (noSchemaType(params.type)) {
|
||||||
fn = MysqlNodeTypes;
|
fn = MysqlNodeTypes;
|
||||||
|
|||||||
@@ -121,7 +121,7 @@
|
|||||||
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
<el-empty class="h100" :description="props.emptyText" :image-size="100" />
|
<el-empty :description="props.emptyText" :image-size="100" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-v2>
|
</el-table-v2>
|
||||||
|
|||||||
@@ -48,21 +48,6 @@
|
|||||||
<el-tooltip :show-after="500" class="box-item" effect="dark" content="commit" placement="top">
|
<el-tooltip :show-after="500" class="box-item" effect="dark" content="commit" placement="top">
|
||||||
<el-link @click="onCommit()" type="success" icon="CircleCheck" :underline="false"> </el-link>
|
<el-link @click="onCommit()" type="success" icon="CircleCheck" :underline="false"> </el-link>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
|
||||||
|
|
||||||
<!-- 表数据展示配置 -->
|
|
||||||
<el-popover
|
|
||||||
popper-style="max-height: 550px; overflow: auto; max-width: 450px"
|
|
||||||
placement="bottom"
|
|
||||||
width="auto"
|
|
||||||
title="展示配置"
|
|
||||||
trigger="click"
|
|
||||||
>
|
|
||||||
<el-checkbox v-model="dbConfig.showColumnComment" label="显示字段备注" :true-value="true" :false-value="false" size="small" />
|
|
||||||
<template #reference>
|
|
||||||
<el-link type="primary" icon="setting" :underline="false"></el-link>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
|
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
|
||||||
@@ -258,7 +243,7 @@ import { DbInst } from '@/views/ops/db/db';
|
|||||||
import DbTableData from './DbTableData.vue';
|
import DbTableData from './DbTableData.vue';
|
||||||
import { DbDialect } from '@/views/ops/db/dialect';
|
import { DbDialect } from '@/views/ops/db/dialect';
|
||||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||||
import { useEventListener, useStorage } from '@vueuse/core';
|
import { useEventListener } from '@vueuse/core';
|
||||||
import { copyToClipboard } from '@/common/utils/string';
|
import { copyToClipboard } from '@/common/utils/string';
|
||||||
import DbTableDataForm from './DbTableDataForm.vue';
|
import DbTableDataForm from './DbTableDataForm.vue';
|
||||||
|
|
||||||
@@ -288,8 +273,6 @@ const condDialogInputRef: Ref = ref(null);
|
|||||||
|
|
||||||
const defaultPageSize = DbInst.DefaultLimit;
|
const defaultPageSize = DbInst.DefaultLimit;
|
||||||
|
|
||||||
const dbConfig = useStorage('dbConfig', { showColumnComment: false });
|
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
datas: [],
|
datas: [],
|
||||||
sql: '', // 当前数据tab执行的sql
|
sql: '', // 当前数据tab执行的sql
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<el-row class="mb5">
|
<el-row class="mb5">
|
||||||
<el-popover v-model:visible="state.dumpInfo.visible" trigger="click" :width="470" placement="right">
|
<el-popover v-model:visible="state.dumpInfo.visible" trigger="click" :width="470" placement="right">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button class="ml5" type="success" size="small">导出</el-button>
|
<el-button :disabled="state.dumpInfo.tables?.length == 0" class="ml5" type="success" size="small">导出</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-form-item label="导出内容: ">
|
<el-form-item label="导出内容: ">
|
||||||
<el-radio-group v-model="dumpInfo.type">
|
<el-radio-group v-model="dumpInfo.type">
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { editor, languages, Position } from 'monaco-editor';
|
|||||||
import { registerCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
import { registerCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
||||||
import { DbDialect, EditorCompletionItem, getDbDialect } from './dialect';
|
import { DbDialect, EditorCompletionItem, getDbDialect } from './dialect';
|
||||||
import { type RemovableRef, useLocalStorage } from '@vueuse/core';
|
import { type RemovableRef, useLocalStorage } from '@vueuse/core';
|
||||||
|
import { DbGetDbNamesMode } from './enums';
|
||||||
|
|
||||||
const hintsStorage: RemovableRef<Map<string, any>> = useLocalStorage('db-table-hints', new Map());
|
const hintsStorage: RemovableRef<Map<string, any>> = useLocalStorage('db-table-hints', new Map());
|
||||||
const tableStorage: RemovableRef<Map<string, any>> = useLocalStorage('db-tables', new Map());
|
const tableStorage: RemovableRef<Map<string, any>> = useLocalStorage('db-tables', new Map());
|
||||||
@@ -503,6 +504,19 @@ export class DbInst {
|
|||||||
col.columnType = col.dataType;
|
col.columnType = col.dataType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据数据库配置信息获取对应的库名列表
|
||||||
|
* @param db db配置信息
|
||||||
|
* @returns 库名列表
|
||||||
|
*/
|
||||||
|
static async getDbNames(db: any) {
|
||||||
|
if (db.getDatabaseMode == DbGetDbNamesMode.Assign.value) {
|
||||||
|
return db.database.split(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await dbApi.getDbNamesByAc.request({ authCertName: db.authCertName });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { EnumValue } from '@/common/Enum';
|
import { EnumValue } from '@/common/Enum';
|
||||||
|
|
||||||
|
export const DbGetDbNamesMode = {
|
||||||
|
Auto: EnumValue.of(-1, '实时获取').setTagType('warning'),
|
||||||
|
Assign: EnumValue.of(1, '指定库名').setTagType('primary'),
|
||||||
|
};
|
||||||
|
|
||||||
// 数据库sql执行类型
|
// 数据库sql执行类型
|
||||||
export const DbSqlExecTypeEnum = {
|
export const DbSqlExecTypeEnum = {
|
||||||
Update: EnumValue.of(1, 'UPDATE').setTagColor('#E4F5EB'),
|
Update: EnumValue.of(1, 'UPDATE').setTagColor('#E4F5EB'),
|
||||||
|
|||||||
@@ -34,20 +34,15 @@
|
|||||||
|
|
||||||
<Pane>
|
<Pane>
|
||||||
<div class="machine-terminal-tabs card pd5">
|
<div class="machine-terminal-tabs card pd5">
|
||||||
<el-tabs
|
<el-tabs v-if="state.tabs.size > 0" type="card" @tab-remove="onRemoveTab" style="width: 100%" v-model="state.activeTermName" class="h100">
|
||||||
v-if="state.tabs.size > 0"
|
|
||||||
type="card"
|
|
||||||
@tab-remove="onRemoveTab"
|
|
||||||
@tab-change="onTabChange"
|
|
||||||
style="width: 100%"
|
|
||||||
v-model="state.activeTermName"
|
|
||||||
class="h100"
|
|
||||||
>
|
|
||||||
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
||||||
<template #label>
|
<template #label>
|
||||||
<el-popconfirm @confirm="handleReconnect(dt, true)" title="确认重新连接?">
|
<el-popconfirm @confirm="handleReconnect(dt, true)" title="确认重新连接?">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-icon class="mr5" :color="dt.status == 1 ? '#67c23a' : '#f56c6c'" :title="dt.status == 1 ? '' : '点击重连'"
|
<el-icon
|
||||||
|
class="mr5"
|
||||||
|
:color="EnumValue.getEnumByValue(TerminalStatusEnum, dt.status)?.extra?.iconColor"
|
||||||
|
:title="dt.status == TerminalStatusEnum.Connected.value ? '' : '点击重连'"
|
||||||
><Connection />
|
><Connection />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
@@ -62,7 +57,7 @@
|
|||||||
<el-descriptions :column="1" size="small">
|
<el-descriptions :column="1" size="small">
|
||||||
<el-descriptions-item label="机器名"> {{ dt.params?.name }} </el-descriptions-item>
|
<el-descriptions-item label="机器名"> {{ dt.params?.name }} </el-descriptions-item>
|
||||||
<el-descriptions-item label="host"> {{ dt.params?.ip }} : {{ dt.params?.port }} </el-descriptions-item>
|
<el-descriptions-item label="host"> {{ dt.params?.ip }} : {{ dt.params?.port }} </el-descriptions-item>
|
||||||
<el-descriptions-item label="username"> {{ dt.params?.username }} </el-descriptions-item>
|
<el-descriptions-item label="username"> {{ dt.params?.selectAuthCert.username }} </el-descriptions-item>
|
||||||
<el-descriptions-item label="remark"> {{ dt.params?.remark }} </el-descriptions-item>
|
<el-descriptions-item label="remark"> {{ dt.params?.remark }} </el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</template>
|
</template>
|
||||||
@@ -165,13 +160,14 @@ import TagTree from '../component/TagTree.vue';
|
|||||||
import { Pane, Splitpanes } from 'splitpanes';
|
import { Pane, Splitpanes } from 'splitpanes';
|
||||||
import { ContextmenuItem } from '@/components/contextmenu/index';
|
import { ContextmenuItem } from '@/components/contextmenu/index';
|
||||||
import TerminalBody from '@/components/terminal/TerminalBody.vue';
|
import TerminalBody from '@/components/terminal/TerminalBody.vue';
|
||||||
import { TerminalStatus } from '@/components/terminal/common';
|
import { TerminalStatus, TerminalStatusEnum } from '@/components/terminal/common';
|
||||||
import MachineRdp from '@/components/terminal-rdp/MachineRdp.vue';
|
import MachineRdp from '@/components/terminal-rdp/MachineRdp.vue';
|
||||||
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
|
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
|
||||||
import ResourceTags from '../component/ResourceTags.vue';
|
import ResourceTags from '../component/ResourceTags.vue';
|
||||||
import { MachineProtocolEnum } from './enums';
|
import { MachineProtocolEnum } from './enums';
|
||||||
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import EnumValue from '@/common/Enum';
|
||||||
|
|
||||||
// 组件
|
// 组件
|
||||||
const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
|
const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
|
||||||
@@ -340,8 +336,13 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
() => state.activeTermName,
|
() => state.activeTermName,
|
||||||
(newValue, oldValue) => {
|
(newValue, oldValue) => {
|
||||||
|
fitTerminal();
|
||||||
|
|
||||||
oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
|
oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
|
||||||
terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
|
terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
|
||||||
|
|
||||||
|
const nowTab = state.tabs.get(state.activeTermName);
|
||||||
|
tagTreeRef.value.setCurrentKey(nowTab?.authCert);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -509,7 +510,7 @@ const onRemoveTab = (targetName: string) => {
|
|||||||
|
|
||||||
state.tabs.delete(targetName);
|
state.tabs.delete(targetName);
|
||||||
state.activeTermName = activeTermName;
|
state.activeTermName = activeTermName;
|
||||||
onTabChange();
|
// onTabChange();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -535,21 +536,13 @@ const onResizeTagTree = () => {
|
|||||||
fitTerminal();
|
fitTerminal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTabChange = () => {
|
|
||||||
fitTerminal();
|
|
||||||
|
|
||||||
const nowTab = state.tabs.get(state.activeTermName);
|
|
||||||
tagTreeRef.value.setCurrentKey(nowTab?.authCert);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fitTerminal = () => {
|
const fitTerminal = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let info = state.tabs.get(state.activeTermName);
|
let info = state.tabs.get(state.activeTermName);
|
||||||
if (info) {
|
if (info) {
|
||||||
terminalRefs[info.key]?.fitTerminal && terminalRefs[info.key]?.fitTerminal();
|
terminalRefs[info.key]?.fitTerminal && terminalRefs[info.key]?.fitTerminal();
|
||||||
terminalRefs[info.key]?.focus && terminalRefs[info.key]?.focus();
|
|
||||||
}
|
}
|
||||||
}, 100);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReconnect = (tab: any, force = false) => {
|
const handleReconnect = (tab: any, force = false) => {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export const machineApi = {
|
|||||||
process: Api.newGet('/machines/{id}/process'),
|
process: Api.newGet('/machines/{id}/process'),
|
||||||
// 终止进程
|
// 终止进程
|
||||||
killProcess: Api.newDelete('/machines/{id}/process'),
|
killProcess: Api.newDelete('/machines/{id}/process'),
|
||||||
|
users: Api.newGet('/machines/{id}/users'),
|
||||||
|
groups: Api.newGet('/machines/{id}/groups'),
|
||||||
testConn: Api.newPost('/machines/test-conn'),
|
testConn: Api.newPost('/machines/test-conn'),
|
||||||
// 保存按钮
|
// 保存按钮
|
||||||
saveMachine: Api.newPost('/machines'),
|
saveMachine: Api.newPost('/machines'),
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-popover placement="right" width="auto" title="机器详情" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<el-link @click="getMachineDetail" type="primary">{{ props.code }}</el-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-descriptions v-loading="state.loading" :column="3" border>
|
||||||
|
<el-descriptions-item :span="1" label="机器id">{{ state.machineDetail.id }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" label="编号">{{ state.machineDetail.code }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" label="名称">{{ state.machineDetail.name }}</el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :span="3" label="关联标签"><ResourceTags :tags="state.machineDetail.tags" /></el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :span="2" label="IP">{{ state.machineDetail.ip }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" label="端口">{{ state.machineDetail.port }}</el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :span="3" label="备注">{{ state.machineDetail.remark }}</el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :span="1.5" label="SSH隧道">{{ state.machineDetail.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1.5" label="终端回放">{{ state.machineDetail.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :span="2" label="创建时间">{{ formatDate(state.machineDetail.createTime) }} </el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" label="创建者">{{ state.machineDetail.creator }}</el-descriptions-item>
|
||||||
|
|
||||||
|
<el-descriptions-item :span="2" label="更新时间">{{ formatDate(state.machineDetail.updateTime) }} </el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="1" label="修改者">{{ state.machineDetail.modifier }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { machineApi } from '../api';
|
||||||
|
import { formatDate } from '@/common/utils/format';
|
||||||
|
import ResourceTags from '../../component/ResourceTags.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
code: {
|
||||||
|
type: [String],
|
||||||
|
requierd: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
loading: false,
|
||||||
|
machineDetail: {} as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getMachineDetail = async () => {
|
||||||
|
try {
|
||||||
|
state.machineDetail = {};
|
||||||
|
state.loading = true;
|
||||||
|
const res = await machineApi.list.request({
|
||||||
|
code: props.code,
|
||||||
|
});
|
||||||
|
if (res.total == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.machineDetail = res.list?.[0];
|
||||||
|
} finally {
|
||||||
|
state.loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
:title="title"
|
:title="title"
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
|
@open="search()"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:before-close="cancel"
|
:before-close="cancel"
|
||||||
:show-close="true"
|
:show-close="true"
|
||||||
@@ -13,20 +14,13 @@
|
|||||||
ref="pageTableRef"
|
ref="pageTableRef"
|
||||||
:page-api="cronJobApi.execList"
|
:page-api="cronJobApi.execList"
|
||||||
:lazy="true"
|
:lazy="true"
|
||||||
:data-handler-fn="parseData"
|
|
||||||
:search-items="searchItems"
|
:search-items="searchItems"
|
||||||
v-model:query-form="params"
|
v-model:query-form="params"
|
||||||
:data="state.data.list"
|
:data="state.data.list"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
>
|
>
|
||||||
<template #machineSelect>
|
<template #machineCode="{ data }">
|
||||||
<el-select v-model="params.machineId" filterable placeholder="选择机器查询" clearable>
|
<MachineDetail :code="data.machineCode" />
|
||||||
<el-option v-for="ac in machineMap.values()" :key="ac.id" :value="ac.id" :label="ac.ip">
|
|
||||||
{{ ac.ip }}
|
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
|
||||||
{{ ac.tagPath }}{{ ac.name }}
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
</template>
|
||||||
</page-table>
|
</page-table>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@@ -34,12 +28,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, ref, toRefs, reactive, Ref } from 'vue';
|
import { ref, toRefs, reactive, Ref } from 'vue';
|
||||||
import { cronJobApi, machineApi } from '../api';
|
import { cronJobApi } from '../api';
|
||||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||||
import { TableColumn } from '@/components/pagetable';
|
import { TableColumn } from '@/components/pagetable';
|
||||||
import { CronJobExecStatusEnum } from '../enums';
|
import { CronJobExecStatusEnum } from '../enums';
|
||||||
import { SearchItem } from '@/components/SearchForm';
|
import { SearchItem } from '@/components/SearchForm';
|
||||||
|
import MachineDetail from '../component/MachineDetail.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -53,13 +48,10 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'update:data', 'cancel']);
|
const searchItems = [SearchItem.input('machineCode', '机器编号'), SearchItem.select('status', '状态').withEnum(CronJobExecStatusEnum)];
|
||||||
|
|
||||||
const searchItems = [SearchItem.slot('machineId', '机器', 'machineSelect'), SearchItem.select('status', '状态').withEnum(CronJobExecStatusEnum)];
|
|
||||||
|
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
TableColumn.new('machineIp', '机器IP').setMinWidth(120),
|
TableColumn.new('machineCode', '机器编号').isSlot(),
|
||||||
TableColumn.new('machineName', '机器名称').setMinWidth(100),
|
|
||||||
TableColumn.new('status', '状态').typeTag(CronJobExecStatusEnum).setMinWidth(70),
|
TableColumn.new('status', '状态').typeTag(CronJobExecStatusEnum).setMinWidth(70),
|
||||||
TableColumn.new('res', '执行结果').setMinWidth(250).canBeautify(),
|
TableColumn.new('res', '执行结果').setMinWidth(250).canBeautify(),
|
||||||
TableColumn.new('execTime', '执行时间').isTime().setMinWidth(150),
|
TableColumn.new('execTime', '执行时间').isTime().setMinWidth(150),
|
||||||
@@ -72,10 +64,10 @@ const state = reactive({
|
|||||||
tags: [] as any,
|
tags: [] as any,
|
||||||
params: {
|
params: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 8,
|
||||||
cronJobId: 0,
|
cronJobId: 0,
|
||||||
status: null,
|
status: null,
|
||||||
machineId: null,
|
machineCode: '',
|
||||||
},
|
},
|
||||||
// 列表数据
|
// 列表数据
|
||||||
data: {
|
data: {
|
||||||
@@ -85,64 +77,17 @@ const state = reactive({
|
|||||||
machines: [],
|
machines: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const machineMap: Map<number, any> = new Map();
|
const { params } = toRefs(state);
|
||||||
|
|
||||||
const { dialogVisible, params } = toRefs(state);
|
const dialogVisible = defineModel<boolean>('visible');
|
||||||
|
|
||||||
watch(props, async (newValue: any) => {
|
|
||||||
state.dialogVisible = newValue.visible;
|
|
||||||
if (!newValue.visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const machineIds = await cronJobApi.relateMachineIds.request({
|
|
||||||
cronJobId: props.data?.id,
|
|
||||||
});
|
|
||||||
const res = await machineApi.list.request({
|
|
||||||
ids: machineIds?.join(','),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.list?.forEach((x: any) => {
|
|
||||||
machineMap.set(x.id, x);
|
|
||||||
});
|
|
||||||
|
|
||||||
state.params.cronJobId = props.data?.id;
|
|
||||||
search();
|
|
||||||
});
|
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
|
state.params.cronJobId = props.data?.id;
|
||||||
pageTableRef.value.search();
|
pageTableRef.value.search();
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseData = async (res: any) => {
|
|
||||||
const dataList = res.list;
|
|
||||||
// 填充机器信息
|
|
||||||
for (let x of dataList) {
|
|
||||||
const machineId = x.machineId;
|
|
||||||
let machine = machineMap.get(machineId);
|
|
||||||
// 如果未找到,则可能被移除,则调接口查询机器信息
|
|
||||||
if (!machine) {
|
|
||||||
const machineRes = await machineApi.list.request({ ids: machineId });
|
|
||||||
if (!machineRes.list) {
|
|
||||||
machine = {
|
|
||||||
id: machineId,
|
|
||||||
ip: machineId,
|
|
||||||
name: '该机器已被删除',
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
machine = machineRes.list[0];
|
|
||||||
}
|
|
||||||
machineMap.set(machineId, machine);
|
|
||||||
}
|
|
||||||
|
|
||||||
x.machineIp = machine?.ip;
|
|
||||||
x.machineName = machine?.name;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit('update:visible', false);
|
dialogVisible.value = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
initData();
|
initData();
|
||||||
}, 500);
|
}, 500);
|
||||||
@@ -152,7 +97,7 @@ const initData = () => {
|
|||||||
state.data.list = [];
|
state.data.list = [];
|
||||||
state.data.total = 0;
|
state.data.total = 0;
|
||||||
state.params.pageNum = 1;
|
state.params.pageNum = 1;
|
||||||
state.params.machineId = null;
|
state.params.machineCode = '';
|
||||||
state.params.status = null;
|
state.params.status = null;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="path" label="路径" min-width="150px" show-overflow-tooltip>
|
<el-table-column prop="path" label="路径" min-width="180" show-overflow-tooltip>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.path" :disabled="scope.row.id != null" clearable> </el-input>
|
<el-input v-model="scope.row.path" :disabled="scope.row.id != null" clearable> </el-input>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" min-wdith="120px">
|
<el-table-column label="操作" min-width="130">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="success-filled" plain></el-button>
|
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="success-filled" plain></el-button>
|
||||||
<el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="tickets" plain></el-button>
|
<el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="tickets" plain></el-button>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
>
|
>
|
||||||
<el-table-column type="selection" width="30" />
|
<el-table-column type="selection" width="30" />
|
||||||
|
|
||||||
<el-table-column prop="name" label="名称">
|
<el-table-column prop="name" label="名称" min-width="380">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="machine-file-table-header">
|
<div class="machine-file-table-header">
|
||||||
<div>
|
<div>
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="size" label="大小" width="100" sortable>
|
<el-table-column prop="size" label="大小" min-width="90" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatByteSize(scope.row.size) }} </span>
|
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatByteSize(scope.row.size) }} </span>
|
||||||
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && scope.row.dirSize"> {{ scope.row.dirSize }} </span>
|
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && scope.row.dirSize"> {{ scope.row.dirSize }} </span>
|
||||||
@@ -182,7 +182,11 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="mode" label="属性" width="110"> </el-table-column>
|
<el-table-column prop="mode" label="属性" width="110"> </el-table-column>
|
||||||
<el-table-column prop="modTime" label="修改时间" width="165" sortable> </el-table-column>
|
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" prop="username" label="用户" min-width="55" show-overflow-tooltip>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" prop="groupname" label="组" min-width="55" show-overflow-tooltip>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="modTime" label="修改时间" width="160" sortable> </el-table-column>
|
||||||
|
|
||||||
<el-table-column width="100">
|
<el-table-column width="100">
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -288,6 +292,7 @@ import MachineFileContent from './MachineFileContent.vue';
|
|||||||
import { getToken } from '@/common/utils/storage';
|
import { getToken } from '@/common/utils/storage';
|
||||||
import { convertToBytes, formatByteSize } from '@/common/utils/format';
|
import { convertToBytes, formatByteSize } from '@/common/utils/format';
|
||||||
import { getMachineConfig } from '@/common/sysconfig';
|
import { getMachineConfig } from '@/common/sysconfig';
|
||||||
|
import { MachineProtocolEnum } from '../enums';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
machineId: { type: Number },
|
machineId: { type: Number },
|
||||||
@@ -303,6 +308,9 @@ const folderUploadRef: any = ref();
|
|||||||
|
|
||||||
const folderType = 'd';
|
const folderType = 'd';
|
||||||
|
|
||||||
|
const userMap = new Map<number, any>();
|
||||||
|
const groupMap = new Map<number, any>();
|
||||||
|
|
||||||
// 路径分隔符
|
// 路径分隔符
|
||||||
const pathSep = '/';
|
const pathSep = '/';
|
||||||
|
|
||||||
@@ -343,10 +351,50 @@ const { basePath, nowPath, loading, fileNameFilter, progressNum, uploadProgressS
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
state.basePath = props.path;
|
state.basePath = props.path;
|
||||||
|
const machineId = props.machineId;
|
||||||
|
|
||||||
|
if (props.protocol == MachineProtocolEnum.Ssh.value) {
|
||||||
|
machineApi.users.request({ id: machineId }).then((res: any) => {
|
||||||
|
for (let user of res) {
|
||||||
|
userMap.set(user.uid, user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
machineApi.groups.request({ id: machineId }).then((res: any) => {
|
||||||
|
for (let group of res) {
|
||||||
|
groupMap.set(group.gid, group);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setFiles(props.path);
|
setFiles(props.path);
|
||||||
state.machineConfig = await getMachineConfig();
|
state.machineConfig = await getMachineConfig();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => props.machineId,
|
||||||
|
// () => {
|
||||||
|
// if (props.protocol != MachineProtocolEnum.Ssh.value) {
|
||||||
|
// userMap.clear();
|
||||||
|
// groupMap.clear();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const machineId = props.machineId;
|
||||||
|
// machineApi.users.request({ machineId }).then((res: any) => {
|
||||||
|
// for (let user of res) {
|
||||||
|
// userMap.set(user.uid, user);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// machineApi.groups.request({ machineId }).then((res: any) => {
|
||||||
|
// for (let group of res) {
|
||||||
|
// groupMap.set(group.gid, group);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
const filterFiles = computed(() =>
|
const filterFiles = computed(() =>
|
||||||
state.files.filter((data: any) => !state.fileNameFilter || data.name.toLowerCase().includes(state.fileNameFilter.toLowerCase()))
|
state.files.filter((data: any) => !state.fileNameFilter || data.name.toLowerCase().includes(state.fileNameFilter.toLowerCase()))
|
||||||
);
|
);
|
||||||
@@ -517,6 +565,11 @@ const lsFile = async (path: string) => {
|
|||||||
path,
|
path,
|
||||||
});
|
});
|
||||||
for (const file of res) {
|
for (const file of res) {
|
||||||
|
if (props.protocol == MachineProtocolEnum.Ssh.value) {
|
||||||
|
file.username = userMap.get(file.uid)?.uname || file.uid;
|
||||||
|
file.groupname = groupMap.get(file.gid)?.gname || file.gid;
|
||||||
|
}
|
||||||
|
|
||||||
const type = file.type;
|
const type = file.type;
|
||||||
if (type == folderType) {
|
if (type == folderType) {
|
||||||
file.isFolder = true;
|
file.isFolder = true;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="codePaths" label="关联机器" min-width="250px" show-overflow-tooltip>
|
<el-table-column prop="codePaths" label="关联机器" min-width="250px" show-overflow-tooltip>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<TagCodePath :path="scope.row.tags.map((tag: any) => tag.codePath)" />
|
<TagCodePath :path="scope.row.tags?.map((tag: any) => tag.codePath)" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip width="120px"> </el-table-column>
|
<el-table-column prop="remark" label="备注" show-overflow-tooltip width="120px"> </el-table-column>
|
||||||
@@ -173,7 +173,7 @@ const openFormDialog = (data: any) => {
|
|||||||
state.form = { ...DefaultForm };
|
state.form = { ...DefaultForm };
|
||||||
} else {
|
} else {
|
||||||
state.form = _.cloneDeep(data);
|
state.form = _.cloneDeep(data);
|
||||||
state.form.codePaths = data.tags.map((tag: any) => tag.codePath);
|
state.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
|
||||||
}
|
}
|
||||||
state.dialogVisible = true;
|
state.dialogVisible = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,14 @@
|
|||||||
<el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="edit(false)">添加</el-button>
|
<el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="edit(false)">添加</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #resourceCode="{ data }">
|
||||||
|
<SvgIcon
|
||||||
|
:name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.resourceType)?.extra.icon"
|
||||||
|
:color="EnumValue.getEnumByValue(TagResourceTypeEnum, data.resourceType)?.extra.iconColor"
|
||||||
|
/>
|
||||||
|
{{ data.resourceCode }}
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #action="{ data }">
|
<template #action="{ data }">
|
||||||
<el-button v-auth="'authcert:save'" @click="edit(data)" type="primary" link>编辑</el-button>
|
<el-button v-auth="'authcert:save'" @click="edit(data)" type="primary" link>编辑</el-button>
|
||||||
|
|
||||||
@@ -41,6 +49,7 @@ import { SearchItem } from '@/components/SearchForm';
|
|||||||
import { AuthCertCiphertextTypeEnum, AuthCertTypeEnum } from './enums';
|
import { AuthCertCiphertextTypeEnum, AuthCertTypeEnum } from './enums';
|
||||||
import { ResourceTypeEnum, TagResourceTypeEnum } from '@/common/commonEnum';
|
import { ResourceTypeEnum, TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
import ResourceAuthCertEdit from '../component/ResourceAuthCertEdit.vue';
|
import ResourceAuthCertEdit from '../component/ResourceAuthCertEdit.vue';
|
||||||
|
import EnumValue from '@/common/Enum';
|
||||||
|
|
||||||
const pageTableRef: Ref<any> = ref(null);
|
const pageTableRef: Ref<any> = ref(null);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
@@ -50,6 +59,7 @@ const state = reactive({
|
|||||||
name: null,
|
name: null,
|
||||||
},
|
},
|
||||||
searchItems: [
|
searchItems: [
|
||||||
|
SearchItem.input('resourceCode', '资源编号'),
|
||||||
SearchItem.input('name', '凭证名称'),
|
SearchItem.input('name', '凭证名称'),
|
||||||
SearchItem.select('resourceType', '资源类型').withEnum(ResourceTypeEnum),
|
SearchItem.select('resourceType', '资源类型').withEnum(ResourceTypeEnum),
|
||||||
SearchItem.select('type', '凭证类型').withEnum(AuthCertTypeEnum),
|
SearchItem.select('type', '凭证类型').withEnum(AuthCertTypeEnum),
|
||||||
@@ -60,8 +70,7 @@ const state = reactive({
|
|||||||
TableColumn.new('type', '凭证类型').typeTag(AuthCertTypeEnum),
|
TableColumn.new('type', '凭证类型').typeTag(AuthCertTypeEnum),
|
||||||
TableColumn.new('username', '用户名'),
|
TableColumn.new('username', '用户名'),
|
||||||
TableColumn.new('ciphertextType', '密文类型').typeTag(AuthCertCiphertextTypeEnum),
|
TableColumn.new('ciphertextType', '密文类型').typeTag(AuthCertCiphertextTypeEnum),
|
||||||
TableColumn.new('resourceType', '资源类型').typeTag(TagResourceTypeEnum),
|
TableColumn.new('resourceCode', '资源编号').isSlot().setAddWidth(30),
|
||||||
TableColumn.new('resourceCode', '资源编号'),
|
|
||||||
TableColumn.new('remark', '备注'),
|
TableColumn.new('remark', '备注'),
|
||||||
TableColumn.new('creator', '创建人'),
|
TableColumn.new('creator', '创建人'),
|
||||||
TableColumn.new('createTime', '创建时间').isTime(),
|
TableColumn.new('createTime', '创建时间').isTime(),
|
||||||
|
|||||||
@@ -100,7 +100,9 @@ const { dvisible, params, form } = toRefs(state);
|
|||||||
|
|
||||||
const { isFetching: saveBtnLoading, execute: saveConfigExec } = configApi.save.useApi(form);
|
const { isFetching: saveBtnLoading, execute: saveConfigExec } = configApi.save.useApi(form);
|
||||||
|
|
||||||
watchEffect(() => {
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
() => {
|
||||||
state.dvisible = props.visible;
|
state.dvisible = props.visible;
|
||||||
if (!state.dvisible) {
|
if (!state.dvisible) {
|
||||||
return;
|
return;
|
||||||
@@ -124,7 +126,8 @@ watchEffect(() => {
|
|||||||
} else {
|
} else {
|
||||||
state.permissionAccount = [];
|
state.permissionAccount = [];
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
// 更新父组件visible prop对应的值为false
|
// 更新父组件visible prop对应的值为false
|
||||||
|
|||||||
@@ -123,9 +123,8 @@ func (a *AccountLogin) RefreshToken(rc *req.Ctx) {
|
|||||||
biz.NotEmpty(refreshToken, "refresh_token不能为空")
|
biz.NotEmpty(refreshToken, "refresh_token不能为空")
|
||||||
|
|
||||||
accountId, username, err := req.ParseToken(refreshToken)
|
accountId, username, err := req.ParseToken(refreshToken)
|
||||||
if err != nil {
|
biz.IsTrueBy(err == nil, errorx.PermissionErr)
|
||||||
panic(errorx.PermissionErr)
|
|
||||||
}
|
|
||||||
token, refreshToken, err := req.CreateToken(accountId, username)
|
token, refreshToken, err := req.CreateToken(accountId, username)
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
rc.ResData = collx.Kvs("token", token, "refresh_token", refreshToken)
|
rc.ResData = collx.Kvs("token", token, "refresh_token", refreshToken)
|
||||||
|
|||||||
@@ -57,10 +57,20 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
|||||||
res, err := d.DbApp.GetPageList(queryCond, page, &dbvos)
|
res, err := d.DbApp.GetPageList(queryCond, page, &dbvos)
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
|
|
||||||
// 填充标签信息
|
instances, _ := d.InstanceApp.GetByIds(collx.ArrayMap(dbvos, func(i *vo.DbListVO) uint64 {
|
||||||
d.TagApp.FillTagInfo(tagentity.TagTypeDbName, collx.ArrayMap(dbvos, func(dbvo *vo.DbListVO) tagentity.ITagResource {
|
return i.InstanceId
|
||||||
return dbvo
|
}))
|
||||||
})...)
|
instancesMap := collx.ArrayToMap(instances, func(i *entity.DbInstance) uint64 {
|
||||||
|
return i.Id
|
||||||
|
})
|
||||||
|
for _, dbvo := range dbvos {
|
||||||
|
di := instancesMap[dbvo.InstanceId]
|
||||||
|
if di != nil {
|
||||||
|
dbvo.InstanceType = di.Type
|
||||||
|
dbvo.Host = di.Host
|
||||||
|
dbvo.Port = di.Port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,12 @@ func (d *Instance) GetDatabaseNames(rc *req.Ctx) {
|
|||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Instance) GetDatabaseNamesByAc(rc *req.Ctx) {
|
||||||
|
res, err := d.InstanceApp.GetDatabasesByAc(rc.PathParam("ac"))
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
rc.ResData = res
|
||||||
|
}
|
||||||
|
|
||||||
// 获取数据库实例server信息
|
// 获取数据库实例server信息
|
||||||
func (d *Instance) GetDbServer(rc *req.Ctx) {
|
func (d *Instance) GetDbServer(rc *req.Ctx) {
|
||||||
instanceId := getInstanceId(rc)
|
instanceId := getInstanceId(rc)
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
package form
|
package form
|
||||||
|
|
||||||
|
import "mayfly-go/internal/db/domain/entity"
|
||||||
|
|
||||||
type DbForm struct {
|
type DbForm struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Code string `binding:"required" json:"code"`
|
Code string `binding:"required" json:"code"`
|
||||||
Name string `binding:"required" json:"name"`
|
Name string `binding:"required" json:"name"`
|
||||||
|
GetDatabaseMode entity.DbGetDatabaseMode `json:"getDatabaseMode"` // 获取数据库方式
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
InstanceId uint64 `binding:"required" json:"instanceId"`
|
InstanceId uint64 `binding:"required" json:"instanceId"`
|
||||||
AuthCertName string `json:"authCertName"`
|
AuthCertName string `json:"authCertName"`
|
||||||
FlowProcdefKey string `json:"flowProcdefKey"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DbSqlSaveForm struct {
|
type DbSqlSaveForm struct {
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
package vo
|
package vo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
"mayfly-go/internal/db/domain/entity"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbListVO struct {
|
type DbListVO struct {
|
||||||
tagentity.ResourceTags
|
|
||||||
|
|
||||||
Id *int64 `json:"id"`
|
Id *int64 `json:"id"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
|
GetDatabaseMode entity.DbGetDatabaseMode `json:"getDatabaseMode"` // 获取数据库方式
|
||||||
Database *string `json:"database"`
|
Database *string `json:"database"`
|
||||||
Remark *string `json:"remark"`
|
Remark *string `json:"remark"`
|
||||||
|
InstanceId uint64 `json:"instanceId"`
|
||||||
InstanceId *int64 `json:"instanceId"`
|
|
||||||
AuthCertName string `json:"authCertName"`
|
AuthCertName string `json:"authCertName"`
|
||||||
InstanceName *string `json:"instanceName"`
|
|
||||||
InstanceType *string `json:"type"`
|
InstanceType string `json:"type" gorm:"-"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host" gorm:"-"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port" gorm:"-"`
|
||||||
|
|
||||||
CreateTime *time.Time `json:"createTime"`
|
CreateTime *time.Time `json:"createTime"`
|
||||||
Creator *string `json:"creator"`
|
Creator *string `json:"creator"`
|
||||||
@@ -28,7 +26,3 @@ type DbListVO struct {
|
|||||||
Modifier *string `json:"modifier"`
|
Modifier *string `json:"modifier"`
|
||||||
ModifierId *int64 `json:"modifierId"`
|
ModifierId *int64 `json:"modifierId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DbListVO) GetCode() string {
|
|
||||||
return d.Code
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
|||||||
di.Id = db.Id
|
di.Id = db.Id
|
||||||
|
|
||||||
checkDb := di.GetDatabase()
|
checkDb := di.GetDatabase()
|
||||||
if !strings.Contains(" "+db.Database+" ", " "+checkDb+" ") {
|
if db.GetDatabaseMode == entity.DbGetDatabaseModeAssign && !strings.Contains(" "+db.Database+" ", " "+checkDb+" ") {
|
||||||
return nil, errorx.NewBiz("未配置数据库【%s】的操作权限", dbName)
|
return nil, errorx.NewBiz("未配置数据库【%s】的操作权限", dbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ func (app *dataSyncAppImpl) InitCronJob() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 修改执行中状态为待执行
|
// 修改执行中状态为待执行
|
||||||
_ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady})
|
_ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning})
|
||||||
|
|
||||||
// 把所有正常任务添加到定时任务中
|
// 把所有正常任务添加到定时任务中
|
||||||
pageParam := &model.PageParam{
|
pageParam := &model.PageParam{
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ type Instance interface {
|
|||||||
// GetDatabases 获取数据库实例的所有数据库列表
|
// GetDatabases 获取数据库实例的所有数据库列表
|
||||||
GetDatabases(entity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) ([]string, error)
|
GetDatabases(entity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) ([]string, error)
|
||||||
|
|
||||||
|
// GetDatabasesByAc 根据授权凭证名获取所有数据库名称列表
|
||||||
|
GetDatabasesByAc(acName string) ([]string, error)
|
||||||
|
|
||||||
// ToDbInfo 根据实例与授权凭证返回对应的DbInfo
|
// ToDbInfo 根据实例与授权凭证返回对应的DbInfo
|
||||||
ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error)
|
ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error)
|
||||||
}
|
}
|
||||||
@@ -53,6 +56,8 @@ type instanceAppImpl struct {
|
|||||||
restoreApp *DbRestoreApp `inject:"DbRestoreApp"`
|
restoreApp *DbRestoreApp `inject:"DbRestoreApp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ (Instance) = (*instanceAppImpl)(nil)
|
||||||
|
|
||||||
// 注入DbInstanceRepo
|
// 注入DbInstanceRepo
|
||||||
func (app *instanceAppImpl) InjectDbInstanceRepo(repo repository.Instance) {
|
func (app *instanceAppImpl) InjectDbInstanceRepo(repo repository.Instance) {
|
||||||
app.Repo = repo
|
app.Repo = repo
|
||||||
@@ -118,7 +123,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
|||||||
return instanceEntity.Id, app.Tx(ctx, func(ctx context.Context) error {
|
return instanceEntity.Id, app.Tx(ctx, func(ctx context.Context) error {
|
||||||
return app.Insert(ctx, instanceEntity)
|
return app.Insert(ctx, instanceEntity)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: instanceEntity.Code,
|
ResourceCode: instanceEntity.Code,
|
||||||
ResourceType: tagentity.TagType(resourceType),
|
ResourceType: tagentity.TagType(resourceType),
|
||||||
AuthCerts: authCerts,
|
AuthCerts: authCerts,
|
||||||
@@ -147,7 +152,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
|||||||
return oldInstance.Id, app.Tx(ctx, func(ctx context.Context) error {
|
return oldInstance.Id, app.Tx(ctx, func(ctx context.Context) error {
|
||||||
return app.UpdateById(ctx, instanceEntity)
|
return app.UpdateById(ctx, instanceEntity)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: oldInstance.Code,
|
ResourceCode: oldInstance.Code,
|
||||||
ResourceType: tagentity.TagType(resourceType),
|
ResourceType: tagentity.TagType(resourceType),
|
||||||
AuthCerts: authCerts,
|
AuthCerts: authCerts,
|
||||||
@@ -205,7 +210,7 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error
|
|||||||
return app.DeleteById(ctx, instanceId)
|
return app.DeleteById(ctx, instanceId)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
// 删除该实例关联的授权凭证信息
|
// 删除该实例关联的授权凭证信息
|
||||||
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: instance.Code,
|
ResourceCode: instance.Code,
|
||||||
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
|
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
|
||||||
})
|
})
|
||||||
@@ -239,16 +244,22 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance, authCert *tagent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ed.Network = ed.GetNetwork()
|
return app.getDatabases(ed, authCert)
|
||||||
dbi := app.toDbInfoByAc(ed, authCert, "")
|
}
|
||||||
|
|
||||||
dbConn, err := dbm.Conn(dbi)
|
func (app *instanceAppImpl) GetDatabasesByAc(acName string) ([]string, error) {
|
||||||
|
ac, err := app.resourceAuthCertApp.GetAuthCert(acName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errorx.NewBiz("该授权凭证不存在")
|
||||||
}
|
}
|
||||||
defer dbConn.Close()
|
|
||||||
|
|
||||||
return dbConn.GetMetaData().GetDbNames()
|
instance := &entity.DbInstance{Code: ac.ResourceCode}
|
||||||
|
err = app.GetByCond(instance)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorx.NewBiz("不存在该授权凭证对应的数据库实例信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.getDatabases(instance, ac)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *instanceAppImpl) ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error) {
|
func (app *instanceAppImpl) ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error) {
|
||||||
@@ -260,6 +271,19 @@ func (app *instanceAppImpl) ToDbInfo(instance *entity.DbInstance, authCertName s
|
|||||||
return app.toDbInfoByAc(instance, ac, database), nil
|
return app.toDbInfoByAc(instance, ac, database), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *instanceAppImpl) getDatabases(instance *entity.DbInstance, ac *tagentity.ResourceAuthCert) ([]string, error) {
|
||||||
|
instance.Network = instance.GetNetwork()
|
||||||
|
dbi := app.toDbInfoByAc(instance, ac, "")
|
||||||
|
|
||||||
|
dbConn, err := dbm.Conn(dbi)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer dbConn.Close()
|
||||||
|
|
||||||
|
return dbConn.GetMetaData().GetDbNames()
|
||||||
|
}
|
||||||
|
|
||||||
func (app *instanceAppImpl) toDbInfoByAc(instance *entity.DbInstance, ac *tagentity.ResourceAuthCert, database string) *dbi.DbInfo {
|
func (app *instanceAppImpl) toDbInfoByAc(instance *entity.DbInstance, ac *tagentity.ResourceAuthCert, database string) *dbi.DbInfo {
|
||||||
di := new(dbi.DbInfo)
|
di := new(dbi.DbInfo)
|
||||||
di.InstanceId = instance.Id
|
di.InstanceId = instance.Id
|
||||||
|
|||||||
@@ -87,16 +87,20 @@ func (app *dbTransferAppImpl) CreateLog(ctx context.Context, taskId uint64) (uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint64) {
|
func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint64) {
|
||||||
|
defer app.logApp.Flush(logId, true)
|
||||||
|
|
||||||
task, err := app.GetById(taskId)
|
task, err := app.GetById(taskId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logx.Errorf("创建DBMS-执行数据迁移日志失败:%v", err)
|
logx.Errorf("创建DBMS-执行数据迁移日志失败:%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.IsRunning(taskId) {
|
||||||
|
logx.Warnf("[%d]该任务正在运行中...", taskId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
defer app.logApp.Flush(logId, true)
|
|
||||||
|
|
||||||
// 修改状态与关联日志id
|
// 修改状态与关联日志id
|
||||||
task.LogId = logId
|
task.LogId = logId
|
||||||
task.RunningState = entity.DbTransferTaskRunStateRunning
|
task.RunningState = entity.DbTransferTaskRunStateRunning
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ SELECT a.indexname AS "i
|
|||||||
indexdef AS "indexDef",
|
indexdef AS "indexDef",
|
||||||
c.attname AS "columnName",
|
c.attname AS "columnName",
|
||||||
c.attnum AS "seqInIndex",
|
c.attnum AS "seqInIndex",
|
||||||
case when a.indexname like '%_pkey' then 1 else 0 end AS "isPrimaryKey"
|
case when a.indexname like '%%_pkey' then 1 else 0 end AS "isPrimaryKey"
|
||||||
FROM pg_indexes a
|
FROM pg_indexes a
|
||||||
join pg_class b on a.indexname = b.relname
|
join pg_class b on a.indexname = b.relname
|
||||||
join pg_attribute c on b.oid = c.attrelid
|
join pg_attribute c on b.oid = c.attrelid
|
||||||
WHERE a.schemaname = (select current_schema())
|
WHERE a.schemaname = (select current_schema())
|
||||||
AND a.tablename = '%s'
|
AND a.tablename = '%s'
|
||||||
AND a.indexname not like '%_pkey'
|
AND a.indexname not like '%%_pkey'
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
--PGSQL_COLUMN_MA 表列信息
|
--PGSQL_COLUMN_MA 表列信息
|
||||||
SELECT a.table_name AS "tableName",
|
SELECT a.table_name AS "tableName",
|
||||||
|
|||||||
@@ -9,8 +9,16 @@ type Db struct {
|
|||||||
|
|
||||||
Code string `orm:"column(code)" json:"code"`
|
Code string `orm:"column(code)" json:"code"`
|
||||||
Name string `orm:"column(name)" json:"name"`
|
Name string `orm:"column(name)" json:"name"`
|
||||||
|
GetDatabaseMode DbGetDatabaseMode `json:"getDatabaseMode"` // 获取数据库方式
|
||||||
Database string `orm:"column(database)" json:"database"`
|
Database string `orm:"column(database)" json:"database"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
InstanceId uint64
|
InstanceId uint64
|
||||||
AuthCertName string `json:"authCertName"`
|
AuthCertName string `json:"authCertName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DbGetDatabaseMode int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
DbGetDatabaseModeAuto DbGetDatabaseMode = -1 // 自动获取(根据凭证获取对应所有库名)
|
||||||
|
DbGetDatabaseModeAssign DbGetDatabaseMode = 1 // 指定库名
|
||||||
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type InstanceQuery struct {
|
|||||||
Name string `json:"name" form:"name"`
|
Name string `json:"name" form:"name"`
|
||||||
Code string `json:"code" form:"code"`
|
Code string `json:"code" form:"code"`
|
||||||
Host string `json:"host" form:"host"`
|
Host string `json:"host" form:"host"`
|
||||||
TagPath string `json:"host" form:"tagPath"`
|
TagPath string `json:"tagPath" form:"tagPath"`
|
||||||
Codes []string
|
Codes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,15 +28,9 @@ type DbTransferLogQuery struct {
|
|||||||
// 数据库查询实体,不与数据库表字段一一对应
|
// 数据库查询实体,不与数据库表字段一一对应
|
||||||
type DbQuery struct {
|
type DbQuery struct {
|
||||||
Id uint64 `form:"id"`
|
Id uint64 `form:"id"`
|
||||||
Code string `json:"code" form:"code"`
|
|
||||||
Name string `orm:"column(name)" json:"name"`
|
|
||||||
Database string `orm:"column(database)" json:"database"`
|
|
||||||
Remark string `json:"remark"`
|
|
||||||
|
|
||||||
Codes []string
|
|
||||||
TagIds []uint64 `orm:"column(tag_id)"`
|
|
||||||
TagPath string `form:"tagPath"`
|
TagPath string `form:"tagPath"`
|
||||||
|
Code string `json:"code" form:"code"`
|
||||||
|
Codes []string
|
||||||
InstanceId uint64 `form:"instanceId"`
|
InstanceId uint64 `form:"instanceId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"mayfly-go/internal/db/domain/entity"
|
"mayfly-go/internal/db/domain/entity"
|
||||||
"mayfly-go/internal/db/domain/repository"
|
"mayfly-go/internal/db/domain/repository"
|
||||||
"mayfly-go/pkg/base"
|
"mayfly-go/pkg/base"
|
||||||
"mayfly-go/pkg/gormx"
|
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,16 +17,6 @@ func newDbRepo() repository.Db {
|
|||||||
|
|
||||||
// 分页获取数据库信息列表
|
// 分页获取数据库信息列表
|
||||||
func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||||
qd := gormx.NewQueryWithTableName("t_db db").Joins("JOIN t_db_instance inst ON db.instance_id = inst.id").
|
pd := model.NewCond().Eq("instance_id", condition.InstanceId).In("code", condition.Codes)
|
||||||
WithCond(model.NewCond().Columns("db.*, inst.name instance_name, inst.type instance_type, inst.host, inst.port ").
|
return d.PageByCondToAny(pd, pageParam, toEntity)
|
||||||
Eq("db.instance_id", condition.InstanceId).
|
|
||||||
Eq("db.id", condition.Id).
|
|
||||||
Like("db.database", condition.Database).
|
|
||||||
Eq("db.code", condition.Code).
|
|
||||||
In("db.code", condition.Codes).
|
|
||||||
Eq0("db."+model.DeletedColumn, model.ModelUndeleted).
|
|
||||||
Eq0("inst."+model.DeletedColumn, model.ModelUndeleted),
|
|
||||||
)
|
|
||||||
|
|
||||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ func InitInstanceRouter(router *gin.RouterGroup) {
|
|||||||
// 获取数据库实例的所有数据库名
|
// 获取数据库实例的所有数据库名
|
||||||
req.NewPost("/databases", d.GetDatabaseNames),
|
req.NewPost("/databases", d.GetDatabaseNames),
|
||||||
|
|
||||||
|
// 根据授权凭证名获取其所有库名
|
||||||
|
req.NewGet("/databases/:ac", d.GetDatabaseNamesByAc),
|
||||||
|
|
||||||
req.NewGet(":instanceId/server-info", d.GetDbServer),
|
req.NewGet(":instanceId/server-info", d.GetDbServer),
|
||||||
|
|
||||||
req.NewDelete(":instanceId", d.DeleteInstance).Log(req.NewLogSave("db-删除数据库实例")),
|
req.NewDelete(":instanceId", d.DeleteInstance).Log(req.NewLogSave("db-删除数据库实例")),
|
||||||
|
|||||||
@@ -173,6 +173,22 @@ func (m *Machine) KillProcess(rc *req.Ctx) {
|
|||||||
biz.ErrIsNil(err, "终止进程失败: %s", res)
|
biz.ErrIsNil(err, "终止进程失败: %s", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Machine) GetUsers(rc *req.Ctx) {
|
||||||
|
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||||
|
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||||
|
res, err := cli.GetUsers()
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
rc.ResData = res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Machine) GetGroups(rc *req.Ctx) {
|
||||||
|
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||||
|
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||||
|
res, err := cli.GetGroups()
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
rc.ResData = res
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Machine) WsSSH(g *gin.Context) {
|
func (m *Machine) WsSSH(g *gin.Context) {
|
||||||
wsConn, err := ws.Upgrader.Upgrade(g.Writer, g.Request, nil)
|
wsConn, err := ws.Upgrader.Upgrade(g.Writer, g.Request, nil)
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -261,7 +277,7 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mi.IfUseSshTunnelChangeIpPort()
|
err = mi.IfUseSshTunnelChangeIpPort(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import (
|
|||||||
"mayfly-go/internal/machine/domain/entity"
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"mayfly-go/pkg/scheduler"
|
"mayfly-go/pkg/scheduler"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
|
|
||||||
|
"github.com/may-fly/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MachineCronJob struct {
|
type MachineCronJob struct {
|
||||||
@@ -58,9 +59,7 @@ func (m *MachineCronJob) Delete(rc *req.Ctx) {
|
|||||||
ids := strings.Split(idsStr, ",")
|
ids := strings.Split(idsStr, ",")
|
||||||
|
|
||||||
for _, v := range ids {
|
for _, v := range ids {
|
||||||
value, err := strconv.Atoi(v)
|
m.MachineCronJobApp.Delete(rc.MetaCtx, cast.ToUint64(v))
|
||||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
|
||||||
m.MachineCronJobApp.Delete(rc.MetaCtx, uint64(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/may-fly/cast"
|
"github.com/may-fly/cast"
|
||||||
|
"github.com/pkg/sftp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MachineFile struct {
|
type MachineFile struct {
|
||||||
|
MachineApp application.Machine `inject:""`
|
||||||
MachineFileApp application.MachineFile `inject:""`
|
MachineFileApp application.MachineFile `inject:""`
|
||||||
MsgApp msgapp.Msg `inject:""`
|
MsgApp msgapp.Msg `inject:""`
|
||||||
}
|
}
|
||||||
@@ -159,15 +161,21 @@ func (m *MachineFile) GetDirEntry(rc *req.Ctx) {
|
|||||||
path = readPath + name
|
path = readPath + name
|
||||||
}
|
}
|
||||||
|
|
||||||
fisVO = append(fisVO, vo.MachineFileInfo{
|
mfi := vo.MachineFileInfo{
|
||||||
Name: fi.Name(),
|
Name: fi.Name(),
|
||||||
Size: fi.Size(),
|
Size: fi.Size(),
|
||||||
Path: path,
|
Path: path,
|
||||||
Type: getFileType(fi.Mode()),
|
Type: getFileType(fi.Mode()),
|
||||||
Mode: fi.Mode().String(),
|
Mode: fi.Mode().String(),
|
||||||
ModTime: timex.DefaultFormat(fi.ModTime()),
|
ModTime: timex.DefaultFormat(fi.ModTime()),
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if sftpFs, ok := fi.Sys().(*sftp.FileStat); ok {
|
||||||
|
mfi.UID = sftpFs.UID
|
||||||
|
mfi.GID = sftpFs.GID
|
||||||
|
}
|
||||||
|
|
||||||
|
fisVO = append(fisVO, mfi)
|
||||||
}
|
}
|
||||||
sort.Sort(vo.MachineFileInfos(fisVO))
|
sort.Sort(vo.MachineFileInfos(fisVO))
|
||||||
rc.ResData = fisVO
|
rc.ResData = fisVO
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import (
|
|||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/jsonx"
|
"mayfly-go/pkg/utils/jsonx"
|
||||||
"mayfly-go/pkg/utils/stringx"
|
"mayfly-go/pkg/utils/stringx"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/may-fly/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MachineScript struct {
|
type MachineScript struct {
|
||||||
@@ -42,9 +43,7 @@ func (m *MachineScript) DeleteMachineScript(rc *req.Ctx) {
|
|||||||
ids := strings.Split(idsStr, ",")
|
ids := strings.Split(idsStr, ",")
|
||||||
|
|
||||||
for _, v := range ids {
|
for _, v := range ids {
|
||||||
value, err := strconv.Atoi(v)
|
m.MachineScriptApp.Delete(rc.MetaCtx, cast.ToUint64(v))
|
||||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
|
||||||
m.MachineScriptApp.Delete(rc.MetaCtx, uint64(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ type MachineFileInfo struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
ModTime string `json:"modTime"`
|
ModTime string `json:"modTime"`
|
||||||
|
|
||||||
|
UID uint32 `json:"uid"`
|
||||||
|
GID uint32 `json:"gid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineFileInfos []MachineFileInfo
|
type MachineFileInfos []MachineFileInfo
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
|
|||||||
return m.Tx(ctx, func(ctx context.Context) error {
|
return m.Tx(ctx, func(ctx context.Context) error {
|
||||||
return m.Insert(ctx, me)
|
return m.Insert(ctx, me)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: me.Code,
|
ResourceCode: me.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
AuthCerts: authCerts,
|
AuthCerts: authCerts,
|
||||||
@@ -138,7 +138,7 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
|
|||||||
return m.Tx(ctx, func(ctx context.Context) error {
|
return m.Tx(ctx, func(ctx context.Context) error {
|
||||||
return m.UpdateById(ctx, me)
|
return m.UpdateById(ctx, me)
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: oldMachine.Code,
|
ResourceCode: oldMachine.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
AuthCerts: authCerts,
|
AuthCerts: authCerts,
|
||||||
@@ -219,7 +219,7 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: machine.Code,
|
ResourceCode: machine.Code,
|
||||||
ResourceType: resourceType,
|
ResourceType: resourceType,
|
||||||
})
|
})
|
||||||
@@ -334,6 +334,7 @@ func (m *machineAppImpl) getMachineAndAuthCert(machineId uint64) (*entity.Machin
|
|||||||
func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAuthCert) (*mcm.MachineInfo, error) {
|
func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAuthCert) (*mcm.MachineInfo, error) {
|
||||||
mi := new(mcm.MachineInfo)
|
mi := new(mcm.MachineInfo)
|
||||||
mi.Id = me.Id
|
mi.Id = me.Id
|
||||||
|
mi.Code = me.Code
|
||||||
mi.Name = me.Name
|
mi.Name = me.Name
|
||||||
mi.Ip = me.Ip
|
mi.Ip = me.Ip
|
||||||
mi.Port = me.Port
|
mi.Port = me.Port
|
||||||
|
|||||||
@@ -8,13 +8,11 @@ import (
|
|||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/base"
|
"mayfly-go/pkg/base"
|
||||||
"mayfly-go/pkg/biz"
|
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/rediscli"
|
"mayfly-go/pkg/rediscli"
|
||||||
"mayfly-go/pkg/scheduler"
|
"mayfly-go/pkg/scheduler"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/stringx"
|
"mayfly-go/pkg/utils/stringx"
|
||||||
"time"
|
"time"
|
||||||
@@ -178,23 +176,19 @@ func (m *machineCronJobAppImpl) addCronJob(mcj *entity.MachineCronJob) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *machineCronJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineCronJob) {
|
func (m *machineCronJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineCronJob) {
|
||||||
defer func() {
|
execRes := &entity.MachineCronJobExec{
|
||||||
if err := recover(); err != nil {
|
|
||||||
res := anyx.ToString(err)
|
|
||||||
m.machineCronJobExecRepo.Insert(context.TODO(), &entity.MachineCronJobExec{
|
|
||||||
MachineId: mid,
|
|
||||||
CronJobId: cronJob.Id,
|
CronJobId: cronJob.Id,
|
||||||
ExecTime: time.Now(),
|
ExecTime: time.Now(),
|
||||||
Status: entity.MachineCronJobExecStatusError,
|
|
||||||
Res: res,
|
|
||||||
})
|
|
||||||
logx.Errorf("机器:[%d]执行[%s]计划任务失败: %s", mid, cronJob.Name, res)
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
machineCli, err := m.machineApp.GetCli(uint64(mid))
|
machineCli, err := m.machineApp.GetCli(uint64(mid))
|
||||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
res := ""
|
||||||
res, err := machineCli.Run(cronJob.Script)
|
if err != nil {
|
||||||
|
machine, _ := m.machineApp.GetById(mid)
|
||||||
|
execRes.MachineCode = machine.Code
|
||||||
|
} else {
|
||||||
|
execRes.MachineCode = machineCli.Info.Code
|
||||||
|
res, err = machineCli.Run(cronJob.Script)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if res == "" {
|
if res == "" {
|
||||||
res = err.Error()
|
res = err.Error()
|
||||||
@@ -203,18 +197,14 @@ func (m *machineCronJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineC
|
|||||||
} else {
|
} else {
|
||||||
logx.Debugf("机器:[%d]执行[%s]计划任务成功, 执行结果: %s", mid, cronJob.Name, res)
|
logx.Debugf("机器:[%d]执行[%s]计划任务成功, 执行结果: %s", mid, cronJob.Name, res)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
execRes.Res = res
|
||||||
|
|
||||||
if cronJob.SaveExecResType == entity.SaveExecResTypeNo ||
|
if cronJob.SaveExecResType == entity.SaveExecResTypeNo ||
|
||||||
(cronJob.SaveExecResType == entity.SaveExecResTypeOnError && err == nil) {
|
(cronJob.SaveExecResType == entity.SaveExecResTypeOnError && err == nil) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
execRes := &entity.MachineCronJobExec{
|
|
||||||
MachineId: mid,
|
|
||||||
CronJobId: cronJob.Id,
|
|
||||||
ExecTime: time.Now(),
|
|
||||||
Res: res,
|
|
||||||
}
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
execRes.Status = entity.MachineCronJobExecStatusSuccess
|
execRes.Status = entity.MachineCronJobExecStatusSuccess
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -222,8 +222,7 @@ func (m *machineFileAppImpl) MkDir(ctx context.Context, opParam *dto.MachineFile
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sftpCli.MkdirAll(path)
|
return mi, sftpCli.MkdirAll(path)
|
||||||
return mi, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *machineFileAppImpl) CreateFile(ctx context.Context, opParam *dto.MachineFileOp) (*mcm.MachineInfo, error) {
|
func (m *machineFileAppImpl) CreateFile(ctx context.Context, opParam *dto.MachineFileOp) (*mcm.MachineInfo, error) {
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
|
|||||||
termOpRecord.MachineId = cli.Info.Id
|
termOpRecord.MachineId = cli.Info.Id
|
||||||
termOpRecord.Username = cli.Info.Username
|
termOpRecord.Username = cli.Info.Username
|
||||||
|
|
||||||
// 回放文件路径为: 基础配置路径/操作日期(202301)/day/hour/randstr.cast
|
// 回放文件路径为: 基础配置路径/机器编号/操作日期(202301)/day/hour/randstr.cast
|
||||||
recRelPath := path.Join(now.Format("200601"), fmt.Sprintf("%d", now.Day()), fmt.Sprintf("%d", now.Hour()))
|
recRelPath := path.Join(cli.Info.Code, now.Format("200601"), fmt.Sprintf("%d", now.Day()), fmt.Sprintf("%d", now.Hour()))
|
||||||
// 文件绝对路径
|
// 文件绝对路径
|
||||||
recAbsPath := path.Join(config.GetMachine().TerminalRecPath, recRelPath)
|
recAbsPath := path.Join(config.GetMachine().TerminalRecPath, recRelPath)
|
||||||
os.MkdirAll(recAbsPath, 0766)
|
os.MkdirAll(recAbsPath, 0766)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ type MachineCronJobExec struct {
|
|||||||
model.DeletedModel
|
model.DeletedModel
|
||||||
|
|
||||||
CronJobId uint64 `json:"cronJobId" form:"cronJobId"`
|
CronJobId uint64 `json:"cronJobId" form:"cronJobId"`
|
||||||
MachineId uint64 `json:"machineId" form:"machineId"`
|
MachineCode string `json:"machineCode" form:"machineCode"`
|
||||||
Status int `json:"status" form:"status"` // 执行状态
|
Status int `json:"status" form:"status"` // 执行状态
|
||||||
Res string `json:"res"` // 执行结果
|
Res string `json:"res"` // 执行结果
|
||||||
ExecTime time.Time `json:"execTime"`
|
ExecTime time.Time `json:"execTime"`
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package entity
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type MachineQuery struct {
|
type MachineQuery struct {
|
||||||
Ids string `json:"ids" form:"ids"`
|
Id uint64 `json:"id" form:"id"`
|
||||||
Code string `json:"code" form:"code"`
|
Code string `json:"code" form:"code"`
|
||||||
Name string `json:"name" form:"name"`
|
Name string `json:"name" form:"name"`
|
||||||
Status int8 `json:"status" form:"status"`
|
Status int8 `json:"status" form:"status"`
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ import (
|
|||||||
"mayfly-go/internal/machine/domain/repository"
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
"mayfly-go/pkg/base"
|
"mayfly-go/pkg/base"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/utils/collx"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/may-fly/cast"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type machineRepoImpl struct {
|
type machineRepoImpl struct {
|
||||||
@@ -22,18 +18,13 @@ func newMachineRepo() repository.Machine {
|
|||||||
// 分页获取机器信息列表
|
// 分页获取机器信息列表
|
||||||
func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||||
qd := model.NewCond().
|
qd := model.NewCond().
|
||||||
|
Eq("id", condition.Id).
|
||||||
Eq("status", condition.Status).
|
Eq("status", condition.Status).
|
||||||
Like("ip", condition.Ip).
|
Like("ip", condition.Ip).
|
||||||
Like("name", condition.Name).
|
Like("name", condition.Name).
|
||||||
In("code", condition.Codes).
|
In("code", condition.Codes).
|
||||||
Like("code", condition.Code).
|
Eq("code", condition.Code).
|
||||||
Eq("protocol", condition.Protocol)
|
Eq("protocol", condition.Protocol)
|
||||||
|
|
||||||
if condition.Ids != "" {
|
|
||||||
qd.In("id", collx.ArrayMap[string, uint64](strings.Split(condition.Ids, ","), func(val string) uint64 {
|
|
||||||
return cast.ToUint64(val)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.PageByCondToAny(qd, pageParam, toEntity)
|
return m.PageByCondToAny(qd, pageParam, toEntity)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import (
|
|||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/may-fly/cast"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 机器客户端
|
// Cli 机器客户端
|
||||||
type Cli struct {
|
type Cli struct {
|
||||||
Info *MachineInfo // 机器信息
|
Info *MachineInfo // 机器信息
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ type Cli struct {
|
|||||||
sftpClient *sftp.Client // sftp客户端
|
sftpClient *sftp.Client // sftp客户端
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取sftp client
|
// GetSftpCli 获取sftp client
|
||||||
func (c *Cli) GetSftpCli() (*sftp.Client, error) {
|
func (c *Cli) GetSftpCli() (*sftp.Client, error) {
|
||||||
if c.sshClient == nil {
|
if c.sshClient == nil {
|
||||||
return nil, errorx.NewBiz("请先进行机器客户端连接")
|
return nil, errorx.NewBiz("请先进行机器客户端连接")
|
||||||
@@ -36,7 +37,7 @@ func (c *Cli) GetSftpCli() (*sftp.Client, error) {
|
|||||||
return sftpclient, nil
|
return sftpclient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取session
|
// GetSession 获取session
|
||||||
func (c *Cli) GetSession() (*ssh.Session, error) {
|
func (c *Cli) GetSession() (*ssh.Session, error) {
|
||||||
if c.sshClient == nil {
|
if c.sshClient == nil {
|
||||||
return nil, errorx.NewBiz("请先进行机器客户端连接")
|
return nil, errorx.NewBiz("请先进行机器客户端连接")
|
||||||
@@ -49,7 +50,7 @@ func (c *Cli) GetSession() (*ssh.Session, error) {
|
|||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行shell
|
// Run 执行shell
|
||||||
// @param shell shell脚本命令
|
// @param shell shell脚本命令
|
||||||
// @return 返回执行成功或错误的消息
|
// @return 返回执行成功或错误的消息
|
||||||
func (c *Cli) Run(shell string) (string, error) {
|
func (c *Cli) Run(shell string) (string, error) {
|
||||||
@@ -58,38 +59,15 @@ func (c *Cli) Run(shell string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
buf, err := session.CombinedOutput(shell)
|
// 将可能存在的windows换行符替换为linux格式
|
||||||
|
buf, err := session.CombinedOutput(strings.ReplaceAll(shell, "\r\n", "\n"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return string(buf), err
|
return string(buf), err
|
||||||
}
|
}
|
||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取机器的所有状态信息
|
// Close 关闭client并从缓存中移除,如果使用隧道则也关闭
|
||||||
func (c *Cli) GetAllStats() *Stats {
|
|
||||||
stats := new(Stats)
|
|
||||||
res, err := c.Run(StatsShell)
|
|
||||||
if err != nil {
|
|
||||||
logx.Errorf("执行机器[id=%d, name=%s]运行状态信息脚本失败: %s", c.Info.Id, c.Info.Name, err.Error())
|
|
||||||
return stats
|
|
||||||
}
|
|
||||||
|
|
||||||
infos := strings.Split(res, "-----")
|
|
||||||
if len(infos) < 8 {
|
|
||||||
return stats
|
|
||||||
}
|
|
||||||
getUptime(infos[0], stats)
|
|
||||||
getHostname(infos[1], stats)
|
|
||||||
getLoad(infos[2], stats)
|
|
||||||
getMemInfo(infos[3], stats)
|
|
||||||
getFSInfo(infos[4], stats)
|
|
||||||
getInterfaces(infos[5], stats)
|
|
||||||
getInterfaceInfo(infos[6], stats)
|
|
||||||
getCPU(infos[7], stats)
|
|
||||||
return stats
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭client并从缓存中移除,如果使用隧道则也关闭
|
|
||||||
func (c *Cli) Close() {
|
func (c *Cli) Close() {
|
||||||
m := c.Info
|
m := c.Info
|
||||||
logx.Debugf("close machine cli -> id=%d, name=%s, ip=%s", m.Id, m.Name, m.Ip)
|
logx.Debugf("close machine cli -> id=%d, name=%s, ip=%s", m.Id, m.Name, m.Ip)
|
||||||
@@ -114,3 +92,77 @@ func (c *Cli) Close() {
|
|||||||
CloseSshTunnelMachine(int(sshTunnelMachineId), m.GetTunnelId())
|
CloseSshTunnelMachine(int(sshTunnelMachineId), m.GetTunnelId())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllStats 获取机器的所有状态信息
|
||||||
|
func (c *Cli) GetAllStats() *Stats {
|
||||||
|
stats := new(Stats)
|
||||||
|
res, err := c.Run(StatsShell)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("执行机器[id=%d, name=%s]运行状态信息脚本失败: %s", c.Info.Id, c.Info.Name, err.Error())
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
infos := strings.Split(res, "-----")
|
||||||
|
if len(infos) < 8 {
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
getUptime(infos[0], stats)
|
||||||
|
getHostname(infos[1], stats)
|
||||||
|
getLoad(infos[2], stats)
|
||||||
|
getMemInfo(infos[3], stats)
|
||||||
|
getFSInfo(infos[4], stats)
|
||||||
|
getInterfaces(infos[5], stats)
|
||||||
|
getInterfaceInfo(infos[6], stats)
|
||||||
|
getCPU(infos[7], stats)
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsers 读取/etc/passwd,获取系统所有用户信息
|
||||||
|
func (c *Cli) GetUsers() ([]*UserInfo, error) {
|
||||||
|
res, err := c.Run("cat /etc/passwd")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var users []*UserInfo
|
||||||
|
userLines := strings.Split(res, "\n")
|
||||||
|
for _, userLine := range userLines {
|
||||||
|
if userLine == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Split(userLine, ":")
|
||||||
|
user := &UserInfo{
|
||||||
|
Username: fields[0],
|
||||||
|
UID: cast.ToUint32(fields[2]),
|
||||||
|
GID: cast.ToUint32(fields[3]),
|
||||||
|
HomeDir: fields[5],
|
||||||
|
Shell: fields[6],
|
||||||
|
}
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroups 读取/etc/group,获取系统所有组信息
|
||||||
|
func (c *Cli) GetGroups() ([]*GroupInfo, error) {
|
||||||
|
res, err := c.Run("cat /etc/group")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups []*GroupInfo
|
||||||
|
groupLines := strings.Split(res, "\n")
|
||||||
|
for _, groupLine := range groupLines {
|
||||||
|
if groupLine == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Split(groupLine, ":")
|
||||||
|
group := &GroupInfo{
|
||||||
|
Groupname: fields[0],
|
||||||
|
GID: cast.ToUint32(fields[2]),
|
||||||
|
}
|
||||||
|
groups = append(groups, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
|
"mayfly-go/pkg/utils/netx"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ type MachineInfo struct {
|
|||||||
Key string `json:"key"` // 缓存key
|
Key string `json:"key"` // 缓存key
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Code string `json:"code"`
|
||||||
Protocol int `json:"protocol"`
|
Protocol int `json:"protocol"`
|
||||||
|
|
||||||
Ip string `json:"ip"` // IP地址
|
Ip string `json:"ip"` // IP地址
|
||||||
@@ -34,12 +36,12 @@ type MachineInfo struct {
|
|||||||
CodePath []string `json:"codePath"`
|
CodePath []string `json:"codePath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineInfo) UseSshTunnel() bool {
|
func (mi *MachineInfo) UseSshTunnel() bool {
|
||||||
return m.SshTunnelMachine != nil
|
return mi.SshTunnelMachine != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineInfo) GetTunnelId() string {
|
func (mi *MachineInfo) GetTunnelId() string {
|
||||||
return fmt.Sprintf("machine:%d", m.Id)
|
return fmt.Sprintf("machine:%d", mi.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接
|
// 连接
|
||||||
@@ -47,7 +49,7 @@ func (mi *MachineInfo) Conn() (*Cli, error) {
|
|||||||
logx.Infof("[%s]机器连接:%s:%d", mi.Name, mi.Ip, mi.Port)
|
logx.Infof("[%s]机器连接:%s:%d", mi.Name, mi.Ip, mi.Port)
|
||||||
|
|
||||||
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
||||||
err := mi.IfUseSshTunnelChangeIpPort()
|
err := mi.IfUseSshTunnelChangeIpPort(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorx.NewBiz("ssh隧道连接失败: %s", err.Error())
|
return nil, errorx.NewBiz("ssh隧道连接失败: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -65,33 +67,39 @@ func (mi *MachineInfo) Conn() (*Cli, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
||||||
func (me *MachineInfo) IfUseSshTunnelChangeIpPort() error {
|
func (mi *MachineInfo) IfUseSshTunnelChangeIpPort(out bool) error {
|
||||||
if !me.UseSshTunnel() {
|
if !mi.UseSshTunnel() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
originId := me.Id
|
originId := mi.Id
|
||||||
if originId == 0 {
|
if originId == 0 {
|
||||||
// 随机设置一个id,如果使用了隧道则用于临时保存隧道
|
// 随机设置一个id,如果使用了隧道则用于临时保存隧道
|
||||||
me.Id = uint64(time.Now().Nanosecond())
|
mi.Id = uint64(time.Now().Nanosecond())
|
||||||
}
|
}
|
||||||
|
|
||||||
sshTunnelMachine, err := GetSshTunnelMachine(int(me.SshTunnelMachine.Id), func(u uint64) (*MachineInfo, error) {
|
sshTunnelMachine, err := GetSshTunnelMachine(int(mi.SshTunnelMachine.Id), func(u uint64) (*MachineInfo, error) {
|
||||||
return me.SshTunnelMachine, nil
|
return mi.SshTunnelMachine, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
exposeIp, exposePort, err := sshTunnelMachine.OpenSshTunnel(me.GetTunnelId(), me.Ip, me.Port)
|
exposeIp, exposePort, err := sshTunnelMachine.OpenSshTunnel(mi.GetTunnelId(), mi.Ip, mi.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否获取局域网的本地IP
|
||||||
|
if out {
|
||||||
|
exposeIp = netx.GetOutBoundIP()
|
||||||
|
}
|
||||||
|
|
||||||
// 修改机器ip地址
|
// 修改机器ip地址
|
||||||
me.Ip = exposeIp
|
mi.Ip = exposeIp
|
||||||
me.Port = exposePort
|
mi.Port = exposePort
|
||||||
// 代理之后置空跳板机信息,防止重复跳
|
// 代理之后置空跳板机信息,防止重复跳
|
||||||
me.TempSshMachineId = me.SshTunnelMachine.Id
|
mi.TempSshMachineId = mi.SshTunnelMachine.Id
|
||||||
me.SshTunnelMachine = nil
|
mi.SshTunnelMachine = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func (stm *SshTunnelMachine) OpenSshTunnel(id string, ip string, port int) (expo
|
|||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
localHost := "127.0.0.1"
|
localHost := "0.0.0.0"
|
||||||
localAddr := fmt.Sprintf("%s:%d", localHost, localPort)
|
localAddr := fmt.Sprintf("%s:%d", localHost, localPort)
|
||||||
listener, err := net.Listen("tcp", localAddr)
|
listener, err := net.Listen("tcp", localAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -287,3 +287,16 @@ func getCPU(cpuInfo string, stats *Stats) (err error) {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
UID uint32 `json:"uid"`
|
||||||
|
Username string `json:"uname"`
|
||||||
|
GID uint32 `json:"gid"`
|
||||||
|
HomeDir string `json:"homeDir"` // 用户登录后的起始工作目录
|
||||||
|
Shell string `json:"shell"` // 用户登录时使用的 shell 程序
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupInfo struct {
|
||||||
|
GID uint32 `json:"gid"`
|
||||||
|
Groupname string `json:"gname"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
|||||||
|
|
||||||
req.NewGet(":machineId/process", m.GetProcess),
|
req.NewGet(":machineId/process", m.GetProcess),
|
||||||
|
|
||||||
|
req.NewGet(":machineId/users", m.GetUsers),
|
||||||
|
|
||||||
|
req.NewGet(":machineId/groups", m.GetGroups),
|
||||||
|
|
||||||
req.NewDelete(":machineId/process", m.KillProcess).Log(req.NewLogSave("终止进程")).RequiredPermissionCode("machine:killprocess"),
|
req.NewDelete(":machineId/process", m.KillProcess).Log(req.NewLogSave("终止进程")).RequiredPermissionCode("machine:killprocess"),
|
||||||
|
|
||||||
req.NewPost("", m.SaveMachine).Log(req.NewLogSave("保存机器信息")).RequiredPermission(saveMachineP),
|
req.NewPost("", m.SaveMachine).Log(req.NewLogSave("保存机器信息")).RequiredPermission(saveMachineP),
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *dto.SaveRedis) erro
|
|||||||
ParentTagCodePaths: tagCodePaths,
|
ParentTagCodePaths: tagCodePaths,
|
||||||
})
|
})
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: re.Code,
|
ResourceCode: re.Code,
|
||||||
ResourceType: tagentity.TagTypeRedis,
|
ResourceType: tagentity.TagTypeRedis,
|
||||||
AuthCerts: []*tagentity.ResourceAuthCert{param.AuthCert},
|
AuthCerts: []*tagentity.ResourceAuthCert{param.AuthCert},
|
||||||
@@ -170,7 +170,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *dto.SaveRedis) erro
|
|||||||
ParentTagCodePaths: tagCodePaths,
|
ParentTagCodePaths: tagCodePaths,
|
||||||
})
|
})
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: oldRedis.Code,
|
ResourceCode: oldRedis.Code,
|
||||||
ResourceType: tagentity.TagTypeRedis,
|
ResourceType: tagentity.TagTypeRedis,
|
||||||
AuthCerts: []*tagentity.ResourceAuthCert{param.AuthCert},
|
AuthCerts: []*tagentity.ResourceAuthCert{param.AuthCert},
|
||||||
@@ -200,7 +200,7 @@ func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, func(ctx context.Context) error {
|
}, func(ctx context.Context) error {
|
||||||
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||||
ResourceCode: re.Code,
|
ResourceCode: re.Code,
|
||||||
ResourceType: tagentity.TagTypeRedis,
|
ResourceType: tagentity.TagTypeRedis,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package vo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"mayfly-go/internal/tag/application/dto"
|
"mayfly-go/internal/tag/application/dto"
|
||||||
"mayfly-go/pkg/utils/collx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TagTreeVOS []*dto.SimpleTagTree
|
type TagTreeVOS []*dto.SimpleTagTree
|
||||||
@@ -18,14 +17,21 @@ func (m *TagTreeVOS) ToTrees(pid uint64) []*TagTreeItem {
|
|||||||
return ttis
|
return ttis
|
||||||
}
|
}
|
||||||
|
|
||||||
ttis = collx.ArrayMap(*m, func(tr *dto.SimpleTagTree) *TagTreeItem { return &TagTreeItem{SimpleTagTree: tr} })
|
tagMap := make(map[string]*TagTreeItem)
|
||||||
tagMap := collx.ArrayToMap(ttis, func(item *TagTreeItem) string {
|
var roots []*TagTreeItem
|
||||||
return item.CodePath
|
for _, tag := range *m {
|
||||||
})
|
tti := &TagTreeItem{SimpleTagTree: tag}
|
||||||
|
tagMap[tag.CodePath] = tti
|
||||||
|
ttis = append(ttis, tti)
|
||||||
|
if tti.IsRoot() {
|
||||||
|
roots = append(roots, tti)
|
||||||
|
tti.Root = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, node := range ttis {
|
for _, node := range ttis {
|
||||||
// 根节点
|
// 根节点
|
||||||
if node.IsRoot() {
|
if node.Root {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parentCodePath := node.GetParentPath(0)
|
parentCodePath := node.GetParentPath(0)
|
||||||
@@ -35,5 +41,5 @@ func (m *TagTreeVOS) ToTrees(pid uint64) []*TagTreeItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return collx.ArrayFilter(ttis, func(tti *TagTreeItem) bool { return tti.IsRoot() })
|
return roots
|
||||||
}
|
}
|
||||||
|
|||||||
13
server/internal/tag/application/dto/auth_cert.go
Normal file
13
server/internal/tag/application/dto/auth_cert.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "mayfly-go/internal/tag/domain/entity"
|
||||||
|
|
||||||
|
type RelateAuthCert struct {
|
||||||
|
ResourceCode string
|
||||||
|
|
||||||
|
// 资源标签类型
|
||||||
|
ResourceType entity.TagType
|
||||||
|
|
||||||
|
// 空数组则为删除该资源绑定的授权凭证
|
||||||
|
AuthCerts []*entity.ResourceAuthCert
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ type SimpleTagTree struct {
|
|||||||
CodePath string `json:"codePath"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型
|
CodePath string `json:"codePath"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型
|
||||||
Name string `json:"name"` // 名称
|
Name string `json:"name"` // 名称
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
|
Root bool `json:"-" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *SimpleTagTree) IsRoot() bool {
|
func (pt *SimpleTagTree) IsRoot() bool {
|
||||||
|
|||||||
@@ -12,21 +12,11 @@ import (
|
|||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RelateAuthCertParam struct {
|
|
||||||
ResourceCode string
|
|
||||||
|
|
||||||
// 资源标签类型
|
|
||||||
ResourceType entity.TagType
|
|
||||||
|
|
||||||
// 空数组则为删除该资源绑定的授权凭证
|
|
||||||
AuthCerts []*entity.ResourceAuthCert
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResourceAuthCert interface {
|
type ResourceAuthCert interface {
|
||||||
base.App[*entity.ResourceAuthCert]
|
base.App[*entity.ResourceAuthCert]
|
||||||
|
|
||||||
// RelateAuthCert 关联资源授权凭证信息
|
// RelateAuthCert 关联资源授权凭证信息
|
||||||
RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error
|
RelateAuthCert(ctx context.Context, param *dto.RelateAuthCert) error
|
||||||
|
|
||||||
// SaveAuthCert 保存授权凭证信息
|
// SaveAuthCert 保存授权凭证信息
|
||||||
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
||||||
@@ -64,7 +54,7 @@ func (r *resourceAuthCertAppImpl) InjectResourceAuthCertRepo(resourceAuthCertRep
|
|||||||
r.Repo = resourceAuthCertRepo
|
r.Repo = resourceAuthCertRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *RelateAuthCertParam) error {
|
func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *dto.RelateAuthCert) error {
|
||||||
resourceCode := params.ResourceCode
|
resourceCode := params.ResourceCode
|
||||||
resourceType := int8(params.ResourceType)
|
resourceType := int8(params.ResourceType)
|
||||||
resourceAuthCerts := params.AuthCerts
|
resourceAuthCerts := params.AuthCerts
|
||||||
@@ -92,11 +82,6 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
resourceAuthCert.ResourceType = int8(resourceType)
|
resourceAuthCert.ResourceType = int8(resourceType)
|
||||||
name2AuthCert[resourceAuthCert.Name] = resourceAuthCert
|
name2AuthCert[resourceAuthCert.Name] = resourceAuthCert
|
||||||
|
|
||||||
existNameAc := &entity.ResourceAuthCert{Name: resourceAuthCert.Name}
|
|
||||||
if resourceAuthCert.Id == 0 && r.GetByCond(existNameAc) == nil && existNameAc.ResourceCode != resourceCode {
|
|
||||||
return errorx.NewBiz("授权凭证的名称不能重复[%s]", resourceAuthCert.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 公共授权凭证,则无需进行密文加密,密文即为公共授权凭证名
|
// 公共授权凭证,则无需进行密文加密,密文即为公共授权凭证名
|
||||||
if resourceAuthCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
if resourceAuthCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||||
continue
|
continue
|
||||||
@@ -111,63 +96,66 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
|||||||
oldAuthCert, _ := r.ListByCond(&entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType})
|
oldAuthCert, _ := r.ListByCond(&entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType})
|
||||||
|
|
||||||
// 新增、删除以及不变的授权凭证名
|
// 新增、删除以及不变的授权凭证名
|
||||||
var adds, dels, unmodifys []string
|
var addAcNames, delAcNames, unmodifyAcNames []string
|
||||||
if len(oldAuthCert) == 0 {
|
if len(oldAuthCert) == 0 {
|
||||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
|
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
|
||||||
adds = collx.MapKeys(name2AuthCert)
|
addAcNames = collx.MapKeys(name2AuthCert)
|
||||||
} else {
|
} else {
|
||||||
oldNames := collx.ArrayMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
oldNames := collx.ArrayMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||||
return ac.Name
|
return ac.Name
|
||||||
})
|
})
|
||||||
adds, dels, unmodifys = collx.ArrayCompare[string](collx.MapKeys(name2AuthCert), oldNames)
|
addAcNames, delAcNames, unmodifyAcNames = collx.ArrayCompare[string](collx.MapKeys(name2AuthCert), oldNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
addAuthCerts := make([]*entity.ResourceAuthCert, 0)
|
addAuthCerts := make([]*entity.ResourceAuthCert, 0)
|
||||||
for _, add := range adds {
|
for _, addAcName := range addAcNames {
|
||||||
addAc := name2AuthCert[add]
|
addAc := name2AuthCert[addAcName]
|
||||||
addAc.Id = 0
|
addAc.Id = 0
|
||||||
|
|
||||||
|
existNameAc := &entity.ResourceAuthCert{Name: addAcName}
|
||||||
|
if r.GetByCond(existNameAc) == nil && existNameAc.ResourceCode != resourceCode {
|
||||||
|
return errorx.NewBiz("授权凭证的名称不能重复[%s]", addAcName)
|
||||||
|
}
|
||||||
|
|
||||||
addAuthCerts = append(addAuthCerts, addAc)
|
addAuthCerts = append(addAuthCerts, addAc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理新增的授权凭证
|
// 处理新增的授权凭证
|
||||||
if len(addAuthCerts) > 0 {
|
if len(addAuthCerts) > 0 {
|
||||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, adds)
|
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, addAcNames)
|
||||||
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
|
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, del := range dels {
|
for _, delAcName := range delAcNames {
|
||||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, del)
|
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, delAcName)
|
||||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil {
|
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: delAcName}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(unmodifys) > 0 {
|
if len(unmodifyAcNames) > 0 {
|
||||||
// 旧凭证名 -> 旧凭证
|
// 旧凭证名 -> 旧凭证
|
||||||
oldName2AuthCert := collx.ArrayToMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
oldName2AuthCert := collx.ArrayToMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||||
return ac.Name
|
return ac.Name
|
||||||
})
|
})
|
||||||
acTagType := GetResourceAuthCertTagType(params.ResourceType)
|
acTagType := GetResourceAuthCertTagType(params.ResourceType)
|
||||||
for _, unmodify := range unmodifys {
|
for _, unmodifyAcName := range unmodifyAcNames {
|
||||||
unmodifyAc := name2AuthCert[unmodify]
|
unmodifyAc := name2AuthCert[unmodifyAcName]
|
||||||
if unmodifyAc.Id == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
oldAuthCert := oldName2AuthCert[unmodify]
|
oldAuthCert := oldName2AuthCert[unmodifyAcName]
|
||||||
if !unmodifyAc.HasChanged(oldAuthCert) {
|
if !unmodifyAc.HasChanged(oldAuthCert) {
|
||||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证[%s]未发生字段变更", resourceType, resourceCode, unmodify)
|
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证[%s]未发生字段变更", resourceType, resourceCode, unmodifyAcName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果修改了用户名,且该凭证关联至标签,则需要更新对应的标签名(资源授权凭证类型的标签名为username)
|
// 如果修改了用户名,且该凭证关联至标签,则需要更新对应的标签名(资源授权凭证类型的标签名为username)
|
||||||
if oldAuthCert.Username != unmodifyAc.Username && acTagType != 0 {
|
if oldAuthCert.Username != unmodifyAc.Username && acTagType != 0 {
|
||||||
r.tagTreeApp.UpdateTagName(ctx, acTagType, unmodify, unmodifyAc.Username)
|
r.tagTreeApp.UpdateTagName(ctx, acTagType, unmodifyAcName, unmodifyAc.Username)
|
||||||
}
|
}
|
||||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodifyAcName)
|
||||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
if err := r.UpdateByCond(ctx, unmodifyAc, &entity.ResourceAuthCert{Name: unmodifyAcName, ResourceCode: resourceCode, ResourceType: resourceType}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return m.Username != rac.Username ||
|
return m.Username != rac.Username ||
|
||||||
m.Ciphertext != rac.Ciphertext ||
|
(m.Ciphertext != "" && rac.Ciphertext != "" && m.Ciphertext != rac.Ciphertext) ||
|
||||||
m.CiphertextType != rac.CiphertextType ||
|
m.CiphertextType != rac.CiphertextType ||
|
||||||
m.Remark != rac.Remark ||
|
m.Remark != rac.Remark ||
|
||||||
m.Type != rac.Type ||
|
m.Type != rac.Type ||
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "fmt"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
AppName = "mayfly-go"
|
AppName = "mayfly-go"
|
||||||
Version = "v1.8.4"
|
Version = "v1.8.6"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAppInfo() string {
|
func GetAppInfo() string {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package netx
|
|||||||
import (
|
import (
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||||
)
|
)
|
||||||
@@ -68,3 +69,13 @@ func Ip2Region(ip string) string {
|
|||||||
}
|
}
|
||||||
return region
|
return region
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOutBoundIP() string {
|
||||||
|
conn, err := net.Dial("udp", "8.8.8.8:53")
|
||||||
|
if err != nil {
|
||||||
|
return "0.0.0.0"
|
||||||
|
}
|
||||||
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
ip := strings.Split(localAddr.String(), ":")[0]
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|||||||
10
server/pkg/utils/netx/netx_test.go
Normal file
10
server/pkg/utils/netx/netx_test.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package netx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIp(t *testing.T) {
|
||||||
|
fmt.Println(GetOutBoundIP())
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -36,6 +36,7 @@ CREATE TABLE `t_db` (
|
|||||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
`code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`name` varchar(191) COLLATE utf8mb4_bin DEFAULT NULL,
|
`name` varchar(191) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
|
`get_database_mode` tinyint NULL COMMENT '库名获取方式(-1.实时获取、1.指定库名)',
|
||||||
`database` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL,
|
`database` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`remark` varchar(191) COLLATE utf8mb4_bin DEFAULT NULL,
|
`remark` varchar(191) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`instance_id` bigint unsigned NOT NULL,
|
`instance_id` bigint unsigned NOT NULL,
|
||||||
@@ -464,9 +465,9 @@ DROP TABLE IF EXISTS `t_machine_cron_job_exec`;
|
|||||||
CREATE TABLE `t_machine_cron_job_exec` (
|
CREATE TABLE `t_machine_cron_job_exec` (
|
||||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`cron_job_id` bigint DEFAULT NULL,
|
`cron_job_id` bigint DEFAULT NULL,
|
||||||
`machine_id` bigint DEFAULT NULL,
|
`machine_code` varchar(36) DEFAULT NULL,
|
||||||
`status` tinyint DEFAULT NULL COMMENT '状态',
|
`status` tinyint DEFAULT NULL COMMENT '状态',
|
||||||
`res` varchar(1000) DEFAULT NULL COMMENT '执行结果',
|
`res` varchar(4000) DEFAULT NULL COMMENT '执行结果',
|
||||||
`exec_time` datetime DEFAULT NULL COMMENT '执行时间',
|
`exec_time` datetime DEFAULT NULL COMMENT '执行时间',
|
||||||
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
||||||
`delete_time` datetime DEFAULT NULL,
|
`delete_time` datetime DEFAULT NULL,
|
||||||
@@ -760,11 +761,10 @@ 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(45, 3, '12sSjal1/lskeiql1/Ljewisd3/', 2, 1, '脚本管理-保存脚本按钮', 'machine:script:save', 120000000, 'null', 1, 'admin', 1, 'admin', '2021-06-08 11:09:01', '2021-06-08 11:09:01', 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(45, 3, '12sSjal1/lskeiql1/Ljewisd3/', 2, 1, '脚本管理-保存脚本按钮', 'machine:script:save', 120000000, 'null', 1, 'admin', 1, 'admin', '2021-06-08 11:09:01', '2021-06-08 11:09:01', 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(46, 3, '12sSjal1/lskeiql1/Ljeew43/', 2, 1, '脚本管理-删除按钮', 'machine:script:del', 130000000, 'null', 1, 'admin', 1, 'admin', '2021-06-08 11:09:27', '2021-06-08 11:09: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(46, 3, '12sSjal1/lskeiql1/Ljeew43/', 2, 1, '脚本管理-删除按钮', 'machine:script:del', 130000000, 'null', 1, 'admin', 1, 'admin', '2021-06-08 11:09:27', '2021-06-08 11:09: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(47, 3, '12sSjal1/lskeiql1/ODewix43/', 2, 1, '脚本管理-执行按钮', 'machine:script:run', 140000000, 'null', 1, 'admin', 1, 'admin', '2021-06-08 11:09:50', '2021-06-08 11:09:50', 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(47, 3, '12sSjal1/lskeiql1/ODewix43/', 2, 1, '脚本管理-执行按钮', 'machine:script:run', 140000000, 'null', 1, 'admin', 1, 'admin', '2021-06-08 11:09:50', '2021-06-08 11:09:50', 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(49, 36, 'dbms23ax/xleaiec2/', 1, 1, '数据库管理', 'dbs', 20000000, '{"component":"ops/db/DbList","icon":"Coin","isKeepAlive":true,"routeName":"DbList"}', 1, 'admin', 1, 'admin', '2021-07-07 15:13:55', '2023-03-15 17:31: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(54, 135, 'dbms23ax/X0f4BxT0/leix3Axl/', 2, 1, '数据库保存', 'db:save', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-08 17:30:36', '2021-07-08 17:31:05', 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(54, 49, 'dbms23ax/xleaiec2/leix3Axl/', 2, 1, '数据库保存', 'db:save', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-08 17:30:36', '2021-07-08 17:31:05', 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(55, 135, 'dbms23ax/X0f4BxT0/ygjL3sxA/', 2, 1, '数据库删除', 'db:del', 20000000, 'null', 1, 'admin', 1, 'admin', '2021-07-08 17:30:48', '2021-07-08 17:30:48', 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(55, 49, 'dbms23ax/xleaiec2/ygjL3sxA/', 2, 1, '数据库删除', 'db:del', 20000000, 'null', 1, 'admin', 1, 'admin', '2021-07-08 17:30:48', '2021-07-08 17:30:48', 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(57, 3, '12sSjal1/lskeiql1/OJewex43/', 2, 1, '基本权限', 'machine', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-09 10:48:02', '2021-07-09 10:48:02', 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(57, 3, '12sSjal1/lskeiql1/OJewex43/', 2, 1, '基本权限', 'machine', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-09 10:48:02', '2021-07-09 10:48:02', 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(58, 49, 'dbms23ax/xleaiec2/AceXe321/', 2, 1, '基本权限', 'db', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-09 10:48:22', '2021-07-09 10:48:22', 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(58, 135, 'dbms23ax/X0f4BxT0/AceXe321/', 2, 1, '数据库基本权限', 'db', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-09 10:48:22', '2021-07-09 10:48:22', 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(59, 38, 'dbms23ax/exaeca2x/ealcia23/', 2, 1, '基本权限', 'db:exec', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-09 10:50:13', '2021-07-09 10:50:13', 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(59, 38, 'dbms23ax/exaeca2x/ealcia23/', 2, 1, '基本权限', 'db:exec', 10000000, 'null', 1, 'admin', 1, 'admin', '2021-07-09 10:50:13', '2021-07-09 10:50:13', 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(60, 0, 'RedisXq4/', 1, 1, 'Redis', '/redis', 50000001, '{"icon":"iconfont icon-redis","isKeepAlive":true,"routeName":"RDS"}', 1, 'admin', 1, 'admin', '2021-07-19 20:15:41', '2023-03-15 16:44:59', 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(60, 0, 'RedisXq4/', 1, 1, 'Redis', '/redis', 50000001, '{"icon":"iconfont icon-redis","isKeepAlive":true,"routeName":"RDS"}', 1, 'admin', 1, 'admin', '2021-07-19 20:15:41', '2023-03-15 16:44:59', 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(61, 60, 'RedisXq4/Exitx4al/', 1, 1, '数据操作', 'data-operation', 10000000, '{"component":"ops/redis/DataOperation","icon":"iconfont icon-redis","isKeepAlive":true,"routeName":"DataOperation"}', 1, 'admin', 1, 'admin', '2021-07-19 20:17:29', '2023-03-15 16:37:50', 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(61, 60, 'RedisXq4/Exitx4al/', 1, 1, '数据操作', 'data-operation', 10000000, '{"component":"ops/redis/DataOperation","icon":"iconfont icon-redis","isKeepAlive":true,"routeName":"DataOperation"}', 1, 'admin', 1, 'admin', '2021-07-19 20:17:29', '2023-03-15 16:37:50', 0, NULL);
|
||||||
@@ -813,8 +813,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(152, 150, 'Jra0n7De/zvAMo2vk/', 2, 1, '编辑', 'db:sync:save', 1703641320, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2023-12-27 09:42:00', '2023-12-27 09:42:12', 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(152, 150, 'Jra0n7De/zvAMo2vk/', 2, 1, '编辑', 'db:sync:save', 1703641320, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2023-12-27 09:42:00', '2023-12-27 09:42:12', 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(151, 150, 'Jra0n7De/uAnHZxEV/', 2, 1, '基本权限', 'db:sync', 1703641202, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2023-12-27 09:40:02', '2023-12-27 09:40:02', 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(151, 150, 'Jra0n7De/uAnHZxEV/', 2, 1, '基本权限', 'db:sync', 1703641202, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2023-12-27 09:40:02', '2023-12-27 09:40:02', 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(150, 36, 'Jra0n7De/', 1, 1, '数据同步', 'sync', 1693040707, '{"component":"ops/db/SyncTaskList","icon":"Coin","isKeepAlive":true,"routeName":"SyncTaskList"}', 12, 'liuzongyang', 12, 'liuzongyang', '2023-12-22 09:51:34', '2023-12-27 10:16: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(150, 36, 'Jra0n7De/', 1, 1, '数据同步', 'sync', 1693040707, '{"component":"ops/db/SyncTaskList","icon":"Coin","isKeepAlive":true,"routeName":"SyncTaskList"}', 12, 'liuzongyang', 12, 'liuzongyang', '2023-12-22 09:51:34', '2023-12-27 10:16: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(160, 49, 'dbms23ax/xleaiec2/3NUXQFIO/', 2, 1, '数据库备份', 'db:backup', 1705973876, 'null', 1, 'admin', 1, 'admin', '2024-01-23 09:37:56', '2024-01-23 09:37:56', 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(160, 135, 'dbms23ax/X0f4BxT0/3NUXQFIO/', 2, 1, '数据库备份', 'db:backup', 1705973876, 'null', 1, 'admin', 1, 'admin', '2024-01-23 09:37:56', '2024-01-23 09:37:56', 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(161, 49, 'dbms23ax/xleaiec2/ghErkTdb/', 2, 1, '数据库恢复', 'db:restore', 1705973909, 'null', 1, 'admin', 1, 'admin', '2024-01-23 09:38:29', '2024-01-23 09:38:29', 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(161, 135, 'dbms23ax/X0f4BxT0/ghErkTdb/', 2, 1, '数据库恢复', 'db:restore', 1705973909, 'null', 1, 'admin', 1, 'admin', '2024-01-23 09:38:29', '2024-01-23 09:38:29', 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(1709208354, 1708911264, '6egfEVYr/fw0Hhvye/b4cNf3iq/', 2, 1, '删除流程', 'flow:procdef:del', 1709208354, 'null', 1, 'admin', 1, 'admin', '2024-02-29 20:05:54', '2024-02-29 20:05: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(1709208354, 1708911264, '6egfEVYr/fw0Hhvye/b4cNf3iq/', 2, 1, '删除流程', 'flow:procdef:del', 1709208354, 'null', 1, 'admin', 1, 'admin', '2024-02-29 20:05:54', '2024-02-29 20:05: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(1709208339, 1708911264, '6egfEVYr/fw0Hhvye/r9ZMTHqC/', 2, 1, '保存流程', 'flow:procdef:save', 1709208339, 'null', 1, 'admin', 1, 'admin', '2024-02-29 20:05:40', '2024-02-29 20:05:40', 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(1709208339, 1708911264, '6egfEVYr/fw0Hhvye/r9ZMTHqC/', 2, 1, '保存流程', 'flow:procdef:save', 1709208339, 'null', 1, 'admin', 1, 'admin', '2024-02-29 20:05:40', '2024-02-29 20:05:40', 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(1709103180, 1708910975, '6egfEVYr/oNCIbynR/', 1, 1, '我的流程', 'procinsts', 1708911263, '{"component":"flow/ProcinstList","icon":"Tickets","isKeepAlive":true,"routeName":"ProcinstList"}', 1, 'admin', 1, 'admin', '2024-02-28 14:53:00', '2024-02-29 20:36:07', 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(1709103180, 1708910975, '6egfEVYr/oNCIbynR/', 1, 1, '我的流程', 'procinsts', 1708911263, '{"component":"flow/ProcinstList","icon":"Tickets","isKeepAlive":true,"routeName":"ProcinstList"}', 1, 'admin', 1, 'admin', '2024-02-28 14:53:00', '2024-02-29 20:36:07', 0, NULL);
|
||||||
|
|||||||
6
server/resources/script/sql/v1.8/v1.8.5.sql
Normal file
6
server/resources/script/sql/v1.8/v1.8.5.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ALTER TABLE t_db ADD get_database_mode tinyint NULL COMMENT '库名获取方式(-1.实时获取、1.指定库名)';
|
||||||
|
UPDATE t_db SET get_database_mode = 1;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE t_machine_cron_job_exec ADD machine_code varchar(36) NULL COMMENT '机器编号';
|
||||||
|
ALTER TABLE t_machine_cron_job_exec DROP COLUMN machine_id;
|
||||||
7
server/resources/script/sql/v1.8/v1.8.6.sql
Normal file
7
server/resources/script/sql/v1.8/v1.8.6.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
UPDATE t_sys_resource SET pid=135, ui_path='dbms23ax/X0f4BxT0/leix3Axl/', `type`=2, status=1, name='数据库保存', code='db:save', weight=1693041085, meta='null', creator_id=1, creator='admin', modifier_id=1, modifier='admin', create_time='2021-07-08 17:30:36', update_time='2024-05-17 21:50:01', is_deleted=0, delete_time=NULL WHERE id=54;
|
||||||
|
UPDATE t_sys_resource SET pid=135, ui_path='dbms23ax/X0f4BxT0/ygjL3sxA/', `type`=2, status=1, name='数据库删除', code='db:del', weight=1693041086, meta='null', creator_id=1, creator='admin', modifier_id=1, modifier='admin', create_time='2021-07-08 17:30:48', update_time='2024-05-17 21:50:04', is_deleted=0, delete_time=NULL WHERE id=55;
|
||||||
|
UPDATE t_sys_resource SET pid=135, ui_path='dbms23ax/X0f4BxT0/AceXe321/', `type`=2, status=1, name='数据库基本权限', code='db', weight=1693041085, meta='null', creator_id=1, creator='admin', modifier_id=1, modifier='admin', create_time='2021-07-09 10:48:22', update_time='2024-05-17 21:52:52', is_deleted=0, delete_time=NULL WHERE id=58;
|
||||||
|
UPDATE t_sys_resource SET pid=135, ui_path='dbms23ax/X0f4BxT0/3NUXQFIO/', `type`=2, status=1, name='数据库备份', code='db:backup', weight=1693041087, meta='null', creator_id=1, creator='admin', modifier_id=1, modifier='admin', create_time='2024-01-23 09:37:56', update_time='2024-05-17 21:50:07', is_deleted=0, delete_time=NULL WHERE id=160;
|
||||||
|
UPDATE t_sys_resource SET pid=135, ui_path='dbms23ax/X0f4BxT0/ghErkTdb/', `type`=2, status=1, name='数据库恢复', code='db:restore', weight=1693041088, meta='null', creator_id=1, creator='admin', modifier_id=1, modifier='admin', create_time='2024-01-23 09:38:29', update_time='2024-05-17 21:50:10', is_deleted=0, delete_time=NULL WHERE id=161;
|
||||||
|
|
||||||
|
DELETE FROM t_sys_resource WHERE id=49;
|
||||||
Reference in New Issue
Block a user