mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
feat: 新增mongo管理与数据操作
This commit is contained in:
22
go.mod
22
go.mod
@@ -3,19 +3,16 @@ module mayfly-go
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
// jwt
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // jwt
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
// 验证码
|
||||
github.com/mojocn/base64Captcha v1.3.5
|
||||
github.com/mojocn/base64Captcha v1.3.5 // 验证码
|
||||
github.com/pkg/sftp v1.13.4
|
||||
// 定时任务
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/robfig/cron/v3 v3.0.1 // 定时任务
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
// ssh
|
||||
golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58
|
||||
go.mongodb.org/mongo-driver v1.9.1 // mongo
|
||||
golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58 // ssh
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
// gorm
|
||||
gorm.io/driver/mysql v1.3.2
|
||||
@@ -28,11 +25,14 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
@@ -40,8 +40,14 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.18.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
|
||||
@@ -18,4 +18,8 @@ export const imports = {
|
||||
// redis
|
||||
"RedisList": () => import('@/views/ops/redis'),
|
||||
"DataOperation": () => import('@/views/ops/redis/DataOperation.vue'),
|
||||
// mongo
|
||||
"MongoDataOp": () => import('@/views/ops/mongo/MongoDataOp.vue'),
|
||||
// redis
|
||||
"MongoList": () => import('@/views/ops/mongo/MongoList.vue'),
|
||||
}
|
||||
@@ -221,26 +221,32 @@ router.beforeEach((to, from, next) => {
|
||||
if (to.path === '/login' && !token) {
|
||||
next();
|
||||
NProgress.done();
|
||||
} else {
|
||||
if (!token) {
|
||||
next(`/login?redirect=${to.path}`);
|
||||
clearSession();
|
||||
resetRoute();
|
||||
NProgress.done();
|
||||
return;
|
||||
}
|
||||
if (!token) {
|
||||
next(`/login?redirect=${to.path}`);
|
||||
clearSession();
|
||||
resetRoute();
|
||||
NProgress.done();
|
||||
|
||||
if (SysWs) {
|
||||
SysWs.close();
|
||||
SysWs = null;
|
||||
}
|
||||
} else if (token && to.path === '/login') {
|
||||
next('/');
|
||||
NProgress.done();
|
||||
} else {
|
||||
if (!SysWs) {
|
||||
SysWs = sockets.sysMsgSocket();
|
||||
}
|
||||
if (store.state.routesList.routesList.length > 0) next();
|
||||
if (SysWs) {
|
||||
SysWs.close();
|
||||
SysWs = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (token && to.path === '/login') {
|
||||
next('/');
|
||||
NProgress.done();
|
||||
return;
|
||||
}
|
||||
|
||||
// 终端不需要连接系统websocket消息
|
||||
if (!SysWs && to.path != '/machine/terminal') {
|
||||
SysWs = sockets.sysMsgSocket();
|
||||
}
|
||||
if (store.state.routesList.routesList.length > 0) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
420
mayfly_go_web/src/views/ops/mongo/MongoDataOp.vue
Normal file
420
mayfly_go_web/src/views/ops/mongo/MongoDataOp.vue
Normal file
@@ -0,0 +1,420 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="toolbar">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24">
|
||||
<project-env-select @changeProjectEnv="changeProjectEnv">
|
||||
<template #default>
|
||||
<el-form-item label="实例" label-width="40px">
|
||||
<el-select v-model="mongoId" placeholder="请选择mongo" @change="changeMongo">
|
||||
<el-option v-for="item in mongoList" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{ ` [${item.uri}]` }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="database" placeholder="请选择库" @change="changeDatabase">
|
||||
<el-option v-for="item in databases" :key="item.Name" :label="item.Name" :value="item.Name">
|
||||
<span style="float: left">{{ item.Name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 4px; font-size: 13px">{{
|
||||
` [${formatByteSize(item.SizeOnDisk)}]`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="集合" label-width="40px">
|
||||
<el-select v-model="collection" placeholder="请选择集合" @change="changeCollection">
|
||||
<el-option v-for="item in collections" :key="item" :label="item" :value="item">
|
||||
<!-- <span style="float: left">{{ item.uri }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{
|
||||
` [${item.name}]`
|
||||
}}</span> -->
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</project-env-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-container id="data-exec" style="border: 1px solid #eee; margin-top: 1px">
|
||||
<el-tabs @tab-remove="removeDataTab" @tab-click="onDataTabClick" style="width: 100%; margin-left: 5px" v-model="activeName">
|
||||
<el-tab-pane closable v-for="dt in dataTabs" :key="dt.name" :label="dt.name" :name="dt.name">
|
||||
<el-row v-if="mongoId">
|
||||
<el-link @click="findCommand(activeName)" icon="refresh" :underline="false" class="ml5"></el-link>
|
||||
<el-link @click="showInsertDocDialog" class="ml5" type="primary" icon="plus" :underline="false"></el-link>
|
||||
</el-row>
|
||||
<el-row class="mt5 mb5">
|
||||
<el-input
|
||||
ref="findParamInputRef"
|
||||
v-model="dt.findParamStr"
|
||||
placeholder="点击输入相应查询条件"
|
||||
@focus="showFindDialog(dt.name)"
|
||||
>
|
||||
<template #prepend>查询参数</template>
|
||||
</el-input>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<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="12" />
|
||||
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
|
||||
<div>
|
||||
<el-button @click="onSaveDoc(item.value)" type="warning" plain size="small">保存</el-button>
|
||||
<el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?">
|
||||
<template #reference>
|
||||
<el-button type="danger" plain size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-container>
|
||||
|
||||
<el-dialog width="400px" title="find参数" v-model="findDialog.visible">
|
||||
<el-form label-width="70px">
|
||||
<el-form-item label="filter">
|
||||
<el-input v-model="findDialog.findParam.filter" type="textarea" :rows="6" clearable auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="sort">
|
||||
<el-input v-model="findDialog.findParam.sort" type="textarea" :rows="3" clearable auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="limit">
|
||||
<el-input v-model.number="findDialog.findParam.limit" type="number" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="skip">
|
||||
<el-input v-model.number="findDialog.findParam.skip" type="number" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="findDialog.visible = false">取 消</el-button>
|
||||
<el-button @click="confirmFindDialog" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="600px" :title="`新增'${activeName}'集合文档`" v-model="insertDocDialog.visible" :close-on-click-modal="false">
|
||||
<el-input v-model="insertDocDialog.doc" type="textarea" :rows="12" clearable auto-complete="off"></el-input>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="insertDocDialog.visible = false">取 消</el-button>
|
||||
<el-button @click="onInsertDoc" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<div style="text-align: center; margin-top: 10px"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { mongoApi } from './api';
|
||||
import { toRefs, ref, reactive, defineComponent } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
|
||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
||||
import { formatByteSize, formatJsonString } from '@/common/utils/format';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoDataOp',
|
||||
components: {
|
||||
ProjectEnvSelect,
|
||||
},
|
||||
setup() {
|
||||
const findParamInputRef: any = ref(null);
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
mongoList: [],
|
||||
query: {
|
||||
envId: 0,
|
||||
},
|
||||
mongoId: null, // 当前选择操作的mongo
|
||||
database: '', // 当前选择操作的库
|
||||
collection: '', //当前选中的collection
|
||||
activeName: '', // 当前操作的tab
|
||||
databases: [],
|
||||
collections: [],
|
||||
dataTabs: {}, // 数据tabs
|
||||
findDialog: {
|
||||
visible: false,
|
||||
findParam: {
|
||||
filter: '',
|
||||
sort: '',
|
||||
},
|
||||
},
|
||||
insertDocDialog: {
|
||||
visible: false,
|
||||
doc: '',
|
||||
},
|
||||
});
|
||||
|
||||
const searchMongo = async () => {
|
||||
notNull(state.query.envId, '请先选择项目环境');
|
||||
const res = await mongoApi.mongoList.request(state.query);
|
||||
state.mongoList = res.list;
|
||||
};
|
||||
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
state.databases = [];
|
||||
state.collections = [];
|
||||
state.mongoId = null;
|
||||
state.collection = '';
|
||||
state.database = '';
|
||||
state.dataTabs = {};
|
||||
if (envId != null) {
|
||||
state.query.envId = envId;
|
||||
searchMongo();
|
||||
}
|
||||
};
|
||||
|
||||
const changeMongo = () => {
|
||||
state.databases = [];
|
||||
state.collections = [];
|
||||
state.dataTabs = {};
|
||||
getDatabases();
|
||||
};
|
||||
|
||||
const getDatabases = async () => {
|
||||
const res = await mongoApi.databases.request({ id: state.mongoId });
|
||||
state.databases = res.Databases;
|
||||
};
|
||||
|
||||
const changeDatabase = () => {
|
||||
state.collections = [];
|
||||
state.collection = '';
|
||||
state.dataTabs = {};
|
||||
getCollections();
|
||||
};
|
||||
|
||||
const getCollections = async () => {
|
||||
state.collections = await mongoApi.collections.request({ id: state.mongoId, database: state.database });
|
||||
};
|
||||
|
||||
const changeCollection = () => {
|
||||
const collection = state.collection;
|
||||
let dataTab = state.dataTabs[collection];
|
||||
if (!dataTab) {
|
||||
// 默认查询参数
|
||||
const findParam = {
|
||||
filter: '{}',
|
||||
sort: '{"_id": -1}',
|
||||
skip: 0,
|
||||
limit: 12,
|
||||
},
|
||||
dataTab = {
|
||||
name: collection,
|
||||
datas: [],
|
||||
findParamStr: JSON.stringify(findParam),
|
||||
findParam,
|
||||
};
|
||||
state.dataTabs[collection] = dataTab;
|
||||
}
|
||||
state.activeName = collection;
|
||||
findCommand(collection);
|
||||
};
|
||||
|
||||
const showFindDialog = (collection: string) => {
|
||||
// 获取当前tab的索引位置,将其输入框失去焦点,防止输入以及重复获取焦点
|
||||
const dataTabNames = Object.keys(state.dataTabs);
|
||||
for (let i = 0; i < dataTabNames.length; i++) {
|
||||
if (collection == dataTabNames[i]) {
|
||||
findParamInputRef.value[i].blur();
|
||||
}
|
||||
}
|
||||
|
||||
state.findDialog.findParam = state.dataTabs[collection].findParam;
|
||||
state.findDialog.visible = true;
|
||||
};
|
||||
|
||||
const confirmFindDialog = () => {
|
||||
state.dataTabs[state.activeName].findParam = state.findDialog.findParam;
|
||||
state.dataTabs[state.activeName].findParamStr = JSON.stringify(state.findDialog.findParam);
|
||||
state.findDialog.visible = false;
|
||||
findCommand(state.activeName);
|
||||
};
|
||||
|
||||
const findCommand = async (collection: string) => {
|
||||
const dataTab = state.dataTabs[collection];
|
||||
const findParma = dataTab.findParam;
|
||||
let filter, sort;
|
||||
try {
|
||||
filter = findParma.filter ? JSON.parse(findParma.filter) : {};
|
||||
sort = findParma.sort ? JSON.parse(findParma.sort) : {};
|
||||
} catch (e) {
|
||||
ElMessage.error('filter或sort字段json字符串值错误。注意: json key需双引号');
|
||||
return;
|
||||
}
|
||||
const datas = await mongoApi.findCommand.request({
|
||||
id: state.mongoId,
|
||||
database: state.database,
|
||||
collection,
|
||||
filter,
|
||||
sort,
|
||||
limit: findParma.limit || 12,
|
||||
skip: findParma.skip || 0,
|
||||
});
|
||||
state.dataTabs[collection].datas = wrapDatas(datas);
|
||||
};
|
||||
|
||||
/**
|
||||
* 包装mongo查询回来的对象,即将其都转为json字符串并用value属性值描述,方便显示
|
||||
*/
|
||||
const wrapDatas = (datas: any) => {
|
||||
const wrapDatas = [] as any;
|
||||
if (!datas) {
|
||||
return wrapDatas;
|
||||
}
|
||||
for (let data of datas) {
|
||||
wrapDatas.push({ value: formatJsonString(JSON.stringify(data), false) });
|
||||
}
|
||||
return wrapDatas;
|
||||
};
|
||||
|
||||
const showInsertDocDialog = () => {
|
||||
// tab数据中的第一个文档,因为该集合的文档都类似,故使用第一个文档赋值至需要新增的文档输入框,方便直接修改新增
|
||||
const datasFirstDoc = state.dataTabs[state.activeName].datas[0];
|
||||
let doc = '';
|
||||
if (datasFirstDoc) {
|
||||
// 移除_id字段,因为新增无需该字段
|
||||
const docObj = JSON.parse(datasFirstDoc.value);
|
||||
delete docObj['_id'];
|
||||
doc = formatJsonString(JSON.stringify(docObj), false);
|
||||
}
|
||||
state.insertDocDialog.doc = doc;
|
||||
state.insertDocDialog.visible = true;
|
||||
};
|
||||
|
||||
const onInsertDoc = async () => {
|
||||
let docObj;
|
||||
try {
|
||||
docObj = JSON.parse(state.insertDocDialog.doc);
|
||||
} catch (e) {
|
||||
ElMessage.error('文档内容错误,无法解析为json对象');
|
||||
}
|
||||
const res = await mongoApi.insertCommand.request({
|
||||
id: state.mongoId,
|
||||
database: state.database,
|
||||
collection: state.activeName,
|
||||
doc: docObj,
|
||||
});
|
||||
isTrue(res.InsertedID, '新增失败');
|
||||
ElMessage.success('新增成功');
|
||||
findCommand(state.activeName);
|
||||
state.insertDocDialog.visible = false;
|
||||
};
|
||||
|
||||
const onSaveDoc = async (doc: string) => {
|
||||
const docObj = parseDocJsonString(doc);
|
||||
const id = docObj._id;
|
||||
notBlank(id, '文档的_id属性不存在');
|
||||
delete docObj['_id'];
|
||||
const res = await mongoApi.updateByIdCommand.request({
|
||||
id: state.mongoId,
|
||||
database: state.database,
|
||||
collection: state.collection,
|
||||
docId: id,
|
||||
update: { $set: docObj },
|
||||
});
|
||||
isTrue(res.ModifiedCount == 1, '修改失败');
|
||||
ElMessage.success('保存成功');
|
||||
};
|
||||
|
||||
const onDeleteDoc = async (doc: string) => {
|
||||
const docObj = parseDocJsonString(doc);
|
||||
const id = docObj._id;
|
||||
notBlank(id, '文档的_id属性不存在');
|
||||
const res = await mongoApi.deleteByIdCommand.request({
|
||||
id: state.mongoId,
|
||||
database: state.database,
|
||||
collection: state.collection,
|
||||
docId: id,
|
||||
});
|
||||
isTrue(res.DeletedCount == 1, '删除失败');
|
||||
ElMessage.success('删除成功');
|
||||
findCommand(state.activeName);
|
||||
};
|
||||
|
||||
/**
|
||||
* 将json字符串解析为json对象
|
||||
*/
|
||||
const parseDocJsonString = (doc: string) => {
|
||||
try {
|
||||
return JSON.parse(doc);
|
||||
} catch (e) {
|
||||
ElMessage.error('文档内容解析为json对象失败');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 数据tab点击
|
||||
*/
|
||||
const onDataTabClick = (tab: any) => {
|
||||
const name = tab.props.name;
|
||||
// 修改选择框绑定的表信息
|
||||
state.collection = name;
|
||||
};
|
||||
|
||||
const removeDataTab = (targetName: string) => {
|
||||
const tabNames = Object.keys(state.dataTabs);
|
||||
let activeName = state.activeName;
|
||||
tabNames.forEach((name, index) => {
|
||||
if (name === targetName) {
|
||||
const nextTab = tabNames[index + 1] || tabNames[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab;
|
||||
}
|
||||
}
|
||||
});
|
||||
state.activeName = activeName;
|
||||
// 如果移除最后一个数据tab,则将选择框绑定的collection置空
|
||||
if (activeName == targetName) {
|
||||
state.collection = '';
|
||||
} else {
|
||||
state.collection = activeName;
|
||||
}
|
||||
|
||||
delete state.dataTabs[targetName];
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
findParamInputRef,
|
||||
changeProjectEnv,
|
||||
changeMongo,
|
||||
changeDatabase,
|
||||
changeCollection,
|
||||
onDataTabClick,
|
||||
removeDataTab,
|
||||
showFindDialog,
|
||||
confirmFindDialog,
|
||||
findCommand,
|
||||
showInsertDocDialog,
|
||||
onInsertDoc,
|
||||
onSaveDoc,
|
||||
onDeleteDoc,
|
||||
formatByteSize,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.mongo-doc-btns {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
right: 3px;
|
||||
top: 2px;
|
||||
max-width: 130px;
|
||||
}
|
||||
</style>
|
||||
188
mayfly_go_web/src/views/ops/mongo/MongoEdit.vue
Normal file
188
mayfly_go_web/src/views/ops/mongo/MongoEdit.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="35%" :destroy-on-close="true">
|
||||
<el-form :model="form" ref="mongoForm" :rules="rules" label-width="65px">
|
||||
<el-form-item prop="projectId" label="项目" required>
|
||||
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="envId" label="环境" required>
|
||||
<el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
|
||||
<el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="名称" required>
|
||||
<el-input v-model.trim="form.name" placeholder="请输入名称" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="uri" label="uri" required>
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
v-model.trim="form.uri"
|
||||
placeholder="形如 mongodb://username:password@host1:port1"
|
||||
auto-complete="off"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel()">取 消</el-button>
|
||||
<el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
|
||||
import { mongoApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoEdit',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
projects: {
|
||||
type: Array,
|
||||
},
|
||||
mongo: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
setup(props: any, { emit }) {
|
||||
const mongoForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
projects: [],
|
||||
envs: [],
|
||||
form: {
|
||||
id: null,
|
||||
name: null,
|
||||
uri: null,
|
||||
project: null,
|
||||
projectId: null,
|
||||
envId: null,
|
||||
env: null,
|
||||
},
|
||||
btnLoading: false,
|
||||
rules: {
|
||||
projectId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择项目',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
envId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择环境',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入名称',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
uri: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入mongo uri',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
watch(props, async (newValue) => {
|
||||
state.dialogVisible = newValue.visible;
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.mongo) {
|
||||
getEnvs(newValue.mongo.projectId);
|
||||
state.form = { ...newValue.mongo };
|
||||
} else {
|
||||
state.envs = [];
|
||||
state.form = { db: 0 } as any;
|
||||
}
|
||||
});
|
||||
|
||||
const getEnvs = async (projectId: any) => {
|
||||
state.envs = await projectApi.projectEnvs.request({ projectId });
|
||||
};
|
||||
|
||||
const changeProject = (projectId: number) => {
|
||||
for (let p of state.projects as any) {
|
||||
if (p.id == projectId) {
|
||||
state.form.project = p.name;
|
||||
}
|
||||
}
|
||||
state.form.envId = null;
|
||||
state.form.env = null;
|
||||
state.envs = [];
|
||||
getEnvs(projectId);
|
||||
};
|
||||
|
||||
const changeEnv = (envId: number) => {
|
||||
for (let p of state.envs as any) {
|
||||
if (p.id == envId) {
|
||||
state.form.env = p.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
mongoForm.value.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
mongoApi.saveMongo.request(state.form).then(() => {
|
||||
ElMessage.success('保存成功');
|
||||
emit('val-change', state.form);
|
||||
state.btnLoading = true;
|
||||
setTimeout(() => {
|
||||
state.btnLoading = false;
|
||||
}, 1000);
|
||||
|
||||
cancel();
|
||||
});
|
||||
} else {
|
||||
ElMessage.error('请正确填写信息');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('update:visible', false);
|
||||
emit('cancel');
|
||||
setTimeout(() => {
|
||||
mongoForm.value.resetFields();
|
||||
// 重置对象属性为null
|
||||
state.form = {} as any;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
mongoForm,
|
||||
changeProject,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
427
mayfly_go_web/src/views/ops/mongo/MongoList.vue
Normal file
427
mayfly_go_web/src/views/ops/mongo/MongoList.vue
Normal file
@@ -0,0 +1,427 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<el-button type="primary" icon="plus" @click="editMongo(true)" plain>添加</el-button>
|
||||
<el-button type="primary" icon="edit" :disabled="currentId == null" @click="editMongo(false)" plain>编辑</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="currentId == null" @click="deleteMongo" plain>删除</el-button>
|
||||
<div style="float: right">
|
||||
<el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
<el-table :data="list" style="width: 100%" @current-change="choose" stripe>
|
||||
<el-table-column label="选择" width="60px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="currentId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="project" label="项目" width></el-table-column>
|
||||
<el-table-column prop="env" label="环境" width></el-table-column>
|
||||
<el-table-column prop="name" label="名称" width></el-table-column>
|
||||
<el-table-column prop="uri" label="连接uri" min-width="150" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.uri.split('@')[1] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="150">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建人"></el-table-column>
|
||||
|
||||
<el-table-column label="操作" width>
|
||||
<template #default="scope">
|
||||
<el-link type="primary" @click="showDatabases(scope.row.id)" plain size="small" :underline="false">数据库</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row style="margin-top: 20px" type="flex" justify="end">
|
||||
<el-pagination
|
||||
style="text-align: right"
|
||||
@current-change="handlePageChange"
|
||||
:total="total"
|
||||
layout="prev, pager, next, total, jumper"
|
||||
v-model:current-page="query.pageNum"
|
||||
:page-size="query.pageSize"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<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="是否为空" />
|
||||
|
||||
<el-table-column min-width="80" 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">
|
||||
{{ 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="70px">
|
||||
<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-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:title="mongoEditDialog.title"
|
||||
v-model:visible="mongoEditDialog.visible"
|
||||
v-model:mongo="mongoEditDialog.data"
|
||||
></mongo-edit>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { mongoApi } from './api';
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import MongoEdit from './MongoEdit.vue';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoList',
|
||||
components: {
|
||||
MongoEdit,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
list: [],
|
||||
total: 0,
|
||||
currentId: null,
|
||||
currentData: null,
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
prjectId: null,
|
||||
clusterId: null,
|
||||
},
|
||||
mongoEditDialog: {
|
||||
visible: false,
|
||||
data: null,
|
||||
title: '新增mongo',
|
||||
},
|
||||
databaseDialog: {
|
||||
visible: false,
|
||||
data: [],
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
collectionsDialog: {
|
||||
database: '',
|
||||
visible: false,
|
||||
data: [],
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
createCollectionDialog: {
|
||||
visible: false,
|
||||
form: {
|
||||
name: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
search();
|
||||
state.projects = (await projectApi.projects.request({ pageNum: 1, pageSize: 100 })).list;
|
||||
});
|
||||
|
||||
const handlePageChange = (curPage: number) => {
|
||||
state.query.pageNum = curPage;
|
||||
search();
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.currentId = item.id;
|
||||
state.currentData = item;
|
||||
};
|
||||
|
||||
// connect() {
|
||||
// Req.post('/open/redis/connect', this.form, res => {
|
||||
// this.redisInfo = res
|
||||
// })
|
||||
// }
|
||||
|
||||
const showDatabases = async (id: number) => {
|
||||
state.databaseDialog.data = (await mongoApi.databases.request({ id })).Databases;
|
||||
state.databaseDialog.title = `数据库列表`;
|
||||
state.databaseDialog.visible = true;
|
||||
};
|
||||
|
||||
const showDatabaseStats = async (dbName: string) => {
|
||||
state.databaseDialog.statsDialog.data = await mongoApi.runCommand.request({
|
||||
id: state.currentId,
|
||||
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.currentId, 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.currentId,
|
||||
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.currentId,
|
||||
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.currentId,
|
||||
database: state.collectionsDialog.database,
|
||||
command: {
|
||||
create: form.name,
|
||||
},
|
||||
});
|
||||
ElMessage.success('集合创建成功');
|
||||
state.createCollectionDialog.visible = false;
|
||||
state.createCollectionDialog.form = {} as any;
|
||||
setCollections(state.collectionsDialog.database);
|
||||
};
|
||||
|
||||
const deleteMongo = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除该mongo?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await mongoApi.deleteMongo.request({ id: state.currentId });
|
||||
ElMessage.success('删除成功');
|
||||
state.currentData = null;
|
||||
state.currentId = null;
|
||||
search();
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
// const info = (redis: any) => {
|
||||
// redisApi.redisInfo.request({ id: redis.id }).then((res: any) => {
|
||||
// state.infoDialog.info = res;
|
||||
// state.infoDialog.title = `'${redis.host}' info`;
|
||||
// state.infoDialog.visible = true;
|
||||
// });
|
||||
// };
|
||||
|
||||
const search = async () => {
|
||||
const res = await mongoApi.mongoList.request(state.query);
|
||||
state.list = res.list;
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const editMongo = (isAdd = false) => {
|
||||
if (isAdd) {
|
||||
state.mongoEditDialog.data = null;
|
||||
state.mongoEditDialog.title = '新增mongo';
|
||||
} else {
|
||||
state.mongoEditDialog.data = state.currentData;
|
||||
state.mongoEditDialog.title = '修改mongo';
|
||||
}
|
||||
state.mongoEditDialog.visible = true;
|
||||
};
|
||||
|
||||
const valChange = () => {
|
||||
state.currentId = null;
|
||||
state.currentData = null;
|
||||
search();
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
showDatabases,
|
||||
showDatabaseStats,
|
||||
showCollections,
|
||||
showCollectionStats,
|
||||
onDeleteCollection,
|
||||
showCreateCollectionDialog,
|
||||
onCreateCollection,
|
||||
formatByteSize,
|
||||
deleteMongo,
|
||||
editMongo,
|
||||
valChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
14
mayfly_go_web/src/views/ops/mongo/api.ts
Normal file
14
mayfly_go_web/src/views/ops/mongo/api.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const mongoApi = {
|
||||
mongoList : Api.create("/mongos", 'get'),
|
||||
saveMongo : Api.create("/mongos", 'post'),
|
||||
deleteMongo : Api.create("/mongos/{id}", 'delete'),
|
||||
databases: Api.create("/mongos/{id}/databases", 'get'),
|
||||
collections: Api.create("/mongos/{id}/collections", 'get'),
|
||||
runCommand: Api.create("/mongos/{id}/run-command", 'post'),
|
||||
findCommand: Api.create("/mongos/{id}/command/find", 'post'),
|
||||
updateByIdCommand: Api.create("/mongos/{id}/command/update-by-id", 'post'),
|
||||
deleteByIdCommand: Api.create("/mongos/{id}/command/delete-by-id", 'post'),
|
||||
insertCommand: Api.create("/mongos/{id}/command/insert", 'post'),
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="35%">
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="35%">
|
||||
<el-form :model="form" ref="redisForm" :rules="rules" label-width="85px">
|
||||
<el-form-item prop="projectId" label="项目:" required>
|
||||
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
|
||||
|
||||
40
server/devops/api/form/mongo.go
Normal file
40
server/devops/api/form/mongo.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package form
|
||||
|
||||
type Mongo struct {
|
||||
Id uint64
|
||||
Uri string `binding:"required" json:"uri"`
|
||||
Name string `binding:"required" json:"name"`
|
||||
ProjectId uint64 `binding:"required" json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
EnvId uint64 `binding:"required" json:"envId"`
|
||||
}
|
||||
|
||||
type MongoCommand struct {
|
||||
Database string `binding:"required" json:"database"`
|
||||
Collection string `binding:"required" json:"collection"`
|
||||
Filter map[string]interface{} `json:"filter"`
|
||||
}
|
||||
|
||||
type MongoRunCommand struct {
|
||||
Database string `binding:"required" json:"database"`
|
||||
Command map[string]interface{} `json:"command"`
|
||||
}
|
||||
|
||||
type MongoFindCommand struct {
|
||||
MongoCommand
|
||||
Sort map[string]interface{} `json:"sort"`
|
||||
Skip int64
|
||||
Limit int64
|
||||
}
|
||||
|
||||
type MongoUpdateByIdCommand struct {
|
||||
MongoCommand
|
||||
DocId interface{} `binding:"required" json:"docId"`
|
||||
Update map[string]interface{} `json:"update"`
|
||||
}
|
||||
|
||||
type MongoInsertCommand struct {
|
||||
MongoCommand
|
||||
Doc map[string]interface{} `json:"doc"`
|
||||
}
|
||||
168
server/devops/api/mongo.go
Normal file
168
server/devops/api/mongo.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/ginx"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/server/devops/api/form"
|
||||
"mayfly-go/server/devops/application"
|
||||
"mayfly-go/server/devops/domain/entity"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Mongo struct {
|
||||
MongoApp application.Mongo
|
||||
}
|
||||
|
||||
func (m *Mongo) Mongos(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
mc := &entity.Mongo{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
|
||||
ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
|
||||
}
|
||||
mc.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = m.MongoApp.GetPageList(mc, ginx.GetPageParam(rc.GinCtx), new([]entity.Mongo))
|
||||
}
|
||||
|
||||
func (m *Mongo) Save(rc *ctx.ReqCtx) {
|
||||
form := &form.Mongo{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, form)
|
||||
|
||||
rc.ReqParam = form
|
||||
|
||||
mongo := new(entity.Mongo)
|
||||
utils.Copy(mongo, form)
|
||||
mongo.SetBaseInfo(rc.LoginAccount)
|
||||
m.MongoApp.Save(mongo)
|
||||
}
|
||||
|
||||
func (m *Mongo) DeleteMongo(rc *ctx.ReqCtx) {
|
||||
m.MongoApp.Delete(m.GetMongoId(rc.GinCtx))
|
||||
}
|
||||
|
||||
func (m *Mongo) Databases(rc *ctx.ReqCtx) {
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
res, err := cli.ListDatabases(context.TODO(), bson.D{})
|
||||
biz.ErrIsNilAppendErr(err, "获取mongo所有库信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) Collections(rc *ctx.ReqCtx) {
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
db := rc.GinCtx.Query("database")
|
||||
biz.NotEmpty(db, "database不能为空")
|
||||
ctx := context.TODO()
|
||||
res, err := cli.Database(db).ListCollectionNames(ctx, bson.D{})
|
||||
biz.ErrIsNilAppendErr(err, "获取库集合信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) RunCommand(rc *ctx.ReqCtx) {
|
||||
commandForm := new(form.MongoRunCommand)
|
||||
ginx.BindJsonAndValid(rc.GinCtx, commandForm)
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
|
||||
ctx := context.TODO()
|
||||
var bm bson.M
|
||||
err := cli.Database(commandForm.Database).RunCommand(
|
||||
ctx,
|
||||
commandForm.Command,
|
||||
).Decode(&bm)
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "执行命令失败: %s")
|
||||
rc.ResData = bm
|
||||
}
|
||||
|
||||
func (m *Mongo) FindCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoFindCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
limit := commandForm.Limit
|
||||
if limit != 0 {
|
||||
biz.IsTrue(limit <= 100, "limit不能超过100")
|
||||
}
|
||||
opts := options.Find().SetSort(commandForm.Sort).
|
||||
SetSkip(commandForm.Skip).
|
||||
SetLimit(limit)
|
||||
ctx := context.TODO()
|
||||
cur, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).Find(ctx, commandForm.Filter, opts)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
var res []bson.M
|
||||
cur.All(ctx, &res)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) UpdateByIdCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoUpdateByIdCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
// 解析docId文档id,如果为string类型则使用ObjectId解析,解析失败则为普通字符串
|
||||
docId := commandForm.DocId
|
||||
docIdVal, ok := docId.(string)
|
||||
if ok {
|
||||
objId, err := primitive.ObjectIDFromHex(docIdVal)
|
||||
if err == nil {
|
||||
docId = objId
|
||||
}
|
||||
}
|
||||
|
||||
res, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).UpdateByID(context.TODO(), docId, commandForm.Update)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
rc.ReqParam = commandForm
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) DeleteByIdCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoUpdateByIdCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
// 解析docId文档id,如果为string类型则使用ObjectId解析,解析失败则为普通字符串
|
||||
docId := commandForm.DocId
|
||||
docIdVal, ok := docId.(string)
|
||||
if ok {
|
||||
objId, err := primitive.ObjectIDFromHex(docIdVal)
|
||||
if err == nil {
|
||||
docId = objId
|
||||
}
|
||||
}
|
||||
|
||||
res, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).DeleteOne(context.TODO(), bson.D{{"_id", docId}})
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
rc.ReqParam = commandForm
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) InsertOneCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoInsertCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
res, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).InsertOne(context.TODO(), commandForm.Doc)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
rc.ReqParam = commandForm
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 获取请求路径上的mongo id
|
||||
func (m *Mongo) GetMongoId(g *gin.Context) uint64 {
|
||||
dbId, _ := strconv.Atoi(g.Param("id"))
|
||||
biz.IsTrue(dbId > 0, "mongoId错误")
|
||||
return uint64(dbId)
|
||||
}
|
||||
133
server/devops/application/mongo_app.go
Normal file
133
server/devops/application/mongo_app.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/cache"
|
||||
"mayfly-go/base/global"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/server/devops/domain/entity"
|
||||
"mayfly-go/server/devops/domain/repository"
|
||||
"mayfly-go/server/devops/infrastructure/persistence"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Mongo interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Mongo) int64
|
||||
|
||||
// 根据条件获取
|
||||
GetBy(condition *entity.Mongo, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Mongo
|
||||
|
||||
Save(entity *entity.Mongo)
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(id uint64)
|
||||
|
||||
// 获取mongo连接client
|
||||
// @param id mongo id
|
||||
GetMongoCli(id uint64) *mongo.Client
|
||||
}
|
||||
|
||||
type mongoAppImpl struct {
|
||||
mongoRepo repository.Mongo
|
||||
}
|
||||
|
||||
var MongoApp Mongo = &mongoAppImpl{
|
||||
mongoRepo: persistence.MongoDao,
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *mongoAppImpl) GetPageList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return d.mongoRepo.GetList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Count(condition *entity.Mongo) int64 {
|
||||
return d.mongoRepo.Count(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (d *mongoAppImpl) GetBy(condition *entity.Mongo, cols ...string) error {
|
||||
return d.mongoRepo.Get(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (d *mongoAppImpl) GetById(id uint64, cols ...string) *entity.Mongo {
|
||||
return d.mongoRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Delete(id uint64) {
|
||||
d.mongoRepo.Delete(id)
|
||||
DeleteMongoCache(id)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Save(m *entity.Mongo) {
|
||||
if m.Id == 0 {
|
||||
d.mongoRepo.Insert(m)
|
||||
} else {
|
||||
// 先关闭连接
|
||||
DeleteMongoCache(m.Id)
|
||||
d.mongoRepo.Update(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) GetMongoCli(id uint64) *mongo.Client {
|
||||
cli, err := GetMongoCli(id, func(u uint64) string {
|
||||
mongo := d.GetById(id)
|
||||
biz.NotNil(mongo, "mongo信息不存在")
|
||||
return mongo.Uri
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "连接mongo失败: %s")
|
||||
return cli
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
|
||||
//mongo客户端连接缓存,30分钟内没有访问则会被关闭
|
||||
var mongoCliCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info("关闭mongo连接: id = ", key)
|
||||
value.(*mongo.Client).Disconnect(context.TODO())
|
||||
})
|
||||
|
||||
func GetMongoCli(mongoId uint64, getMongoUri func(uint64) string) (*mongo.Client, error) {
|
||||
cli, err := mongoCliCache.ComputeIfAbsent(mongoId, func(key interface{}) (interface{}, error) {
|
||||
c, err := connect(getMongoUri(mongoId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
})
|
||||
|
||||
if cli != nil {
|
||||
return cli.(*mongo.Client), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func DeleteMongoCache(mongoId uint64) {
|
||||
mongoCliCache.Delete(mongoId)
|
||||
}
|
||||
|
||||
// 连接mongo,并返回client
|
||||
func connect(uri string) (*mongo.Client, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri).SetMaxPoolSize(2))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = client.Ping(context.TODO(), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, err
|
||||
}
|
||||
14
server/devops/domain/entity/mongo.go
Normal file
14
server/devops/domain/entity/mongo.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/base/model"
|
||||
|
||||
type Mongo struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Uri string `orm:"column(uri)" json:"uri"`
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
EnvId uint64 `json:"envId"`
|
||||
Env string `json:"env"`
|
||||
}
|
||||
25
server/devops/domain/repository/mongo.go
Normal file
25
server/devops/domain/repository/mongo.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/server/devops/domain/entity"
|
||||
)
|
||||
|
||||
type Mongo interface {
|
||||
// 分页获取列表
|
||||
GetList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Mongo) int64
|
||||
|
||||
// 根据条件获取
|
||||
Get(condition *entity.Mongo, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Mongo
|
||||
|
||||
Insert(db *entity.Mongo)
|
||||
|
||||
Update(db *entity.Mongo)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
61
server/devops/infrastructure/persistence/mongo_repo.go
Normal file
61
server/devops/infrastructure/persistence/mongo_repo.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/server/devops/domain/entity"
|
||||
"mayfly-go/server/devops/domain/repository"
|
||||
)
|
||||
|
||||
type mongoRepo struct{}
|
||||
|
||||
var MongoDao repository.Mongo = &mongoRepo{}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *mongoRepo) GetList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_mongo d JOIN t_project_member pm ON d.project_id = pm.project_id WHERE 1 = 1 "
|
||||
if condition.CreatorId != 0 {
|
||||
// 使用创建者id模拟项目成员id
|
||||
sql = fmt.Sprintf("%s AND pm.account_id = %d", sql, condition.CreatorId)
|
||||
}
|
||||
if condition.ProjectId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.project_id = %d", sql, condition.ProjectId)
|
||||
}
|
||||
if condition.EnvId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.env_id = %d", sql, condition.EnvId)
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Count(condition *entity.Mongo) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (d *mongoRepo) Get(condition *entity.Mongo, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (d *mongoRepo) GetById(id uint64, cols ...string) *entity.Mongo {
|
||||
db := new(entity.Mongo)
|
||||
if err := model.GetById(db, id, cols...); err != nil {
|
||||
return nil
|
||||
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Insert(db *entity.Mongo) {
|
||||
biz.ErrIsNil(model.Insert(db), "新增mongo信息失败")
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Update(db *entity.Mongo) {
|
||||
biz.ErrIsNil(model.UpdateById(db), "更新mongo信息失败")
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Delete(id uint64) {
|
||||
model.DeleteById(new(entity.Mongo), id)
|
||||
}
|
||||
86
server/devops/router/mongo.go
Normal file
86
server/devops/router/mongo.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/server/devops/api"
|
||||
"mayfly-go/server/devops/application"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitMongoRouter(router *gin.RouterGroup) {
|
||||
m := router.Group("mongos")
|
||||
{
|
||||
ma := &api.Mongo{
|
||||
MongoApp: application.MongoApp,
|
||||
}
|
||||
|
||||
// 获取所有mongo列表
|
||||
m.GET("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.Mongos)
|
||||
})
|
||||
|
||||
saveMongo := ctx.NewLogInfo("保存mongo信息")
|
||||
m.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(saveMongo).
|
||||
Handle(ma.Save)
|
||||
})
|
||||
|
||||
deleteMongo := ctx.NewLogInfo("删除mongo信息")
|
||||
m.DELETE(":id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(deleteMongo).
|
||||
Handle(ma.DeleteMongo)
|
||||
})
|
||||
|
||||
// 获取mongo下的所有数据库
|
||||
m.GET(":id/databases", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.Databases)
|
||||
})
|
||||
|
||||
// 获取mongo指定库的所有集合
|
||||
m.GET(":id/collections", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.Collections)
|
||||
})
|
||||
|
||||
// 获取mongo runCommand
|
||||
m.POST(":id/run-command", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.RunCommand)
|
||||
})
|
||||
|
||||
// 执行mongo find命令
|
||||
m.POST(":id/command/find", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.FindCommand)
|
||||
})
|
||||
|
||||
// 执行mongo update by id命令
|
||||
updateDocById := ctx.NewLogInfo("mongo-更新文档")
|
||||
m.POST(":id/command/update-by-id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(updateDocById).
|
||||
Handle(ma.UpdateByIdCommand)
|
||||
})
|
||||
|
||||
// 执行mongo delete by id命令
|
||||
deleteDoc := ctx.NewLogInfo("mongo-删除文档")
|
||||
m.POST(":id/command/delete-by-id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(deleteDoc).
|
||||
Handle(ma.DeleteByIdCommand)
|
||||
})
|
||||
|
||||
// 执行mongo insert 命令
|
||||
insertDoc := ctx.NewLogInfo("mongo-新增文档")
|
||||
m.POST(":id/command/insert", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(insertDoc).
|
||||
Handle(ma.InsertOneCommand)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ func InitRouter() *gin.Engine {
|
||||
devops_router.InitMachineRouter(api)
|
||||
devops_router.InitMachineScriptRouter(api)
|
||||
devops_router.InitMachineFileRouter(api)
|
||||
devops_router.InitMongoRouter(api)
|
||||
}
|
||||
|
||||
return router
|
||||
|
||||
@@ -401,6 +401,11 @@ INSERT INTO `t_sys_resource` VALUES (64, 63, 2, 1, '基本权限', 'redis:manage
|
||||
INSERT INTO `t_sys_resource` VALUES (70, 48, 2, 1, '项目删除', 'project:del', 6, 'null', 1, 'admin', 1, 'admin', '2021-08-17 11:20:37', '2021-08-17 11:20:37');
|
||||
INSERT INTO `t_sys_resource` VALUES (71, 61, 2, 1, '数据保存', 'redis:data:save', 6, 'null', 1, 'admin', 1, 'admin', '2021-08-17 11:20:37', '2021-08-17 11:20:37');
|
||||
INSERT INTO `t_sys_resource` VALUES (72, 3, 2, 1, '终止进程', 'machine:killprocess', 6, 'null', 1, 'admin', 1, 'admin', '2021-08-17 11:20:37', '2021-08-17 11:20:37');
|
||||
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (79, 2, 1, 1, 'Mongo', 'mongo', 5, '{\"icon\":\"Document\",\"isKeepAlive\":true,\"routeName\":\"Mongo\"}', 1, 'admin', 1, 'admin', '2022-05-13 14:00:41', '2022-05-13 14:00:52');
|
||||
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (80, 79, 1, 1, '数据操作', 'mongo-data-operation', 1, '{\"component\":\"MongoDataOp\",\"icon\":\"Document\",\"isKeepAlive\":true,\"routeName\":\"MongoDataOp\"}', 1, 'admin', 1, 'admin', '2022-05-13 14:03:58', '2022-05-14 20:16:07');
|
||||
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (81, 80, 2, 1, '基本权限', 'mongo:base', 1, 'null', 1, 'admin', 1, 'admin', '2022-05-13 14:04:16', '2022-05-13 14:04:16');
|
||||
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (82, 79, 1, 1, 'Mongo管理', 'mongo-manage', 2, '{\"component\":\"MongoList\",\"icon\":\"Menu\",\"isKeepAlive\":true,\"routeName\":\"MongoList\"}', 1, 'admin', 1, 'admin', '2022-05-16 18:13:06', '2022-05-16 18:13:06');
|
||||
INSERT INTO `t_sys_resource`(`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`) VALUES (83, 82, 2, 1, '基本权限', 'mongo:manage:base', 1, 'null', 1, 'admin', 1, 'admin', '2022-05-16 18:13:25', '2022-05-16 18:13:25');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -599,4 +604,27 @@ INSERT INTO `t_sys_role_resource` VALUES (498, 8, 63, 1, 'admin', '2021-11-05 15
|
||||
INSERT INTO `t_sys_role_resource` VALUES (499, 8, 64, 1, 'admin', '2021-11-05 15:59:16');
|
||||
COMMIT;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_mongo
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `t_mongo`;
|
||||
CREATE TABLE `t_mongo` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(36) COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
|
||||
`uri` varchar(255) COLLATE utf8mb4_bin NOT NULL COMMENT '连接uri',
|
||||
`project_id` bigint(20) NOT NULL,
|
||||
`project` varchar(36) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`env_id` bigint(20) DEFAULT NULL,
|
||||
`env` varchar(36) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator_id` bigint(20) DEFAULT NULL,
|
||||
`creator` varchar(36) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`update_time` datetime DEFAULT NULL,
|
||||
`modifier_id` bigint(20) DEFAULT NULL,
|
||||
`modifier` varchar(36) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
Reference in New Issue
Block a user