mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat: 使用标签替代项目
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export function dateFormat(fmt: string, date: Date) {
|
||||
export function dateFormat2(fmt: string, date: Date) {
|
||||
let ret;
|
||||
const opt = {
|
||||
"y+": date.getFullYear().toString(), // 年
|
||||
@@ -19,5 +19,9 @@ export function dateFormat(fmt: string, date: Date) {
|
||||
}
|
||||
|
||||
export function dateStrFormat(fmt: string, dateStr: string) {
|
||||
return dateFormat(fmt, new Date(dateStr))
|
||||
return dateFormat2(fmt, new Date(dateStr))
|
||||
}
|
||||
|
||||
export function dateFormat(dateStr: string) {
|
||||
return dateFormat2('yyyy-MM-dd HH:mm:ss',new Date(dateStr))
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getUseWatermark4Session, getUserInfo4Session } from '@/common/utils/storage.ts';
|
||||
import { dateFormat } from '@/common/utils/date.ts'
|
||||
import { dateFormat2 } from '@/common/utils/date.ts'
|
||||
|
||||
// 页面添加水印效果
|
||||
const setWatermark = (str: any) => {
|
||||
@@ -46,7 +46,7 @@ const watermark = {
|
||||
setTimeout(() => {
|
||||
const userinfo = getUserInfo4Session()
|
||||
if (userinfo && getUseWatermark4Session()) {
|
||||
set(`${userinfo.username} ${dateFormat('yyyy-MM-dd HH:mm:ss', new Date())}`)
|
||||
set(`${userinfo.username} ${dateFormat2('yyyy-MM-dd HH:mm:ss', new Date())}`)
|
||||
} else {
|
||||
del();
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ export const imports = {
|
||||
"SyslogList": () => import('@/views/system/syslog/SyslogList.vue'),
|
||||
"ConfigList": () => import('@/views/system/config/ConfigList.vue'),
|
||||
|
||||
// project
|
||||
"ProjectList": () => import('@/views/ops/project/ProjectList.vue'),
|
||||
// tag
|
||||
"TagTreeList": () => import('@/views/ops/tag/TagTreeList.vue'),
|
||||
"TeamList": () => import('@/views/ops/tag/TeamList.vue'),
|
||||
// db
|
||||
"DbList": () => import('@/views/ops/db/DbList.vue'),
|
||||
"SqlExec": () => import('@/views/ops/db'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import Layout from '@/views/layout/index.vue'
|
||||
import RouterParent from '@/views/layout/routerView/parent.vue';
|
||||
// import RouterParent from '@/views/layout/routerView/parent.vue';
|
||||
|
||||
// 定义动态路由
|
||||
export const dynamicRoutes = [
|
||||
@@ -12,119 +12,108 @@ export const dynamicRoutes = [
|
||||
meta: {
|
||||
isKeepAlive: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
meta: {
|
||||
title: '首页',
|
||||
// iframe链接
|
||||
link: '',
|
||||
// 是否在菜单栏显示,默认显示
|
||||
isHide: false,
|
||||
isKeepAlive: true,
|
||||
// tag标签是否不可删除
|
||||
isAffix: true,
|
||||
// 是否为iframe
|
||||
isIframe: false,
|
||||
icon: 'el-icon-s-home',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/sys',
|
||||
name: 'Resource',
|
||||
redirect: '/sys/resources',
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
// 资源code,用于校验用户是否拥有该资源权限
|
||||
code: 'sys',
|
||||
// isKeepAlive: true,
|
||||
icon: 'el-icon-monitor',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'sys/resources',
|
||||
name: 'ResourceList',
|
||||
component: () => import('@/views/system/resource'),
|
||||
meta: {
|
||||
title: '资源管理',
|
||||
code: 'resource:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sys/roles',
|
||||
name: 'RoleList',
|
||||
component: () => import('@/views/system/role'),
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
code: 'role:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sys/accounts',
|
||||
name: 'ResourceList',
|
||||
component: () => import('@/views/system/account'),
|
||||
meta: {
|
||||
title: '账号管理',
|
||||
code: 'account:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/machine',
|
||||
name: 'Machine',
|
||||
redirect: '/machine/list',
|
||||
meta: {
|
||||
title: '机器管理',
|
||||
// 资源code,用于校验用户是否拥有该资源权限
|
||||
code: 'machine',
|
||||
// isKeepAlive: true,
|
||||
icon: 'el-icon-monitor',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/list',
|
||||
name: 'MachineList',
|
||||
component: () => import('@/views/ops/machine'),
|
||||
meta: {
|
||||
title: '机器列表',
|
||||
code: 'machine:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/personal',
|
||||
name: 'personal',
|
||||
component: () => import('@/views/personal/index.vue'),
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-user',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/iframes',
|
||||
name: 'layoutIfameView',
|
||||
component: RouterParent,
|
||||
meta: {
|
||||
title: 'iframe',
|
||||
link: 'https://gitee.com/lyt-top/vue-next-admin',
|
||||
isIframe: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
// children: [
|
||||
// {
|
||||
// path: '/home',
|
||||
// name: 'home',
|
||||
// component: () => import('@/views/home/index.vue'),
|
||||
// meta: {
|
||||
// title: '首页',
|
||||
// // iframe链接
|
||||
// link: '',
|
||||
// // 是否在菜单栏显示,默认显示
|
||||
// isHide: false,
|
||||
// isKeepAlive: true,
|
||||
// // tag标签是否不可删除
|
||||
// isAffix: true,
|
||||
// // 是否为iframe
|
||||
// isIframe: false,
|
||||
// icon: 'el-icon-s-home',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: '/sys',
|
||||
// name: 'Resource',
|
||||
// redirect: '/sys/resources',
|
||||
// meta: {
|
||||
// title: '系统管理',
|
||||
// // 资源code,用于校验用户是否拥有该资源权限
|
||||
// code: 'sys',
|
||||
// // isKeepAlive: true,
|
||||
// icon: 'el-icon-monitor',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'sys/resources',
|
||||
// name: 'ResourceList',
|
||||
// component: () => import('@/views/system/resource'),
|
||||
// meta: {
|
||||
// title: '资源管理',
|
||||
// code: 'resource:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'sys/roles',
|
||||
// name: 'RoleList',
|
||||
// component: () => import('@/views/system/role'),
|
||||
// meta: {
|
||||
// title: '角色管理',
|
||||
// code: 'role:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'sys/accounts',
|
||||
// name: 'ResourceList',
|
||||
// component: () => import('@/views/system/account'),
|
||||
// meta: {
|
||||
// title: '账号管理',
|
||||
// code: 'account:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/machine',
|
||||
// name: 'Machine',
|
||||
// redirect: '/machine/list',
|
||||
// meta: {
|
||||
// title: '机器管理',
|
||||
// // 资源code,用于校验用户是否拥有该资源权限
|
||||
// code: 'machine',
|
||||
// // isKeepAlive: true,
|
||||
// icon: 'el-icon-monitor',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/list',
|
||||
// name: 'MachineList',
|
||||
// component: () => import('@/views/ops/machine'),
|
||||
// meta: {
|
||||
// title: '机器列表',
|
||||
// code: 'machine:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/personal',
|
||||
// name: 'personal',
|
||||
// component: () => import('@/views/personal/index.vue'),
|
||||
// meta: {
|
||||
// title: '个人中心',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-user',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -165,17 +154,6 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
titleRename: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/machine/terminal-rec',
|
||||
name: 'machineTerminalRec',
|
||||
component: () => import('@/views/ops/machine/MachineRec.vue'),
|
||||
meta: {
|
||||
// 将路径 'xxx?name=名字' 里的name字段值替换到title里
|
||||
title: '终端回放 | {name}',
|
||||
// 是否根据query对标题名进行参数替换,即最终显示为‘终端_机器名’
|
||||
titleRename: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// 定义404界面
|
||||
|
||||
@@ -43,25 +43,25 @@ export default {
|
||||
const state = reactive({
|
||||
topCardItemList: [
|
||||
{
|
||||
title: '项目数',
|
||||
id: 'projectNum',
|
||||
color: '#FEBB50',
|
||||
},
|
||||
{
|
||||
title: 'Linux机器数',
|
||||
title: 'Linux机器',
|
||||
id: 'machineNum',
|
||||
color: '#F95959',
|
||||
},
|
||||
{
|
||||
title: '数据库总数',
|
||||
title: '数据库',
|
||||
id: 'dbNum',
|
||||
color: '#8595F4',
|
||||
},
|
||||
{
|
||||
title: 'redis总数',
|
||||
title: 'redis',
|
||||
id: 'redisNum',
|
||||
color: '#1abc9c',
|
||||
},
|
||||
{
|
||||
title: 'Mongo',
|
||||
id: 'mongoNum',
|
||||
color: '#FEBB50',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ export default {
|
||||
const initNumCountUp = async () => {
|
||||
const res: any = await indexApi.getIndexCount.request();
|
||||
nextTick(() => {
|
||||
new CountUp('projectNum', res.projectNum).start();
|
||||
new CountUp('mongoNum', res.mongoNum).start();
|
||||
new CountUp('machineNum', res.machineNum).start();
|
||||
new CountUp('dbNum', res.dbNum).start();
|
||||
new CountUp('redisNum', res.redisNum).start();
|
||||
@@ -87,8 +87,8 @@ export default {
|
||||
router.push('/personal');
|
||||
break;
|
||||
}
|
||||
case 'projectNum': {
|
||||
router.push('/project/projects');
|
||||
case 'mongoNum': {
|
||||
router.push('/mongo/mongo-data-operation');
|
||||
break;
|
||||
}
|
||||
case 'machineNum': {
|
||||
|
||||
81
mayfly_go_web/src/views/ops/component/TagSelect.vue
Normal file
81
mayfly_go_web/src/views/ops/component/TagSelect.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tree-select
|
||||
@check="changeTag"
|
||||
style="width: 100%"
|
||||
v-model="selectTags"
|
||||
:data="tags"
|
||||
:render-after-expand="true"
|
||||
:default-expanded-keys="[selectTags]"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'codePath',
|
||||
children: 'children',
|
||||
}"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span style="font-size: 13px">
|
||||
{{ data.code }}
|
||||
<span style="color: #3c8dbc">【</span>
|
||||
{{ data.name }}
|
||||
<span style="color: #3c8dbc">】</span>
|
||||
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { tagApi } from '../tag/api';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TagSelect',
|
||||
props: {
|
||||
tagId: {
|
||||
type: Number,
|
||||
},
|
||||
tagPath: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
setup(props: any, { emit }) {
|
||||
const state = reactive({
|
||||
tags: [],
|
||||
// 单选则为id,多选为id数组
|
||||
selectTags: null as any,
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.tagId) {
|
||||
state.selectTags = props.tagId;
|
||||
}
|
||||
state.tags = await tagApi.getTagTrees.request(null);
|
||||
});
|
||||
|
||||
const changeTag = (tag: any, checkInfo: any) => {
|
||||
if (checkInfo.checkedNodes.length > 0) {
|
||||
emit('update:tagId', tag.id);
|
||||
emit('update:tagPath', tag.codePath);
|
||||
emit('changeTag', tag);
|
||||
} else {
|
||||
emit('update:tagId', null);
|
||||
emit('update:tagPath', null);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
changeTag,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -2,17 +2,10 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
|
||||
<el-form :model="form" ref="dbForm" :rules="rules" label-width="95px">
|
||||
<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 prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</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>
|
||||
@@ -76,6 +69,10 @@
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="remark" label="备注:">
|
||||
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
|
||||
<el-col :span="3">
|
||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1" :false-label="-1"></el-checkbox>
|
||||
@@ -108,14 +105,17 @@
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
|
||||
import { dbApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { machineApi } from '../machine/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DbEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@@ -139,10 +139,14 @@ export default defineComponent({
|
||||
envs: [],
|
||||
allDatabases: [] as any,
|
||||
databaseList: [] as any,
|
||||
sshTunnelMachineList: [],
|
||||
sshTunnelMachineList: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
tagId: null as any,
|
||||
tagPath: null as any,
|
||||
type: null,
|
||||
name: null,
|
||||
host: '',
|
||||
port: 3306,
|
||||
username: null,
|
||||
password: null,
|
||||
@@ -152,6 +156,7 @@ export default defineComponent({
|
||||
projectId: null,
|
||||
envId: null,
|
||||
env: null,
|
||||
remark: '',
|
||||
enableSshTunnel: null,
|
||||
sshTunnelMachineId: null,
|
||||
},
|
||||
@@ -218,7 +223,6 @@ export default defineComponent({
|
||||
}
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.db) {
|
||||
getEnvs(newValue.db.projectId);
|
||||
state.form = { ...newValue.db };
|
||||
// 将数据库名使用空格切割,获取所有数据库列表
|
||||
state.databaseList = newValue.db.database.split(' ');
|
||||
@@ -244,22 +248,6 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
@@ -272,7 +260,7 @@ export default defineComponent({
|
||||
const reqForm = { ...state.form };
|
||||
reqForm.password = await RsaEncrypt(reqForm.password);
|
||||
state.allDatabases = await dbApi.getAllDatabase.request(reqForm);
|
||||
ElMessage.success('获取成功, 请选择需要管理操作的数据库')
|
||||
ElMessage.success('获取成功, 请选择需要管理操作的数据库');
|
||||
};
|
||||
|
||||
const getDbPwd = async () => {
|
||||
@@ -324,7 +312,6 @@ export default defineComponent({
|
||||
getDbPwd,
|
||||
changeDatabase,
|
||||
getSshTunnelMachines,
|
||||
changeProject,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
>删除</el-button
|
||||
>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" 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 @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-button v-waves type="primary" icon="search" @click="search()" class="ml5">查询</el-button>
|
||||
</div>
|
||||
@@ -20,8 +20,7 @@
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="project" label="项目" min-width="100" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="env" label="环境" min-width="100"></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="160" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column min-width="170" label="host:port" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
@@ -42,13 +41,14 @@
|
||||
size="small"
|
||||
v-for="db in scope.row.dbs"
|
||||
:key="db"
|
||||
style="cursor: pointer; margin-left: 3px; margin-bottom: 3px;"
|
||||
style="cursor: pointer; margin-left: 3px; margin-bottom: 3px"
|
||||
>{{ db }}</el-tag
|
||||
>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="100"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
|
||||
<el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column>
|
||||
<el-table-column min-width="160" prop="createTime" label="创建时间" show-overflow-tooltip>
|
||||
@@ -190,7 +190,7 @@
|
||||
<el-table-column prop="creator" label="执行人" min-width="60" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="createTime" label="执行时间" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="60" show-overflow-tooltip> </el-table-column>
|
||||
@@ -249,7 +249,6 @@
|
||||
|
||||
<db-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:title="dbEditDialog.title"
|
||||
v-model:visible="dbEditDialog.visible"
|
||||
v-model:db="dbEditDialog.data"
|
||||
@@ -266,11 +265,12 @@ import DbEdit from './DbEdit.vue';
|
||||
import CreateTable from './CreateTable.vue';
|
||||
import { dbApi } from './api';
|
||||
import enums from './enums';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import SqlExecBox from './component/SqlExecBox.ts';
|
||||
import config from '@/common/config';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
import { isTrue } from '@/common/assert';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DbList',
|
||||
@@ -286,7 +286,7 @@ export default defineComponent({
|
||||
saveDb: 'db:save',
|
||||
delDb: 'db:del',
|
||||
},
|
||||
projects: [],
|
||||
tags: [],
|
||||
chooseId: null,
|
||||
/**
|
||||
* 选中的数据
|
||||
@@ -296,6 +296,7 @@ export default defineComponent({
|
||||
* 查询条件
|
||||
*/
|
||||
query: {
|
||||
tagPath: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
@@ -406,12 +407,11 @@ export default defineComponent({
|
||||
search();
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const editDb = async (isAdd = false) => {
|
||||
await getProjects();
|
||||
if (isAdd) {
|
||||
state.dbEditDialog.data = null;
|
||||
state.dbEditDialog.title = '新增数据库资源';
|
||||
@@ -616,7 +616,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getProjects,
|
||||
dateFormat,
|
||||
getTags,
|
||||
filterTableInfos,
|
||||
enums,
|
||||
search,
|
||||
|
||||
@@ -3,46 +3,56 @@
|
||||
<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="资源">
|
||||
<el-select v-model="dbId" placeholder="请选择资源实例" @change="changeDbInstance" filterable style="width: 150px">
|
||||
<el-option v-for="item in dbs" :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.host}:${item.port} ${item.type}`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form class="search-form" label-position="right" :inline="true">
|
||||
<el-form-item label="标签">
|
||||
<el-select @change="changeTag" @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" filterable style="width: 220px">
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据库">
|
||||
<el-select
|
||||
v-model="db"
|
||||
placeholder="请选择数据库"
|
||||
@change="changeDb"
|
||||
@clear="clearDb"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 150px"
|
||||
<el-form-item label="资源">
|
||||
<el-select v-model="dbId" placeholder="请选择资源实例" @change="changeDbInstance" filterable style="width: 220px">
|
||||
<el-option v-for="item in dbs" :key="item.id" :label="`${item.name} [${item.tagPath}]`" :value="item.id">
|
||||
<span style="float: left">{{ `${item.name} [${item.tagPath}]` }}</span>
|
||||
<span style="float: rignt; color: #8492a6; margin-left: 10px; font-size: 13px">{{
|
||||
`${item.host}:${item.port} ${item.type}`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据库">
|
||||
<el-select
|
||||
v-model="db"
|
||||
placeholder="请选择数据库"
|
||||
@change="changeDb"
|
||||
@clear="clearDb"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option v-for="item in databaseList" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-width="20" label="表">
|
||||
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable style="width: 250px">
|
||||
<el-option
|
||||
v-for="item in tableMetadata"
|
||||
:key="item.tableName"
|
||||
:label="item.tableName + (item.tableComment != '' ? `【${item.tableComment}】` : '')"
|
||||
:value="item.tableName"
|
||||
>
|
||||
<el-option v-for="item in databaseList" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-form-item label-width="20" label="表">
|
||||
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable style="width: 250px">
|
||||
<el-option
|
||||
v-for="item in tableMetadata"
|
||||
:key="item.tableName"
|
||||
:label="item.tableName + (item.tableComment != '' ? `【${item.tableComment}】` : '')"
|
||||
:value="item.tableName"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <project-env-select @changeTag="changeTag">
|
||||
<template #default>
|
||||
|
||||
</template>
|
||||
</project-env-select>
|
||||
</project-env-select> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -316,17 +326,15 @@ import 'codemirror/addon/hint/sql-hint.js';
|
||||
import { format as sqlFormatter } from 'sql-formatter';
|
||||
import { notBlank, notEmpty, isTrue } from '@/common/assert';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
import config from '@/common/config';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
import SqlExecBox from './component/SqlExecBox';
|
||||
import { dateStrFormat } from '@/common/utils/date.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SqlExec',
|
||||
components: {
|
||||
ProjectEnvSelect,
|
||||
},
|
||||
components: {},
|
||||
setup() {
|
||||
const codeTextarea: any = ref(null);
|
||||
const token = getSession('token');
|
||||
@@ -336,11 +344,12 @@ export default defineComponent({
|
||||
const state = reactive({
|
||||
token: token,
|
||||
defalutLimit: 20, // 默认查询数量
|
||||
dbs: [], // 数据库实例列表
|
||||
tags: [],
|
||||
dbs: [] as any, // 数据库实例列表
|
||||
databaseList: [], // 数据库实例拥有的数据库列表,1数据库实例 -> 多数据库
|
||||
db: '', // 当前操作的数据库
|
||||
dbType: '',
|
||||
tables: [],
|
||||
tables: [] as any,
|
||||
dbId: null, // 当前选中操作的数据库实例
|
||||
tableName: '',
|
||||
tableMetadata: [],
|
||||
@@ -349,7 +358,7 @@ export default defineComponent({
|
||||
activeName: 'Query',
|
||||
queryTabName: 'Query',
|
||||
nowTableName: '', // 当前表格数据操作的数据库表名,用于双击编辑表内容使用
|
||||
dataTabs: {}, // 点击表信息后执行结果数据展示tabs
|
||||
dataTabs: {} as any, // 点击表信息后执行结果数据展示tabs
|
||||
dataTabsTableHeight: 600,
|
||||
// 查询tab
|
||||
queryTab: {
|
||||
@@ -366,8 +375,8 @@ export default defineComponent({
|
||||
},
|
||||
params: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
envId: null,
|
||||
pageSize: 100,
|
||||
tagPath: null,
|
||||
},
|
||||
conditionDialog: {
|
||||
title: '',
|
||||
@@ -440,16 +449,17 @@ export default defineComponent({
|
||||
/**
|
||||
* 项目及环境更改后的回调事件
|
||||
*/
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
const changeTag = () => {
|
||||
state.dbs = [];
|
||||
state.dbId = null;
|
||||
state.db = '';
|
||||
state.databaseList = [];
|
||||
clearDb();
|
||||
if (envId != null) {
|
||||
state.params.envId = envId;
|
||||
search();
|
||||
}
|
||||
search();
|
||||
};
|
||||
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const onBeforeChange = (instance: any, changeObj: any) => {
|
||||
@@ -1221,8 +1231,9 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getTags,
|
||||
codeTextarea,
|
||||
changeProjectEnv,
|
||||
changeTag,
|
||||
changeTable,
|
||||
cellClick,
|
||||
onRunSql,
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true" :before-close="cancel" width="38%">
|
||||
<el-form :model="form" ref="machineForm" :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>
|
||||
<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 prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</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="ip" label="ip:" required>
|
||||
<el-col :span="18">
|
||||
<el-input :disabled="form.id" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off"></el-input>
|
||||
<el-input :disabled="form.id != 0" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off"></el-input>
|
||||
</el-col>
|
||||
<el-col style="text-align: center" :span="1">:</el-col>
|
||||
<el-col :span="5">
|
||||
@@ -91,9 +89,13 @@ import { machineApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MachineEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@@ -112,12 +114,15 @@ export default defineComponent({
|
||||
const machineForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
projects: [],
|
||||
sshTunnelMachineList: [],
|
||||
projects: [] as any,
|
||||
sshTunnelMachineList: [] as any,
|
||||
tags: [],
|
||||
selectTags: [],
|
||||
form: {
|
||||
id: null,
|
||||
projectId: null,
|
||||
projectName: null,
|
||||
tagId: null as any,
|
||||
tagPath: '',
|
||||
ip: null,
|
||||
name: null,
|
||||
authMethod: 1,
|
||||
port: 22,
|
||||
@@ -131,17 +136,10 @@ export default defineComponent({
|
||||
pwd: '',
|
||||
btnLoading: false,
|
||||
rules: {
|
||||
projectId: [
|
||||
tagId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择项目',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
envId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择环境',
|
||||
message: '请选择标签',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
@@ -206,14 +204,6 @@ export default defineComponent({
|
||||
state.pwd = await machineApi.getMachinePwd.request({ id: state.form.id });
|
||||
};
|
||||
|
||||
const changeProject = (projectId: number) => {
|
||||
for (let p of state.projects as any) {
|
||||
if (p.id == projectId) {
|
||||
state.form.projectName = p.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
if (!state.form.id) {
|
||||
notBlank(state.form.password, '新增操作,密码不可为空');
|
||||
@@ -258,7 +248,6 @@ export default defineComponent({
|
||||
machineForm,
|
||||
getSshTunnelMachines,
|
||||
getPwd,
|
||||
changeProject,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
|
||||
@@ -3,21 +3,15 @@
|
||||
<el-card>
|
||||
<div>
|
||||
<el-button v-auth="'machine:add'" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加</el-button>
|
||||
<el-button
|
||||
v-auth="'machine:update'"
|
||||
type="primary"
|
||||
icon="edit"
|
||||
:disabled="currentId == null"
|
||||
@click="openFormDialog(currentData)"
|
||||
plain
|
||||
<el-button v-auth="'machine:update'" type="primary" icon="edit" :disabled="!currentId" @click="openFormDialog(currentData)" plain
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button v-auth="'machine:del'" :disabled="currentId == null" @click="deleteMachine(currentId)" type="danger" icon="delete"
|
||||
<el-button v-auth="'machine:del'" :disabled="!currentId" @click="deleteMachine(currentId)" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" v-model="params.projectId" placeholder="请选择项目" @clear="search" filterable clearable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
<el-select @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" @clear="search" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
class="ml5"
|
||||
@@ -66,11 +60,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="90"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目" min-width="120"></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="250" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="165">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建者" min-width="80"></el-table-column>
|
||||
@@ -170,7 +164,6 @@
|
||||
|
||||
<machine-edit
|
||||
:title="machineEditDialog.title"
|
||||
:projects="projects"
|
||||
v-model:visible="machineEditDialog.visible"
|
||||
v-model:machine="machineEditDialog.data"
|
||||
@valChange="submitSuccess"
|
||||
@@ -187,6 +180,8 @@
|
||||
:machineId="machineStatsDialog.machineId"
|
||||
:title="machineStatsDialog.title"
|
||||
></machine-stats>
|
||||
|
||||
<machine-rec v-model:visible="machineRecDialog.visible" :machineId="machineRecDialog.machineId" :title="machineRecDialog.title"></machine-rec>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -195,12 +190,14 @@ import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { machineApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import ServiceManage from './ServiceManage.vue';
|
||||
import FileManage from './FileManage.vue';
|
||||
import MachineEdit from './MachineEdit.vue';
|
||||
import ProcessList from './ProcessList.vue';
|
||||
import MachineStats from './MachineStats.vue';
|
||||
import MachineRec from './MachineRec.vue';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MachineList',
|
||||
@@ -210,17 +207,19 @@ export default defineComponent({
|
||||
FileManage,
|
||||
MachineEdit,
|
||||
MachineStats,
|
||||
MachineRec,
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
tags: [] as any,
|
||||
stats: '',
|
||||
params: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
ip: null,
|
||||
name: null,
|
||||
tagPath: null,
|
||||
},
|
||||
// 列表数据
|
||||
data: {
|
||||
@@ -228,7 +227,7 @@ export default defineComponent({
|
||||
total: 10,
|
||||
},
|
||||
// 当前选中数据id
|
||||
currentId: null,
|
||||
currentId: 0,
|
||||
currentData: null,
|
||||
serviceDialog: {
|
||||
visible: false,
|
||||
@@ -252,7 +251,7 @@ export default defineComponent({
|
||||
},
|
||||
machineEditDialog: {
|
||||
visible: false,
|
||||
data: null,
|
||||
data: null as any,
|
||||
title: '新增机器',
|
||||
},
|
||||
machineRecDialog: {
|
||||
@@ -296,12 +295,11 @@ export default defineComponent({
|
||||
search();
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const openFormDialog = async (machine: any) => {
|
||||
await getProjects();
|
||||
let dialogTitle;
|
||||
if (machine) {
|
||||
state.machineEditDialog.data = state.currentData as any;
|
||||
@@ -324,7 +322,7 @@ export default defineComponent({
|
||||
});
|
||||
await machineApi.del.request({ id });
|
||||
ElMessage.success('操作成功');
|
||||
state.currentId = null;
|
||||
state.currentId = 0;
|
||||
state.currentData = null;
|
||||
search();
|
||||
} catch (err) {}
|
||||
@@ -353,7 +351,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const submitSuccess = () => {
|
||||
state.currentId = null;
|
||||
state.currentId = 0;
|
||||
state.currentData = null;
|
||||
search();
|
||||
};
|
||||
@@ -380,20 +378,16 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const showRec = (row: any) => {
|
||||
const { href } = router.resolve({
|
||||
path: `/machine/terminal-rec`,
|
||||
query: {
|
||||
id: row.id,
|
||||
name: `${row.name}[${row.ip}]-终端回放记录`,
|
||||
},
|
||||
});
|
||||
window.open(href, '_blank');
|
||||
state.machineRecDialog.title = `${row.name}[${row.ip}]-终端回放记录`;
|
||||
state.machineRecDialog.machineId = row.id;
|
||||
state.machineRecDialog.visible = true;
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dateFormat,
|
||||
choose,
|
||||
getProjects,
|
||||
getTags,
|
||||
showTerminal,
|
||||
openFormDialog,
|
||||
deleteMachine,
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="toolbar">
|
||||
<span style="dispaly: inline-block" class="ml10">{{ title }}</span>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-select @change="getUsers" v-model="operateDate" placeholder="操作日期" filterable>
|
||||
<el-option v-for="item in operateDates" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="getRecs" filterable v-model="user" placeholder="请选择操作人">
|
||||
<el-option v-for="item in users" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="playRec" filterable v-model="rec" placeholder="请选择操作记录">
|
||||
<el-option v-for="item in recs" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
快捷键-> space[空格键]: 暂停/播放 | f: 全屏/取消全屏
|
||||
</div>
|
||||
<div ref="playerRef" id="rc-player"></div>
|
||||
<div id="terminalRecDialog">
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="dialogVisible"
|
||||
:before-close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
width="70%"
|
||||
>
|
||||
<div class="toolbar">
|
||||
<el-select @change="getUsers" v-model="operateDate" placeholder="操作日期" filterable>
|
||||
<el-option v-for="item in operateDates" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="getRecs" filterable v-model="user" placeholder="请选择操作人">
|
||||
<el-option v-for="item in users" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="playRec" filterable v-model="rec" placeholder="请选择操作记录">
|
||||
<el-option v-for="item in recs" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
快捷键-> space[空格键]: 暂停/播放
|
||||
</div>
|
||||
<div ref="playerRef" id="rc-player"></div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, onMounted, ref, reactive, defineComponent } from 'vue';
|
||||
import { toRefs, watch, ref, reactive, defineComponent } from 'vue';
|
||||
import { machineApi } from './api';
|
||||
import * as AsciinemaPlayer from 'asciinema-player';
|
||||
import 'asciinema-player/dist/bundle/asciinema-player.css';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MachineRec',
|
||||
components: {},
|
||||
@@ -34,7 +41,6 @@ export default defineComponent({
|
||||
title: { type: String },
|
||||
},
|
||||
setup(props: any, context) {
|
||||
const route = useRoute();
|
||||
const playerRef = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
@@ -48,10 +54,14 @@ export default defineComponent({
|
||||
rec: '',
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
state.machineId = Number.parseInt(route.query.id as string);
|
||||
state.title = route.query.name as string;
|
||||
getOperateDate();
|
||||
watch(props, async (newValue) => {
|
||||
const visible = newValue.visible;
|
||||
if (visible) {
|
||||
state.machineId = newValue.machineId;
|
||||
state.title = newValue.title;
|
||||
await getOperateDate();
|
||||
}
|
||||
state.dialogVisible = visible;
|
||||
});
|
||||
|
||||
const getOperateDate = async () => {
|
||||
@@ -118,3 +128,10 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
#terminalRecDialog {
|
||||
.el-overlay .el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,36 +3,45 @@
|
||||
<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 class="search-form" label-position="right" :inline="true">
|
||||
<el-form-item label="标签">
|
||||
<el-select
|
||||
@change="changeTag"
|
||||
@focus="getTags"
|
||||
v-model="query.tagPath"
|
||||
placeholder="请选择标签"
|
||||
filterable
|
||||
style="width: 250px"
|
||||
>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<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" filterable>
|
||||
<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="20px">
|
||||
<el-select v-model="database" placeholder="请选择库" @change="changeDatabase" filterable>
|
||||
<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" filterable>
|
||||
<el-option v-for="item in collections" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</project-env-select>
|
||||
<el-form-item label="集合" label-width="40px">
|
||||
<el-select v-model="collection" placeholder="请选择集合" @change="changeCollection" filterable>
|
||||
<el-option v-for="item in collections" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -64,12 +73,7 @@
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-link
|
||||
@click="onSaveDoc(item.value)"
|
||||
:underline="false"
|
||||
type="warning"
|
||||
icon="DocumentChecked"
|
||||
></el-link>
|
||||
<el-link @click="onSaveDoc(item.value)" :underline="false" type="warning" icon="DocumentChecked"></el-link>
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
@@ -132,36 +136,38 @@
|
||||
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 } from '@/common/utils/format';
|
||||
import JsonEdit from '@/components/jsonedit/index.vue';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoDataOp',
|
||||
components: {
|
||||
ProjectEnvSelect,
|
||||
JsonEdit,
|
||||
},
|
||||
setup() {
|
||||
const findParamInputRef: any = ref(null);
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
mongoList: [],
|
||||
tags: [],
|
||||
mongoList: [] as any,
|
||||
query: {
|
||||
envId: 0,
|
||||
tagPath: null,
|
||||
},
|
||||
mongoId: null, // 当前选择操作的mongo
|
||||
database: '', // 当前选择操作的库
|
||||
collection: '', //当前选中的collection
|
||||
activeName: '', // 当前操作的tab
|
||||
databases: [],
|
||||
collections: [],
|
||||
dataTabs: {}, // 数据tabs
|
||||
databases: [] as any,
|
||||
collections: [] as any,
|
||||
dataTabs: {} as any, // 数据tabs
|
||||
findDialog: {
|
||||
visible: false,
|
||||
findParam: {
|
||||
limit: 0,
|
||||
skip: 0,
|
||||
filter: '',
|
||||
sort: '',
|
||||
},
|
||||
@@ -178,24 +184,27 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const searchMongo = async () => {
|
||||
notNull(state.query.envId, '请先选择项目环境');
|
||||
notNull(state.query.tagPath, '请先选择标签');
|
||||
const res = await mongoApi.mongoList.request(state.query);
|
||||
state.mongoList = res.list;
|
||||
};
|
||||
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
const changeTag = (tagPath: string) => {
|
||||
state.databases = [];
|
||||
state.collections = [];
|
||||
state.mongoId = null;
|
||||
state.collection = '';
|
||||
state.database = '';
|
||||
state.dataTabs = {};
|
||||
if (envId != null) {
|
||||
state.query.envId = envId;
|
||||
if (tagPath != null) {
|
||||
searchMongo();
|
||||
}
|
||||
};
|
||||
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const changeMongo = () => {
|
||||
state.databases = [];
|
||||
state.collections = [];
|
||||
@@ -419,7 +428,8 @@ export default defineComponent({
|
||||
return {
|
||||
...toRefs(state),
|
||||
findParamInputRef,
|
||||
changeProjectEnv,
|
||||
getTags,
|
||||
changeTag,
|
||||
changeMongo,
|
||||
changeDatabase,
|
||||
changeCollection,
|
||||
|
||||
@@ -2,17 +2,10 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="38%" :destroy-on-close="true">
|
||||
<el-form :model="form" ref="mongoForm" :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>
|
||||
<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 prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</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>
|
||||
@@ -58,19 +51,19 @@
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
|
||||
import { mongoApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { machineApi } from '../machine/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
projects: {
|
||||
type: Array,
|
||||
},
|
||||
mongo: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
@@ -82,33 +75,22 @@ export default defineComponent({
|
||||
const mongoForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
projects: [],
|
||||
envs: [],
|
||||
sshTunnelMachineList: [],
|
||||
sshTunnelMachineList: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
name: null,
|
||||
uri: null,
|
||||
enableSshTunnel: -1,
|
||||
sshTunnelMachineId: null,
|
||||
project: null,
|
||||
projectId: null,
|
||||
envId: null,
|
||||
env: null,
|
||||
tagId: null as any,
|
||||
tagPath: null as any,
|
||||
},
|
||||
btnLoading: false,
|
||||
rules: {
|
||||
projectId: [
|
||||
tagId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择项目',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
envId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择环境',
|
||||
message: '请选择标签',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
@@ -134,12 +116,9 @@ export default defineComponent({
|
||||
if (!state.dialogVisible) {
|
||||
return;
|
||||
}
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.mongo) {
|
||||
getEnvs(newValue.mongo.projectId);
|
||||
state.form = { ...newValue.mongo };
|
||||
} else {
|
||||
state.envs = [];
|
||||
state.form = { db: 0 } as any;
|
||||
}
|
||||
getSshTunnelMachines();
|
||||
@@ -152,30 +131,6 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
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(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
@@ -206,9 +161,7 @@ export default defineComponent({
|
||||
return {
|
||||
...toRefs(state),
|
||||
mongoForm,
|
||||
changeProject,
|
||||
getSshTunnelMachines,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<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 @focus="getProjects" 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 @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
@@ -18,8 +18,7 @@
|
||||
</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="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></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">
|
||||
@@ -28,7 +27,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="150">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建人"></el-table-column>
|
||||
@@ -180,7 +179,6 @@
|
||||
|
||||
<mongo-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:title="mongoEditDialog.title"
|
||||
v-model:visible="mongoEditDialog.visible"
|
||||
v-model:mongo="mongoEditDialog.data"
|
||||
@@ -192,9 +190,10 @@
|
||||
import { mongoApi } from './api';
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import MongoEdit from './MongoEdit.vue';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoList',
|
||||
@@ -203,7 +202,7 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
tags: [],
|
||||
list: [],
|
||||
total: 0,
|
||||
currentId: null,
|
||||
@@ -211,12 +210,11 @@ export default defineComponent({
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
prjectId: null,
|
||||
clusterId: null,
|
||||
tagPath: null,
|
||||
},
|
||||
mongoEditDialog: {
|
||||
visible: false,
|
||||
data: null,
|
||||
data: null as any,
|
||||
title: '新增mongo',
|
||||
},
|
||||
databaseDialog: {
|
||||
@@ -225,7 +223,7 @@ export default defineComponent({
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
@@ -236,7 +234,7 @@ export default defineComponent({
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
@@ -370,12 +368,11 @@ export default defineComponent({
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const editMongo = async (isAdd = false) => {
|
||||
await getProjects();
|
||||
if (isAdd) {
|
||||
state.mongoEditDialog.data = null;
|
||||
state.mongoEditDialog.title = '新增mongo';
|
||||
@@ -394,7 +391,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getProjects,
|
||||
dateFormat,
|
||||
getTags,
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
<template>
|
||||
<div class="project-list">
|
||||
<el-card>
|
||||
<div>
|
||||
<el-button @click="showAddProjectDialog" v-auth="permissions.saveProject" type="primary" icon="plus">添加</el-button>
|
||||
<el-button
|
||||
@click="showAddProjectDialog(chooseData)"
|
||||
v-auth="permissions.saveProject"
|
||||
:disabled="chooseId == null"
|
||||
type="primary"
|
||||
icon="edit"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="user">成员管理</el-button>
|
||||
|
||||
<el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="setting">环境管理</el-button>
|
||||
|
||||
<el-button v-auth="permissions.delProject" @click="delProject" :disabled="chooseId == null" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
|
||||
<div style="float: right">
|
||||
<el-input class="mr2" placeholder="请输入项目名!" style="width: 200px" v-model="query.name" @clear="search" clearable></el-input>
|
||||
<el-button @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="projects" @current-change="choose" ref="table" style="width: 100%">
|
||||
<el-table-column label="选择" width="55px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="项目名"></el-table-column>
|
||||
<el-table-column prop="remark" label="描述" min-width="180px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间">
|
||||
<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="查看更多" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-link @click.prevent="showMembers(scope.row)" type="success">成员</el-link>
|
||||
|
||||
<el-link class="ml5" @click.prevent="showEnv(scope.row)" type="info">环境</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="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible">
|
||||
<el-form :model="addProjectDialog.form" label-width="70px">
|
||||
<el-form-item prop="name" label="项目名:" required>
|
||||
<el-input :disabled="addProjectDialog.form.id ? true : false" v-model="addProjectDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述:">
|
||||
<el-input v-model="addProjectDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddProject()">取 消</el-button>
|
||||
<el-button @click="addProject" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showEnvDialog.title" v-model="showEnvDialog.visible">
|
||||
<div class="toolbar">
|
||||
<el-button @click="showAddEnvDialog" v-auth="permissions.saveMember" type="primary" icon="plus">添加</el-button>
|
||||
<el-button @click="deleteEnv" v-auth="permissions.delProject" :disabled="showEnvDialog.chooseId == null" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
<el-table @current-change="chooseEnv" border :data="showEnvDialog.envs">
|
||||
<el-table-column label="选择" width="50px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="showEnvDialog.chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="name" label="环境名" width="125"></el-table-column>
|
||||
<el-table-column property="remark" label="描述" width="125"></el-table-column>
|
||||
<el-table-column property="createTime" label="创建时间">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog width="400px" title="添加环境" :before-close="cancelAddEnv" v-model="showEnvDialog.addVisible">
|
||||
<el-form :model="showEnvDialog.envForm" label-width="70px">
|
||||
<el-form-item prop="name" label="环境名:" required>
|
||||
<el-input v-model="showEnvDialog.envForm.name" auto-complete="off"></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 class="dialog-footer">
|
||||
<el-button @click="cancelAddEnv()">取 消</el-button>
|
||||
<el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showMemDialog.title" v-model="showMemDialog.visible">
|
||||
<div class="toolbar">
|
||||
<el-button v-auth="permissions.saveMember" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
|
||||
<el-button v-auth="permissions.delMember" @click="deleteMember" :disabled="showMemDialog.chooseId == null" type="danger" icon="delete"
|
||||
>移除</el-button
|
||||
>
|
||||
</div>
|
||||
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list">
|
||||
<el-table-column label="选择" width="50px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="username" label="账号" width="125"></el-table-column>
|
||||
<el-table-column property="createTime" label="加入时间">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="creator" label="分配者" width="125"></el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@current-change="setMemebers"
|
||||
style="text-align: center"
|
||||
background
|
||||
layout="prev, pager, next, total, jumper"
|
||||
:total="showMemDialog.members.total"
|
||||
v-model:current-page="showMemDialog.query.pageNum"
|
||||
:page-size="showMemDialog.query.pageSize"
|
||||
/>
|
||||
|
||||
<el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
|
||||
<el-form :model="showMemDialog.memForm" label-width="70px">
|
||||
<el-form-item label="账号:">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
remote
|
||||
:remote-method="getAccount"
|
||||
v-model="showMemDialog.memForm.accountId"
|
||||
filterable
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
>
|
||||
<el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</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 class="dialog-footer">
|
||||
<el-button @click="cancelAddMember()">取 消</el-button>
|
||||
<el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { projectApi } from './api';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { notEmpty, notNull } from '@/common/assert';
|
||||
export default defineComponent({
|
||||
name: 'ProjectList',
|
||||
components: {},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
permissions: {
|
||||
saveProject: 'project:save',
|
||||
delProject: 'project:del',
|
||||
saveMember: 'project:member:add',
|
||||
delMember: 'project:member:del',
|
||||
saveEnv: 'project:env:add',
|
||||
},
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
},
|
||||
total: 0,
|
||||
projects: [],
|
||||
btnLoading: false,
|
||||
chooseId: null as any,
|
||||
chooseData: null as any,
|
||||
addProjectDialog: {
|
||||
title: '新增项目',
|
||||
visible: false,
|
||||
form: { name: '', remark: '' },
|
||||
},
|
||||
showEnvDialog: {
|
||||
visible: false,
|
||||
chooseId: null,
|
||||
chooseData: null,
|
||||
envs: [],
|
||||
title: '',
|
||||
addVisible: false,
|
||||
envForm: {
|
||||
name: '',
|
||||
remark: '',
|
||||
projectId: 0,
|
||||
},
|
||||
},
|
||||
showMemDialog: {
|
||||
visible: false,
|
||||
chooseId: null,
|
||||
chooseData: null,
|
||||
query: {
|
||||
pageSize: 8,
|
||||
pageNum: 1,
|
||||
projectId: null,
|
||||
},
|
||||
members: {
|
||||
list: [],
|
||||
total: null,
|
||||
},
|
||||
title: '',
|
||||
addVisible: false,
|
||||
memForm: {},
|
||||
accounts: [],
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let res = await projectApi.projects.request(state.query);
|
||||
state.projects = res.list;
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const handlePageChange = (curPage: number) => {
|
||||
state.query.pageNum = curPage;
|
||||
search();
|
||||
};
|
||||
|
||||
const showAddProjectDialog = (data: any) => {
|
||||
if (data) {
|
||||
state.addProjectDialog.form = { ...data };
|
||||
} else {
|
||||
state.addProjectDialog.form = {} as any;
|
||||
}
|
||||
state.addProjectDialog.visible = true;
|
||||
};
|
||||
|
||||
const cancelAddProject = () => {
|
||||
state.addProjectDialog.visible = false;
|
||||
state.addProjectDialog.form = {} as any;
|
||||
};
|
||||
|
||||
const addProject = async () => {
|
||||
const form = state.addProjectDialog.form as any;
|
||||
notEmpty(form.name, '项目名不能为空');
|
||||
notEmpty(form.remark, '项目描述不能为空');
|
||||
|
||||
await projectApi.saveProject.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
cancelAddProject();
|
||||
};
|
||||
|
||||
const delProject = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除该项目?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await projectApi.delProject.request({ id: state.chooseId });
|
||||
ElMessage.success('删除成功');
|
||||
state.chooseData = null;
|
||||
state.chooseId = null;
|
||||
search();
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.chooseId = item.id;
|
||||
state.chooseData = item;
|
||||
};
|
||||
|
||||
const showMembers = async (project: any) => {
|
||||
state.showMemDialog.query.projectId = project.id;
|
||||
await setMemebers();
|
||||
state.showMemDialog.title = `${project.name}的成员信息`;
|
||||
state.showMemDialog.visible = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中成员
|
||||
*/
|
||||
const chooseMember = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.showMemDialog.chooseData = item;
|
||||
state.showMemDialog.chooseId = item.id;
|
||||
};
|
||||
|
||||
const deleteMember = async () => {
|
||||
notNull(state.showMemDialog.chooseData, '请选选择成员');
|
||||
await projectApi.deleteProjectMem.request(state.showMemDialog.chooseData);
|
||||
ElMessage.success('移除成功');
|
||||
// 重新赋值成员列表
|
||||
setMemebers();
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置成员列表信息
|
||||
*/
|
||||
const setMemebers = async () => {
|
||||
const res = await projectApi.projectMems.request(state.showMemDialog.query);
|
||||
state.showMemDialog.members.list = res.list;
|
||||
state.showMemDialog.members.total = res.total;
|
||||
};
|
||||
|
||||
const showEnv = async (project: any) => {
|
||||
state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: project.id });
|
||||
state.showEnvDialog.title = `${project.name}的环境信息`;
|
||||
state.showEnvDialog.visible = true;
|
||||
};
|
||||
|
||||
const chooseEnv = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.showEnvDialog.chooseData = item;
|
||||
state.showEnvDialog.chooseId = item.id;
|
||||
};
|
||||
|
||||
const deleteEnv = async () => {
|
||||
notNull(state.showEnvDialog.chooseData, '请选选择环境');
|
||||
await projectApi.delProjectEnvs.request({ id: state.showEnvDialog.chooseId });
|
||||
ElMessage.success('删除成功');
|
||||
state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: state.chooseId });
|
||||
};
|
||||
|
||||
const showAddMemberDialog = () => {
|
||||
state.showMemDialog.addVisible = true;
|
||||
};
|
||||
|
||||
const addMember = async () => {
|
||||
const memForm = state.showMemDialog.memForm as any;
|
||||
memForm.projectId = state.chooseData.id;
|
||||
notEmpty(memForm.accountId, '请先选择账号');
|
||||
|
||||
await projectApi.saveProjectMem.request(memForm);
|
||||
ElMessage.success('保存成功');
|
||||
setMemebers();
|
||||
cancelAddMember();
|
||||
};
|
||||
|
||||
const cancelAddMember = () => {
|
||||
state.showMemDialog.memForm = {};
|
||||
state.showMemDialog.addVisible = false;
|
||||
state.showMemDialog.chooseData = null;
|
||||
state.showMemDialog.chooseId = null;
|
||||
};
|
||||
|
||||
const getAccount = (username: any) => {
|
||||
accountApi.list.request({ username }).then((res) => {
|
||||
state.showMemDialog.accounts = res.list;
|
||||
});
|
||||
};
|
||||
|
||||
const showAddEnvDialog = () => {
|
||||
state.showEnvDialog.addVisible = true;
|
||||
};
|
||||
|
||||
const addEnv = async () => {
|
||||
const envForm = state.showEnvDialog.envForm;
|
||||
envForm.projectId = state.chooseData.id;
|
||||
await projectApi.saveProjectEnv.request(envForm);
|
||||
ElMessage.success('保存成功');
|
||||
state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: envForm.projectId });
|
||||
cancelAddEnv();
|
||||
};
|
||||
|
||||
const cancelAddEnv = () => {
|
||||
state.showEnvDialog.envForm = {} as any;
|
||||
state.showEnvDialog.addVisible = false;
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
showAddProjectDialog,
|
||||
addProject,
|
||||
delProject,
|
||||
cancelAddProject,
|
||||
showMembers,
|
||||
setMemebers,
|
||||
showEnv,
|
||||
deleteEnv,
|
||||
showAddMemberDialog,
|
||||
addMember,
|
||||
chooseMember,
|
||||
deleteMember,
|
||||
cancelAddMember,
|
||||
showAddEnvDialog,
|
||||
chooseEnv,
|
||||
addEnv,
|
||||
cancelAddEnv,
|
||||
getAccount,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -1,17 +0,0 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const projectApi = {
|
||||
// 获取账号可访问的项目列表
|
||||
accountProjects: Api.create("/accounts/projects", 'get'),
|
||||
projects: Api.create("/projects", 'get'),
|
||||
saveProject: Api.create("/projects", 'post'),
|
||||
delProject: Api.create("/projects", 'delete'),
|
||||
// 获取项目下的环境信息
|
||||
projectEnvs: Api.create("/projects/{projectId}/envs", 'get'),
|
||||
delProjectEnvs: Api.create("/projects/envs", 'delete'),
|
||||
saveProjectEnv: Api.create("/projects/{projectId}/envs", 'post'),
|
||||
// 获取项目下的成员信息
|
||||
projectMems: Api.create("/projects/{projectId}/members", 'get'),
|
||||
saveProjectMem: Api.create("/projects/{projectId}/members", 'post'),
|
||||
deleteProjectMem: Api.create("/projects/{projectId}/members/{accountId}", 'delete'),
|
||||
}
|
||||
@@ -4,34 +4,57 @@
|
||||
<div style="float: left">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24">
|
||||
<project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearRedis">
|
||||
<template #default>
|
||||
<el-form-item label="redis" label-width="40px">
|
||||
<el-select v-model="scanParam.id" placeholder="请选择redis" @change="changeRedis" @clear="clearRedis" clearable>
|
||||
<el-option v-for="item in redisList" :key="item.id" :label="item.host" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="scanParam.db" @change="changeDb" placeholder="库" style="width: 85px">
|
||||
<el-option v-for="db in dbList" :key="db" :label="db" :value="db"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</project-env-select>
|
||||
<el-form class="search-form" label-position="right" :inline="true">
|
||||
<el-form-item label="标签">
|
||||
<el-select
|
||||
@change="changeTag"
|
||||
@focus="getTags"
|
||||
v-model="query.tagPath"
|
||||
placeholder="请选择标签"
|
||||
filterable
|
||||
style="width: 250px"
|
||||
>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="redis" label-width="40px">
|
||||
<el-select
|
||||
v-model="scanParam.id"
|
||||
placeholder="请选择redis"
|
||||
@change="changeRedis"
|
||||
@clear="clearRedis"
|
||||
clearable
|
||||
style="width: 250px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in redisList"
|
||||
:key="item.id"
|
||||
:label="`${item.name ? item.name : ''} [${item.host}]`"
|
||||
:value="item.id"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="scanParam.db" @change="changeDb" placeholder="库" style="width: 85px">
|
||||
<el-option v-for="db in dbList" :key="db" :label="db" :value="db"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col class="mt10">
|
||||
<el-form class="search-form" label-position="right" :inline="true" label-width="60px">
|
||||
<el-form-item label="key" label-width="40px">
|
||||
<el-input
|
||||
placeholder="match 支持*模糊key"
|
||||
style="width: 240px"
|
||||
style="width: 250px"
|
||||
v-model="scanParam.match"
|
||||
@clear="clear()"
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="count" label-width="60px">
|
||||
<el-input placeholder="count" style="width: 62px" v-model.number="scanParam.count"></el-input>
|
||||
<el-form-item label="count" label-width="40px">
|
||||
<el-input placeholder="count" style="width: 70px" v-model.number="scanParam.count"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="searchKey()" type="success" icon="search" plain></el-button>
|
||||
@@ -127,12 +150,12 @@
|
||||
import { redisApi } from './api';
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
import HashValue from './HashValue.vue';
|
||||
import StringValue from './StringValue.vue';
|
||||
import SetValue from './SetValue.vue';
|
||||
import ListValue from './ListValue.vue';
|
||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DataOperation',
|
||||
@@ -141,15 +164,15 @@ export default defineComponent({
|
||||
HashValue,
|
||||
SetValue,
|
||||
ListValue,
|
||||
ProjectEnvSelect,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
redisList: [],
|
||||
tags: [],
|
||||
redisList: [] as any,
|
||||
dbList: [],
|
||||
query: {
|
||||
envId: 0,
|
||||
tagPath: null,
|
||||
},
|
||||
scanParam: {
|
||||
id: null,
|
||||
@@ -185,19 +208,22 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const searchRedis = async () => {
|
||||
notNull(state.query.envId, '请先选择项目环境');
|
||||
notBlank(state.query.tagPath, '请先选择标签');
|
||||
const res = await redisApi.redisList.request(state.query);
|
||||
state.redisList = res.list;
|
||||
};
|
||||
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
const changeTag = (tagPath: string) => {
|
||||
clearRedis();
|
||||
if (envId != null) {
|
||||
state.query.envId = envId;
|
||||
if (tagPath != null) {
|
||||
searchRedis();
|
||||
}
|
||||
};
|
||||
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const changeRedis = (id: number) => {
|
||||
resetScanParam(id);
|
||||
state.scanParam.db = null;
|
||||
@@ -385,7 +411,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
changeProjectEnv,
|
||||
getTags,
|
||||
changeTag,
|
||||
changeRedis,
|
||||
changeDb,
|
||||
clearRedis,
|
||||
|
||||
@@ -2,16 +2,11 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
|
||||
<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>
|
||||
<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 prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</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 prop="name" label="名称:" required>
|
||||
<el-input v-model.trim="form.name" placeholder="请输入redis名称" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="mode" label="mode:" required>
|
||||
<el-select style="width: 100%" v-model="form.mode" placeholder="请选择模式">
|
||||
@@ -88,9 +83,13 @@ import { projectApi } from '../project/api.ts';
|
||||
import { machineApi } from '../machine/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RedisEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@@ -114,6 +113,8 @@ export default defineComponent({
|
||||
sshTunnelMachineList: [],
|
||||
form: {
|
||||
id: null,
|
||||
tagId: null as any,
|
||||
tatPath: null as any,
|
||||
name: null,
|
||||
mode: 'standalone',
|
||||
host: '',
|
||||
@@ -176,7 +177,6 @@ export default defineComponent({
|
||||
}
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.redis) {
|
||||
getEnvs(newValue.redis.projectId);
|
||||
state.form = { ...newValue.redis };
|
||||
convertDb(state.form.db);
|
||||
} else {
|
||||
@@ -205,34 +205,10 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
const getEnvs = async (projectId: any) => {
|
||||
state.envs = await projectApi.projectEnvs.request({ projectId });
|
||||
};
|
||||
|
||||
const getPwd = async () => {
|
||||
state.pwd = await redisApi.getRedisPwd.request({ id: state.form.id });
|
||||
};
|
||||
|
||||
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 () => {
|
||||
redisForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
@@ -270,8 +246,6 @@ export default defineComponent({
|
||||
changeDb,
|
||||
getSshTunnelMachines,
|
||||
getPwd,
|
||||
changeProject,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<el-button type="primary" icon="edit" :disabled="currentId == null" @click="editRedis(false)" plain>编辑</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="currentId == null" @click="deleteRedis" plain>删除</el-button>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" 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 @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
@@ -18,14 +18,14 @@
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="project" label="项目" min-width="100"></el-table-column>
|
||||
<el-table-column prop="env" label="环境" min-width="100"></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="100"></el-table-column>
|
||||
<el-table-column prop="host" label="host:port" min-width="150" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="mode" label="mode" min-width="100"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="160">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建人" min-width="100"></el-table-column>
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
<redis-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:tags="tags"
|
||||
:title="redisEditDialog.title"
|
||||
v-model:visible="redisEditDialog.visible"
|
||||
v-model:redis="redisEditDialog.data"
|
||||
@@ -151,8 +151,9 @@ import Info from './Info.vue';
|
||||
import { redisApi } from './api';
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import RedisEdit from './RedisEdit.vue';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RedisList',
|
||||
@@ -162,15 +163,15 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
tags: [],
|
||||
redisTable: [],
|
||||
total: 0,
|
||||
currentId: null,
|
||||
currentData: null,
|
||||
query: {
|
||||
tagPath: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
prjectId: null,
|
||||
clusterId: null,
|
||||
},
|
||||
redisInfo: {
|
||||
@@ -263,12 +264,11 @@ export default defineComponent({
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const editRedis = async (isAdd = false) => {
|
||||
await getProjects();
|
||||
if (isAdd) {
|
||||
state.redisEditDialog.data = null;
|
||||
state.redisEditDialog.title = '新增redis';
|
||||
@@ -287,7 +287,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getProjects,
|
||||
dateFormat,
|
||||
getTags,
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
|
||||
300
mayfly_go_web/src/views/ops/tag/TagTreeList.vue
Normal file
300
mayfly_go_web/src/views/ops/tag/TagTreeList.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="menu">
|
||||
<div class="toolbar">
|
||||
<el-button v-auth="'tag:save'" type="primary" icon="plus" @click="showSaveTabDialog(null)">添加</el-button>
|
||||
<div style="float: right">
|
||||
<el-tooltip effect="dark" placement="top">
|
||||
<template #content>
|
||||
1. 用于将资产进行归类
|
||||
<br />2. 可在团队管理中进行分配,用于资源隔离
|
||||
<br />3. 父标签可访问及操作所有子标签关联的资源
|
||||
</template>
|
||||
<span>标签作用<el-icon><question-filled /></el-icon></span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<el-tree
|
||||
class="none-select"
|
||||
:indent="38"
|
||||
node-key="id"
|
||||
:props="props"
|
||||
:data="data"
|
||||
@node-expand="handleNodeExpand"
|
||||
@node-collapse="handleNodeCollapse"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
:expand-on-click-node="false"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span style="font-size: 13px">
|
||||
{{ data.code }}
|
||||
<span style="color: #3c8dbc">【</span>
|
||||
{{ data.name }}
|
||||
<span style="color: #3c8dbc">】</span>
|
||||
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag>
|
||||
</span>
|
||||
|
||||
<el-link @click.prevent="info(data)" style="margin-left: 25px" icon="view" type="info" :underline="false" />
|
||||
|
||||
<el-link v-auth="'tag:save'" @click.prevent="showEditTagDialog(data)" class="ml5" type="primary" icon="edit" :underline="false" />
|
||||
|
||||
<el-link
|
||||
v-auth="'tag:save'"
|
||||
@click.prevent="showSaveTabDialog(data)"
|
||||
icon="circle-plus"
|
||||
:underline="false"
|
||||
type="success"
|
||||
class="ml5"
|
||||
/>
|
||||
|
||||
<!-- <el-link
|
||||
v-auth="'resource:changeStatus'"
|
||||
@click.prevent="changeStatus(data, -1)"
|
||||
v-if="data.status === 1 && data.type === enums.ResourceTypeEnum.PERMISSION.value"
|
||||
icon="circle-close"
|
||||
:underline="false"
|
||||
type="warning"
|
||||
class="ml5"
|
||||
/>
|
||||
|
||||
<el-link
|
||||
v-auth="'resource:changeStatus'"
|
||||
@click.prevent="changeStatus(data, 1)"
|
||||
v-if="data.status === -1 && data.type === enums.ResourceTypeEnum.PERMISSION.value"
|
||||
type="success"
|
||||
icon="circle-check"
|
||||
:underline="false"
|
||||
plain
|
||||
class="ml5"
|
||||
/> -->
|
||||
|
||||
<el-link
|
||||
v-auth="'tag:del'"
|
||||
@click.prevent="deleteTag(data)"
|
||||
v-if="data.children == null"
|
||||
type="danger"
|
||||
icon="delete"
|
||||
:underline="false"
|
||||
plain
|
||||
class="ml5"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<el-dialog width="500px" :title="saveTabDialog.title" :before-close="cancelSaveTag" v-model="saveTabDialog.visible">
|
||||
<el-form ref="tagForm" :rules="rules" :model="saveTabDialog.form" label-width="70px">
|
||||
<el-form-item prop="code" label="标识:" required>
|
||||
<el-input :disabled="saveTabDialog.form.id ? true : false" v-model="saveTabDialog.form.code" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="名称:" required>
|
||||
<el-input v-model="saveTabDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注:">
|
||||
<el-input v-model="saveTabDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelSaveTag()">取 消</el-button>
|
||||
<el-button @click="saveTag" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="infoDialog.visible">
|
||||
<el-descriptions title="节点信息" :column="2" border>
|
||||
<el-descriptions-item label="code">{{ infoDialog.data.code }}</el-descriptions-item>
|
||||
<el-descriptions-item label="code路径">{{ infoDialog.data.codePath }}</el-descriptions-item>
|
||||
<el-descriptions-item label="名称">{{ infoDialog.data.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ dateFormat(infoDialog.data.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, ref, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { tagApi } from './api';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TagTreeList',
|
||||
components: {},
|
||||
setup() {
|
||||
const tagForm: any = ref(null);
|
||||
const state = reactive({
|
||||
saveTabDialog: {
|
||||
title: '新增标签',
|
||||
visible: false,
|
||||
form: { id: 0, pid: 0, code: '', name: '', remark: '' },
|
||||
},
|
||||
//资源信息弹出框对象
|
||||
infoDialog: {
|
||||
title: '',
|
||||
visible: false,
|
||||
// 资源类型选择是否选
|
||||
data: null as any,
|
||||
},
|
||||
data: [],
|
||||
props: {
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
},
|
||||
// 展开的节点
|
||||
defaultExpandedKeys: [] as any[],
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: '标识符不能为空', trigger: 'blur' },
|
||||
// {
|
||||
// pattern: /^\w+$/g,
|
||||
// message: '标识符只能为空数字字母下划线等',
|
||||
// trigger: 'blur',
|
||||
// },
|
||||
],
|
||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let res = await tagApi.getTagTrees.request(null);
|
||||
state.data = res;
|
||||
};
|
||||
|
||||
const info = async (data: any) => {
|
||||
state.infoDialog.data = data;
|
||||
state.infoDialog.visible = true;
|
||||
};
|
||||
|
||||
const showSaveTabDialog = (data: any) => {
|
||||
if (data) {
|
||||
state.saveTabDialog.form.pid = data.id;
|
||||
state.saveTabDialog.title = `新增 [${data.codePath}] 子标签信息`;
|
||||
} else {
|
||||
state.saveTabDialog.title = '新增根标签信息';
|
||||
}
|
||||
state.saveTabDialog.visible = true;
|
||||
};
|
||||
|
||||
const showEditTagDialog = (data: any) => {
|
||||
state.saveTabDialog.form.id = data.id;
|
||||
state.saveTabDialog.form.code = data.code;
|
||||
state.saveTabDialog.form.name = data.name;
|
||||
state.saveTabDialog.form.remark = data.remark;
|
||||
state.saveTabDialog.title = `修改 [${data.codePath}] 信息`;
|
||||
state.saveTabDialog.visible = true;
|
||||
};
|
||||
|
||||
const saveTag = async () => {
|
||||
tagForm.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
const form = state.saveTabDialog.form;
|
||||
await tagApi.saveTagTree.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
cancelSaveTag();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancelSaveTag = () => {
|
||||
state.saveTabDialog.visible = false;
|
||||
state.saveTabDialog.form = {} as any;
|
||||
tagForm.value.resetFields();
|
||||
};
|
||||
|
||||
const deleteTag = (data: any) => {
|
||||
ElMessageBox.confirm(`此操作将删除 [${data.codePath}], 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
await tagApi.delTagTree.request({ id: data.id });
|
||||
ElMessage.success('删除成功!');
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
// const changeStatus = async (data: any, status: any) => {
|
||||
// await resourceApi.changeStatus.request({
|
||||
// id: data.id,
|
||||
// status: status,
|
||||
// });
|
||||
// data.status = status;
|
||||
// ElMessage.success((status === 1 ? '启用' : '禁用') + '成功!');
|
||||
// };
|
||||
|
||||
// 节点被展开时触发的事件
|
||||
const handleNodeExpand = (data: any, node: any) => {
|
||||
const id: any = node.data.id;
|
||||
if (!state.defaultExpandedKeys.includes(id)) {
|
||||
state.defaultExpandedKeys.push(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭节点
|
||||
const handleNodeCollapse = (data: any, node: any) => {
|
||||
removeDeafultExpandId(node.data.id);
|
||||
|
||||
let childNodes = node.childNodes;
|
||||
for (let cn of childNodes) {
|
||||
if (cn.expanded) {
|
||||
removeDeafultExpandId(cn.data.id);
|
||||
}
|
||||
// 递归删除展开的子节点节点id
|
||||
handleNodeCollapse(data, cn);
|
||||
}
|
||||
};
|
||||
|
||||
const removeDeafultExpandId = (id: any) => {
|
||||
let index = state.defaultExpandedKeys.indexOf(id);
|
||||
if (index > -1) {
|
||||
state.defaultExpandedKeys.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dateFormat,
|
||||
tagForm,
|
||||
info,
|
||||
saveTag,
|
||||
showSaveTabDialog,
|
||||
showEditTagDialog,
|
||||
cancelSaveTag,
|
||||
deleteTag,
|
||||
handleNodeExpand,
|
||||
handleNodeCollapse,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.menu {
|
||||
height: 100%;
|
||||
.el-tree-node__content {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.none-select {
|
||||
moz-user-select: -moz-none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
440
mayfly_go_web/src/views/ops/tag/TeamList.vue
Executable file
440
mayfly_go_web/src/views/ops/tag/TeamList.vue
Executable file
@@ -0,0 +1,440 @@
|
||||
<template>
|
||||
<div class="role-list">
|
||||
<el-card>
|
||||
<el-button v-auth="'team:save'" type="primary" icon="plus" @click="showSaveTeamDialog(false)">添加</el-button>
|
||||
<el-button v-auth="'team:save'" :disabled="chooseId == null" @click="showSaveTeamDialog(chooseData)" type="primary" icon="edit"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button v-auth="'team:del'" :disabled="chooseId == null" @click="deleteTeam(chooseData)" type="danger" icon="delete">删除</el-button>
|
||||
|
||||
<div style="float: right">
|
||||
<el-input placeholder="请输入团队名称" class="mr2" style="width: 200px" v-model="query.name" @clear="search" clearable></el-input>
|
||||
<el-button @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
<el-table :data="data" @current-change="choose" ref="table" style="width: 100%">
|
||||
<el-table-column label="选择" width="55px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="团队名称"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="160px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间">
|
||||
<template #default="scope">
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建者"> </el-table-column>
|
||||
<el-table-column label="操作" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-link @click.prevent="showMembers(scope.row)" :underline="false" type="primary">成员</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-link @click.prevent="showTags(scope.row)" :underline="false" type="success">标签</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="400px" title="团队编辑" :before-close="cancelSaveTeam" v-model="addTeamDialog.visible">
|
||||
<el-form ref="teamForm" :model="addTeamDialog.form" label-width="70px">
|
||||
<el-form-item prop="name" label="团队名:" required>
|
||||
<el-input v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注:">
|
||||
<el-input v-model="addTeamDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelSaveTeam()">取 消</el-button>
|
||||
<el-button @click="saveTeam" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog" v-model="showTagDialog.visible">
|
||||
<el-form label-width="70px">
|
||||
<el-form-item prop="project" label="标签:">
|
||||
<el-tree-select
|
||||
ref="tagTreeRef"
|
||||
style="width: 100%"
|
||||
v-model="showTagDialog.tagTreeTeams"
|
||||
:data="showTagDialog.tags"
|
||||
:default-expanded-keys="showTagDialog.tagTreeTeams"
|
||||
multiple
|
||||
:render-after-expand="true"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
:props="showTagDialog.props"
|
||||
@check="tagTreeNodeCheck"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span style="font-size: 13px">
|
||||
{{ data.code }}
|
||||
<span style="color: #3c8dbc">【</span>
|
||||
{{ data.name }}
|
||||
<span style="color: #3c8dbc">】</span>
|
||||
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeTagDialog()">取 消</el-button>
|
||||
<el-button v-auth="'team:tag:save'" @click="saveTags()" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="600px" :title="showMemDialog.title" v-model="showMemDialog.visible">
|
||||
<div class="toolbar">
|
||||
<el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
|
||||
<el-button v-auth="'team:member:del'" @click="deleteMember" :disabled="showMemDialog.chooseId == null" type="danger" icon="delete">移除</el-button>
|
||||
</div>
|
||||
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list">
|
||||
<el-table-column label="选择" width="50px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="username" label="账号" width="135"></el-table-column>
|
||||
<el-table-column property="createTime" label="加入时间">
|
||||
<template #default="scope">
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="creator" label="分配者" width="135"></el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@current-change="setMemebers"
|
||||
style="text-align: center"
|
||||
background
|
||||
layout="prev, pager, next, total, jumper"
|
||||
:total="showMemDialog.members.total"
|
||||
v-model:current-page="showMemDialog.query.pageNum"
|
||||
:page-size="showMemDialog.query.pageSize"
|
||||
/>
|
||||
|
||||
<el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
|
||||
<el-form :model="showMemDialog.memForm" label-width="70px">
|
||||
<el-form-item label="账号:">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
remote
|
||||
:remote-method="getAccount"
|
||||
v-model="showMemDialog.memForm.accountId"
|
||||
filterable
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
>
|
||||
<el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddMember()">取 消</el-button>
|
||||
<el-button @click="addMember" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { tagApi } from './api';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
import { notBlank } from '@/common/assert';
|
||||
export default defineComponent({
|
||||
name: 'TeamList',
|
||||
components: {},
|
||||
setup() {
|
||||
const teamForm: any = ref(null);
|
||||
const tagTreeRef: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogFormVisible: false,
|
||||
currentEditPermissions: false,
|
||||
addTeamDialog: {
|
||||
title: '新增团队',
|
||||
visible: false,
|
||||
form: { id: 0, name: '', remark: '' },
|
||||
},
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
},
|
||||
total: 0,
|
||||
data: [],
|
||||
chooseId: 0,
|
||||
chooseData: null,
|
||||
showMemDialog: {
|
||||
visible: false,
|
||||
chooseId: 0,
|
||||
chooseData: null,
|
||||
query: {
|
||||
pageSize: 8,
|
||||
pageNum: 1,
|
||||
teamId: null,
|
||||
},
|
||||
members: {
|
||||
list: [],
|
||||
total: null,
|
||||
},
|
||||
title: '',
|
||||
addVisible: false,
|
||||
memForm: {
|
||||
accountId: null as any,
|
||||
teamId: 0,
|
||||
},
|
||||
accounts: Array(),
|
||||
},
|
||||
showTagDialog: {
|
||||
title: '项目信息',
|
||||
visible: false,
|
||||
tags: [],
|
||||
teamId: 0,
|
||||
tagTreeTeams: [] as any,
|
||||
props: {
|
||||
value: 'id',
|
||||
label: 'codePath',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let res = await tagApi.getTeams.request(state.query);
|
||||
state.data = res.list;
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const handlePageChange = (curPage: number) => {
|
||||
state.query.pageNum = curPage;
|
||||
search();
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.chooseId = item.id;
|
||||
state.chooseData = item;
|
||||
};
|
||||
|
||||
const showSaveTeamDialog = (data: any) => {
|
||||
if (data) {
|
||||
state.addTeamDialog.form.id = data.id;
|
||||
state.addTeamDialog.form.name = data.name;
|
||||
state.addTeamDialog.form.remark = data.remark;
|
||||
state.addTeamDialog.title = `修改 [${data.codePath}] 信息`;
|
||||
}
|
||||
state.addTeamDialog.visible = true;
|
||||
};
|
||||
|
||||
const saveTeam = async () => {
|
||||
teamForm.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
const form = state.addTeamDialog.form;
|
||||
await tagApi.saveTeam.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
cancelSaveTeam();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancelSaveTeam = () => {
|
||||
state.addTeamDialog.visible = false;
|
||||
state.addTeamDialog.form = {} as any;
|
||||
teamForm.value.resetFields();
|
||||
};
|
||||
|
||||
const deleteTeam = (data: any) => {
|
||||
ElMessageBox.confirm(`此操作将删除 [${data.name}], 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
await tagApi.delTeam.request({ id: data.id });
|
||||
ElMessage.success('删除成功!');
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
/********** 团队成员相关 ***********/
|
||||
|
||||
const showMembers = async (team: any) => {
|
||||
state.showMemDialog.query.teamId = team.id;
|
||||
await setMemebers();
|
||||
state.showMemDialog.title = `[${team.name}] 成员信息`;
|
||||
state.showMemDialog.visible = true;
|
||||
};
|
||||
|
||||
const getAccount = (username: any) => {
|
||||
accountApi.list.request({ username }).then((res) => {
|
||||
state.showMemDialog.accounts = res.list;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中成员
|
||||
*/
|
||||
const chooseMember = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.showMemDialog.chooseData = item;
|
||||
state.showMemDialog.chooseId = item.id;
|
||||
};
|
||||
|
||||
const deleteMember = async () => {
|
||||
await tagApi.delTeamMem.request(state.showMemDialog.chooseData);
|
||||
ElMessage.success('移除成功');
|
||||
// 重新赋值成员列表
|
||||
setMemebers();
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置成员列表信息
|
||||
*/
|
||||
const setMemebers = async () => {
|
||||
const res = await tagApi.getTeamMem.request(state.showMemDialog.query);
|
||||
state.showMemDialog.members.list = res.list;
|
||||
state.showMemDialog.members.total = res.total;
|
||||
};
|
||||
|
||||
const showAddMemberDialog = () => {
|
||||
state.showMemDialog.addVisible = true;
|
||||
};
|
||||
|
||||
const addMember = async () => {
|
||||
const memForm = state.showMemDialog.memForm;
|
||||
memForm.teamId = state.chooseId;
|
||||
notBlank(memForm.accountId, '请先选择账号');
|
||||
|
||||
await tagApi.saveTeamMem.request(memForm);
|
||||
ElMessage.success('保存成功');
|
||||
setMemebers();
|
||||
cancelAddMember();
|
||||
};
|
||||
|
||||
const cancelAddMember = () => {
|
||||
state.showMemDialog.memForm = {} as any;
|
||||
state.showMemDialog.addVisible = false;
|
||||
state.showMemDialog.chooseData = null;
|
||||
state.showMemDialog.chooseId = 0;
|
||||
};
|
||||
|
||||
/********** 标签相关 ***********/
|
||||
|
||||
const showTags = async (team: any) => {
|
||||
state.showTagDialog.tags = await tagApi.getTagTrees.request(null);
|
||||
state.showTagDialog.tagTreeTeams = await tagApi.getTeamTagIds.request({ teamId: team.id });
|
||||
state.showTagDialog.title = `[${team.name}] 项目信息`;
|
||||
state.showTagDialog.teamId = team.id;
|
||||
state.showTagDialog.visible = true;
|
||||
};
|
||||
|
||||
const closeTagDialog = () => {
|
||||
state.showTagDialog.visible = false;
|
||||
setTimeout(() => {
|
||||
state.showTagDialog.tagTreeTeams = [];
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const saveTags = async () => {
|
||||
await tagApi.saveTeamTags.request({
|
||||
teamId: state.showTagDialog.teamId,
|
||||
tagIds: state.showTagDialog.tagTreeTeams,
|
||||
});
|
||||
ElMessage.success('保存成功');
|
||||
closeTagDialog();
|
||||
};
|
||||
|
||||
const tagTreeNodeCheck = (data: any, checkInfo: any) => {
|
||||
const node = tagTreeRef.value.getNode(data.id);
|
||||
console.log(node);
|
||||
// state.showTagDialog.tagTreeTeams = [16]
|
||||
if (node.checked) {
|
||||
if (node.parent) {
|
||||
console.log(node.parent);
|
||||
// removeCheckedTagId(node.parent.key);
|
||||
tagTreeRef.value.setChecked(node.parent, false, false);
|
||||
}
|
||||
// // parentNode = node.parent
|
||||
// for (let parentNode of node.parent) {
|
||||
// parentNode.setChecked(false);
|
||||
// }
|
||||
}
|
||||
console.log(data);
|
||||
console.log(checkInfo);
|
||||
};
|
||||
|
||||
// function removeCheckedTagId(id: any) {
|
||||
// console.log(state.showTagDialog.tagTreeTeams);
|
||||
// for (let i = 0; i < state.showTagDialog.tagTreeTeams.length; i++) {
|
||||
// if (state.showTagDialog.tagTreeTeams[i] == id) {
|
||||
// console.log('has id', id);
|
||||
// state.showTagDialog.tagTreeTeams.splice(i, 1);
|
||||
// }
|
||||
// }
|
||||
// console.log(state.showTagDialog.tagTreeTeams);
|
||||
// }
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
teamForm,
|
||||
tagTreeRef,
|
||||
dateFormat,
|
||||
choose,
|
||||
search,
|
||||
handlePageChange,
|
||||
showSaveTeamDialog,
|
||||
saveTeam,
|
||||
cancelSaveTeam,
|
||||
deleteTeam,
|
||||
showMembers,
|
||||
setMemebers,
|
||||
getAccount,
|
||||
showAddMemberDialog,
|
||||
addMember,
|
||||
cancelAddMember,
|
||||
chooseMember,
|
||||
deleteMember,
|
||||
showTags,
|
||||
closeTagDialog,
|
||||
saveTags,
|
||||
tagTreeNodeCheck,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
19
mayfly_go_web/src/views/ops/tag/api.ts
Normal file
19
mayfly_go_web/src/views/ops/tag/api.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const tagApi = {
|
||||
getAccountTags: Api.create("/tag-trees/account-has", 'get'),
|
||||
getTagTrees: Api.create("/tag-trees", 'get'),
|
||||
saveTagTree: Api.create("/tag-trees", 'post'),
|
||||
delTagTree: Api.create("/tag-trees/{id}", 'delete'),
|
||||
|
||||
getTeams: Api.create("/teams", 'get'),
|
||||
saveTeam: Api.create("/teams", 'post'),
|
||||
delTeam: Api.create("/teams/{id}", 'delete'),
|
||||
|
||||
getTeamMem: Api.create("/teams/{teamId}/members", 'get'),
|
||||
saveTeamMem: Api.create("/teams/{teamId}/members", 'post'),
|
||||
delTeamMem: Api.create("/teams/{teamId}/members/{accountId}", 'delete'),
|
||||
|
||||
getTeamTagIds: Api.create("/teams/{teamId}/tags", 'get'),
|
||||
saveTeamTags: Api.create("/teams/{teamId}/tags", 'post'),
|
||||
}
|
||||
@@ -73,6 +73,7 @@ export default defineComponent({
|
||||
state.form = { ...newValue.account };
|
||||
state.edit = true;
|
||||
} else {
|
||||
state.edit = false;
|
||||
state.form = {} as any;
|
||||
}
|
||||
state.dialogVisible = newValue.visible;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="500px" :destroy-on-close="true">
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="750px" :destroy-on-close="true">
|
||||
<el-form ref="configForm" :model="form" label-width="90px">
|
||||
<el-form-item prop="name" label="配置项:" required>
|
||||
<el-input v-model="form.name"></el-input>
|
||||
@@ -8,9 +8,28 @@
|
||||
<el-form-item prop="key" label="配置key:" required>
|
||||
<el-input :disabled="form.id != null" v-model="form.key"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="value" label="配置值:" required>
|
||||
<el-input v-model="form.value"></el-input>
|
||||
|
||||
<el-row style="margin-left: 30px; margin-bottom: 5px">
|
||||
<el-button @click="onAddParam" size="small" type="success">新增配置项</el-button>
|
||||
</el-row>
|
||||
<el-form-item :key="param" v-for="(param, index) in params" prop="params" :label="`参数${index + 1}`">
|
||||
<el-row>
|
||||
<el-col :span="5"><el-input v-model="param.model" placeholder="model"></el-input></el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="4"><el-input v-model="param.name" placeholder="字段名"></el-input></el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="4"><el-input v-model="param.placeholder" placeholder="字段说明"></el-input></el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
||||
</el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="2"><el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button></el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="value" label="配置值:" required>
|
||||
<el-input v-model="form.value"></el-input>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="备注:">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||
</el-form-item>
|
||||
@@ -46,10 +65,12 @@ export default defineComponent({
|
||||
const configForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dvisible: false,
|
||||
params: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
name: '',
|
||||
key: '',
|
||||
params: '',
|
||||
value: '',
|
||||
remark: '',
|
||||
},
|
||||
@@ -60,11 +81,25 @@ export default defineComponent({
|
||||
state.dvisible = newValue.visible;
|
||||
if (newValue.data) {
|
||||
state.form = { ...newValue.data };
|
||||
if (state.form.params) {
|
||||
state.params = JSON.parse(state.form.params);
|
||||
} else {
|
||||
state.params = [];
|
||||
}
|
||||
} else {
|
||||
state.form = {} as any;
|
||||
state.params = [];
|
||||
}
|
||||
});
|
||||
|
||||
const onAddParam = () => {
|
||||
state.params.push({ name: '', model: '', placeholder: '' });
|
||||
};
|
||||
|
||||
const onDeleteParam = (idx: number) => {
|
||||
state.params.splice(idx, 1);
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
// 更新父组件visible prop对应的值为false
|
||||
emit('update:visible', false);
|
||||
@@ -75,6 +110,9 @@ export default defineComponent({
|
||||
const btnOk = async () => {
|
||||
configForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (state.params) {
|
||||
state.form.params = JSON.stringify(state.params);
|
||||
}
|
||||
await configApi.save.request(state.form);
|
||||
emit('val-change', state.form);
|
||||
cancel();
|
||||
@@ -88,6 +126,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
onAddParam,
|
||||
onDeleteParam,
|
||||
configForm,
|
||||
btnOk,
|
||||
cancel,
|
||||
|
||||
@@ -16,12 +16,25 @@
|
||||
<el-table-column prop="key" label="配置key"></el-table-column>
|
||||
<el-table-column prop="value" label="配置值" min-width="100px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="100px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="updateTime" label="更新时间">
|
||||
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="modifier" label="修改者" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" min-width="50" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
:disabled="scope.row.status == -1"
|
||||
type="warning"
|
||||
@click="showSetConfigDialog(scope.row)"
|
||||
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
|
||||
@@ -35,6 +48,42 @@
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-dialog :before-close="closeSetConfigDialog" title="配置项设置" v-model="paramsDialog.visible" width="500px">
|
||||
<el-form v-if="paramsDialog.paramsFormItem.length > 0" ref="paramsForm" :model="paramsDialog.params" label-width="90px">
|
||||
<el-form-item v-for="item in paramsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required>
|
||||
<el-input
|
||||
v-if="!item.options"
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
filterable
|
||||
autocomplete="off"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="option in item.options.split(',')" :key="option" :label="option" :value="option" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form v-else ref="paramsForm" label-width="90px">
|
||||
<el-form-item label="配置值" required>
|
||||
<el-input v-model="paramsDialog.params" :placeholder="paramsDialog.config.remark" autocomplete="off" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="closeSetConfigDialog()">取 消</el-button>
|
||||
<el-button type="primary" @click="setConfig()">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<config-edit :title="configEdit.title" v-model:visible="configEdit.visible" :data="configEdit.config" @val-change="configEditChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -62,6 +111,12 @@ export default defineComponent({
|
||||
configs: [],
|
||||
chooseId: null,
|
||||
chooseData: null,
|
||||
paramsDialog: {
|
||||
visible: false,
|
||||
config: null as any,
|
||||
params: {},
|
||||
paramsFormItem: [] as any,
|
||||
},
|
||||
configEdit: {
|
||||
title: '配置修改',
|
||||
visible: false,
|
||||
@@ -84,6 +139,62 @@ export default defineComponent({
|
||||
search();
|
||||
};
|
||||
|
||||
const showSetConfigDialog = (row: any) => {
|
||||
state.paramsDialog.config = row;
|
||||
// 存在配置项则弹窗提示输入对应的配置项
|
||||
if (row.params) {
|
||||
state.paramsDialog.paramsFormItem = JSON.parse(row.params);
|
||||
if (state.paramsDialog.paramsFormItem && state.paramsDialog.paramsFormItem.length > 0) {
|
||||
if (row.value) {
|
||||
state.paramsDialog.params = JSON.parse(row.value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.paramsDialog.params = row.value;
|
||||
}
|
||||
state.paramsDialog.visible = true;
|
||||
};
|
||||
|
||||
const closeSetConfigDialog = () => {
|
||||
state.paramsDialog.visible = false;
|
||||
setTimeout(() => {
|
||||
state.paramsDialog.config = {};
|
||||
state.paramsDialog.params = {};
|
||||
state.paramsDialog.paramsFormItem = [];
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const setConfig = async () => {
|
||||
let paramsValue = state.paramsDialog.params;
|
||||
if (state.paramsDialog.paramsFormItem.length > 0) {
|
||||
// 如果配置项删除,则需要将value中对应的字段移除
|
||||
for (let paramKey in paramsValue) {
|
||||
if (!hasParam(paramKey, state.paramsDialog.paramsFormItem)) {
|
||||
delete paramsValue[paramKey];
|
||||
}
|
||||
}
|
||||
paramsValue = JSON.stringify(paramsValue);
|
||||
}
|
||||
await configApi.save.request({
|
||||
id: state.paramsDialog.config.id,
|
||||
key: state.paramsDialog.config.key,
|
||||
name: state.paramsDialog.config.name,
|
||||
value: paramsValue,
|
||||
});
|
||||
ElMessage.success('保存成功');
|
||||
closeSetConfigDialog();
|
||||
search();
|
||||
};
|
||||
|
||||
const hasParam = (paramKey: string, paramItems: any) => {
|
||||
for (let paramItem of paramItems) {
|
||||
if (paramItem.model == paramKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
@@ -93,7 +204,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const configEditChange = () => {
|
||||
ElMessage.success('修改成功!');
|
||||
ElMessage.success('保存成功');
|
||||
state.chooseId = null;
|
||||
state.chooseData = null;
|
||||
search();
|
||||
@@ -111,6 +222,9 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
showSetConfigDialog,
|
||||
closeSetConfigDialog,
|
||||
setConfig,
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
||||
go.mongodb.org/mongo-driver v1.9.1 // mongo
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // ssh
|
||||
golang.org/x/crypto v0.1.0 // ssh
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
// gorm
|
||||
gorm.io/driver/mysql v1.4.1
|
||||
@@ -50,10 +50,10 @@ require (
|
||||
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/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/net v0.1.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
db_router "mayfly-go/internal/db/router"
|
||||
machine_router "mayfly-go/internal/machine/router"
|
||||
mongo_router "mayfly-go/internal/mongo/router"
|
||||
project_router "mayfly-go/internal/project/router"
|
||||
redis_router "mayfly-go/internal/redis/router"
|
||||
sys_router "mayfly-go/internal/sys/router"
|
||||
tag_router "mayfly-go/internal/tag/router"
|
||||
"mayfly-go/pkg/config"
|
||||
"mayfly-go/pkg/middleware"
|
||||
"mayfly-go/static"
|
||||
@@ -74,7 +74,8 @@ func InitRouter() *gin.Engine {
|
||||
|
||||
sys_router.Init(api)
|
||||
|
||||
project_router.Init(api)
|
||||
// project_router.Init(api)
|
||||
tag_router.Init(api)
|
||||
machine_router.Init(api)
|
||||
db_router.Init(api)
|
||||
redis_router.Init(api)
|
||||
|
||||
@@ -3,28 +3,43 @@ package api
|
||||
import (
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
dbentity "mayfly-go/internal/db/domain/entity"
|
||||
machineentity "mayfly-go/internal/machine/domain/entity"
|
||||
projectentity "mayfly-go/internal/project/domain/entity"
|
||||
redisentity "mayfly-go/internal/redis/domain/entity"
|
||||
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
machineentity "mayfly-go/internal/machine/domain/entity"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
mongoentity "mayfly-go/internal/mongo/domain/entity"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
redisentity "mayfly-go/internal/redis/domain/entity"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
)
|
||||
|
||||
type Index struct {
|
||||
ProjectApp projectapp.Project
|
||||
TagApp tagapp.TagTree
|
||||
MachineApp machineapp.Machine
|
||||
DbApp dbapp.Db
|
||||
RedisApp redisapp.Redis
|
||||
MongoApp mongoapp.Mongo
|
||||
}
|
||||
|
||||
func (i *Index) Count(rc *ctx.ReqCtx) {
|
||||
accountId := rc.LoginAccount.Id
|
||||
tagIds := i.TagApp.ListTagIdByAccountId(accountId)
|
||||
|
||||
var mongoNum int64
|
||||
var redisNum int64
|
||||
var dbNum int64
|
||||
var machienNum int64
|
||||
|
||||
if len(tagIds) > 0 {
|
||||
mongoNum = i.MongoApp.Count(&mongoentity.MongoQuery{TagIds: tagIds})
|
||||
machienNum = i.MachineApp.Count(&machineentity.MachineQuery{TagIds: tagIds})
|
||||
dbNum = i.DbApp.Count(&dbentity.DbQuery{TagIds: tagIds})
|
||||
redisNum = i.RedisApp.Count(&redisentity.RedisQuery{TagIds: tagIds})
|
||||
}
|
||||
rc.ResData = map[string]interface{}{
|
||||
"projectNum": i.ProjectApp.Count(new(projectentity.Project)),
|
||||
"machineNum": i.MachineApp.Count(new(machineentity.Machine)),
|
||||
"dbNum": i.DbApp.Count(new(dbentity.Db)),
|
||||
"redisNum": i.RedisApp.Count(new(redisentity.Redis)),
|
||||
"mongoNum": mongoNum,
|
||||
"machineNum": machienNum,
|
||||
"dbNum": dbNum,
|
||||
"redisNum": redisNum,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"mayfly-go/internal/common/api"
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -14,10 +15,11 @@ import (
|
||||
func InitIndexRouter(router *gin.RouterGroup) {
|
||||
index := router.Group("common/index")
|
||||
i := &api.Index{
|
||||
ProjectApp: projectapp.GetProjectApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
MachineApp: machineapp.GetMachineApp(),
|
||||
DbApp: dbapp.GetDbApp(),
|
||||
RedisApp: redisapp.GetRedisApp(),
|
||||
MongoApp: mongoapp.GetMongoApp(),
|
||||
}
|
||||
{
|
||||
// 首页基本信息统计
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"mayfly-go/internal/db/api/vo"
|
||||
"mayfly-go/internal/db/application"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
@@ -27,19 +27,24 @@ type Db struct {
|
||||
DbApp application.Db
|
||||
DbSqlExecApp application.DbSqlExec
|
||||
MsgApp sysapp.Msg
|
||||
ProjectApp projectapp.Project
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
const DEFAULT_ROW_SIZE = 1800
|
||||
|
||||
// @router /api/dbs [get]
|
||||
func (d *Db) Dbs(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
m := &entity.Db{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
|
||||
ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
|
||||
condition := new(entity.DbQuery)
|
||||
condition.TagPathLike = rc.GinCtx.Query("tagPath")
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := d.TagApp.ListTagIdByAccountId(rc.LoginAccount.Id)
|
||||
if len(tagIds) == 0 {
|
||||
rc.ResData = model.EmptyPageResult()
|
||||
return
|
||||
}
|
||||
m.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = d.DbApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.SelectDataDbVO))
|
||||
condition.TagIds = tagIds
|
||||
rc.ResData = d.DbApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]vo.SelectDataDbVO))
|
||||
}
|
||||
|
||||
func (d *Db) Save(rc *ctx.ReqCtx) {
|
||||
@@ -121,7 +126,7 @@ func (d *Db) ExecSql(rc *ctx.ReqCtx) {
|
||||
id := GetDbId(g)
|
||||
db := form.Db
|
||||
dbInstance := d.DbApp.GetDbInstance(id, db)
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbInstance.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.TagPath), "%s")
|
||||
|
||||
// 去除前后空格及换行符
|
||||
sql := strings.TrimFunc(form.Sql, func(r rune) bool {
|
||||
@@ -163,7 +168,7 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
|
||||
db := d.DbApp.GetDbInstance(dbId, db)
|
||||
|
||||
dbEntity := d.DbApp.GetById(dbId)
|
||||
dbInfo := fmt.Sprintf("于%s的%s环境", dbEntity.Name, dbEntity.Env)
|
||||
dbInfo := fmt.Sprintf("于%s的%s环境", dbEntity.Name, dbEntity.TagPath)
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@@ -174,7 +179,7 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
|
||||
}
|
||||
}()
|
||||
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, db.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, db.TagPath), "%s")
|
||||
|
||||
tokens := sqlparser.NewTokenizer(file)
|
||||
for {
|
||||
@@ -208,7 +213,7 @@ func (d *Db) DumpSql(rc *ctx.ReqCtx) {
|
||||
needData := dumpType == "2" || dumpType == "3"
|
||||
|
||||
dbInstance := d.DbApp.GetDbInstance(dbId, db)
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbInstance.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.TagPath), "%s")
|
||||
|
||||
now := time.Now()
|
||||
filename := fmt.Sprintf("%s.%s.sql", db, now.Format("200601021504"))
|
||||
@@ -294,7 +299,7 @@ func (d *Db) DumpSql(rc *ctx.ReqCtx) {
|
||||
// @router /api/db/:dbId/t-metadata [get]
|
||||
func (d *Db) TableMA(rc *ctx.ReqCtx) {
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbi.TagPath), "%s")
|
||||
rc.ResData = dbi.GetMeta().GetTables()
|
||||
}
|
||||
|
||||
@@ -305,14 +310,14 @@ func (d *Db) ColumnMA(rc *ctx.ReqCtx) {
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
|
||||
// biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.TagPath), "%s")
|
||||
rc.ResData = dbi.GetMeta().GetColumns(tn)
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/hint-tables [get]
|
||||
func (d *Db) HintTables(rc *ctx.ReqCtx) {
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
|
||||
// biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.TagPath), "%s")
|
||||
|
||||
dm := dbi.GetMeta()
|
||||
// 获取所有表
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
package form
|
||||
|
||||
type DbForm struct {
|
||||
Id uint64
|
||||
Name string `binding:"required" json:"name"`
|
||||
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
||||
Host string `binding:"required" json:"host"`
|
||||
Port int `binding:"required" json:"port"`
|
||||
Username string `binding:"required" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Params string `json:"params"`
|
||||
Database string `json:"database"`
|
||||
ProjectId uint64 `binding:"required" json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
EnvId uint64 `binding:"required" json:"envId"`
|
||||
Id uint64
|
||||
Name string `binding:"required" json:"name"`
|
||||
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
||||
Host string `binding:"required" json:"host"`
|
||||
Port int `binding:"required" json:"port"`
|
||||
Username string `binding:"required" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Params string `json:"params"`
|
||||
Database string `json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
|
||||
EnableSshTunnel int8 `json:"enableSshTunnel"`
|
||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"`
|
||||
|
||||
@@ -12,10 +12,9 @@ type SelectDataDbVO struct {
|
||||
Params *string `json:"params"`
|
||||
Database *string `json:"database"`
|
||||
Username *string `json:"username"`
|
||||
ProjectId *int64 `json:"projectId"`
|
||||
Project *string `json:"project"`
|
||||
Env *string `json:"env"`
|
||||
EnvId *int64 `json:"envId"`
|
||||
Remark *string `json:"remark"`
|
||||
TagId *int64 `json:"tagId"`
|
||||
TagPath *string `json:"tagPath"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
|
||||
@@ -27,9 +27,9 @@ import (
|
||||
|
||||
type Db interface {
|
||||
// 分页获取
|
||||
GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetPageList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Db) int64
|
||||
Count(condition *entity.DbQuery) int64
|
||||
|
||||
// 根据条件获取
|
||||
GetDbBy(condition *entity.Db, cols ...string) error
|
||||
@@ -64,11 +64,11 @@ type dbAppImpl struct {
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *dbAppImpl) GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
func (d *dbAppImpl) GetPageList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return d.dbRepo.GetDbList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) Count(condition *entity.Db) int64 {
|
||||
func (d *dbAppImpl) Count(condition *entity.DbQuery) int64 {
|
||||
return d.dbRepo.Count(condition)
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func (d *dbAppImpl) Save(dbEntity *entity.Db) {
|
||||
}
|
||||
|
||||
// 查找是否存在该库
|
||||
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, EnvId: dbEntity.EnvId}
|
||||
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, TagId: dbEntity.TagId}
|
||||
err := d.GetDbBy(oldDb)
|
||||
|
||||
if dbEntity.Id == 0 {
|
||||
@@ -194,7 +194,7 @@ func (da *dbAppImpl) GetDbInstance(id uint64, db string) *DbInstance {
|
||||
biz.IsTrue(strings.Contains(d.Database, db), "未配置该库的操作权限")
|
||||
|
||||
cacheKey := GetDbCacheKey(id, db)
|
||||
dbi := &DbInstance{Id: cacheKey, Type: d.Type, ProjectId: d.ProjectId, sshTunnelMachineId: d.SshTunnelMachineId}
|
||||
dbi := &DbInstance{Id: cacheKey, Type: d.Type, TagPath: d.TagPath, sshTunnelMachineId: d.SshTunnelMachineId}
|
||||
|
||||
DB, err := GetDbConn(d, db)
|
||||
if err != nil {
|
||||
@@ -206,7 +206,7 @@ func (da *dbAppImpl) GetDbInstance(id uint64, db string) *DbInstance {
|
||||
// 最大连接周期,超过时间的连接就close
|
||||
// DB.SetConnMaxLifetime(100 * time.Second)
|
||||
// 设置最大连接数
|
||||
DB.SetMaxOpenConns(2)
|
||||
DB.SetMaxOpenConns(5)
|
||||
// 设置闲置连接数
|
||||
DB.SetMaxIdleConns(1)
|
||||
|
||||
@@ -224,7 +224,7 @@ func (da *dbAppImpl) GetDbInstance(id uint64, db string) *DbInstance {
|
||||
type DbInstance struct {
|
||||
Id string
|
||||
Type string
|
||||
ProjectId uint64
|
||||
TagPath string
|
||||
db *sql.DB
|
||||
sshTunnelMachineId uint64
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
PGSQL_COLUMN_MA = `SELECT
|
||||
C.relname AS "tableName",
|
||||
A.attname AS "columnName",
|
||||
tc.is_nullable AS "nullable",
|
||||
concat_ws ( '', t.typname, SUBSTRING ( format_type ( a.atttypid, a.atttypmod ) FROM '\(.*\)' ) ) AS "columnType",
|
||||
d.description AS "columnComment"
|
||||
FROM
|
||||
@@ -28,6 +29,7 @@ const (
|
||||
LEFT JOIN pg_class c ON A.attrelid = c.oid
|
||||
LEFT JOIN pg_namespace pn ON c.relnamespace = pn.oid
|
||||
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
||||
JOIN information_schema.columns tc ON tc.column_name = a.attname AND tc.table_name = C.relname AND tc.table_schema = pn.nspname
|
||||
WHERE
|
||||
A.attnum >= 0
|
||||
AND pn.nspname = (select current_schema())
|
||||
|
||||
@@ -9,19 +9,18 @@ import (
|
||||
type Db struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Type string `orm:"column(type)" json:"type"` // 类型,mysql oracle等
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Port int `orm:"column(port)" json:"port"`
|
||||
Network string `orm:"column(network)" json:"network"`
|
||||
Username string `orm:"column(username)" json:"username"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Params string `json:"params"`
|
||||
ProjectId uint64
|
||||
Project string
|
||||
EnvId uint64
|
||||
Env string
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Type string `orm:"column(type)" json:"type"` // 类型,mysql oracle等
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Port int `orm:"column(port)" json:"port"`
|
||||
Network string `orm:"column(network)" json:"network"`
|
||||
Username string `orm:"column(username)" json:"username"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Params string `json:"params"`
|
||||
Remark string `json:"remark"`
|
||||
TagId uint64
|
||||
TagPath string
|
||||
|
||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
|
||||
24
server/internal/db/domain/entity/query.go
Normal file
24
server/internal/db/domain/entity/query.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 数据库查询实体,不与数据库表字段一一对应
|
||||
type DbQuery struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Type string `orm:"column(type)" json:"type"` // 类型,mysql oracle等
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Port int `orm:"column(port)" json:"port"`
|
||||
Network string `orm:"column(network)" json:"network"`
|
||||
Username string `orm:"column(username)" json:"username"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Params string `json:"params"`
|
||||
Remark string `json:"remark"`
|
||||
TagId uint64
|
||||
|
||||
ProjectIds []uint64
|
||||
TagIds []uint64
|
||||
TagPathLike string
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
type Db interface {
|
||||
// 分页获取机器信息列表
|
||||
GetDbList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Db) int64
|
||||
Count(condition *entity.DbQuery) int64
|
||||
|
||||
// 根据条件获取账号信息
|
||||
GetDb(condition *entity.Db, cols ...string) error
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type dbRepoImpl struct{}
|
||||
@@ -15,30 +17,33 @@ func newDbRepo() repository.Db {
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *dbRepoImpl) GetDbList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_db 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)
|
||||
}
|
||||
func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_db d WHERE 1 = 1 "
|
||||
if condition.Host != "" {
|
||||
sql = sql + " AND d.host LIKE '%" + condition.Host + "%'"
|
||||
}
|
||||
if condition.Database != "" {
|
||||
sql = sql + " AND d.database LIKE '%" + condition.Database + "%'"
|
||||
}
|
||||
if len(condition.TagIds) > 0 {
|
||||
sql = sql + " AND d.tag_id IN " + fmt.Sprintf("(%s)", strings.Join(utils.NumberArr2StrArr(condition.TagIds), ","))
|
||||
}
|
||||
if condition.TagPathLike != "" {
|
||||
sql = sql + " AND d.tag_path LIKE '" + condition.TagPathLike + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (d *dbRepoImpl) Count(condition *entity.Db) int64 {
|
||||
return model.CountBy(condition)
|
||||
func (d *dbRepoImpl) Count(condition *entity.DbQuery) int64 {
|
||||
where := make(map[string]interface{})
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
if condition.TagId != 0 {
|
||||
where["tag_id"] = condition.TagId
|
||||
}
|
||||
return model.CountByMap(new(entity.Db), where)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
|
||||
@@ -23,5 +23,5 @@ func (d *dbSqlExecRepoImpl) DeleteBy(condition *entity.DbSqlExec) {
|
||||
|
||||
// 分页获取
|
||||
func (d *dbSqlExecRepoImpl) GetPageList(condition *entity.DbSqlExec, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package router
|
||||
import (
|
||||
"mayfly-go/internal/db/api"
|
||||
"mayfly-go/internal/db/application"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -17,7 +17,7 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
DbApp: application.GetDbApp(),
|
||||
DbSqlExecApp: application.GetDbSqlExecApp(),
|
||||
MsgApp: sysapp.GetMsgApp(),
|
||||
ProjectApp: projectapp.GetProjectApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
// 获取所有数据库列表
|
||||
db.GET("", func(c *gin.Context) {
|
||||
|
||||
@@ -2,8 +2,6 @@ package form
|
||||
|
||||
type MachineForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Ip string `json:"ip" binding:"required"` // IP地址
|
||||
Username string `json:"username" binding:"required"` // 用户名
|
||||
@@ -14,6 +12,8 @@ type MachineForm struct {
|
||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
}
|
||||
|
||||
type MachineRunForm struct {
|
||||
|
||||
@@ -8,11 +8,12 @@ import (
|
||||
"mayfly-go/internal/machine/application"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/infrastructure/machine"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/config"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"mayfly-go/pkg/ws"
|
||||
"os"
|
||||
@@ -27,16 +28,22 @@ import (
|
||||
|
||||
type Machine struct {
|
||||
MachineApp application.Machine
|
||||
ProjectApp projectapp.Project
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
func (m *Machine) Machines(rc *ctx.ReqCtx) {
|
||||
condition := new(entity.Machine)
|
||||
// 使用创建者id模拟账号成员id
|
||||
condition.CreatorId = rc.LoginAccount.Id
|
||||
condition := new(entity.MachineQuery)
|
||||
condition.Ip = rc.GinCtx.Query("ip")
|
||||
condition.Name = rc.GinCtx.Query("name")
|
||||
condition.ProjectId = uint64(ginx.QueryInt(rc.GinCtx, "projectId", 0))
|
||||
condition.TagPathLike = rc.GinCtx.Query("tagPath")
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := m.TagApp.ListTagIdByAccountId(rc.LoginAccount.Id)
|
||||
if len(tagIds) == 0 {
|
||||
rc.ResData = model.EmptyPageResult()
|
||||
return
|
||||
}
|
||||
condition.TagIds = tagIds
|
||||
|
||||
res := m.MachineApp.GetMachineList(condition, ginx.GetPageParam(rc.GinCtx), new([]*vo.MachineVO))
|
||||
if res.Total == 0 {
|
||||
@@ -130,7 +137,7 @@ func (m *Machine) GetProcess(rc *ctx.ReqCtx) {
|
||||
cmd += "| head -n " + count
|
||||
|
||||
cli := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().TagPath), "%s")
|
||||
|
||||
res, err := cli.Run(cmd)
|
||||
biz.ErrIsNilAppendErr(err, "获取进程信息失败: %s")
|
||||
@@ -143,7 +150,7 @@ func (m *Machine) KillProcess(rc *ctx.ReqCtx) {
|
||||
biz.NotEmpty(pid, "进程id不能为空")
|
||||
|
||||
cli := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().TagPath), "%s")
|
||||
|
||||
_, err := cli.Run("sudo kill -9 " + pid)
|
||||
biz.ErrIsNilAppendErr(err, "终止进程失败: %s")
|
||||
@@ -168,7 +175,7 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
||||
}
|
||||
|
||||
cli := m.MachineApp.GetCli(GetMachineId(g))
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().TagPath), "%s")
|
||||
|
||||
cols := ginx.QueryInt(g, "cols", 80)
|
||||
rows := ginx.QueryInt(g, "rows", 40)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"mayfly-go/internal/machine/api/vo"
|
||||
"mayfly-go/internal/machine/application"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
type MachineScript struct {
|
||||
MachineScriptApp application.MachineScript
|
||||
MachineApp application.Machine
|
||||
ProjectApp projectapp.Project
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
func (m *MachineScript) MachineScripts(rc *ctx.ReqCtx) {
|
||||
@@ -65,7 +65,7 @@ func (m *MachineScript) RunMachineScript(rc *ctx.ReqCtx) {
|
||||
script = utils.TemplateParse(ms.Script, utils.Json2Map(params))
|
||||
}
|
||||
cli := m.MachineApp.GetCli(machineId)
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().TagPath), "%s")
|
||||
|
||||
res, err := cli.Run(script)
|
||||
// 记录请求参数
|
||||
|
||||
@@ -2,22 +2,8 @@ package vo
|
||||
|
||||
import "time"
|
||||
|
||||
type AccountVO struct {
|
||||
//models.BaseModel
|
||||
Id *int64 `json:"id"`
|
||||
Username *string `json:"username"`
|
||||
CreateTime *string `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
// Role *RoleVO `json:"roles"`
|
||||
//Status int8 `json:"status"`
|
||||
}
|
||||
|
||||
type MachineVO struct {
|
||||
//models.BaseModel
|
||||
Id *uint64 `json:"id"`
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Name *string `json:"name"`
|
||||
Username *string `json:"username"`
|
||||
Ip *string `json:"ip"`
|
||||
@@ -35,6 +21,8 @@ type MachineVO struct {
|
||||
HasCli bool `json:"hasCli" gorm:"-"`
|
||||
Remark *string `json:"remark"`
|
||||
EnableRecorder int8 `json:"enableRecorder"`
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
}
|
||||
|
||||
type MachineScriptVO struct {
|
||||
|
||||
@@ -19,7 +19,7 @@ type Machine interface {
|
||||
// 调整机器状态
|
||||
ChangeStatus(id uint64, status int8)
|
||||
|
||||
Count(condition *entity.Machine) int64
|
||||
Count(condition *entity.MachineQuery) int64
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
@@ -27,7 +27,7 @@ type Machine interface {
|
||||
GetById(id uint64, cols ...string) *entity.Machine
|
||||
|
||||
// 分页获取机器信息列表
|
||||
GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 获取机器连接
|
||||
GetCli(id uint64) *machine.Cli
|
||||
@@ -46,11 +46,11 @@ type machineAppImpl struct {
|
||||
}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (m *machineAppImpl) GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
func (m *machineAppImpl) GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return m.machineRepo.GetMachineList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) Count(condition *entity.Machine) int64 {
|
||||
func (m *machineAppImpl) Count(condition *entity.MachineQuery) int64 {
|
||||
return m.machineRepo.Count(condition)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
|
||||
type Machine struct {
|
||||
model.Model
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Username string `json:"username"` // 用户名
|
||||
@@ -20,6 +19,8 @@ type Machine struct {
|
||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
16
server/internal/machine/domain/entity/machine_task_config.go
Normal file
16
server/internal/machine/domain/entity/machine_task_config.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 机器任务配置
|
||||
type MachineTaskConfig struct {
|
||||
model.Model
|
||||
|
||||
Name string `json:"name"`
|
||||
Cron string `json:"cron"` // cron表达式
|
||||
Script string `json:"script"` // 任务内容
|
||||
Status string `json:"status"`
|
||||
EnableNotify int `json:"enableNotify"` // 是否启用通知
|
||||
NotifyTemplate string `json:"notifyTemplate"` // 通知模板
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
25
server/internal/machine/domain/entity/query.go
Normal file
25
server/internal/machine/domain/entity/query.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type MachineQuery struct {
|
||||
model.Model
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Username string `json:"username"` // 用户名
|
||||
AuthMethod int8 `json:"authMethod"` // 授权认证方式
|
||||
Password string `json:"-"`
|
||||
Port int `json:"port"` // 端口号
|
||||
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||
Remark string `json:"remark"` // 备注
|
||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||
|
||||
ProjectIds []uint64
|
||||
TagId uint64
|
||||
TagPathLike string
|
||||
TagIds []uint64
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
type Machine interface {
|
||||
// 分页获取机器信息列表
|
||||
GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Machine) int64
|
||||
Count(condition *entity.MachineQuery) int64
|
||||
|
||||
// 根据条件获取账号信息
|
||||
GetMachine(condition *entity.Machine, cols ...string) error
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type MachineTaskConfig interface {
|
||||
GetPageList(condition *entity.MachineTaskConfig, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据条件获取
|
||||
GetBy(condition *entity.MachineTaskConfig, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.MachineTaskConfig
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
Create(entity *entity.MachineTaskConfig)
|
||||
|
||||
UpdateById(entity *entity.MachineTaskConfig)
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type machineRepoImpl struct{}
|
||||
@@ -15,27 +17,34 @@ func newMachineRepo() repository.Machine {
|
||||
}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (m *machineRepoImpl) GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT m.* FROM t_machine m JOIN t_project_member pm ON m.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 m.project_id = %d", sql, condition.ProjectId)
|
||||
}
|
||||
func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT m.* FROM t_machine m WHERE 1 = 1 "
|
||||
if condition.Ip != "" {
|
||||
sql = sql + " AND m.ip LIKE '%" + condition.Ip + "%'"
|
||||
}
|
||||
if condition.Name != "" {
|
||||
sql = sql + " AND m.name LIKE '%" + condition.Name + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY m.project_id, m.create_time DESC"
|
||||
if len(condition.TagIds) > 0 {
|
||||
sql = fmt.Sprintf("%s AND m.tag_id IN (%s) ", sql, strings.Join(utils.NumberArr2StrArr(condition.TagIds), ","))
|
||||
}
|
||||
if condition.TagPathLike != "" {
|
||||
sql = sql + " AND m.tag_path LIKE '" + condition.TagPathLike + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY m.tag_id, m.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (m *machineRepoImpl) Count(condition *entity.Machine) int64 {
|
||||
return model.CountBy(condition)
|
||||
func (m *machineRepoImpl) Count(condition *entity.MachineQuery) int64 {
|
||||
where := make(map[string]interface{})
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
if condition.TagId != 0 {
|
||||
where["tag_id"] = condition.TagId
|
||||
}
|
||||
|
||||
return model.CountByMap(new(entity.Machine), where)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
|
||||
@@ -15,7 +15,7 @@ func newMachineFileRepo() repository.MachineFile {
|
||||
|
||||
// 分页获取机器文件信息列表
|
||||
func (m *machineFileRepoImpl) GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
|
||||
@@ -15,7 +15,7 @@ func newMachineScriptRepo() repository.MachineScript {
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (m *machineScriptRepoImpl) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
|
||||
@@ -3,7 +3,7 @@ package router
|
||||
import (
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/internal/machine/application"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func InitMachineRouter(router *gin.RouterGroup) {
|
||||
m := &api.Machine{
|
||||
MachineApp: application.GetMachineApp(),
|
||||
ProjectApp: projectapp.GetProjectApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
|
||||
machines := router.Group("machines")
|
||||
|
||||
@@ -3,7 +3,7 @@ package router
|
||||
import (
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/internal/machine/application"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -15,7 +15,7 @@ func InitMachineScriptRouter(router *gin.RouterGroup) {
|
||||
ms := &api.MachineScript{
|
||||
MachineScriptApp: application.GetMachineScriptApp(),
|
||||
MachineApp: application.GetMachineApp(),
|
||||
ProjectApp: projectapp.GetProjectApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
|
||||
// 获取指定机器脚本列表
|
||||
|
||||
@@ -6,10 +6,8 @@ type Mongo struct {
|
||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
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"`
|
||||
TagId uint64 `binding:"required" json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
}
|
||||
|
||||
type MongoCommand struct {
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"mayfly-go/internal/mongo/api/form"
|
||||
"mayfly-go/internal/mongo/application"
|
||||
"mayfly-go/internal/mongo/domain/entity"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -20,15 +22,21 @@ import (
|
||||
|
||||
type Mongo struct {
|
||||
MongoApp application.Mongo
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
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)),
|
||||
condition := new(entity.MongoQuery)
|
||||
condition.TagPathLike = rc.GinCtx.Query("tagPath")
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := m.TagApp.ListTagIdByAccountId(rc.LoginAccount.Id)
|
||||
if len(tagIds) == 0 {
|
||||
rc.ResData = model.EmptyPageResult()
|
||||
return
|
||||
}
|
||||
mc.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = m.MongoApp.GetPageList(mc, ginx.GetPageParam(rc.GinCtx), new([]entity.Mongo))
|
||||
condition.TagIds = tagIds
|
||||
rc.ResData = m.MongoApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]entity.Mongo))
|
||||
}
|
||||
|
||||
func (m *Mongo) Save(rc *ctx.ReqCtx) {
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
|
||||
type Mongo interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetPageList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Mongo) int64
|
||||
Count(condition *entity.MongoQuery) int64
|
||||
|
||||
// 根据条件获取
|
||||
GetBy(condition *entity.Mongo, cols ...string) error
|
||||
@@ -53,11 +53,11 @@ type mongoAppImpl struct {
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *mongoAppImpl) GetPageList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
func (d *mongoAppImpl) GetPageList(condition *entity.MongoQuery, 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 {
|
||||
func (d *mongoAppImpl) Count(condition *entity.MongoQuery) int64 {
|
||||
return d.mongoRepo.Count(condition)
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func DeleteMongoCache(mongoId uint64) {
|
||||
|
||||
type MongoInstance struct {
|
||||
Id uint64
|
||||
ProjectId uint64
|
||||
TagPath string
|
||||
Cli *mongo.Client
|
||||
sshTunnelMachineId uint64
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func connect(me *entity.Mongo) (*MongoInstance, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoInstance := &MongoInstance{Id: me.Id, ProjectId: me.ProjectId}
|
||||
mongoInstance := &MongoInstance{Id: me.Id, TagPath: me.TagPath}
|
||||
|
||||
mongoOptions := options.Client().ApplyURI(me.Uri).
|
||||
SetMaxPoolSize(1)
|
||||
|
||||
@@ -9,8 +9,6 @@ type Mongo struct {
|
||||
Uri string `orm:"column(uri)" json:"uri"`
|
||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
EnvId uint64 `json:"envId"`
|
||||
Env string `json:"env"`
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
}
|
||||
|
||||
17
server/internal/mongo/domain/entity/query.go
Normal file
17
server/internal/mongo/domain/entity/query.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type MongoQuery struct {
|
||||
model.Model
|
||||
|
||||
Name string
|
||||
Uri string
|
||||
EnableSshTunnel int8 // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 // ssh隧道机器id
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
|
||||
TagIds []uint64
|
||||
TagPathLike string
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
type Mongo interface {
|
||||
// 分页获取列表
|
||||
GetList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Mongo) int64
|
||||
Count(condition *entity.MongoQuery) int64
|
||||
|
||||
// 根据条件获取
|
||||
Get(condition *entity.Mongo, cols ...string) error
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"mayfly-go/internal/mongo/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type mongoRepoImpl struct{}
|
||||
@@ -15,24 +17,28 @@ func newMongoRepo() repository.Mongo {
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *mongoRepoImpl) 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)
|
||||
func (d *mongoRepoImpl) GetList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_mongo d WHERE 1=1 "
|
||||
|
||||
if len(condition.TagIds) > 0 {
|
||||
sql = sql + " AND d.tag_id IN " + fmt.Sprintf("(%s)", strings.Join(utils.NumberArr2StrArr(condition.TagIds), ","))
|
||||
}
|
||||
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)
|
||||
if condition.TagPathLike != "" {
|
||||
sql = sql + " AND d.tag_path LIKE '" + condition.TagPathLike + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (d *mongoRepoImpl) Count(condition *entity.Mongo) int64 {
|
||||
return model.CountBy(condition)
|
||||
func (d *mongoRepoImpl) Count(condition *entity.MongoQuery) int64 {
|
||||
where := make(map[string]interface{})
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
if condition.TagId != 0 {
|
||||
where["tag_id"] = condition.TagId
|
||||
}
|
||||
return model.CountByMap(new(entity.Mongo), where)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
|
||||
@@ -3,6 +3,7 @@ package router
|
||||
import (
|
||||
"mayfly-go/internal/mongo/api"
|
||||
"mayfly-go/internal/mongo/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -13,6 +14,7 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
{
|
||||
ma := &api.Mongo{
|
||||
MongoApp: application.GetMongoApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
|
||||
// 获取所有mongo列表
|
||||
@@ -60,7 +62,7 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
})
|
||||
|
||||
// 执行mongo update by id命令
|
||||
updateDocById := ctx.NewLogInfo("mongo-更新文档")
|
||||
updateDocById := ctx.NewLogInfo("mongo-更新文档").WithSave(true)
|
||||
m.POST(":id/command/update-by-id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(updateDocById).
|
||||
@@ -68,7 +70,7 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
})
|
||||
|
||||
// 执行mongo delete by id命令
|
||||
deleteDoc := ctx.NewLogInfo("mongo-删除文档")
|
||||
deleteDoc := ctx.NewLogInfo("mongo-删除文档").WithSave(true)
|
||||
m.POST(":id/command/delete-by-id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(deleteDoc).
|
||||
@@ -76,7 +78,7 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
})
|
||||
|
||||
// 执行mongo insert 命令
|
||||
insertDoc := ctx.NewLogInfo("mongo-新增文档")
|
||||
insertDoc := ctx.NewLogInfo("mongo-新增文档").WithSave(true)
|
||||
m.POST(":id/command/insert", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(insertDoc).
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/project/api/vo"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
sys_applicaiton "mayfly-go/internal/sys/application"
|
||||
sys_entity "mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
ProjectApp projectapp.Project
|
||||
AccountApp sys_applicaiton.Account
|
||||
}
|
||||
|
||||
// 获取当前登录用户可以访问的项目列表
|
||||
func (p *Project) GetProjectsByLoginAccount(rc *ctx.ReqCtx) {
|
||||
// 获取登录用户拥有的项目ids
|
||||
projectMembers := &[]entity.ProjectMember{}
|
||||
p.ProjectApp.ListMember(&entity.ProjectMember{AccountId: rc.LoginAccount.Id}, projectMembers)
|
||||
var pids []uint64
|
||||
for _, pm := range *projectMembers {
|
||||
pids = append(pids, pm.ProjectId)
|
||||
}
|
||||
|
||||
// 获取项目信息
|
||||
projects := &vo.AccountProjects{}
|
||||
p.ProjectApp.ListProjectByIds(pids, projects)
|
||||
rc.ResData = projects
|
||||
}
|
||||
|
||||
func (p *Project) GetProjects(rc *ctx.ReqCtx) {
|
||||
condition := &entity.Project{}
|
||||
ginx.BindQuery(rc.GinCtx, condition)
|
||||
// condition.Name = rc.GinCtx.Query("name")
|
||||
rc.ResData = p.ProjectApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]entity.Project))
|
||||
}
|
||||
|
||||
func (p *Project) SaveProject(rc *ctx.ReqCtx) {
|
||||
project := &entity.Project{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, project)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("projectId: %d, projectName: %s, remark: %s", project.Id, project.Name, project.Remark)
|
||||
|
||||
project.SetBaseInfo(rc.LoginAccount)
|
||||
p.ProjectApp.SaveProject(project)
|
||||
}
|
||||
|
||||
func (p *Project) DelProject(rc *ctx.ReqCtx) {
|
||||
p.ProjectApp.DelProject(uint64(ginx.QueryInt(rc.GinCtx, "id", 0)))
|
||||
}
|
||||
|
||||
func (p *Project) DelProjectEnv(rc *ctx.ReqCtx) {
|
||||
p.ProjectApp.DelProjectEnv(uint64(ginx.QueryInt(rc.GinCtx, "id", 0)))
|
||||
}
|
||||
|
||||
// 获取项目下的环境信息
|
||||
func (p *Project) GetProjectEnvs(rc *ctx.ReqCtx) {
|
||||
projectEnvs := &[]entity.ProjectEnv{}
|
||||
p.ProjectApp.ListEnvByProjectId(uint64(ginx.PathParamInt(rc.GinCtx, "projectId")), projectEnvs)
|
||||
rc.ResData = projectEnvs
|
||||
}
|
||||
|
||||
// 保存项目下的环境信息
|
||||
func (p *Project) SaveProjectEnvs(rc *ctx.ReqCtx) {
|
||||
projectEnv := &entity.ProjectEnv{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, projectEnv)
|
||||
rc.ReqParam = projectEnv
|
||||
|
||||
projectEnv.SetBaseInfo(rc.LoginAccount)
|
||||
p.ProjectApp.SaveProjectEnv(projectEnv)
|
||||
}
|
||||
|
||||
// 获取项目下的成员信息
|
||||
func (p *Project) GetProjectMembers(rc *ctx.ReqCtx) {
|
||||
projectMems := &[]entity.ProjectMember{}
|
||||
rc.ResData = p.ProjectApp.GetMemberPage(&entity.ProjectMember{ProjectId: uint64(ginx.PathParamInt(rc.GinCtx, "projectId"))},
|
||||
ginx.GetPageParam(rc.GinCtx), projectMems)
|
||||
}
|
||||
|
||||
// 保存项目的成员信息
|
||||
func (p *Project) SaveProjectMember(rc *ctx.ReqCtx) {
|
||||
projectMem := &entity.ProjectMember{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, projectMem)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("projectId: %d, username: %s", projectMem.ProjectId, projectMem.Username)
|
||||
|
||||
// 校验账号,并赋值username
|
||||
account := &sys_entity.Account{}
|
||||
account.Id = projectMem.AccountId
|
||||
biz.ErrIsNil(p.AccountApp.GetAccount(account, "Id", "Username"), "账号不存在")
|
||||
projectMem.Username = account.Username
|
||||
|
||||
projectMem.SetBaseInfo(rc.LoginAccount)
|
||||
p.ProjectApp.SaveProjectMember(projectMem)
|
||||
}
|
||||
|
||||
// 删除项目成员
|
||||
func (p *Project) DelProjectMember(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
pid := ginx.PathParamInt(g, "projectId")
|
||||
aid := ginx.PathParamInt(g, "accountId")
|
||||
rc.ReqParam = fmt.Sprintf("projectId: %d, accountId: %d", pid, aid)
|
||||
|
||||
p.ProjectApp.DeleteMember(uint64(pid), uint64(aid))
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
"mayfly-go/internal/project/infrastructure/persistence"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
)
|
||||
|
||||
var (
|
||||
projectApp Project = newProjectApp(
|
||||
persistence.GetProjectRepo(),
|
||||
persistence.GetProjectEnvRepo(),
|
||||
persistence.GetProjectMemberRepo(),
|
||||
machineapp.GetMachineApp(),
|
||||
redisapp.GetRedisApp(),
|
||||
dbapp.GetDbApp(),
|
||||
mongoapp.GetMongoApp(),
|
||||
)
|
||||
)
|
||||
|
||||
func GetProjectApp() Project {
|
||||
return projectApp
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
dbentity "mayfly-go/internal/db/domain/entity"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
machineentity "mayfly-go/internal/machine/domain/entity"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
mongoentity "mayfly-go/internal/mongo/domain/entity"
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
"mayfly-go/internal/project/domain/repository"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
redisentity "mayfly-go/internal/redis/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Project interface {
|
||||
// 分页获取项目信息列表
|
||||
GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Project) int64
|
||||
|
||||
ListProjectByIds(ids []uint64, toEntity interface{}, orderBy ...string)
|
||||
|
||||
SaveProject(project *entity.Project)
|
||||
|
||||
DelProject(id uint64)
|
||||
|
||||
DelProjectEnv(id uint64)
|
||||
|
||||
// 根据项目id获取所有该项目下的环境信息列表
|
||||
ListEnvByProjectId(projectId uint64, listPtr interface{})
|
||||
|
||||
// 保存项目环境信息
|
||||
SaveProjectEnv(projectEnv *entity.ProjectEnv)
|
||||
|
||||
// 根据条件获取项目成员信息
|
||||
ListMember(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string)
|
||||
|
||||
SaveProjectMember(pm *entity.ProjectMember)
|
||||
|
||||
// 根据条件获取项目成员信息
|
||||
GetMemberPage(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
DeleteMember(projectId, accountId uint64)
|
||||
|
||||
// 账号是否有权限访问该项目关联的资源信息
|
||||
CanAccess(accountId, projectId uint64) error
|
||||
}
|
||||
|
||||
func newProjectApp(projectRepo repository.Project,
|
||||
projectEnvRepo repository.ProjectEnv,
|
||||
projectMemberRepo repository.ProjectMemeber,
|
||||
machineApp machineapp.Machine,
|
||||
redisApp redisapp.Redis,
|
||||
dbApp dbapp.Db,
|
||||
mongoApp mongoapp.Mongo) Project {
|
||||
return &projectAppImpl{
|
||||
projectRepo: projectRepo,
|
||||
projectEnvRepo: projectEnvRepo,
|
||||
projectMemberRepo: projectMemberRepo,
|
||||
machineApp: machineApp,
|
||||
redisApp: redisApp,
|
||||
dbApp: dbApp,
|
||||
mongoApp: mongoApp,
|
||||
}
|
||||
}
|
||||
|
||||
type projectAppImpl struct {
|
||||
projectRepo repository.Project
|
||||
projectEnvRepo repository.ProjectEnv
|
||||
projectMemberRepo repository.ProjectMemeber
|
||||
machineApp machineapp.Machine
|
||||
redisApp redisapp.Redis
|
||||
mongoApp mongoapp.Mongo
|
||||
dbApp dbapp.Db
|
||||
}
|
||||
|
||||
// 分页获取项目信息列表
|
||||
func (p *projectAppImpl) GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return p.projectRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) Count(condition *entity.Project) int64 {
|
||||
return p.projectRepo.Count(condition)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) ListProjectByIds(ids []uint64, toEntity interface{}, orderBy ...string) {
|
||||
p.projectRepo.GetByIdIn(ids, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) SaveProject(project *entity.Project) {
|
||||
if project.Id == 0 {
|
||||
p.projectRepo.Save(project)
|
||||
} else {
|
||||
// 防止误传导致项目名更新
|
||||
project.Name = ""
|
||||
p.projectRepo.Update(project)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) DelProject(id uint64) {
|
||||
biz.IsTrue(p.machineApp.Count(&machineentity.Machine{ProjectId: id}) == 0, "请先删除该项目关联的机器信息")
|
||||
biz.IsTrue(p.redisApp.Count(&redisentity.Redis{ProjectId: id}) == 0, "请先删除该项目关联的redis信息")
|
||||
biz.IsTrue(p.dbApp.Count(&dbentity.Db{ProjectId: id}) == 0, "请先删除该项目关联的数据库信息")
|
||||
biz.IsTrue(p.mongoApp.Count(&mongoentity.Mongo{ProjectId: id}) == 0, "请先删除该项目关联的Mongo信息")
|
||||
p.projectRepo.Delete(id)
|
||||
p.projectEnvRepo.DeleteEnvs(id)
|
||||
p.projectMemberRepo.DeleteMems(id)
|
||||
}
|
||||
|
||||
// 根据项目id获取所有该项目下的环境信息列表
|
||||
func (p *projectAppImpl) ListEnvByProjectId(projectId uint64, listPtr interface{}) {
|
||||
p.projectEnvRepo.ListEnv(&entity.ProjectEnv{ProjectId: projectId}, listPtr)
|
||||
}
|
||||
|
||||
// 保存项目环境信息
|
||||
func (p *projectAppImpl) SaveProjectEnv(projectEnv *entity.ProjectEnv) {
|
||||
p.projectEnvRepo.Save(projectEnv)
|
||||
}
|
||||
|
||||
// 删除项目环境信息
|
||||
func (p *projectAppImpl) DelProjectEnv(id uint64) {
|
||||
biz.IsTrue(p.redisApp.Count(&redisentity.Redis{EnvId: id}) == 0, "请先删除该项目环境关联的redis信息")
|
||||
biz.IsTrue(p.dbApp.Count(&dbentity.Db{EnvId: id}) == 0, "请先删除该项目环境关联的数据库信息")
|
||||
biz.IsTrue(p.mongoApp.Count(&mongoentity.Mongo{ProjectId: id}) == 0, "请先删除该项目环境关联的Mongo信息")
|
||||
p.projectEnvRepo.DeleteEnv(id)
|
||||
}
|
||||
|
||||
// 根据条件获取项目成员信息
|
||||
func (p *projectAppImpl) ListMember(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string) {
|
||||
p.projectMemberRepo.ListMemeber(condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) SaveProjectMember(pm *entity.ProjectMember) {
|
||||
pms := new([]entity.ProjectMember)
|
||||
p.ListMember(&entity.ProjectMember{ProjectId: pm.ProjectId, AccountId: pm.AccountId}, pms)
|
||||
biz.IsTrue(len(*pms) == 0, "该成员已存在")
|
||||
p.projectMemberRepo.Save(pm)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) GetMemberPage(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return p.projectMemberRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) DeleteMember(projectId, accountId uint64) {
|
||||
p.projectMemberRepo.DeleteByPidMid(projectId, accountId)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) CanAccess(accountId, projectId uint64) error {
|
||||
if p.projectMemberRepo.IsExist(projectId, accountId) {
|
||||
return nil
|
||||
}
|
||||
return biz.NewBizErr("您无权操作该资源")
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 项目环境
|
||||
type ProjectEnv struct {
|
||||
model.Model
|
||||
Name string `json:"name"` // 环境名
|
||||
ProjectId uint64 `json:"projectId"` // 项目id
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 项目成员,用于对项目下组件的访问控制
|
||||
type ProjectMember struct {
|
||||
model.Model
|
||||
AccountId uint64 `json:"accountId"` // 账号
|
||||
Username string `json:"username"` // 账号用户名
|
||||
ProjectId uint64 `json:"projectId"` // 项目id
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Project interface {
|
||||
GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Project) int64
|
||||
|
||||
GetByIdIn(ids []uint64, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(p *entity.Project)
|
||||
|
||||
Update(project *entity.Project)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package repository
|
||||
|
||||
import "mayfly-go/internal/project/domain/entity"
|
||||
|
||||
type ProjectEnv interface {
|
||||
// 获取项目环境列表
|
||||
ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(entity *entity.ProjectEnv)
|
||||
|
||||
DeleteEnvs(projectId uint64)
|
||||
|
||||
DeleteEnv(envId uint64)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type ProjectMemeber interface {
|
||||
|
||||
// 获取项目成员列表
|
||||
ListMemeber(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(mp *entity.ProjectMember)
|
||||
|
||||
GetPageList(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据成员id和项目id删除关联关系
|
||||
DeleteByPidMid(projectId, accountId uint64)
|
||||
|
||||
DeleteMems(projectId uint64)
|
||||
|
||||
// 是否存在指定的项目成员关联信息
|
||||
IsExist(projectId, accountId uint64) bool
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package persistence
|
||||
|
||||
import "mayfly-go/internal/project/domain/repository"
|
||||
|
||||
var (
|
||||
projectRepo repository.Project = newProjectRepo()
|
||||
projectEnvRepo repository.ProjectEnv = newProjectEnvRepo()
|
||||
projectMemberRepo repository.ProjectMemeber = newProjectMemberRepo()
|
||||
)
|
||||
|
||||
func GetProjectRepo() repository.Project {
|
||||
return projectRepo
|
||||
}
|
||||
|
||||
func GetProjectEnvRepo() repository.ProjectEnv {
|
||||
return projectEnvRepo
|
||||
}
|
||||
|
||||
func GetProjectMemberRepo() repository.ProjectMemeber {
|
||||
return projectMemberRepo
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
"mayfly-go/internal/project/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type projectRepoImpl struct{}
|
||||
|
||||
func newProjectRepo() repository.Project {
|
||||
return new(projectRepoImpl)
|
||||
}
|
||||
|
||||
func (p *projectRepoImpl) GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectRepoImpl) Count(condition *entity.Project) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
func (p *projectRepoImpl) GetByIdIn(ids []uint64, toEntity interface{}, orderBy ...string) {
|
||||
model.GetByIdIn(new(entity.Project), toEntity, ids, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectRepoImpl) Save(project *entity.Project) {
|
||||
biz.ErrIsNil(model.Insert(project), "保存项目失败")
|
||||
}
|
||||
|
||||
func (p *projectRepoImpl) Update(project *entity.Project) {
|
||||
biz.ErrIsNil(model.UpdateById(project), "更新项目信息")
|
||||
}
|
||||
|
||||
func (p *projectRepoImpl) Delete(id uint64) {
|
||||
model.DeleteById(new(entity.Project), id)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
"mayfly-go/internal/project/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type projectEnvRepoImpl struct{}
|
||||
|
||||
func newProjectEnvRepo() repository.ProjectEnv {
|
||||
return new(projectEnvRepoImpl)
|
||||
}
|
||||
|
||||
func (p *projectEnvRepoImpl) ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string) {
|
||||
model.ListByOrder(condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectEnvRepoImpl) Save(entity *entity.ProjectEnv) {
|
||||
biz.ErrIsNilAppendErr(model.Insert(entity), "保存环境失败:%s")
|
||||
}
|
||||
|
||||
func (p *projectEnvRepoImpl) DeleteEnvs(projectId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectEnv{ProjectId: projectId})
|
||||
}
|
||||
|
||||
func (p *projectEnvRepoImpl) DeleteEnv(envId uint64) {
|
||||
model.DeleteById(new(entity.ProjectEnv), envId)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/project/domain/entity"
|
||||
"mayfly-go/internal/project/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type projectMemberRepoImpl struct{}
|
||||
|
||||
func newProjectMemberRepo() repository.ProjectMemeber {
|
||||
return new(projectMemberRepoImpl)
|
||||
}
|
||||
|
||||
func (p *projectMemberRepoImpl) ListMemeber(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string) {
|
||||
model.ListByOrder(condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectMemberRepoImpl) Save(pm *entity.ProjectMember) {
|
||||
biz.ErrIsNilAppendErr(model.Insert(pm), "保存项目成员失败:%s")
|
||||
}
|
||||
|
||||
func (p *projectMemberRepoImpl) GetPageList(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectMemberRepoImpl) DeleteByPidMid(projectId, accountId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId})
|
||||
}
|
||||
|
||||
func (p *projectMemberRepoImpl) DeleteMems(projectId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId})
|
||||
}
|
||||
|
||||
func (p *projectMemberRepoImpl) IsExist(projectId, accountId uint64) bool {
|
||||
return model.CountBy(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId}) > 0
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/project/api"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitProjectRouter(router *gin.RouterGroup) {
|
||||
m := &api.Project{
|
||||
ProjectApp: projectapp.GetProjectApp(),
|
||||
AccountApp: sysapp.GetAccountApp()}
|
||||
|
||||
project := router.Group("/projects")
|
||||
{
|
||||
router.GET("/accounts/projects", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectsByLoginAccount)
|
||||
})
|
||||
|
||||
// 获取项目列表
|
||||
project.GET("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjects)
|
||||
})
|
||||
|
||||
saveProjectLog := ctx.NewLogInfo("保存项目信息").WithSave(true)
|
||||
savePP := ctx.NewPermission("project:save")
|
||||
// 保存项目下的环境信息
|
||||
project.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveProjectLog).
|
||||
WithRequiredPermission(savePP).
|
||||
Handle(m.SaveProject)
|
||||
})
|
||||
|
||||
delProjectLog := ctx.NewLogInfo("删除项目信息").WithSave(true)
|
||||
delPP := ctx.NewPermission("project:del")
|
||||
// 删除项目
|
||||
project.DELETE("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delProjectLog).
|
||||
WithRequiredPermission(delPP).
|
||||
Handle(m.DelProject)
|
||||
})
|
||||
|
||||
// 获取项目下的环境信息列表
|
||||
project.GET("/:projectId/envs", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectEnvs)
|
||||
})
|
||||
|
||||
saveProjectEnvLog := ctx.NewLogInfo("新增项目环境信息").WithSave(true)
|
||||
savePeP := ctx.NewPermission("project:env:add")
|
||||
// 保存项目下的环境信息
|
||||
project.POST("/:projectId/envs", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveProjectEnvLog).
|
||||
WithRequiredPermission(savePeP).
|
||||
Handle(m.SaveProjectEnvs)
|
||||
})
|
||||
|
||||
delProjectEnvLog := ctx.NewLogInfo("删除项目环境信息").WithSave(true)
|
||||
project.DELETE("/envs", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delProjectEnvLog).
|
||||
WithRequiredPermission(delPP).
|
||||
Handle(m.DelProjectEnv)
|
||||
})
|
||||
|
||||
// 获取项目下的成员信息列表
|
||||
project.GET("/:projectId/members", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectMembers)
|
||||
})
|
||||
|
||||
// 保存项目成员
|
||||
saveProjectMemLog := ctx.NewLogInfo("新增项目成员").WithSave(true)
|
||||
savePmP := ctx.NewPermission("project:member:add")
|
||||
project.POST("/:projectId/members", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveProjectMemLog).
|
||||
WithRequiredPermission(savePmP).
|
||||
Handle(m.SaveProjectMember)
|
||||
})
|
||||
|
||||
// 删除项目成员
|
||||
delProjectMemLog := ctx.NewLogInfo("删除项目成员").WithSave(true)
|
||||
savePmdP := ctx.NewPermission("project:member:del")
|
||||
project.DELETE("/:projectId/members/:accountId", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delProjectMemLog).
|
||||
WithRequiredPermission(savePmdP).
|
||||
Handle(m.DelProjectMember)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,15 @@ package form
|
||||
|
||||
type Redis struct {
|
||||
Id uint64
|
||||
Name string `json:"name"`
|
||||
Host string `binding:"required" json:"host"`
|
||||
Password string `json:"password"`
|
||||
Mode string `json:"mode"`
|
||||
Db string `json:"db"`
|
||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
ProjectId uint64 `binding:"required" json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
EnvId uint64 `binding:"required" json:"envId"`
|
||||
TagId uint64 `binding:"required" json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/internal/redis/api/vo"
|
||||
"mayfly-go/internal/redis/application"
|
||||
"mayfly-go/internal/redis/domain/entity"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -20,17 +21,22 @@ import (
|
||||
)
|
||||
|
||||
type Redis struct {
|
||||
RedisApp application.Redis
|
||||
ProjectApp projectapp.Project
|
||||
RedisApp application.Redis
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
func (r *Redis) RedisList(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
m := &entity.Redis{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
|
||||
ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
|
||||
condition := new(entity.RedisQuery)
|
||||
condition.TagPathLike = rc.GinCtx.Query("tagPath")
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := r.TagApp.ListTagIdByAccountId(rc.LoginAccount.Id)
|
||||
if len(tagIds) == 0 {
|
||||
rc.ResData = model.EmptyPageResult()
|
||||
return
|
||||
}
|
||||
m.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = r.RedisApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.Redis))
|
||||
condition.TagIds = tagIds
|
||||
rc.ResData = r.RedisApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]vo.Redis))
|
||||
}
|
||||
|
||||
func (r *Redis) Save(rc *ctx.ReqCtx) {
|
||||
@@ -184,7 +190,7 @@ func (r *Redis) ClusterInfo(rc *ctx.ReqCtx) {
|
||||
func (r *Redis) Scan(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
|
||||
form := &form.RedisScanForm{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, form)
|
||||
@@ -263,7 +269,7 @@ func (r *Redis) DeleteKey(rc *ctx.ReqCtx) {
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
|
||||
rc.ReqParam = key
|
||||
ri.GetCmdable().Del(context.Background(), key)
|
||||
@@ -275,7 +281,7 @@ func (r *Redis) checkKey(rc *ctx.ReqCtx) (*application.RedisInstance, string) {
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
|
||||
return ri, key
|
||||
}
|
||||
@@ -293,7 +299,7 @@ func (r *Redis) SetStringValue(rc *ctx.ReqCtx) {
|
||||
ginx.BindJsonAndValid(g, keyValue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
|
||||
str, err := ri.GetCmdable().Set(context.TODO(), keyValue.Key, keyValue.Value, time.Second*time.Duration(keyValue.Timed)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s")
|
||||
@@ -345,7 +351,7 @@ func (r *Redis) SetHashValue(rc *ctx.ReqCtx) {
|
||||
ginx.BindJsonAndValid(g, hashValue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
|
||||
cmd := ri.GetCmdable()
|
||||
key := hashValue.Key
|
||||
@@ -372,7 +378,7 @@ func (r *Redis) SetSetValue(rc *ctx.ReqCtx) {
|
||||
ginx.BindJsonAndValid(g, keyvalue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
cmd := ri.GetCmdable()
|
||||
|
||||
key := keyvalue.Key
|
||||
@@ -411,7 +417,7 @@ func (r *Redis) SaveListValue(rc *ctx.ReqCtx) {
|
||||
ginx.BindJsonAndValid(g, listValue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
cmd := ri.GetCmdable()
|
||||
|
||||
key := listValue.Key
|
||||
@@ -431,7 +437,7 @@ func (r *Redis) SetListValue(rc *ctx.ReqCtx) {
|
||||
ginx.BindJsonAndValid(g, listSetValue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db"))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.TagPath), "%s")
|
||||
|
||||
_, err := ri.GetCmdable().LSet(context.TODO(), listSetValue.Key, listSetValue.Index, listSetValue.Value).Result()
|
||||
biz.ErrIsNilAppendErr(err, "list set失败: %s")
|
||||
|
||||
@@ -3,8 +3,8 @@ package vo
|
||||
import "time"
|
||||
|
||||
type Redis struct {
|
||||
Id *int64 `json:"id"`
|
||||
// Name *string `json:"name"`
|
||||
Id *int64 `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Host *string `json:"host"`
|
||||
Db string `json:"db"`
|
||||
ProjectId *int64 `json:"projectId"`
|
||||
@@ -13,8 +13,8 @@ type Redis struct {
|
||||
EnableSshTunnel *int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark *string `json:"remark"`
|
||||
Env *string `json:"env"`
|
||||
EnvId *int64 `json:"envId"`
|
||||
TagId *uint64 `json:"tagId"`
|
||||
TagPath *string `json:"tagPath"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
|
||||
type Redis interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetPageList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Redis) int64
|
||||
Count(condition *entity.RedisQuery) int64
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Redis
|
||||
@@ -55,11 +55,11 @@ type redisAppImpl struct {
|
||||
}
|
||||
|
||||
// 分页获取机器脚本信息列表
|
||||
func (r *redisAppImpl) GetPageList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
func (r *redisAppImpl) GetPageList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return r.redisRepo.GetRedisList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) Count(condition *entity.Redis) int64 {
|
||||
func (r *redisAppImpl) Count(condition *entity.RedisQuery) int64 {
|
||||
return r.redisRepo.Count(condition)
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func getRedisCacheKey(id uint64, db int) string {
|
||||
}
|
||||
|
||||
func getRedisCient(re *entity.Redis, db int) *RedisInstance {
|
||||
ri := &RedisInstance{Id: getRedisCacheKey(re.Id, db), ProjectId: re.ProjectId, Mode: re.Mode}
|
||||
ri := &RedisInstance{Id: getRedisCacheKey(re.Id, db), TagPath: re.TagPath, Mode: re.Mode}
|
||||
|
||||
redisOptions := &redis.Options{
|
||||
Addr: re.Host,
|
||||
@@ -191,7 +191,7 @@ func getRedisCient(re *entity.Redis, db int) *RedisInstance {
|
||||
}
|
||||
|
||||
func getRedisClusterClient(re *entity.Redis) *RedisInstance {
|
||||
ri := &RedisInstance{Id: getRedisCacheKey(re.Id, 0), ProjectId: re.ProjectId, Mode: re.Mode}
|
||||
ri := &RedisInstance{Id: getRedisCacheKey(re.Id, 0), TagPath: re.TagPath, Mode: re.Mode}
|
||||
|
||||
redisClusterOptions := &redis.ClusterOptions{
|
||||
Addrs: strings.Split(re.Host, ","),
|
||||
@@ -207,7 +207,7 @@ func getRedisClusterClient(re *entity.Redis) *RedisInstance {
|
||||
}
|
||||
|
||||
func getRedisSentinelCient(re *entity.Redis, db int) *RedisInstance {
|
||||
ri := &RedisInstance{Id: getRedisCacheKey(re.Id, db), ProjectId: re.ProjectId, Mode: re.Mode}
|
||||
ri := &RedisInstance{Id: getRedisCacheKey(re.Id, db), TagPath: re.TagPath, Mode: re.Mode}
|
||||
// sentinel模式host为 masterName=host:port,host:port
|
||||
masterNameAndHosts := strings.Split(re.Host, "=")
|
||||
sentinelOptions := &redis.FailoverOptions{
|
||||
@@ -294,7 +294,7 @@ func TestRedisConnection(re *entity.Redis) {
|
||||
// redis实例
|
||||
type RedisInstance struct {
|
||||
Id string
|
||||
ProjectId uint64
|
||||
TagPath string
|
||||
Mode string
|
||||
Cli *redis.Client
|
||||
ClusterCli *redis.ClusterClient
|
||||
|
||||
20
server/internal/redis/domain/entity/query.go
Normal file
20
server/internal/redis/domain/entity/query.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type RedisQuery struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Mode string `json:"mode"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Db string `orm:"column(database)" json:"db"`
|
||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark string
|
||||
TagId uint64
|
||||
|
||||
TagIds []uint64
|
||||
TagPathLike string
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
type Redis struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Mode string `json:"mode"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
@@ -15,10 +16,8 @@ type Redis struct {
|
||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark string
|
||||
ProjectId uint64
|
||||
Project string
|
||||
EnvId uint64
|
||||
Env string
|
||||
TagId uint64
|
||||
TagPath string
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
type Redis interface {
|
||||
// 分页获取机器信息列表
|
||||
GetRedisList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Redis) int64
|
||||
Count(condition *entity.RedisQuery) int64
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Redis
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"mayfly-go/internal/redis/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type redisRepoImpl struct{}
|
||||
@@ -15,27 +17,32 @@ func newRedisRepo() repository.Redis {
|
||||
}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (r *redisRepoImpl) GetRedisList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_redis 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)
|
||||
}
|
||||
func (r *redisRepoImpl) GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_redis d WHERE 1=1 "
|
||||
|
||||
if condition.Host != "" {
|
||||
sql = sql + " AND d.host LIKE '%" + condition.Host + "%'"
|
||||
}
|
||||
if len(condition.TagIds) > 0 {
|
||||
sql = sql + " AND d.tag_id IN " + fmt.Sprintf("(%s)", strings.Join(utils.NumberArr2StrArr(condition.TagIds), ","))
|
||||
}
|
||||
if condition.TagPathLike != "" {
|
||||
sql = sql + " AND d.tag_path LIKE '" + condition.TagPathLike + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (r *redisRepoImpl) Count(condition *entity.Redis) int64 {
|
||||
return model.CountBy(condition)
|
||||
func (r *redisRepoImpl) Count(condition *entity.RedisQuery) int64 {
|
||||
where := make(map[string]interface{})
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
if condition.TagId != 0 {
|
||||
where["tag_id"] = condition.TagId
|
||||
}
|
||||
|
||||
return model.CountByMap(new(entity.Redis), where)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
projectapp "mayfly-go/internal/project/application"
|
||||
"mayfly-go/internal/redis/api"
|
||||
"mayfly-go/internal/redis/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -13,8 +13,8 @@ func InitRedisRouter(router *gin.RouterGroup) {
|
||||
redis := router.Group("redis")
|
||||
{
|
||||
rs := &api.Redis{
|
||||
RedisApp: application.GetRedisApp(),
|
||||
ProjectApp: projectapp.GetProjectApp(),
|
||||
RedisApp: application.GetRedisApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
|
||||
// 获取redis list
|
||||
|
||||
@@ -2,8 +2,9 @@ package form
|
||||
|
||||
type ConfigForm struct {
|
||||
Id int
|
||||
Name string `binding:"required"`
|
||||
Key string `binding:"required"`
|
||||
Name string
|
||||
Key string
|
||||
Params string
|
||||
Value string
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
import (
|
||||
"encoding/json"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigKeyUseLoginCaptcha string = "UseLoginCaptcha" // 是否使用登录验证码
|
||||
@@ -10,6 +13,7 @@ type Config struct {
|
||||
model.Model
|
||||
Name string `json:"name"` // 配置名
|
||||
Key string `json:"key"` // 配置key
|
||||
Params string `json:"params"`
|
||||
Value string `json:"value"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
@@ -27,3 +31,13 @@ func (c *Config) BoolValue(defaultValue bool) bool {
|
||||
}
|
||||
return c.Value == "1"
|
||||
}
|
||||
|
||||
// 值返回json map
|
||||
func (c *Config) GetJsonMap() map[string]string {
|
||||
var res map[string]string
|
||||
if c.Id == 0 || c.Value == "" {
|
||||
return res
|
||||
}
|
||||
_ = json.Unmarshal([]byte(c.Value), &res)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func newConfigRepo() repository.Config {
|
||||
}
|
||||
|
||||
func (m *configRepoImpl) GetPageList(condition *entity.Config, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity)
|
||||
}
|
||||
|
||||
func (m *configRepoImpl) Insert(config *entity.Config) {
|
||||
|
||||
@@ -14,7 +14,7 @@ func newMsgRepo() repository.Msg {
|
||||
}
|
||||
|
||||
func (m *msgRepoImpl) GetPageList(condition *entity.Msg, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity)
|
||||
}
|
||||
|
||||
func (m *msgRepoImpl) Insert(account *entity.Msg) {
|
||||
|
||||
@@ -14,7 +14,7 @@ func newRoleRepo() repository.Role {
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) GetPageList(condition *entity.Role, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) Delete(id uint64) {
|
||||
|
||||
@@ -13,7 +13,7 @@ func newSyslogRepo() repository.Syslog {
|
||||
}
|
||||
|
||||
func (m *syslogRepoImpl) GetPageList(condition *entity.Syslog, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *syslogRepoImpl) Insert(syslog *entity.Syslog) {
|
||||
|
||||
6
server/internal/tag/api/form/tag_tree.go
Normal file
6
server/internal/tag/api/form/tag_tree.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package form
|
||||
|
||||
type TagTreeTeam struct {
|
||||
TeamId uint64 `json:"teamId"`
|
||||
TagIds []uint64 `json:"tagIds"`
|
||||
}
|
||||
39
server/internal/tag/api/tag_tree.go
Normal file
39
server/internal/tag/api/tag_tree.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/tag/api/vo"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
)
|
||||
|
||||
type TagTree struct {
|
||||
TagTreeApp application.TagTree
|
||||
}
|
||||
|
||||
func (p *TagTree) GetAccountTags(rc *ctx.ReqCtx) {
|
||||
rc.ResData = p.TagTreeApp.ListTagByAccountId(rc.LoginAccount.Id)
|
||||
}
|
||||
|
||||
func (p *TagTree) GetTagTree(rc *ctx.ReqCtx) {
|
||||
var tagTrees vo.TagTreeVOS
|
||||
p.TagTreeApp.ListByQuery(new(entity.TagTreeQuery), &tagTrees)
|
||||
rc.ResData = tagTrees.ToTrees(0)
|
||||
}
|
||||
|
||||
func (p *TagTree) SaveTagTree(rc *ctx.ReqCtx) {
|
||||
projectTree := &entity.TagTree{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, projectTree)
|
||||
|
||||
loginAccount := rc.LoginAccount
|
||||
projectTree.SetBaseInfo(loginAccount)
|
||||
p.TagTreeApp.Save(projectTree)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, codePath: %s", projectTree.Id, projectTree.Name, projectTree.CodePath)
|
||||
}
|
||||
|
||||
func (p *TagTree) DelTagTree(rc *ctx.ReqCtx) {
|
||||
p.TagTreeApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
}
|
||||
133
server/internal/tag/api/team.go
Normal file
133
server/internal/tag/api/team.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys_applicaiton "mayfly-go/internal/sys/application"
|
||||
sys_entity "mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/internal/tag/api/form"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/utils"
|
||||
)
|
||||
|
||||
type Team struct {
|
||||
TeamApp application.Team
|
||||
TagApp application.TagTree
|
||||
AccountApp sys_applicaiton.Account
|
||||
}
|
||||
|
||||
func (p *Team) GetTeams(rc *ctx.ReqCtx) {
|
||||
teams := &[]entity.Team{}
|
||||
rc.ResData = p.TeamApp.GetPageList(&entity.Team{}, ginx.GetPageParam(rc.GinCtx), teams)
|
||||
}
|
||||
|
||||
func (p *Team) SaveTeam(rc *ctx.ReqCtx) {
|
||||
team := &entity.Team{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, team)
|
||||
|
||||
isAdd := team.Id == 0
|
||||
|
||||
loginAccount := rc.LoginAccount
|
||||
team.SetBaseInfo(loginAccount)
|
||||
p.TeamApp.Save(team)
|
||||
|
||||
// 如果是新增团队则默认将自己加入该团队
|
||||
if isAdd {
|
||||
teamMem := &entity.TeamMember{}
|
||||
teamMem.AccountId = loginAccount.Id
|
||||
teamMem.Username = loginAccount.Username
|
||||
teamMem.TeamId = team.Id
|
||||
|
||||
teamMem.SetBaseInfo(rc.LoginAccount)
|
||||
p.TeamApp.SaveMember(teamMem)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Team) DelTeam(rc *ctx.ReqCtx) {
|
||||
p.TeamApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
}
|
||||
|
||||
// 获取团队的成员信息
|
||||
func (p *Team) GetTeamMembers(rc *ctx.ReqCtx) {
|
||||
teamMems := &[]entity.TeamMember{}
|
||||
rc.ResData = p.TeamApp.GetMemberPage(&entity.TeamMember{TeamId: uint64(ginx.PathParamInt(rc.GinCtx, "id"))},
|
||||
ginx.GetPageParam(rc.GinCtx), teamMems)
|
||||
}
|
||||
|
||||
// 保存团队信息
|
||||
func (p *Team) SaveTeamMember(rc *ctx.ReqCtx) {
|
||||
projectMem := &entity.TeamMember{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, projectMem)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("projectId: %d, username: %s", projectMem.TeamId, projectMem.Username)
|
||||
|
||||
// 校验账号,并赋值username
|
||||
account := &sys_entity.Account{}
|
||||
account.Id = projectMem.AccountId
|
||||
biz.ErrIsNil(p.AccountApp.GetAccount(account, "Id", "Username"), "账号不存在")
|
||||
projectMem.Username = account.Username
|
||||
|
||||
projectMem.SetBaseInfo(rc.LoginAccount)
|
||||
p.TeamApp.SaveMember(projectMem)
|
||||
}
|
||||
|
||||
// 删除团队成员
|
||||
func (p *Team) DelTeamMember(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
tid := ginx.PathParamInt(g, "id")
|
||||
aid := ginx.PathParamInt(g, "accountId")
|
||||
rc.ReqParam = fmt.Sprintf("teamId: %d, accountId: %d", tid, aid)
|
||||
|
||||
p.TeamApp.DeleteMember(uint64(tid), uint64(aid))
|
||||
}
|
||||
|
||||
// 获取团队关联的标签id
|
||||
func (p *Team) GetTagIds(rc *ctx.ReqCtx) {
|
||||
rc.ResData = p.TeamApp.ListTagIds(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
}
|
||||
|
||||
// 保存团队关联标签信息
|
||||
func (p *Team) SaveTags(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
|
||||
var form form.TagTreeTeam
|
||||
ginx.BindJsonAndValid(g, &form)
|
||||
|
||||
teamId := form.TeamId
|
||||
|
||||
// 将[]uint64转为[]interface{}
|
||||
oIds := p.TeamApp.ListTagIds(teamId)
|
||||
var oldIds []interface{}
|
||||
for _, v := range oIds {
|
||||
oldIds = append(oldIds, v)
|
||||
}
|
||||
|
||||
var newIds []interface{}
|
||||
for _, v := range form.TagIds {
|
||||
newIds = append(newIds, v)
|
||||
}
|
||||
|
||||
// 比较新旧两合集
|
||||
addIds, delIds, _ := utils.ArrayCompare(newIds, oldIds, func(i1, i2 interface{}) bool {
|
||||
return i1.(uint64) == i2.(uint64)
|
||||
})
|
||||
|
||||
loginAccount := rc.LoginAccount
|
||||
for _, v := range addIds {
|
||||
tagId := v.(uint64)
|
||||
tag := p.TagApp.GetById(tagId)
|
||||
biz.NotNil(tag, "存在非法标签id")
|
||||
|
||||
ptt := &entity.TagTreeTeam{TeamId: teamId, TagId: tagId, TagPath: tag.CodePath}
|
||||
ptt.SetBaseInfo(loginAccount)
|
||||
p.TeamApp.SaveTag(ptt)
|
||||
}
|
||||
for _, v := range delIds {
|
||||
p.TeamApp.DeleteTag(teamId, v.(uint64))
|
||||
}
|
||||
|
||||
rc.ReqParam = form
|
||||
}
|
||||
50
server/internal/tag/api/vo/tag_tree.go
Normal file
50
server/internal/tag/api/vo/tag_tree.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package vo
|
||||
|
||||
import "time"
|
||||
|
||||
type TagTreeVO struct {
|
||||
Id int `json:"id"`
|
||||
Pid int `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
CodePath string `json:"codePath"`
|
||||
Remark string `json:"remark"`
|
||||
Creator string `json:"creator"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
Modifier string `json:"modifier"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
type TagTreeVOS []TagTreeVO
|
||||
|
||||
type TagTreeItem struct {
|
||||
TagTreeVO
|
||||
Children []TagTreeItem `json:"children"`
|
||||
}
|
||||
|
||||
func (m *TagTreeVOS) ToTrees(pid int) []TagTreeItem {
|
||||
var resourceTree []TagTreeItem
|
||||
|
||||
list := m.findChildren(pid)
|
||||
if len(list) == 0 {
|
||||
return resourceTree
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
Children := m.ToTrees(int(v.Id))
|
||||
resourceTree = append(resourceTree, TagTreeItem{v, Children})
|
||||
}
|
||||
|
||||
return resourceTree
|
||||
}
|
||||
|
||||
func (m *TagTreeVOS) findChildren(pid int) []TagTreeVO {
|
||||
child := []TagTreeVO{}
|
||||
|
||||
for _, v := range *m {
|
||||
if v.Pid == pid {
|
||||
child = append(child, v)
|
||||
}
|
||||
}
|
||||
return child
|
||||
}
|
||||
34
server/internal/tag/application/application.go
Normal file
34
server/internal/tag/application/application.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
"mayfly-go/internal/tag/infrastructure/persistence"
|
||||
)
|
||||
|
||||
var (
|
||||
tagTreeApp TagTree = newTagTreeApp(
|
||||
persistence.GetTagTreeRepo(),
|
||||
persistence.GetTagTreeTeamRepo(),
|
||||
machineapp.GetMachineApp(),
|
||||
redisapp.GetRedisApp(),
|
||||
dbapp.GetDbApp(),
|
||||
mongoapp.GetMongoApp(),
|
||||
)
|
||||
|
||||
teamApp Team = newTeamApp(
|
||||
persistence.GetTeamRepo(),
|
||||
persistence.GetTeamMemberRepo(),
|
||||
persistence.GetTagTreeTeamRepo(),
|
||||
)
|
||||
)
|
||||
|
||||
func GetTagTreeApp() TagTree {
|
||||
return tagTreeApp
|
||||
}
|
||||
|
||||
func GetTeamApp() Team {
|
||||
return teamApp
|
||||
}
|
||||
150
server/internal/tag/application/tag_tree.go
Normal file
150
server/internal/tag/application/tag_tree.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
dbentity "mayfly-go/internal/db/domain/entity"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
machineentity "mayfly-go/internal/machine/domain/entity"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
mongoentity "mayfly-go/internal/mongo/domain/entity"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
redisentity "mayfly-go/internal/redis/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TagTree interface {
|
||||
ListByQuery(condition *entity.TagTreeQuery, toEntity interface{})
|
||||
|
||||
GetById(id uint64) *entity.TagTree
|
||||
|
||||
Save(project *entity.TagTree)
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
// 获取账号id拥有的可访问的标签id
|
||||
ListTagIdByAccountId(accountId uint64) []uint64
|
||||
|
||||
// 获取以指定tagPath数组开头的所有标签id
|
||||
ListTagIdByPath(tagPath ...string) []uint64
|
||||
|
||||
// 根据账号id获取其可访问标签信息
|
||||
ListTagByAccountId(accountId uint64) []string
|
||||
|
||||
// 账号是否有权限访问该标签关联的资源信息
|
||||
CanAccess(accountId uint64, tagPath string) error
|
||||
}
|
||||
|
||||
func newTagTreeApp(tagTreeRepo repository.TagTree,
|
||||
tagTreeTeamRepo repository.TagTreeTeam,
|
||||
machineApp machineapp.Machine,
|
||||
redisApp redisapp.Redis,
|
||||
dbApp dbapp.Db,
|
||||
mongoApp mongoapp.Mongo) TagTree {
|
||||
return &tagTreeAppImpl{
|
||||
tagTreeRepo: tagTreeRepo,
|
||||
tagTreeTeamRepo: tagTreeTeamRepo,
|
||||
machineApp: machineApp,
|
||||
redisApp: redisApp,
|
||||
dbApp: dbApp,
|
||||
mongoApp: mongoApp,
|
||||
}
|
||||
}
|
||||
|
||||
type tagTreeAppImpl struct {
|
||||
tagTreeRepo repository.TagTree
|
||||
tagTreeTeamRepo repository.TagTreeTeam
|
||||
machineApp machineapp.Machine
|
||||
redisApp redisapp.Redis
|
||||
mongoApp mongoapp.Mongo
|
||||
dbApp dbapp.Db
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) Save(tag *entity.TagTree) {
|
||||
// 新建项目树节点信息
|
||||
if tag.Id == 0 {
|
||||
biz.IsTrue(!strings.Contains(tag.Code, entity.CodePathSeparator), "标识符不能包含'/'")
|
||||
if tag.Pid != 0 {
|
||||
parentTag := p.tagTreeRepo.SelectById(tag.Pid)
|
||||
biz.NotNil(parentTag, "父节点不存在")
|
||||
tag.CodePath = parentTag.CodePath + entity.CodePathSeparator + tag.Code
|
||||
} else {
|
||||
tag.CodePath = tag.Code
|
||||
}
|
||||
// 判断该路径是否存在
|
||||
var hasLikeTags []entity.TagTree
|
||||
p.tagTreeRepo.SelectByCondition(&entity.TagTreeQuery{CodePathLike: tag.CodePath}, &hasLikeTags)
|
||||
biz.IsTrue(len(hasLikeTags) == 0, "已存在该标签路径开头的标签, 请修改该标识code")
|
||||
|
||||
// 校验同级标签,是否有以该code为开头的标识符
|
||||
p.tagTreeRepo.SelectByCondition(&entity.TagTreeQuery{Pid: tag.Pid}, &hasLikeTags)
|
||||
for _, v := range hasLikeTags {
|
||||
if strings.HasPrefix(tag.Code, v.Code) {
|
||||
panic(biz.NewBizErr(fmt.Sprintf("同级标签下的[%s]与[%s]存在相似开头字符, 请修改该标识code", v.Code, tag.Code)))
|
||||
}
|
||||
}
|
||||
p.tagTreeRepo.Insert(tag)
|
||||
return
|
||||
}
|
||||
|
||||
// 防止误传导致被更新
|
||||
tag.Code = ""
|
||||
tag.CodePath = ""
|
||||
p.tagTreeRepo.UpdateById(tag)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity interface{}) {
|
||||
p.tagTreeRepo.SelectByCondition(condition, toEntity)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetById(tagId uint64) *entity.TagTree {
|
||||
return p.tagTreeRepo.SelectById(tagId)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagIdByAccountId(accountId uint64) []uint64 {
|
||||
// 获取该账号可操作的标签路径
|
||||
return p.ListTagIdByPath(p.ListTagByAccountId(accountId)...)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagIdByPath(tagPaths ...string) []uint64 {
|
||||
tagIds := make([]uint64, 0)
|
||||
if len(tagPaths) == 0 {
|
||||
return tagIds
|
||||
}
|
||||
|
||||
var tags []entity.TagTree
|
||||
p.tagTreeRepo.SelectByCondition(&entity.TagTreeQuery{CodePathLikes: tagPaths}, &tags)
|
||||
for _, v := range tags {
|
||||
tagIds = append(tagIds, v.Id)
|
||||
}
|
||||
return tagIds
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
|
||||
return p.tagTreeTeamRepo.SelectTagPathsByAccountId(accountId)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath string) error {
|
||||
tagPaths := p.ListTagByAccountId(accountId)
|
||||
// 判断该资源标签是否为该账号拥有的标签或其子标签
|
||||
for _, v := range tagPaths {
|
||||
if strings.HasPrefix(tagPath, v) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return biz.NewBizErr("您无权操作该资源")
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) Delete(id uint64) {
|
||||
biz.IsTrue(p.machineApp.Count(&machineentity.MachineQuery{TagId: id}) == 0, "请先删除该项目关联的机器信息")
|
||||
biz.IsTrue(p.redisApp.Count(&redisentity.RedisQuery{TagId: id}) == 0, "请先删除该项目关联的redis信息")
|
||||
biz.IsTrue(p.dbApp.Count(&dbentity.DbQuery{TagId: id}) == 0, "请先删除该项目关联的数据库信息")
|
||||
biz.IsTrue(p.mongoApp.Count(&mongoentity.MongoQuery{TagId: id}) == 0, "请先删除该项目关联的Mongo信息")
|
||||
p.tagTreeRepo.Delete(id)
|
||||
// 删除该标签关联的团队信息
|
||||
p.tagTreeTeamRepo.DeleteBy(&entity.TagTreeTeam{TagId: id})
|
||||
}
|
||||
111
server/internal/tag/application/team.go
Normal file
111
server/internal/tag/application/team.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Team interface {
|
||||
// 分页获取项目团队信息列表
|
||||
GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Save(projectTeam *entity.Team)
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
//--------------- 团队成员相关接口 ---------------
|
||||
|
||||
GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
SaveMember(projectTeamMember *entity.TeamMember)
|
||||
|
||||
DeleteMember(teamId, accountId uint64)
|
||||
|
||||
// 账号是否有权限访问该项目关联的资源信息
|
||||
// CanAccess(accountId, projectId uint64) error
|
||||
|
||||
//--------------- 关联项目相关接口 ---------------
|
||||
|
||||
ListTagIds(teamId uint64) []uint64
|
||||
|
||||
SaveTag(tagTeam *entity.TagTreeTeam)
|
||||
|
||||
DeleteTag(teamId, projectId uint64)
|
||||
}
|
||||
|
||||
func newTeamApp(projectTeamRepo repository.Team,
|
||||
projectTeamMemberRepo repository.TeamMember,
|
||||
tagTreeTeamRepo repository.TagTreeTeam,
|
||||
) Team {
|
||||
return &projectTeamAppImpl{
|
||||
projectTeamRepo: projectTeamRepo,
|
||||
projectTeamMemberRepo: projectTeamMemberRepo,
|
||||
tagTreeTeamRepo: tagTreeTeamRepo,
|
||||
}
|
||||
}
|
||||
|
||||
type projectTeamAppImpl struct {
|
||||
projectTeamRepo repository.Team
|
||||
projectTeamMemberRepo repository.TeamMember
|
||||
tagTreeTeamRepo repository.TagTreeTeam
|
||||
}
|
||||
|
||||
func (p *projectTeamAppImpl) GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return p.projectTeamRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectTeamAppImpl) Save(projectTeam *entity.Team) {
|
||||
if projectTeam.Id == 0 {
|
||||
p.projectTeamRepo.Insert(projectTeam)
|
||||
} else {
|
||||
p.projectTeamRepo.UpdateById(projectTeam)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *projectTeamAppImpl) Delete(id uint64) {
|
||||
p.projectTeamRepo.Delete(id)
|
||||
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: id})
|
||||
}
|
||||
|
||||
// --------------- 团队成员相关接口 ---------------
|
||||
|
||||
func (p *projectTeamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return p.projectTeamMemberRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 保存团队成员信息
|
||||
func (p *projectTeamAppImpl) SaveMember(projectTeamMember *entity.TeamMember) {
|
||||
projectTeamMember.Id = 0
|
||||
biz.IsTrue(!p.projectTeamMemberRepo.IsExist(projectTeamMember.TeamId, projectTeamMember.AccountId), "该成员已存在")
|
||||
p.projectTeamMemberRepo.Save(projectTeamMember)
|
||||
}
|
||||
|
||||
// 删除团队成员信息
|
||||
func (p *projectTeamAppImpl) DeleteMember(teamId, accountId uint64) {
|
||||
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: teamId, AccountId: accountId})
|
||||
}
|
||||
|
||||
//--------------- 关联项目相关接口 ---------------
|
||||
|
||||
func (p *projectTeamAppImpl) ListTagIds(teamId uint64) []uint64 {
|
||||
projects := &[]entity.TagTreeTeam{}
|
||||
p.tagTreeTeamRepo.ListProject(&entity.TagTreeTeam{TeamId: teamId}, projects)
|
||||
ids := make([]uint64, 0)
|
||||
for _, v := range *projects {
|
||||
ids = append(ids, v.TagId)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// 保存关联项目信息
|
||||
func (p *projectTeamAppImpl) SaveTag(projectTreeTeam *entity.TagTreeTeam) {
|
||||
projectTreeTeam.Id = 0
|
||||
p.tagTreeTeamRepo.Save(projectTreeTeam)
|
||||
}
|
||||
|
||||
// 删除关联项目信息
|
||||
func (p *projectTeamAppImpl) DeleteTag(teamId, tagId uint64) {
|
||||
p.tagTreeTeamRepo.DeleteBy(&entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user