refactor: 数据库管理迁移至数据库实例-库管理、机器管理-文件支持用户和组信息查看

This commit is contained in:
meilin.huang
2024-05-21 12:34:26 +08:00
parent a7632fbf58
commit bb1522f4dc
29 changed files with 486 additions and 535 deletions

View File

@@ -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.5', version: 'v1.8.6',
}; };
export default config; export default config;

View File

@@ -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) => {

View File

@@ -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)' }),
};

View File

@@ -1,93 +1,112 @@
<template> <template>
<div class="db-list"> <div class="db-list">
<page-table <el-drawer
ref="pageTableRef" :title="title"
:page-api="dbApi.dbs" v-model="dialogVisible"
:before-query-fn="checkRouteTagPath" @open="search"
:search-items="searchItems" :before-close="cancel"
v-model:query-form="query" :destroy-on-close="true"
:columns="columns" :close-on-click-modal="true"
lazy size="60%"
> >
<template #instanceSelect> <template #header>
<el-select remote :remote-method="getInstances" v-model="query.instanceId" placeholder="输入并选择实例" filterable clearable> <DrawerHeader :header="title" :back="cancel">
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id"> <template #extra>
{{ item.name }} <div class="mr20">
<el-divider direction="vertical" border-style="dashed" /> <span>{{ $props.instance?.tags?.[0]?.codePath }}</span>
<el-divider direction="vertical" border-style="dashed" />
{{ item.type }} / {{ item.host }}:{{ item.port }} <SvgIcon :name="getDbDialect($props.instance?.type).getInfo()?.icon" :size="20" />
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
{{ item.username }} <span>{{ $props.instance?.host }}:{{ $props.instance?.port }}</span>
</el-option> </div>
</el-select>
</template>
<template #type="{ data }">
<el-tooltip :content="data.type" placement="top">
<SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
</el-tooltip>
</template>
<template #host="{ data }">
{{ `${data.host}:${data.port}` }}
</template>
<template #database="{ data }">
<el-popover placement="bottom" :width="200" trigger="click">
<template #reference>
<el-button @click="getDbNames(data)" type="primary" link>查看库</el-button>
</template> </template>
<el-table :data="filterDbs" v-loading="state.loadingDbNames" size="small"> </DrawerHeader>
<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> </template>
<template #tagPath="{ data }"> <page-table
<ResourceTags :tags="data.tags" /> ref="pageTableRef"
</template> :page-api="dbApi.dbs"
v-model:query-form="query"
:columns="columns"
lazy
show-selection
v-model:selection-data="state.selectionData"
>
<template #tableHeader>
<el-button v-auth="perms.saveDb" type="primary" circle icon="Plus" @click="editDb(null)"> </el-button>
<el-button v-auth="perms.delDb" :disabled="state.selectionData.length < 1" @click="deleteDb" type="danger" circle icon="delete"></el-button>
</template>
<template #action="{ data }"> <template #type="{ data }">
<el-button type="primary" @click="onShowSqlExec(data)" link>SQL记录</el-button> <el-tooltip :content="data.type" placement="top">
<el-divider direction="vertical" border-style="dashed" /> <SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
</el-tooltip>
</template>
<el-dropdown @command="handleMoreActionCommand"> <template #database="{ data }">
<span class="el-dropdown-link-more"> <el-popover placement="bottom" :width="200" trigger="click">
更多 <template #reference>
<el-icon class="el-icon--right"> <el-button @click="getDbNames(data)" type="primary" link>查看库</el-button>
<arrow-down /> </template>
</el-icon> <el-table :data="filterDbs" v-loading="state.loadingDbNames" size="small">
</span> <el-table-column prop="dbName" label="数据库">
<template #dropdown> <template #header>
<el-dropdown-menu> <el-input v-model="state.dbNameSearch" size="small" placeholder="库名: 输入可过滤" clearable />
<el-dropdown-item :command="{ type: 'detail', data }"> 详情 </el-dropdown-item> </template>
<el-dropdown-item :command="{ type: 'dumpDb', data }"> 导出 </el-dropdown-item> </el-table-column>
<el-dropdown-item :command="{ type: 'backupDb', data }" v-if="actionBtns[perms.backupDb] && supportAction('backupDb', data.type)"> </el-table>
备份任务 </el-popover>
</el-dropdown-item> </template>
<el-dropdown-item
:command="{ type: 'backupHistory', data }"
v-if="actionBtns[perms.backupDb] && supportAction('backupDb', data.type)"
>
备份历史
</el-dropdown-item>
<el-dropdown-item
:command="{ type: 'restoreDb', data }"
v-if="actionBtns[perms.restoreDb] && supportAction('restoreDb', data.type)"
>
恢复任务
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</page-table>
<el-dialog width="750px" :title="`${db} 数据库导出`" v-model="exportDialog.visible"> <template #tagPath="{ data }">
<ResourceTags :tags="data.tags" />
</template>
<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-divider direction="vertical" border-style="dashed" />
<el-dropdown @command="handleMoreActionCommand">
<span class="el-dropdown-link-more">
更多
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<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>
<el-dropdown-item
:command="{ type: 'backupHistory', data }"
v-if="actionBtns[perms.backupDb] && supportAction('backupDb', data.type)"
>
备份历史
</el-dropdown-item>
<el-dropdown-item
:command="{ type: 'restoreDb', data }"
v-if="actionBtns[perms.restoreDb] && supportAction('restoreDb', data.type)"
>
恢复任务
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</page-table>
</el-drawer>
<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,54 +187,29 @@
<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';
@@ -223,44 +217,47 @@ import ResourceTags from '../component/ResourceTags.vue';
import { sleep } from '@/common/utils/loading'; import { sleep } from '@/common/utils/loading';
import { DbGetDbNamesMode } from './enums'; import { DbGetDbNamesMode } from './enums';
import { DbInst } from './db'; 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('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,
dbId: 0,
db: '',
loadingDbNames: false, loadingDbNames: false,
currentDbNames: [], currentDbNames: [],
dbNameSearch: '', dbNameSearch: '',
@@ -268,29 +265,20 @@ const state = reactive({
/** /**
* 选中的数据 * 选中的数据
*/ */
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,
}, },
// 数据库备份弹框 // 数据库备份弹框
@@ -321,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: [],
@@ -339,14 +328,12 @@ 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();
} };
search();
});
const getDbNames = async (db: any) => { const getDbNames = async (db: any) => {
try { try {
@@ -372,42 +359,46 @@ const filterDbs = computed(() => {
}); });
}); });
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();
} }
}; };
@@ -415,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;
@@ -441,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;
}; };
@@ -454,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({
@@ -481,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;
@@ -524,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 {

View File

@@ -1,190 +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="getDatabaseMode" label="获库方式" min-width="80">
<template #default="scope">
<EnumTag :enums="DbGetDbNamesMode" :value="scope.row.getDatabaseMode" />
</template>
</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="getDbNames(scope.row)" type="primary" link>查看库</el-button>
</template>
<el-table :data="filterDbs" size="small" v-loading="state.loadingDbNames">
<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';
import EnumTag from '@/components/enumtag/EnumTag.vue';
import { DbGetDbNamesMode } from './enums';
import { DbInst } from './db';
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,
loadingDbNames: false,
currentDbNames: [], // 当前数据库名
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 getDbNames = async (db: any) => {
try {
state.loadingDbNames = true;
state.currentDbNames = await DbInst.getDbNames(db);
} finally {
state.loadingDbNames = false;
}
};
const filterDbs = computed(() => {
const dbNames = state.currentDbNames;
if (!dbNames) {
return [];
}
const dbNameObjs = dbNames.map((x) => {
return {
dbName: x,
};
});
return dbNameObjs.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>

View File

@@ -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;
}; };

View File

@@ -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) => {

View File

@@ -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'),

View File

@@ -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"
@@ -27,7 +28,7 @@
</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 } 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';
@@ -47,8 +48,6 @@ const props = defineProps({
}, },
}); });
const emit = defineEmits(['update:visible', 'update:data', 'cancel']);
const searchItems = [SearchItem.input('machineCode', '机器编号'), SearchItem.select('status', '状态').withEnum(CronJobExecStatusEnum)]; const searchItems = [SearchItem.input('machineCode', '机器编号'), SearchItem.select('status', '状态').withEnum(CronJobExecStatusEnum)];
const columns = ref([ const columns = ref([
@@ -65,7 +64,7 @@ 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,
machineCode: '', machineCode: '',
@@ -78,24 +77,17 @@ const state = reactive({
machines: [], machines: [],
}); });
const { dialogVisible, params } = toRefs(state); const { params } = toRefs(state);
watch(props, async (newValue: any) => { const dialogVisible = defineModel<boolean>('visible');
state.dialogVisible = newValue.visible;
if (!newValue.visible) {
return;
}
state.params.cronJobId = props.data?.id;
setTimeout(() => search(), 300);
});
const search = async () => { const search = async () => {
state.params.cronJobId = props.data?.id;
pageTableRef.value.search(); pageTableRef.value.search();
}; };
const cancel = () => { const cancel = () => {
emit('update:visible', false); dialogVisible.value = false;
setTimeout(() => { setTimeout(() => {
initData(); initData();
}, 500); }, 500);

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;
}; };

View File

@@ -100,31 +100,34 @@ 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(
state.dvisible = props.visible; () => props.visible,
if (!state.dvisible) { () => {
return; state.dvisible = props.visible;
} if (!state.dvisible) {
return;
}
if (props.data) { if (props.data) {
state.form = { ...(props.data as any) }; state.form = { ...(props.data as any) };
if (state.form.params) { if (state.form.params) {
state.params = JSON.parse(state.form.params); state.params = JSON.parse(state.form.params);
} else {
state.params = [];
}
} else { } else {
state.form = { permission: 'all' } as any;
state.params = []; state.params = [];
} }
} else {
state.form = { permission: 'all' } as any;
state.params = [];
}
if (state.form.permission != 'all') { if (state.form.permission != 'all') {
const accounts = state.form.permission.split(','); const accounts = state.form.permission.split(',');
state.permissionAccount = accounts.slice(0, accounts.length - 1); state.permissionAccount = accounts.slice(0, accounts.length - 1);
} else { } else {
state.permissionAccount = []; state.permissionAccount = [];
}
} }
}); );
const cancel = () => { const cancel = () => {
// 更新父组件visible prop对应的值为false // 更新父组件visible prop对应的值为false

View File

@@ -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
} }

View File

@@ -2,26 +2,22 @@ package vo
import ( import (
"mayfly-go/internal/db/domain/entity" "mayfly-go/internal/db/domain/entity"
tagentity "mayfly-go/internal/tag/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"` // 获取数据库方式 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"`
AuthCertName string `json:"authCertName"`
InstanceId *int64 `json:"instanceId"` InstanceType string `json:"type" gorm:"-"`
AuthCertName string `json:"authCertName"` Host string `json:"host" gorm:"-"`
InstanceName *string `json:"instanceName"` Port int `json:"port" gorm:"-"`
InstanceType *string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
CreateTime *time.Time `json:"createTime"` CreateTime *time.Time `json:"createTime"`
Creator *string `json:"creator"` Creator *string `json:"creator"`
@@ -30,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
}

View File

@@ -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

View File

@@ -27,16 +27,10 @@ type DbTransferLogQuery struct {
// 数据库查询实体,不与数据库表字段一一对应 // 数据库查询实体,不与数据库表字段一一对应
type DbQuery struct { type DbQuery struct {
Id uint64 `form:"id"` Id uint64 `form:"id"`
Code string `json:"code" form:"code"` TagPath string `form:"tagPath"`
Name string `orm:"column(name)" json:"name"` Code string `json:"code" form:"code"`
Database string `orm:"column(database)" json:"database"` Codes []string
Remark string `json:"remark"`
Codes []string
TagIds []uint64 `orm:"column(tag_id)"`
TagPath string `form:"tagPath"`
InstanceId uint64 `form:"instanceId"` InstanceId uint64 `form:"instanceId"`
} }

View File

@@ -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)
} }

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -23,7 +23,7 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
Like("ip", condition.Ip). Like("ip", condition.Ip).
Like("name", condition.Name). Like("name", condition.Name).
In("code", condition.Codes). In("code", condition.Codes).
Like("code", condition.Code). Eq("code", condition.Code).
Eq("protocol", condition.Protocol) Eq("protocol", condition.Protocol)
return m.PageByCondToAny(qd, pageParam, toEntity) return m.PageByCondToAny(qd, pageParam, toEntity)

View File

@@ -5,6 +5,7 @@ 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"
) )
@@ -66,30 +67,6 @@ func (c *Cli) Run(shell string) (string, error) {
return string(buf), nil return string(buf), nil
} }
// 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
}
// Close 关闭client并从缓存中移除如果使用隧道则也关闭 // Close 关闭client并从缓存中移除如果使用隧道则也关闭
func (c *Cli) Close() { func (c *Cli) Close() {
m := c.Info m := c.Info
@@ -115,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
}

View File

@@ -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"`
}

View File

@@ -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),

View File

@@ -4,7 +4,7 @@ import "fmt"
const ( const (
AppName = "mayfly-go" AppName = "mayfly-go"
Version = "v1.8.5" Version = "v1.8.6"
) )
func GetAppInfo() string { func GetAppInfo() string {

View File

@@ -761,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);
@@ -814,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);

View 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;