mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
feat: mongo优化
This commit is contained in:
@@ -10,7 +10,7 @@ RUN yarn
|
||||
RUN yarn build
|
||||
|
||||
# 构建后端资源
|
||||
FROM golang:1.20-alpine3.16 as be-builder
|
||||
FROM golang:1.21.0 as be-builder
|
||||
|
||||
ENV GOPROXY https://goproxy.cn
|
||||
WORKDIR /mayfly
|
||||
@@ -31,6 +31,9 @@ FROM alpine:3.16
|
||||
|
||||
RUN apk add --no-cache ca-certificates bash expat
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||
|
||||
WORKDIR /mayfly
|
||||
|
||||
COPY --from=be-builder /mayfly/mayfly-go /usr/local/bin/mayfly-go
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum ResultEnum {
|
||||
SUCCESS = 200,
|
||||
ERROR = 400,
|
||||
PARAM_ERROR = 405,
|
||||
SERVER_ERROR = 500,
|
||||
NO_PERMISSION = 501,
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export function exportCsv(filename: string, columns: string[], datas: []) {
|
||||
let link = document.createElement('a');
|
||||
let exportContent = '\uFEFF';
|
||||
let blob = new Blob([exportContent + csvString], {
|
||||
type: 'text/plain;charset=utrf-8',
|
||||
type: 'text/plain;charset=utf-8',
|
||||
});
|
||||
link.id = 'download-csv';
|
||||
link.setAttribute('href', URL.createObjectURL(blob));
|
||||
|
||||
@@ -35,55 +35,81 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="20">
|
||||
<el-container id="mongo-tab" style="border: 1px solid #eee; margin-top: 1px">
|
||||
<el-tabs @tab-remove="removeDataTab" style="width: 100%; margin-left: 5px" v-model="state.activeName">
|
||||
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
|
||||
<el-row>
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
<el-link @click="findCommand(state.activeName)" icon="refresh" :underline="false" class=""> </el-link>
|
||||
<el-link @click="onEditDoc(null)" class="ml5" type="primary" icon="plus" :underline="false"> </el-link>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
ref="findParamInputRef"
|
||||
v-model="dt.findParamStr"
|
||||
placeholder="点击输入相应查询条件"
|
||||
@focus="showFindDialog(dt.key)"
|
||||
>
|
||||
<template #prepend>查询参数</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :style="`height: ${dataHeight}; overflow: auto;`">
|
||||
<el-col :span="6" v-for="item in dt.datas" :key="item">
|
||||
<el-card :body-style="{ padding: '0px', position: 'relative' }">
|
||||
<el-input type="textarea" v-model="item.value" :rows="10" />
|
||||
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
|
||||
<div>
|
||||
<el-link @click="onEditDoc(item)" :underline="false" type="success" icon="MagicStick"></el-link>
|
||||
<div id="mongo-tab" style="border: 1px solid #eee; margin-top: 1px">
|
||||
<el-row v-if="nowColl">
|
||||
<el-descriptions :column="10" size="small" border>
|
||||
<!-- <el-descriptions-item label-align="right" label="tag">xxx</el-descriptions-item> -->
|
||||
|
||||
<!-- <el-divider direction="vertical" border-style="dashed" /> -->
|
||||
<el-descriptions-item label="ns" label-align="right">
|
||||
{{ nowColl.stats.ns }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="count" label-align="right">
|
||||
{{ nowColl.stats.count }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="avgObjSize" label-align="right">
|
||||
{{ formatByteSize(nowColl.stats.avgObjSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="size" label-align="right">
|
||||
{{ formatByteSize(nowColl.stats.size) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="totalSize" label-align="right">
|
||||
{{ formatByteSize(nowColl.stats.totalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="storageSize" label-align="right">
|
||||
{{ formatByteSize(nowColl.stats.storageSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="freeStorageSize" label-align="right">
|
||||
{{ formatByteSize(nowColl.stats.freeStorageSize) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-row>
|
||||
|
||||
<!-- <el-link @click="onSaveDoc(item.value)" :underline="false"
|
||||
type="warning" icon="DocumentChecked"></el-link> -->
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?">
|
||||
<template #reference>
|
||||
<el-link :underline="false" type="danger" icon="DocumentDelete"> </el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
<el-row type="flex">
|
||||
<el-tabs @tab-remove="removeDataTab" style="width: 100%; margin-left: 5px" v-model="state.activeName">
|
||||
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
|
||||
<el-row>
|
||||
<el-col :span="2">
|
||||
<div class="mt5">
|
||||
<el-link @click="findCommand(state.activeName)" icon="refresh" :underline="false" class=""> </el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-link v-auth="perms.saveData" @click="onEditDoc(null)" type="primary" icon="plus" :underline="false"> </el-link>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-container>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
ref="findParamInputRef"
|
||||
v-model="dt.findParamStr"
|
||||
placeholder="点击输入相应查询条件"
|
||||
@focus="showFindDialog(dt.key)"
|
||||
>
|
||||
<template #prepend>查询参数</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :style="`height: ${dataHeight}; overflow: auto;`">
|
||||
<el-col :span="6" v-for="item in dt.datas" :key="item">
|
||||
<el-card :body-style="{ padding: '0px', position: 'relative' }">
|
||||
<el-input type="textarea" v-model="item.value" :rows="10" />
|
||||
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
|
||||
<div>
|
||||
<el-link @click="onEditDoc(item)" :underline="false" type="success" icon="MagicStick"></el-link>
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?" width="160">
|
||||
<template #reference>
|
||||
<el-link v-auth="perms.delData" :underline="false" type="danger" icon="DocumentDelete"> </el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@@ -120,7 +146,7 @@
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="docEditDialog.visible = false">取 消</el-button>
|
||||
<el-button @click="onSaveDoc" type="primary">确 定</el-button>
|
||||
<el-button v-auth="perms.saveData" @click="onSaveDoc" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -131,7 +157,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { mongoApi } from './api';
|
||||
import { defineAsyncComponent, reactive, ref, toRefs } from 'vue';
|
||||
import { computed, defineAsyncComponent, reactive, ref, toRefs } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { isTrue, notBlank } from '@/common/assert';
|
||||
@@ -141,6 +167,11 @@ import { formatByteSize } from '@/common/utils/format';
|
||||
|
||||
const MonacoEditor = defineAsyncComponent(() => import('@/components/monaco/MonacoEditor.vue'));
|
||||
|
||||
const perms = {
|
||||
saveData: 'mongo:data:save',
|
||||
delData: 'mongo:data:del',
|
||||
};
|
||||
|
||||
/**
|
||||
* 树节点类型
|
||||
*/
|
||||
@@ -154,7 +185,7 @@ class NodeType {
|
||||
const findParamInputRef: any = ref(null);
|
||||
const state = reactive({
|
||||
tags: [],
|
||||
dataHeight: `${window.innerHeight - 194}px`,
|
||||
dataHeight: `${window.innerHeight - 194 - 35}px`,
|
||||
mongoList: [] as any,
|
||||
activeName: '', // 当前操作的tab
|
||||
dataTabs: {} as any, // 数据tabs
|
||||
@@ -185,6 +216,10 @@ const state = reactive({
|
||||
|
||||
const { dataHeight, findDialog, docEditDialog } = toRefs(state);
|
||||
|
||||
const nowColl = computed(() => {
|
||||
return getNowDataTab();
|
||||
});
|
||||
|
||||
/**
|
||||
* instmap; tagPaht -> mongo info[]
|
||||
*/
|
||||
@@ -279,15 +314,15 @@ const getCollections = async (id: any, database: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const nodeClick = (data: any) => {
|
||||
const nodeClick = async (data: any) => {
|
||||
// 点击集合
|
||||
if (data.type === NodeType.Coll) {
|
||||
const { id, database, collection } = data.params;
|
||||
changeCollection(id, database, collection);
|
||||
await changeCollection(id, database, collection);
|
||||
}
|
||||
};
|
||||
|
||||
const changeCollection = (id: any, schema: string, collection: string) => {
|
||||
const changeCollection = async (id: any, schema: string, collection: string) => {
|
||||
const label = `${id}:\`${schema}\`.${collection}`;
|
||||
let dataTab = state.dataTabs[label];
|
||||
if (!dataTab) {
|
||||
@@ -345,6 +380,7 @@ const findCommand = async (key: string) => {
|
||||
ElMessage.error('filter或sort字段json字符串值错误。注意: json key需双引号');
|
||||
return;
|
||||
}
|
||||
|
||||
const datas = await mongoApi.findCommand.request({
|
||||
id: dataTab.mongoId,
|
||||
database: dataTab.database,
|
||||
@@ -355,6 +391,17 @@ const findCommand = async (key: string) => {
|
||||
skip: findParma.skip || 0,
|
||||
});
|
||||
state.dataTabs[key].datas = wrapDatas(datas);
|
||||
|
||||
// 获取coll stats
|
||||
state.dataTabs[key].stats = await mongoApi.runCommand.request({
|
||||
id: dataTab.mongoId,
|
||||
database: dataTab.database,
|
||||
command: [
|
||||
{
|
||||
collStats: dataTab.collection,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
352
mayfly_go_web/src/views/ops/mongo/MongoDbs.vue
Normal file
352
mayfly_go_web/src/views/ops/mongo/MongoDbs.vue
Normal file
@@ -0,0 +1,352 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog width="800px" title="数据库列表" :before-close="close" v-model="databaseDialog.visible">
|
||||
<div class="mb5">
|
||||
<el-button @click="showCreateDbDialog" type="primary" icon="plus" size="small">新建</el-button>
|
||||
</div>
|
||||
<el-table :data="databaseDialog.data" :max-height="500">
|
||||
<el-table-column min-width="130" property="Name" label="库名" />
|
||||
<el-table-column min-width="90" property="SizeOnDisk" label="size">
|
||||
<template #default="scope">
|
||||
{{ formatByteSize(scope.row.SizeOnDisk) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="80" property="Empty" label="是否为空" />
|
||||
|
||||
<el-table-column min-width="150" label="操作">
|
||||
<template #default="scope">
|
||||
<el-link type="success" @click="showDatabaseStats(scope.row.Name)" plain size="small" :underline="false">stats</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-link type="primary" @click="showCollections(scope.row.Name)" plain size="small" :underline="false">集合</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-popconfirm @confirm="onDeleteDb(scope.row.Name)" title="确定删除该库?">
|
||||
<template #reference>
|
||||
<el-link type="danger" plain size="small" :underline="false">删除</el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog width="700px" :title="databaseDialog.statsDialog.title" v-model="databaseDialog.statsDialog.visible">
|
||||
<el-descriptions title="库状态信息" :column="3" border>
|
||||
<el-descriptions-item label="db" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.db }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="collections" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.collections }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="objects" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.objects }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="indexes" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.indexes }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="avgObjSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.avgObjSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="dataSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.dataSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="totalSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.totalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="storageSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.storageSize) }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="fsTotalSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.fsTotalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="fsUsedSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.fsUsedSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="indexSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.indexSize) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="600px" :title="collectionsDialog.title" v-model="collectionsDialog.visible">
|
||||
<div class="mb5">
|
||||
<el-button @click="showCreateCollectionDialog" type="primary" icon="plus" size="small">新建</el-button>
|
||||
</div>
|
||||
<el-table stripe :data="collectionsDialog.data" :max-height="500">
|
||||
<el-table-column prop="name" label="名称" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column min-width="80" label="操作">
|
||||
<template #default="scope">
|
||||
<el-link type="success" @click="showCollectionStats(scope.row.name)" plain size="small" :underline="false">stats</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-popconfirm @confirm="onDeleteCollection(scope.row.name)" width="160" title="确定删除该集合?">
|
||||
<template #reference>
|
||||
<el-link type="danger" plain size="small" :underline="false">删除</el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog width="700px" :title="collectionsDialog.statsDialog.title" v-model="collectionsDialog.statsDialog.visible">
|
||||
<el-descriptions title="集合状态信息" :column="3" border>
|
||||
<el-descriptions-item label="ns" label-align="right" :span="2" align="center">
|
||||
{{ collectionsDialog.statsDialog.data.ns }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="count" label-align="right" align="center">
|
||||
{{ collectionsDialog.statsDialog.data.count }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="avgObjSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.avgObjSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="nindexes" label-align="right" align="center">
|
||||
{{ collectionsDialog.statsDialog.data.nindexes }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="size" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.size) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="totalSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.totalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="storageSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.storageSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="freeStorageSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.freeStorageSize) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="400px" title="新建库&集合" v-model="createDbDialog.visible" :destroy-on-close="true">
|
||||
<el-form :model="createDbDialog.form" label-width="auto">
|
||||
<el-form-item prop="dbName" label="库名" required>
|
||||
<el-input v-model="createDbDialog.form.dbName" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="collectionName" label="集合名" required>
|
||||
<el-input v-model="createDbDialog.form.collectionName" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="createDbDialog.visible = false">取 消</el-button>
|
||||
<el-button @click="onCreateDb" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="400px" title="新建集合" v-model="createCollectionDialog.visible" :destroy-on-close="true">
|
||||
<el-form :model="createCollectionDialog.form" label-width="auto">
|
||||
<el-form-item prop="name" label="集合名" required>
|
||||
<el-input v-model="createCollectionDialog.form.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="createCollectionDialog.visible = false">取 消</el-button>
|
||||
<el-button @click="onCreateCollection" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { mongoApi } from './api';
|
||||
import { watch, toRefs, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
id: {
|
||||
type: [Number],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
const state = reactive({
|
||||
databaseDialog: {
|
||||
visible: false,
|
||||
data: [],
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
collectionsDialog: {
|
||||
database: '',
|
||||
visible: false,
|
||||
data: [],
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
createCollectionDialog: {
|
||||
visible: false,
|
||||
form: {
|
||||
name: '',
|
||||
},
|
||||
},
|
||||
createDbDialog: {
|
||||
visible: false,
|
||||
form: {
|
||||
dbName: '',
|
||||
collectionName: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { databaseDialog, collectionsDialog, createCollectionDialog, createDbDialog } = toRefs(state);
|
||||
|
||||
watch(props, async (newValue: any) => {
|
||||
if (!newValue.visible) {
|
||||
state.databaseDialog.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
showDatabases();
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const showDatabases = async () => {
|
||||
state.databaseDialog.data = (await mongoApi.databases.request({ id: props.id })).Databases;
|
||||
state.databaseDialog.visible = true;
|
||||
};
|
||||
|
||||
const showDatabaseStats = async (dbName: string) => {
|
||||
state.databaseDialog.statsDialog.data = await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: dbName,
|
||||
command: [
|
||||
{
|
||||
dbStats: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
state.databaseDialog.statsDialog.title = `'${dbName}' stats`;
|
||||
state.databaseDialog.statsDialog.visible = true;
|
||||
};
|
||||
|
||||
const showCollections = async (database: string) => {
|
||||
state.collectionsDialog.database = database;
|
||||
state.collectionsDialog.data = [];
|
||||
setCollections(database);
|
||||
state.collectionsDialog.title = `'${database}' 集合`;
|
||||
state.collectionsDialog.visible = true;
|
||||
};
|
||||
|
||||
const setCollections = async (database: string) => {
|
||||
const res = await mongoApi.collections.request({ id: props.id, database });
|
||||
const collections = [] as any;
|
||||
for (let r of res) {
|
||||
collections.push({ name: r });
|
||||
}
|
||||
state.collectionsDialog.data = collections;
|
||||
};
|
||||
|
||||
/**
|
||||
* 显示集合状态
|
||||
*/
|
||||
const showCollectionStats = async (collection: string) => {
|
||||
state.collectionsDialog.statsDialog.data = await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: state.collectionsDialog.database,
|
||||
command: [
|
||||
{
|
||||
collStats: collection,
|
||||
},
|
||||
],
|
||||
});
|
||||
state.collectionsDialog.statsDialog.title = `'${collection}' stats`;
|
||||
state.collectionsDialog.statsDialog.visible = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除集合
|
||||
*/
|
||||
const onDeleteCollection = async (collection: string) => {
|
||||
await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: state.collectionsDialog.database,
|
||||
command: [
|
||||
{
|
||||
drop: collection,
|
||||
},
|
||||
],
|
||||
});
|
||||
ElMessage.success('集合删除成功');
|
||||
setCollections(state.collectionsDialog.database);
|
||||
};
|
||||
|
||||
const showCreateCollectionDialog = () => {
|
||||
state.createCollectionDialog.visible = true;
|
||||
};
|
||||
|
||||
const onCreateCollection = async () => {
|
||||
const form = state.createCollectionDialog.form;
|
||||
await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: state.collectionsDialog.database,
|
||||
command: [
|
||||
{
|
||||
create: form.name,
|
||||
},
|
||||
],
|
||||
});
|
||||
ElMessage.success('集合创建成功');
|
||||
state.createCollectionDialog.visible = false;
|
||||
state.createCollectionDialog.form = {} as any;
|
||||
setCollections(state.collectionsDialog.database);
|
||||
};
|
||||
|
||||
const showCreateDbDialog = () => {
|
||||
state.createDbDialog.visible = true;
|
||||
};
|
||||
|
||||
const onCreateDb = async () => {
|
||||
const form = state.createDbDialog.form;
|
||||
await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: form.dbName,
|
||||
command: [
|
||||
{
|
||||
create: form.collectionName,
|
||||
},
|
||||
],
|
||||
});
|
||||
ElMessage.success('数据库与集合创建成功');
|
||||
state.createDbDialog.visible = false;
|
||||
state.createDbDialog.form = {} as any;
|
||||
showDatabases();
|
||||
};
|
||||
|
||||
const onDeleteDb = async (db: string) => {
|
||||
await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: db,
|
||||
command: [
|
||||
{
|
||||
dropDatabase: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
ElMessage.success('库删除成功');
|
||||
showDatabases();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -34,136 +34,15 @@
|
||||
<template #action="{ data }">
|
||||
<el-button @click="showDatabases(data.id)" link>数据库</el-button>
|
||||
|
||||
<el-button type="primary" @click="editMongo(data)" link>编辑</el-button>
|
||||
<el-button @click="showUsers(data.id)" link type="success">cmd</el-button>
|
||||
|
||||
<el-button @click="editMongo(data)" link type="primary">编辑</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<el-dialog width="800px" :title="databaseDialog.title" v-model="databaseDialog.visible">
|
||||
<el-table :data="databaseDialog.data" size="small">
|
||||
<el-table-column min-width="130" property="Name" label="库名" />
|
||||
<el-table-column min-width="90" property="SizeOnDisk" label="size">
|
||||
<template #default="scope">
|
||||
{{ formatByteSize(scope.row.SizeOnDisk) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="80" property="Empty" label="是否为空" />
|
||||
<mongo-dbs v-model:visible="dbsVisible" :id="state.dbOps.dbId"></mongo-dbs>
|
||||
|
||||
<el-table-column min-width="150" label="操作">
|
||||
<template #default="scope">
|
||||
<el-link type="success" @click="showDatabaseStats(scope.row.Name)" plain size="small" :underline="false">stats</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-link type="primary" @click="showCollections(scope.row.Name)" plain size="small" :underline="false">集合</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog width="700px" :title="databaseDialog.statsDialog.title" v-model="databaseDialog.statsDialog.visible">
|
||||
<el-descriptions title="库状态信息" :column="3" border size="small">
|
||||
<el-descriptions-item label="db" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.db }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="collections" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.collections }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="objects" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.objects }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="indexes" label-align="right" align="center">
|
||||
{{ databaseDialog.statsDialog.data.indexes }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="avgObjSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.avgObjSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="dataSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.dataSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="totalSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.totalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="storageSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.storageSize) }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="fsTotalSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.fsTotalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="fsUsedSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.fsUsedSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="indexSize" label-align="right" align="center">
|
||||
{{ formatByteSize(databaseDialog.statsDialog.data.indexSize) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="600px" :title="collectionsDialog.title" v-model="collectionsDialog.visible">
|
||||
<div>
|
||||
<el-button @click="showCreateCollectionDialog" type="primary" icon="plus" size="small">新建</el-button>
|
||||
</div>
|
||||
<el-table border stripe :data="collectionsDialog.data" size="small">
|
||||
<el-table-column prop="name" label="名称" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column min-width="80" label="操作">
|
||||
<template #default="scope">
|
||||
<el-link type="success" @click="showCollectionStats(scope.row.name)" plain size="small" :underline="false">stats</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-popconfirm @confirm="onDeleteCollection(scope.row.name)" title="确定删除该集合?">
|
||||
<template #reference>
|
||||
<el-link type="danger" plain size="small" :underline="false">删除</el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog width="700px" :title="collectionsDialog.statsDialog.title" v-model="collectionsDialog.statsDialog.visible">
|
||||
<el-descriptions title="集合状态信息" :column="3" border size="small">
|
||||
<el-descriptions-item label="ns" label-align="right" :span="2" align="center">
|
||||
{{ collectionsDialog.statsDialog.data.ns }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="count" label-align="right" align="center">
|
||||
{{ collectionsDialog.statsDialog.data.count }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="avgObjSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.avgObjSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="nindexes" label-align="right" align="center">
|
||||
{{ collectionsDialog.statsDialog.data.nindexes }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="size" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.size) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="totalSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.totalSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="storageSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.storageSize) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="freeStorageSize" label-align="right" align="center">
|
||||
{{ formatByteSize(collectionsDialog.statsDialog.data.freeStorageSize) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="400px" title="新建集合" v-model="createCollectionDialog.visible" :destroy-on-close="true">
|
||||
<el-form :model="createCollectionDialog.form" label-width="auto">
|
||||
<el-form-item prop="name" label="集合名" required>
|
||||
<el-input v-model="createCollectionDialog.form.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="描述:">
|
||||
<el-input v-model="showEnvDialog.envForm.remark" auto-complete="off"></el-input>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="createCollectionDialog.visible = false">取 消</el-button>
|
||||
<el-button @click="onCreateCollection" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<mongo-run-command v-model:visible="usersVisible" :id="state.dbOps.dbId" />
|
||||
|
||||
<mongo-edit
|
||||
@val-change="valChange"
|
||||
@@ -176,14 +55,16 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { mongoApi } from './api';
|
||||
import { ref, toRefs, reactive, onMounted } from 'vue';
|
||||
import { defineAsyncComponent, ref, toRefs, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import MongoEdit from './MongoEdit.vue';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
import TagInfo from '../component/TagInfo.vue';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn, TableQuery } from '@/components/pagetable';
|
||||
|
||||
const MongoEdit = defineAsyncComponent(() => import('./MongoEdit.vue'));
|
||||
const MongoDbs = defineAsyncComponent(() => import('./MongoDbs.vue'));
|
||||
const MongoRunCommand = defineAsyncComponent(() => import('./MongoRunCommand.vue'));
|
||||
|
||||
const pageTableRef: any = ref(null);
|
||||
|
||||
const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect')];
|
||||
@@ -193,7 +74,7 @@ const columns = ref([
|
||||
TableColumn.new('uri', '连接uri'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
TableColumn.new('creator', '创建人'),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(100).fixedRight().alignCenter(),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(145).fixedRight().alignCenter(),
|
||||
]);
|
||||
|
||||
const state = reactive({
|
||||
@@ -215,126 +96,24 @@ const state = reactive({
|
||||
data: null as any,
|
||||
title: '新增mongo',
|
||||
},
|
||||
databaseDialog: {
|
||||
visible: false,
|
||||
data: [],
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
collectionsDialog: {
|
||||
database: '',
|
||||
visible: false,
|
||||
data: [],
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
createCollectionDialog: {
|
||||
visible: false,
|
||||
form: {
|
||||
name: '',
|
||||
},
|
||||
},
|
||||
dbsVisible: false,
|
||||
usersVisible: false,
|
||||
});
|
||||
|
||||
const { tags, list, total, selectionData, query, mongoEditDialog, databaseDialog, collectionsDialog, createCollectionDialog } = toRefs(state);
|
||||
const { tags, list, total, selectionData, query, mongoEditDialog, dbsVisible, usersVisible } = toRefs(state);
|
||||
|
||||
onMounted(async () => {
|
||||
search();
|
||||
});
|
||||
|
||||
const showDatabases = async (id: number) => {
|
||||
// state.query.tagPath = row.tagPath
|
||||
state.dbOps.dbId = id;
|
||||
|
||||
state.databaseDialog.data = (await mongoApi.databases.request({ id })).Databases;
|
||||
state.databaseDialog.title = `数据库列表`;
|
||||
state.databaseDialog.visible = true;
|
||||
state.dbsVisible = true;
|
||||
};
|
||||
|
||||
const showDatabaseStats = async (dbName: string) => {
|
||||
state.databaseDialog.statsDialog.data = await mongoApi.runCommand.request({
|
||||
id: state.dbOps.dbId,
|
||||
database: dbName,
|
||||
command: {
|
||||
dbStats: 1,
|
||||
},
|
||||
});
|
||||
state.databaseDialog.statsDialog.title = `'${dbName}' stats`;
|
||||
state.databaseDialog.statsDialog.visible = true;
|
||||
};
|
||||
|
||||
const showCollections = async (database: string) => {
|
||||
state.collectionsDialog.database = database;
|
||||
state.collectionsDialog.data = [];
|
||||
setCollections(database);
|
||||
state.collectionsDialog.title = `'${database}' 集合`;
|
||||
state.collectionsDialog.visible = true;
|
||||
};
|
||||
|
||||
const setCollections = async (database: string) => {
|
||||
const res = await mongoApi.collections.request({ id: state.dbOps.dbId, database });
|
||||
const collections = [] as any;
|
||||
for (let r of res) {
|
||||
collections.push({ name: r });
|
||||
}
|
||||
state.collectionsDialog.data = collections;
|
||||
};
|
||||
|
||||
/**
|
||||
* 显示集合状态
|
||||
*/
|
||||
const showCollectionStats = async (collection: string) => {
|
||||
state.collectionsDialog.statsDialog.data = await mongoApi.runCommand.request({
|
||||
id: state.dbOps.dbId,
|
||||
database: state.collectionsDialog.database,
|
||||
command: {
|
||||
collStats: collection,
|
||||
},
|
||||
});
|
||||
state.collectionsDialog.statsDialog.title = `'${collection}' stats`;
|
||||
state.collectionsDialog.statsDialog.visible = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除集合
|
||||
*/
|
||||
const onDeleteCollection = async (collection: string) => {
|
||||
await mongoApi.runCommand.request({
|
||||
id: state.dbOps.dbId,
|
||||
database: state.collectionsDialog.database,
|
||||
command: {
|
||||
drop: collection,
|
||||
},
|
||||
});
|
||||
ElMessage.success('集合删除成功');
|
||||
setCollections(state.collectionsDialog.database);
|
||||
};
|
||||
|
||||
const showCreateCollectionDialog = () => {
|
||||
state.createCollectionDialog.visible = true;
|
||||
};
|
||||
|
||||
const onCreateCollection = async () => {
|
||||
const form = state.createCollectionDialog.form;
|
||||
await mongoApi.runCommand.request({
|
||||
id: state.dbOps.dbId,
|
||||
database: state.collectionsDialog.database,
|
||||
command: {
|
||||
create: form.name,
|
||||
},
|
||||
});
|
||||
ElMessage.success('集合创建成功');
|
||||
state.createCollectionDialog.visible = false;
|
||||
state.createCollectionDialog.form = {} as any;
|
||||
setCollections(state.collectionsDialog.database);
|
||||
const showUsers = async (id: number) => {
|
||||
state.dbOps.dbId = id;
|
||||
state.usersVisible = true;
|
||||
};
|
||||
|
||||
const deleteMongo = async () => {
|
||||
|
||||
196
mayfly_go_web/src/views/ops/mongo/MongoRunCommand.vue
Normal file
196
mayfly_go_web/src/views/ops/mongo/MongoRunCommand.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog width="700px" title="runCommand" v-model="runCmdDialog.visible" :before-close="close" :destroy-on-close="true">
|
||||
<el-form label-width="auto">
|
||||
<el-row class="mb10">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="模板">
|
||||
<el-select class="w100" @change="changeCmd" filterable v-model="runCmdDialog.cmdName" placeholder="选择命令模板">
|
||||
<el-option v-for="item in mongoCmds" :key="item.name" :label="`${item.name} | ${item.description}`" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="库">
|
||||
<el-select v-model="runCmdDialog.db" filterable placeholder="选择库">
|
||||
<el-option v-for="item in dbs" :key="item.Name" :label="item.Name" :value="item.Name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item class="ml10">
|
||||
<el-button @click="onRunCommand" type="primary">Run</el-button>
|
||||
<el-tooltip effect="dark" placement="top">
|
||||
<template #content> 更多命令查看-> https://www.mongodb.com/docs/manual/reference/command/ </template>
|
||||
<span class="ml10">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="cmd">
|
||||
<monaco-editor style="width: 100%" height="235px" v-model="runCmdDialog.cmd" language="json" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="res">
|
||||
<monaco-editor style="width: 100%" height="235px" v-model="runCmdDialog.cmdRes" language="json" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { mongoApi } from './api';
|
||||
import { watch, defineAsyncComponent, toRefs, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const MonacoEditor = defineAsyncComponent(() => import('@/components/monaco/MonacoEditor.vue'));
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
id: {
|
||||
type: [Number],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
const mongoCmds = {
|
||||
usersInfo: {
|
||||
name: 'usersInfo',
|
||||
description: '获取用户信息',
|
||||
cmd: {
|
||||
usersInfo: 1,
|
||||
showCredentials: false,
|
||||
showCustomData: false,
|
||||
showPrivileges: false,
|
||||
showAuthenticationRestrictions: false,
|
||||
filter: {},
|
||||
},
|
||||
},
|
||||
createUser: {
|
||||
name: 'createUser',
|
||||
description: '创建新用户',
|
||||
cmd: {
|
||||
createUser: '<username>',
|
||||
pwd: '<cleartext password>',
|
||||
roles: [
|
||||
{
|
||||
role: '<role>',
|
||||
db: '<database>',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
grantRolesToUser: {
|
||||
name: 'grantRolesToUser',
|
||||
description: '授予对用户的额外角色',
|
||||
cmd: {
|
||||
grantRolesToUser: '<user>',
|
||||
roles: [''],
|
||||
},
|
||||
},
|
||||
dropUser: {
|
||||
name: 'dropUser',
|
||||
description: '删除用户',
|
||||
cmd: {
|
||||
dropUser: '<user>',
|
||||
},
|
||||
},
|
||||
roleInfo: {
|
||||
name: 'roleInfo',
|
||||
description: '获取角色信息',
|
||||
cmd: {
|
||||
rolesInfo: 1,
|
||||
showAuthenticationRestrictions: false,
|
||||
showBuiltinRoles: true,
|
||||
showPrivileges: false,
|
||||
},
|
||||
},
|
||||
createRole: {
|
||||
name: 'createRole',
|
||||
description: '创建角色',
|
||||
cmd: {
|
||||
createRole: '<new role>',
|
||||
privileges: [{ resource: {}, actions: ['<action>'] }],
|
||||
roles: [{ role: '<role>', db: '<database>' }],
|
||||
authenticationRestrictions: [
|
||||
{
|
||||
clientSource: ['<IP> | <CIDR range>'],
|
||||
serverAddress: ['<IP> |<CIDR range>'],
|
||||
},
|
||||
],
|
||||
writeConcern: '<write concern document>',
|
||||
comment: '<any>',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
dbs: [] as any,
|
||||
selectDbDisabled: false,
|
||||
runCmdDialog: {
|
||||
visible: false,
|
||||
cmdName: '',
|
||||
db: '',
|
||||
cmd: '',
|
||||
cmdRes: '',
|
||||
},
|
||||
});
|
||||
|
||||
const { dbs, runCmdDialog } = toRefs(state);
|
||||
|
||||
watch(props, async (newValue: any) => {
|
||||
if (!newValue.visible) {
|
||||
state.runCmdDialog.visible = false;
|
||||
return;
|
||||
}
|
||||
state.runCmdDialog.visible = newValue.visible;
|
||||
state.dbs = (await mongoApi.databases.request({ id: props.id })).Databases;
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
emit('update:visible', false);
|
||||
state.runCmdDialog.cmd = '';
|
||||
state.runCmdDialog.cmdRes = '';
|
||||
state.runCmdDialog.cmdName = '';
|
||||
state.runCmdDialog.db = '';
|
||||
state.dbs = [];
|
||||
};
|
||||
|
||||
const changeCmd = (val: any) => {
|
||||
const mongoCmd = mongoCmds[val];
|
||||
state.runCmdDialog.cmd = JSON.stringify(mongoCmd.cmd, null, 4);
|
||||
state.runCmdDialog.db = state?.dbs[0]?.Name;
|
||||
state.runCmdDialog.cmdRes = '';
|
||||
};
|
||||
|
||||
const onRunCommand = async () => {
|
||||
const orderCmds = [] as any;
|
||||
const cmdObj = JSON.parse(state.runCmdDialog.cmd);
|
||||
|
||||
for (let item of Object.keys(cmdObj)) {
|
||||
let obj = {};
|
||||
obj[item] = cmdObj[item];
|
||||
orderCmds.push(obj);
|
||||
}
|
||||
|
||||
state.runCmdDialog.cmdRes = '';
|
||||
const res = await mongoApi.runCommand.request({
|
||||
id: props.id,
|
||||
database: state.runCmdDialog.db,
|
||||
command: orderCmds,
|
||||
});
|
||||
state.runCmdDialog.cmdRes = JSON.stringify(res, null, 4);
|
||||
ElMessage.success('执行成功');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -701,10 +701,10 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
countup.js@^2.0.7:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.nlark.com/countup.js/download/countup.js-2.0.8.tgz"
|
||||
integrity sha1-7KDDHJ2z93acuklNkxXNUtuq8bk=
|
||||
countup.js@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.npmmirror.com/countup.js/-/countup.js-2.7.0.tgz#a5521bd935f0ae83417d0128e73f2a2d2543c9c7"
|
||||
integrity sha512-IP9nYLGgW//0If73eXQdFlReGhpFGHaStqB1v82FknxnUWueF6HFuuOXySW4sEDMc88PsZL1EOn/NPkfTZalmQ==
|
||||
|
||||
cropperjs@^1.5.11:
|
||||
version "1.5.12"
|
||||
@@ -1414,10 +1414,10 @@ mitt@^3.0.1:
|
||||
resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1"
|
||||
integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
|
||||
|
||||
monaco-editor@^0.40.0:
|
||||
version "0.40.0"
|
||||
resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.40.0.tgz#d10834e15ad50a15ec61fd01892e508464ebe2fe"
|
||||
integrity sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==
|
||||
monaco-editor@^0.41.0:
|
||||
version "0.41.0"
|
||||
resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.41.0.tgz#2ba31e5af7e3ae93ac5d7467ec2772ef9b3d967f"
|
||||
integrity sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA==
|
||||
|
||||
monaco-sql-languages@^0.11.0:
|
||||
version "0.11.0"
|
||||
@@ -1575,7 +1575,7 @@ postcss@^8.1.10:
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
postcss@^8.4.26:
|
||||
postcss@^8.4.27:
|
||||
version "8.4.27"
|
||||
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.27.tgz#234d7e4b72e34ba5a92c29636734349e0d9c3057"
|
||||
integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==
|
||||
@@ -1666,10 +1666,10 @@ rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rollup@^3.25.2:
|
||||
version "3.26.2"
|
||||
resolved "https://registry.npmmirror.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c"
|
||||
integrity sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==
|
||||
rollup@^3.27.1:
|
||||
version "3.28.0"
|
||||
resolved "https://registry.npmmirror.com/rollup/-/rollup-3.28.0.tgz#a3c70004b01934760c0cb8df717c7a1d932389a2"
|
||||
integrity sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
@@ -1855,14 +1855,14 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
vite@^4.4.7:
|
||||
version "4.4.7"
|
||||
resolved "https://registry.npmmirror.com/vite/-/vite-4.4.7.tgz#71b8a37abaf8d50561aca084dbb77fa342824154"
|
||||
integrity sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==
|
||||
vite@^4.4.9:
|
||||
version "4.4.9"
|
||||
resolved "https://registry.npmmirror.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d"
|
||||
integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==
|
||||
dependencies:
|
||||
esbuild "^0.18.10"
|
||||
postcss "^8.4.26"
|
||||
rollup "^3.25.2"
|
||||
postcss "^8.4.27"
|
||||
rollup "^3.27.1"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
module mayfly-go
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/buger/jsonparser v1.1.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.0
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.14.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d
|
||||
github.com/mojocn/base64Captcha v1.3.5 // 验证码
|
||||
github.com/pkg/sftp v1.13.5
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/redis/go-redis/v9 v9.0.5
|
||||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/robfig/cron/v3 v3.0.1 // 定时任务
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
||||
go.mongodb.org/mongo-driver v1.11.4 // mongo
|
||||
golang.org/x/crypto v0.11.0 // ssh
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
go.mongodb.org/mongo-driver v1.12.1 // mongo
|
||||
golang.org/x/crypto v0.12.0 // ssh
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
// gorm
|
||||
gorm.io/driver/mysql v1.5.1
|
||||
gorm.io/gorm v1.25.2
|
||||
gorm.io/gorm v1.25.4
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -35,9 +38,6 @@ require (
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
@@ -48,25 +48,26 @@ require (
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
||||
@@ -16,8 +16,8 @@ type MongoCommand struct {
|
||||
}
|
||||
|
||||
type MongoRunCommand struct {
|
||||
Database string `binding:"required" json:"database"`
|
||||
Command map[string]any `json:"command"`
|
||||
Database string `binding:"required" json:"database"`
|
||||
Command []map[string]any `json:"command"`
|
||||
}
|
||||
|
||||
type MongoFindCommand struct {
|
||||
|
||||
@@ -91,12 +91,24 @@ func (m *Mongo) RunCommand(rc *req.Ctx) {
|
||||
commandForm := new(form.MongoRunCommand)
|
||||
ginx.BindJsonAndValid(rc.GinCtx, commandForm)
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
rc.ReqParam = commandForm
|
||||
|
||||
// 顺序执行
|
||||
commands := bson.D{}
|
||||
for _, cmd := range commandForm.Command {
|
||||
e := bson.E{}
|
||||
for k, v := range cmd {
|
||||
e.Key = k
|
||||
e.Value = v
|
||||
}
|
||||
commands = append(commands, e)
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
var bm bson.M
|
||||
err := cli.Database(commandForm.Database).RunCommand(
|
||||
ctx,
|
||||
commandForm.Command,
|
||||
commands,
|
||||
).Decode(&bm)
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "执行命令失败: %s")
|
||||
|
||||
@@ -17,6 +17,8 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
|
||||
saveDataPerm := req.NewPermission("mongo:data:save")
|
||||
|
||||
reqs := [...]*req.Conf{
|
||||
// 获取所有mongo列表
|
||||
req.NewGet("", ma.Mongos),
|
||||
@@ -39,13 +41,13 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
// 执行mongo find命令
|
||||
req.NewPost(":id/command/find", ma.FindCommand),
|
||||
|
||||
req.NewPost(":id/command/update-by-id", ma.UpdateByIdCommand).Log(req.NewLogSave("mongo-更新文档")),
|
||||
req.NewPost(":id/command/update-by-id", ma.UpdateByIdCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSave("mongo-更新文档")),
|
||||
|
||||
// 执行mongo delete by id命令
|
||||
req.NewPost(":id/command/delete-by-id", ma.DeleteByIdCommand).Log(req.NewLogSave("mongo-删除文档")),
|
||||
req.NewPost(":id/command/delete-by-id", ma.DeleteByIdCommand).RequiredPermission(req.NewPermission("mongo:data:del")).Log(req.NewLogSave("mongo-删除文档")),
|
||||
|
||||
// 执行mongo insert 命令
|
||||
req.NewPost(":id/command/insert", ma.InsertOneCommand).Log(req.NewLogSave("mogno-插入文档")),
|
||||
req.NewPost(":id/command/insert", ma.InsertOneCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSave("mogno-插入文档")),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(m, reqs[:])
|
||||
|
||||
Reference in New Issue
Block a user