mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
feat: 新增机器授权凭证管理与其他优化
This commit is contained in:
@@ -9,17 +9,17 @@
|
|||||||
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
|
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.10",
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
"asciinema-player": "^3.1.0",
|
"asciinema-player": "^3.1.0",
|
||||||
"axios": "^1.3.2",
|
"axios": "^1.3.4",
|
||||||
"countup.js": "^2.0.7",
|
"countup.js": "^2.0.7",
|
||||||
"cropperjs": "^1.5.11",
|
"cropperjs": "^1.5.11",
|
||||||
"echarts": "^5.4.0",
|
"echarts": "^5.4.0",
|
||||||
"element-plus": "^2.2.32",
|
"element-plus": "^2.2.33",
|
||||||
"jsencrypt": "^3.2.1",
|
"jsencrypt": "^3.3.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"monaco-editor": "^0.35.0",
|
"monaco-editor": "^0.36.1",
|
||||||
"monaco-sql-languages": "^0.11.0",
|
"monaco-sql-languages": "^0.11.0",
|
||||||
"monaco-themes": "^0.4.2",
|
"monaco-themes": "^0.4.2",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
@@ -43,13 +43,13 @@
|
|||||||
"@vitejs/plugin-vue": "^2.3.3",
|
"@vitejs/plugin-vue": "^2.3.3",
|
||||||
"@vue/compiler-sfc": "^3.0.11",
|
"@vue/compiler-sfc": "^3.0.11",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"eslint": "^8.5.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-plugin-vue": "^8.2.0",
|
"eslint-plugin-vue": "^8.2.0",
|
||||||
"prettier": "^2.3.0",
|
"prettier": "^2.3.0",
|
||||||
"sass": "^1.58.0",
|
"sass": "^1.58.0",
|
||||||
"sass-loader": "^13.2.0",
|
"sass-loader": "^13.2.0",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.1.1",
|
"vite": "^4.1.4",
|
||||||
"vue-eslint-parser": "^8.0.1"
|
"vue-eslint-parser": "^8.0.1"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|||||||
@@ -19,24 +19,6 @@ class Api {
|
|||||||
this.method = method;
|
this.method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置rl
|
|
||||||
* @param {String} uri 请求url
|
|
||||||
*/
|
|
||||||
setUrl(url: string) {
|
|
||||||
this.url = url;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* url的请求方法
|
|
||||||
* @param {String} method 请求方法
|
|
||||||
*/
|
|
||||||
setMethod(method: string) {
|
|
||||||
this.method = method;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取权限的完整url
|
* 获取权限的完整url
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const config = {
|
|||||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||||
|
|
||||||
// 系统版本
|
// 系统版本
|
||||||
version: 'v1.4.0'
|
version: 'v1.4.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
@@ -6,14 +6,14 @@ export const imports = {
|
|||||||
"Home": () => import('@/views/home/Home.vue'),
|
"Home": () => import('@/views/home/Home.vue'),
|
||||||
'Personal': () => import('@/views/personal/index.vue'),
|
'Personal': () => import('@/views/personal/index.vue'),
|
||||||
// machine
|
// machine
|
||||||
"MachineList": () => import('@/views/ops/machine'),
|
"MachineList": () => import('@/views/ops/machine/MachineList.vue'),
|
||||||
|
"AuthCertList": () => import('@/views/ops/machine/authcert/AuthCertList.vue'),
|
||||||
// sys
|
// sys
|
||||||
"ResourceList": () => import('@/views/system/resource'),
|
"ResourceList": () => import('@/views/system/resource'),
|
||||||
"RoleList": () => import('@/views/system/role'),
|
"RoleList": () => import('@/views/system/role'),
|
||||||
"AccountList": () => import('@/views/system/account'),
|
"AccountList": () => import('@/views/system/account'),
|
||||||
"SyslogList": () => import('@/views/system/syslog/SyslogList.vue'),
|
"SyslogList": () => import('@/views/system/syslog/SyslogList.vue'),
|
||||||
"ConfigList": () => import('@/views/system/config/ConfigList.vue'),
|
"ConfigList": () => import('@/views/system/config/ConfigList.vue'),
|
||||||
|
|
||||||
// tag
|
// tag
|
||||||
"TagTreeList": () => import('@/views/ops/tag/TagTreeList.vue'),
|
"TagTreeList": () => import('@/views/ops/tag/TagTreeList.vue'),
|
||||||
"TeamList": () => import('@/views/ops/tag/TeamList.vue'),
|
"TeamList": () => import('@/views/ops/tag/TeamList.vue'),
|
||||||
|
|||||||
61
mayfly_go_web/src/views/ops/component/SshTunnelSelect.vue
Normal file
61
mayfly_go_web/src/views/ops/component/SshTunnelSelect.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div style="width: 100%">
|
||||||
|
<el-select @focus="getSshTunnelMachines" @change="change" style="width: 100%" v-model="sshTunnelMachineId"
|
||||||
|
@clear="clear" placeholder="请选择SSH隧道机器" clearable>
|
||||||
|
<el-option v-for="item in sshTunnelMachineList" :key="item.id" :label="`${item.ip}:${item.port} [${item.name}]`"
|
||||||
|
:value="item.id">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { toRefs, reactive, onMounted } from 'vue';
|
||||||
|
import { machineApi } from '../machine/api';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//定义事件
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
// 单选则为id,多选为id数组
|
||||||
|
sshTunnelMachineId: null as any,
|
||||||
|
sshTunnelMachineList: [] as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
sshTunnelMachineId,
|
||||||
|
sshTunnelMachineList,
|
||||||
|
} = toRefs(state)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!props.modelValue || props.modelValue <= 0) {
|
||||||
|
state.sshTunnelMachineId = null;
|
||||||
|
} else {
|
||||||
|
state.sshTunnelMachineId = props.modelValue;
|
||||||
|
}
|
||||||
|
await getSshTunnelMachines();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getSshTunnelMachines = async () => {
|
||||||
|
if (state.sshTunnelMachineList.length == 0) {
|
||||||
|
const res = await machineApi.list.request({ pageNum: 1, pageSize: 100 });
|
||||||
|
state.sshTunnelMachineList = res.list;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
state.sshTunnelMachineId = null;
|
||||||
|
change();
|
||||||
|
}
|
||||||
|
|
||||||
|
const change = () => {
|
||||||
|
emit('update:modelValue', state.sshTunnelMachineId);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss"></style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-tree-select @check="changeTag" style="width: 100%" v-model="selectTags" :data="tags"
|
<el-tree-select @check="changeTag" style="width: 100%" v-model="selectTags" :data="tags" placeholder="请选择关联标签"
|
||||||
:render-after-expand="true" :default-expanded-keys="[selectTags]" show-checkbox check-strictly node-key="id"
|
:render-after-expand="true" :default-expanded-keys="[selectTags]" show-checkbox check-strictly node-key="id"
|
||||||
:props="{
|
:props="{
|
||||||
value: 'id',
|
value: 'id',
|
||||||
|
|||||||
@@ -3,88 +3,85 @@
|
|||||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false"
|
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false"
|
||||||
:destroy-on-close="true" width="38%">
|
:destroy-on-close="true" width="38%">
|
||||||
<el-form :model="form" ref="dbForm" :rules="rules" label-width="95px">
|
<el-form :model="form" ref="dbForm" :rules="rules" label-width="95px">
|
||||||
<el-form-item prop="tagId" label="标签:" required>
|
<el-tabs v-model="tabActiveName">
|
||||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
<el-tab-pane label="基础信息" name="basic">
|
||||||
</el-form-item>
|
<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-form-item prop="name" label="别名:" required>
|
||||||
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
|
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="type" label="类型:" required>
|
<el-form-item prop="type" label="类型:" required>
|
||||||
<el-select style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
|
<el-select style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
|
||||||
<el-option key="item.id" label="mysql" value="mysql"> </el-option>
|
<el-option key="item.id" label="mysql" value="mysql"> </el-option>
|
||||||
<el-option key="item.id" label="postgres" value="postgres"> </el-option>
|
<el-option key="item.id" label="postgres" value="postgres"> </el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="host" label="host:" required>
|
<el-form-item prop="host" label="host:" required>
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<el-input :disabled="form.id !== undefined" v-model.trim="form.host" placeholder="请输入主机ip"
|
<el-input :disabled="form.id !== undefined" v-model.trim="form.host" placeholder="请输入主机ip"
|
||||||
auto-complete="off"></el-input>
|
auto-complete="off"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col style="text-align: center" :span="1">:</el-col>
|
<el-col style="text-align: center" :span="1">:</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-input type="number" v-model.number="form.port" placeholder="请输入端口"></el-input>
|
<el-input type="number" v-model.number="form.port" placeholder="请输入端口"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="username" label="用户名:" required>
|
<el-form-item prop="username" label="用户名:" required>
|
||||||
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
|
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="password" label="密码:">
|
<el-form-item prop="password" label="密码:">
|
||||||
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码,修改操作可不填"
|
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码,修改操作可不填"
|
||||||
autocomplete="new-password">
|
autocomplete="new-password">
|
||||||
<template v-if="form.id && form.id != 0" #suffix>
|
<template v-if="form.id && form.id != 0" #suffix>
|
||||||
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
|
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
|
||||||
:content="pwd">
|
:content="pwd">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-link @click="getDbPwd" :underline="false" type="primary" class="mr5">原密码
|
<el-link @click="getDbPwd" :underline="false" type="primary" class="mr5">原密码
|
||||||
</el-link>
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-input>
|
||||||
</template>
|
</el-form-item>
|
||||||
</el-input>
|
<el-form-item prop="database" label="数据库名:" required>
|
||||||
</el-form-item>
|
<el-col :span="19">
|
||||||
<el-form-item prop="params" label="连接参数:">
|
<el-select @change="changeDatabase" v-model="databaseList" multiple clearable collapse-tags
|
||||||
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
|
collapse-tags-tooltip filterable allow-create placeholder="请确保数据库实例信息填写完整后获取库名"
|
||||||
<template #suffix>
|
style="width: 100%">
|
||||||
<el-link target="_blank" href="https://github.com/go-sql-driver/mysql#parameters"
|
<el-option v-for="db in allDatabases" :key="db" :label="db" :value="db" />
|
||||||
:underline="false" type="primary" class="mr5">参数参考</el-link>
|
</el-select>
|
||||||
</template>
|
</el-col>
|
||||||
</el-input>
|
<el-col style="text-align: center" :span="1">
|
||||||
</el-form-item>
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
<el-form-item prop="database" label="数据库名:" required>
|
</el-col>
|
||||||
<el-col :span="19">
|
<el-col :span="4">
|
||||||
<el-select @change="changeDatabase" v-model="databaseList" multiple clearable collapse-tags
|
<el-link @click="getAllDatabase" :underline="false" type="success">获取库名</el-link>
|
||||||
collapse-tags-tooltip filterable allow-create placeholder="请确保数据库实例信息填写完整后获取库名"
|
</el-col>
|
||||||
style="width: 100%">
|
</el-form-item>
|
||||||
<el-option v-for="db in allDatabases" :key="db" :label="db" :value="db" />
|
|
||||||
</el-select>
|
|
||||||
</el-col>
|
|
||||||
<el-col style="text-align: center" :span="1">
|
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="4">
|
|
||||||
<el-link @click="getAllDatabase" :underline="false" type="success">获取库名</el-link>
|
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item prop="remark" label="备注:">
|
<el-form-item prop="remark" label="备注:">
|
||||||
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
|
<el-tab-pane label="其他配置" name="other">
|
||||||
<el-col :span="3">
|
<el-form-item prop="params" label="连接参数:">
|
||||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
|
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
|
||||||
:false-label="-1"></el-checkbox>
|
<template #suffix>
|
||||||
</el-col>
|
<el-link target="_blank" href="https://github.com/go-sql-driver/mysql#parameters"
|
||||||
<el-col :span="5" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
|
:underline="false" type="primary" class="mr5">参数参考</el-link>
|
||||||
<el-col :span="16" v-if="form.enableSshTunnel == 1">
|
</template>
|
||||||
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
|
</el-input>
|
||||||
<el-option v-for="item in sshTunnelMachineList" :key="item.id"
|
</el-form-item>
|
||||||
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
<el-form-item prop="sshTunnelMachineId" label="SSH隧道:">
|
||||||
</el-col>
|
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -100,11 +97,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, watch, ref } from 'vue';
|
import { toRefs, reactive, watch, ref } from 'vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import { machineApi } from '../machine/api.ts';
|
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { notBlank } from '@/common/assert';
|
import { notBlank } from '@/common/assert';
|
||||||
import { RsaEncrypt } from '@/common/rsa';
|
import { RsaEncrypt } from '@/common/rsa';
|
||||||
import TagSelect from '../component/TagSelect.vue';
|
import TagSelect from '../component/TagSelect.vue';
|
||||||
|
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -170,9 +167,9 @@ const dbForm: any = ref(null);
|
|||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
|
tabActiveName: 'basic',
|
||||||
allDatabases: [] as any,
|
allDatabases: [] as any,
|
||||||
databaseList: [] as any,
|
databaseList: [] as any,
|
||||||
sshTunnelMachineList: [] as any,
|
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
tagId: null as any,
|
tagId: null as any,
|
||||||
@@ -185,13 +182,8 @@ const state = reactive({
|
|||||||
password: null,
|
password: null,
|
||||||
params: null,
|
params: null,
|
||||||
database: '',
|
database: '',
|
||||||
project: null,
|
|
||||||
projectId: null,
|
|
||||||
envId: null,
|
|
||||||
env: null,
|
|
||||||
remark: '',
|
remark: '',
|
||||||
enableSshTunnel: null,
|
sshTunnelMachineId: null as any,
|
||||||
sshTunnelMachineId: null,
|
|
||||||
},
|
},
|
||||||
// 原密码
|
// 原密码
|
||||||
pwd: '',
|
pwd: '',
|
||||||
@@ -200,9 +192,9 @@ const state = reactive({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
dialogVisible,
|
dialogVisible,
|
||||||
|
tabActiveName,
|
||||||
allDatabases,
|
allDatabases,
|
||||||
databaseList,
|
databaseList,
|
||||||
sshTunnelMachineList,
|
|
||||||
form,
|
form,
|
||||||
pwd,
|
pwd,
|
||||||
btnLoading,
|
btnLoading,
|
||||||
@@ -213,15 +205,15 @@ watch(props, (newValue: any) => {
|
|||||||
if (!state.dialogVisible) {
|
if (!state.dialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.tabActiveName = 'basic';
|
||||||
if (newValue.db) {
|
if (newValue.db) {
|
||||||
state.form = { ...newValue.db };
|
state.form = { ...newValue.db };
|
||||||
// 将数据库名使用空格切割,获取所有数据库列表
|
// 将数据库名使用空格切割,获取所有数据库列表
|
||||||
state.databaseList = newValue.db.database.split(' ');
|
state.databaseList = newValue.db.database.split(' ');
|
||||||
} else {
|
} else {
|
||||||
state.form = { port: 3306, enableSshTunnel: -1 } as any;
|
state.form = { port: 3306 } as any;
|
||||||
state.databaseList = [];
|
state.databaseList = [];
|
||||||
}
|
}
|
||||||
getSshTunnelMachines();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,13 +223,6 @@ const changeDatabase = () => {
|
|||||||
state.form.database = state.databaseList.length == 0 ? '' : state.databaseList.join(' ');
|
state.form.database = state.databaseList.length == 0 ? '' : state.databaseList.join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSshTunnelMachines = async () => {
|
|
||||||
if (state.form.enableSshTunnel == 1 && state.sshTunnelMachineList.length == 0) {
|
|
||||||
const res = await machineApi.list.request({ pageNum: 1, pageSize: 100 });
|
|
||||||
state.sshTunnelMachineList = res.list;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAllDatabase = async () => {
|
const getAllDatabase = async () => {
|
||||||
const reqForm = { ...state.form };
|
const reqForm = { ...state.form };
|
||||||
reqForm.password = await RsaEncrypt(reqForm.password);
|
reqForm.password = await RsaEncrypt(reqForm.password);
|
||||||
@@ -257,6 +242,9 @@ const btnOk = async () => {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
const reqForm = { ...state.form };
|
const reqForm = { ...state.form };
|
||||||
reqForm.password = await RsaEncrypt(reqForm.password);
|
reqForm.password = await RsaEncrypt(reqForm.password);
|
||||||
|
if (!state.form.sshTunnelMachineId) {
|
||||||
|
reqForm.sshTunnelMachineId = -1;
|
||||||
|
}
|
||||||
dbApi.saveDb.request(reqForm).then(() => {
|
dbApi.saveDb.request(reqForm).then(() => {
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
emit('val-change', state.form);
|
emit('val-change', state.form);
|
||||||
@@ -287,6 +275,4 @@ const cancel = () => {
|
|||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -264,7 +264,7 @@
|
|||||||
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
||||||
<el-descriptions-item :span="3" label="数据库">{{ infoDialog.data.database }}</el-descriptions-item>
|
<el-descriptions-item :span="3" label="数据库">{{ infoDialog.data.database }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="3" label="SSH隧道">{{ infoDialog.data.enableSshTunnel == 1 ? '是' : '否' }}
|
<el-descriptions-item :span="3" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }}
|
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }}
|
||||||
@@ -322,7 +322,6 @@ const state = reactive({
|
|||||||
*/
|
*/
|
||||||
query: {
|
query: {
|
||||||
tagPath: null,
|
tagPath: null,
|
||||||
projectId: null,
|
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,76 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true"
|
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true"
|
||||||
:before-close="cancel" width="38%">
|
:before-close="cancel" width="650px">
|
||||||
<el-form :model="form" ref="machineForm" :rules="rules" label-width="85px">
|
<el-form :model="form" ref="machineForm" :rules="rules" label-width="85px">
|
||||||
<el-form-item prop="tagId" label="标签:" required>
|
<el-tabs v-model="tabActiveName">
|
||||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
<el-tab-pane label="基础信息" name="basic">
|
||||||
</el-form-item>
|
<el-form-item prop="tagId" label="标签:" required :rules="{
|
||||||
<el-form-item prop="name" label="名称:" required>
|
required: true,
|
||||||
<el-input v-model.trim="form.name" placeholder="请输入机器别名" auto-complete="off"></el-input>
|
message: '请选择标签',
|
||||||
</el-form-item>
|
trigger: ['change', 'blur'],
|
||||||
<el-form-item prop="ip" label="ip:" required>
|
}">
|
||||||
<el-col :span="18">
|
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||||
<el-input :disabled="form.id" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off">
|
</el-form-item>
|
||||||
</el-input>
|
<el-form-item prop="name" label="名称:" required>
|
||||||
</el-col>
|
<el-input v-model.trim="form.name" placeholder="请输入机器别名" auto-complete="off"></el-input>
|
||||||
<el-col style="text-align: center" :span="1">:</el-col>
|
</el-form-item>
|
||||||
<el-col :span="5">
|
<el-form-item prop="ip" label="ip:" required>
|
||||||
<el-input type="number" v-model.number="form.port" placeholder="端口"></el-input>
|
<el-col :span="18">
|
||||||
</el-col>
|
<el-input :disabled="form.id" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item prop="username" label="用户名:" required>
|
</el-col>
|
||||||
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
|
<el-col style="text-align: center" :span="1">:</el-col>
|
||||||
</el-form-item>
|
<el-col :span="5">
|
||||||
<el-form-item prop="authMethod" label="认证方式:" required>
|
<el-input type="number" v-model.number="form.port" placeholder="端口"></el-input>
|
||||||
<el-select style="width: 100%" v-model="form.authMethod" placeholder="请选择认证方式">
|
</el-col>
|
||||||
<el-option key="1" label="Password" :value="1"> </el-option>
|
</el-form-item>
|
||||||
<el-option key="2" label="PublicKey" :value="2"> </el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="form.authMethod == 1" prop="password" label="密码:">
|
|
||||||
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码,修改操作可不填"
|
|
||||||
autocomplete="new-password">
|
|
||||||
<template v-if="form.id && form.id != 0" #suffix>
|
|
||||||
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
|
|
||||||
:content="pwd">
|
|
||||||
<template #reference>
|
|
||||||
<el-link @click="getPwd" :underline="false" type="primary" class="mr5">原密码</el-link>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="form.authMethod == 2" prop="password" label="秘钥:">
|
|
||||||
<el-input type="textarea" :rows="3" v-model="form.password" placeholder="请将私钥文件内容拷贝至此,修改操作可不填">
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="remark" label="备注:">
|
|
||||||
<el-input type="textarea" v-model="form.remark"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item prop="enableRecorder" label="终端回放:">
|
<el-form-item prop="username" label="用户名:">
|
||||||
<el-checkbox v-model="form.enableRecorder" :true-label="1" :false-label="-1"></el-checkbox>
|
<el-input v-model.trim="form.username" placeholder="请输授权用户名" autocomplete="new-password">
|
||||||
</el-form-item>
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
|
<el-form-item label="认证方式:">
|
||||||
<el-col :span="3">
|
<el-select @change="changeAuthMethod" style="width: 100%" v-model="state.authType"
|
||||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
|
placeholder="请选认证方式">
|
||||||
:false-label="-1"></el-checkbox>
|
<el-option key="1" label="密码" :value="1"> </el-option>
|
||||||
</el-col>
|
<el-option key="2" label="授权凭证" :value="2"> </el-option>
|
||||||
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
|
</el-select>
|
||||||
<el-col :span="19" v-if="form.enableSshTunnel == 1">
|
</el-form-item>
|
||||||
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
|
<el-form-item v-if="state.authType == 1" prop="password" label="密码:">
|
||||||
<el-option v-for="item in sshTunnelMachineList" :key="item.id"
|
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码"
|
||||||
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
|
autocomplete="new-password">
|
||||||
</el-option>
|
</el-input>
|
||||||
</el-select>
|
</el-form-item>
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
<el-form-item v-if="state.authType == 2" prop="authCertId" label="授权凭证:" required>
|
||||||
|
<auth-cert-select ref="authCertSelectRef" v-model="form.authCertId" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="remark" label="备注:">
|
||||||
|
<el-input type="textarea" v-model="form.remark"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="其他配置" name="other">
|
||||||
|
<el-form-item prop="enableRecorder" label="终端回放:">
|
||||||
|
<el-checkbox v-model="form.enableRecorder" :true-label="1" :false-label="-1"></el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="sshTunnelMachineId" label="SSH隧道:">
|
||||||
|
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div>
|
<div>
|
||||||
|
<el-button @click="testConn" :loading="testConnBtnLoading" type="success">测试连接</el-button>
|
||||||
<el-button @click="cancel()">取 消</el-button>
|
<el-button @click="cancel()">取 消</el-button>
|
||||||
<el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
|
<el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,17 +80,14 @@
|
|||||||
import { toRefs, reactive, watch, ref } from 'vue';
|
import { toRefs, reactive, watch, ref } from 'vue';
|
||||||
import { machineApi } from './api';
|
import { machineApi } from './api';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { notBlank } from '@/common/assert';
|
|
||||||
import { RsaEncrypt } from '@/common/rsa';
|
|
||||||
import TagSelect from '../component/TagSelect.vue';
|
import TagSelect from '../component/TagSelect.vue';
|
||||||
|
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||||
|
import AuthCertSelect from './authcert/AuthCertSelect.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
projects: {
|
|
||||||
type: Array,
|
|
||||||
},
|
|
||||||
machine: {
|
machine: {
|
||||||
type: [Boolean, Object],
|
type: [Boolean, Object],
|
||||||
},
|
},
|
||||||
@@ -106,13 +100,6 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
|
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
tagId: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择标签',
|
|
||||||
trigger: ['change', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
name: [
|
name: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -127,50 +114,62 @@ const rules = {
|
|||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
username: [
|
authCertId: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入用户名',
|
message: '请选择授权凭证',
|
||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
authMethod: [
|
username: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请选择认证方式',
|
message: '请输入授权用户名',
|
||||||
|
trigger: ['change', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入授权密码',
|
||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
const machineForm: any = ref(null);
|
const machineForm: any = ref(null);
|
||||||
|
const authCertSelectRef: any = ref(null);
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
|
tabActiveName: 'basic',
|
||||||
sshTunnelMachineList: [] as any,
|
sshTunnelMachineList: [] as any,
|
||||||
|
authCerts: [] as any,
|
||||||
|
authType: 1,
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
tagId: null as any,
|
|
||||||
tagPath: '',
|
|
||||||
ip: null,
|
ip: null,
|
||||||
name: null,
|
|
||||||
authMethod: 1,
|
|
||||||
port: 22,
|
port: 22,
|
||||||
|
name: null,
|
||||||
|
authCertId: null as any,
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
tagId: null as any,
|
||||||
|
tagPath: null as any,
|
||||||
remark: '',
|
remark: '',
|
||||||
enableSshTunnel: null,
|
sshTunnelMachineId: null as any,
|
||||||
sshTunnelMachineId: null,
|
|
||||||
enableRecorder: -1,
|
enableRecorder: -1,
|
||||||
},
|
},
|
||||||
pwd: '',
|
pwd: '',
|
||||||
|
testConnBtnLoading: false,
|
||||||
btnLoading: false,
|
btnLoading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
dialogVisible,
|
dialogVisible,
|
||||||
sshTunnelMachineList,
|
tabActiveName,
|
||||||
form,
|
form,
|
||||||
pwd,
|
testConnBtnLoading,
|
||||||
btnLoading,
|
btnLoading,
|
||||||
} = toRefs(state)
|
} = toRefs(state)
|
||||||
|
|
||||||
@@ -179,53 +178,65 @@ watch(props, async (newValue: any) => {
|
|||||||
if (!state.dialogVisible) {
|
if (!state.dialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.tabActiveName = 'basic';
|
||||||
if (newValue.machine) {
|
if (newValue.machine) {
|
||||||
state.form = { ...newValue.machine };
|
state.form = { ...newValue.machine };
|
||||||
|
// 如果凭证类型为公共的,则表示使用授权凭证认证
|
||||||
|
const authCertId = (state.form as any).authCertId
|
||||||
|
if (authCertId > 0) {
|
||||||
|
state.authType = 2;
|
||||||
|
} else {
|
||||||
|
state.authType = 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
state.form = { port: 22, authMethod: 1 } as any;
|
state.form = { port: 22 } as any;
|
||||||
|
state.authType = 1;
|
||||||
}
|
}
|
||||||
getSshTunnelMachines();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSshTunnelMachines = async () => {
|
const changeAuthMethod = (val: any) => {
|
||||||
if (state.form.enableSshTunnel == 1 && state.sshTunnelMachineList.length == 0) {
|
if (state.form.id) {
|
||||||
const res = await machineApi.list.request({ pageNum: 1, pageSize: 100 });
|
if (val == 2) {
|
||||||
state.sshTunnelMachineList = res.list;
|
state.form.authCertId = null;
|
||||||
|
} else {
|
||||||
|
state.form.password = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const getSshTunnelMachine = (machineId: any) => {
|
const testConn = async () => {
|
||||||
notBlank(machineId, '请选择或先创建一台隧道机器');
|
|
||||||
return state.sshTunnelMachineList.find((x: any) => x.id == machineId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPwd = async () => {
|
|
||||||
state.pwd = await machineApi.getMachinePwd.request({ id: state.form.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
const btnOk = async () => {
|
|
||||||
if (!state.form.id) {
|
|
||||||
notBlank(state.form.password, '新增操作,密码不可为空');
|
|
||||||
}
|
|
||||||
machineForm.value.validate(async (valid: boolean) => {
|
machineForm.value.validate(async (valid: boolean) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const form: any = state.form;
|
const form = getReqForm();
|
||||||
if (form.enableSshTunnel == 1) {
|
if (!form) {
|
||||||
const tunnelMachine: any = getSshTunnelMachine(form.sshTunnelMachineId);
|
return;
|
||||||
if (tunnelMachine.ip == form.ip && tunnelMachine.port == form.port) {
|
|
||||||
ElMessage.error('隧道机器不能与本机器一致');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const reqForm: any = { ...form };
|
state.testConnBtnLoading = true;
|
||||||
if (reqForm.authMethod == 1) {
|
try {
|
||||||
reqForm.password = await RsaEncrypt(state.form.password);
|
await machineApi.testConn.request(form);
|
||||||
|
ElMessage.success('连接成功');
|
||||||
|
} finally {
|
||||||
|
state.testConnBtnLoading = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('请正确填写信息');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const btnOk = async () => {
|
||||||
|
machineForm.value.validate(async (valid: boolean) => {
|
||||||
|
if (valid) {
|
||||||
|
const form = getReqForm();
|
||||||
|
if (!form) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
state.btnLoading = true;
|
state.btnLoading = true;
|
||||||
try {
|
try {
|
||||||
await machineApi.saveMachine.request(reqForm);
|
await machineApi.saveMachine.request(form);
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
emit('val-change', state.form);
|
emit('val-change', form);
|
||||||
cancel();
|
cancel();
|
||||||
} finally {
|
} finally {
|
||||||
state.btnLoading = false;
|
state.btnLoading = false;
|
||||||
@@ -237,11 +248,22 @@ const btnOk = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getReqForm = () => {
|
||||||
|
const reqForm: any = { ...state.form };
|
||||||
|
debugger
|
||||||
|
// 如果为密码认证,则置空授权凭证id
|
||||||
|
if (state.authType == 1) {
|
||||||
|
reqForm.authCertId = -1;
|
||||||
|
}
|
||||||
|
if (!state.form.sshTunnelMachineId || state.form.sshTunnelMachineId <= 0) {
|
||||||
|
reqForm.sshTunnelMachineId = -1
|
||||||
|
}
|
||||||
|
return reqForm
|
||||||
|
}
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
<el-input class="ml5" placeholder="请输入名称" style="width: 150px" v-model="params.name" @clear="search"
|
<el-input class="ml5" placeholder="请输入名称" style="width: 150px" v-model="params.name" @clear="search"
|
||||||
plain clearable></el-input>
|
plain clearable></el-input>
|
||||||
<el-input class="ml5" placeholder="请输入ip" style="width: 150px" v-model="params.ip" @clear="search"
|
<el-input class="ml5" placeholder="请输入ip" style="width: 150px" v-model="params.ip" @clear="search" plain
|
||||||
plain clearable></el-input>
|
clearable></el-input>
|
||||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,14 +38,19 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="name" label="名称" min-width="140" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="name" label="名称" min-width="140" show-overflow-tooltip></el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="ip" label="ip:port" min-width="150">
|
<el-table-column prop="ip" label="ip:port" min-width="150">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-link :disabled="scope.row.status == -1" @click="showMachineStats(scope.row)" type="primary"
|
<el-link :disabled="scope.row.status == -1" @click="showMachineStats(scope.row)" type="primary"
|
||||||
:underline="false">
|
:underline="false">
|
||||||
{{ `${scope.row.ip}:${scope.row.port}`}}
|
{{ `${scope.row.ip}:${scope.row.port}` }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="username" label="用户名" min-width="100">
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="status" label="状态" min-width="80">
|
<el-table-column prop="status" label="状态" min-width="80">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-switch v-auth:disabled="'machine:update'" :width="52" v-model="scope.row.status"
|
<el-switch v-auth:disabled="'machine:update'" :width="52" v-model="scope.row.status"
|
||||||
@@ -54,7 +59,7 @@
|
|||||||
@change="changeStatus(scope.row)"></el-switch>
|
@change="changeStatus(scope.row)"></el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="username" label="用户名" min-width="90"></el-table-column>
|
|
||||||
<el-table-column prop="remark" label="备注" min-width="250" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="remark" label="备注" min-width="250" show-overflow-tooltip></el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" min-width="235" fixed="right">
|
<el-table-column label="操作" min-width="235" fixed="right">
|
||||||
@@ -66,13 +71,13 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-auth="'machine:file'">
|
<span v-auth="'machine:file'">
|
||||||
<el-link type="success" :disabled="scope.row.status == -1"
|
<el-link type="success" :disabled="scope.row.status == -1" @click="showFileManage(scope.row)"
|
||||||
@click="showFileManage(scope.row)" plain size="small" :underline="false">文件</el-link>
|
plain size="small" :underline="false">文件</el-link>
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<el-link :disabled="scope.row.status == -1" type="warning" @click="serviceManager(scope.row)"
|
<el-link :disabled="scope.row.status == -1" type="warning" @click="serviceManager(scope.row)" plain
|
||||||
plain size="small" :underline="false">脚本</el-link>
|
size="small" :underline="false">脚本</el-link>
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
@@ -90,8 +95,8 @@
|
|||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-link @click="showProcess(scope.row)" :disabled="scope.row.status == -1"
|
<el-link @click="showProcess(scope.row)" :disabled="scope.row.status == -1" plain
|
||||||
plain :underline="false" size="small">进程</el-link>
|
:underline="false" size="small">进程</el-link>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
|
||||||
<el-dropdown-item v-if="scope.row.enableRecorder == 1">
|
<el-dropdown-item v-if="scope.row.enableRecorder == 1">
|
||||||
@@ -128,14 +133,13 @@
|
|||||||
<el-descriptions-item :span="1" label="端口">{{ infoDialog.data.port }}</el-descriptions-item>
|
<el-descriptions-item :span="1" label="端口">{{ infoDialog.data.port }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="用户名">{{ infoDialog.data.username }}</el-descriptions-item>
|
<el-descriptions-item :span="2" label="用户名">{{ infoDialog.data.username }}</el-descriptions-item>
|
||||||
<el-descriptions-item :span="1" label="认证方式">{{
|
<el-descriptions-item :span="1" label="认证方式">
|
||||||
infoDialog.data.authMethod == 1 ? 'Password' :
|
{{ infoDialog.data.authCertId > 1 ? '授权凭证' : '密码' }}
|
||||||
'PublicKey'
|
</el-descriptions-item>
|
||||||
}}</el-descriptions-item>
|
|
||||||
|
|
||||||
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.enableSshTunnel == 1 ? '是' : '否' }}
|
<el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }}
|
<el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|||||||
@@ -26,19 +26,27 @@
|
|||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-input v-model="param.model" placeholder="内容中用{{.model}}替换"></el-input>
|
<el-input v-model="param.model" placeholder="内容中用{{.model}}替换"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-input v-model="param.name" placeholder="字段名"></el-input>
|
<el-input v-model="param.name" placeholder="字段名"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-input v-model="param.placeholder" placeholder="字段说明"></el-input>
|
<el-input v-model="param.placeholder" placeholder="字段说明"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="2">
|
<el-col :span="2">
|
||||||
<el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button>
|
<el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -172,6 +180,4 @@ const cancel = () => {
|
|||||||
state.params = [];
|
state.params = [];
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const machineApi = {
|
|||||||
// 终止进程
|
// 终止进程
|
||||||
killProcess: Api.create("/machines/{id}/process", 'delete'),
|
killProcess: Api.create("/machines/{id}/process", 'delete'),
|
||||||
closeCli: Api.create("/machines/{id}/close-cli", 'delete'),
|
closeCli: Api.create("/machines/{id}/close-cli", 'delete'),
|
||||||
|
testConn: Api.create("/machines/test-conn", 'post'),
|
||||||
// 保存按钮
|
// 保存按钮
|
||||||
saveMachine: Api.create("/machines", 'post'),
|
saveMachine: Api.create("/machines", 'post'),
|
||||||
// 调整状态
|
// 调整状态
|
||||||
@@ -35,4 +36,11 @@ export const machineApi = {
|
|||||||
delConf: Api.create("/machines/{machineId}/files/{id}", 'delete'),
|
delConf: Api.create("/machines/{machineId}/files/{id}", 'delete'),
|
||||||
terminal: Api.create("/api/machines/{id}/terminal", 'get'),
|
terminal: Api.create("/api/machines/{id}/terminal", 'get'),
|
||||||
recDirNames: Api.create("/machines/rec/names", 'get')
|
recDirNames: Api.create("/machines/rec/names", 'get')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authCertApi = {
|
||||||
|
baseList : Api.create("/sys/authcerts/base", 'get'),
|
||||||
|
list: Api.create("/sys/authcerts", 'get'),
|
||||||
|
save: Api.create("/sys/authcerts", 'post'),
|
||||||
|
delete: Api.create("/sys/authcerts/{id}", 'delete'),
|
||||||
}
|
}
|
||||||
126
mayfly_go_web/src/views/ops/machine/authcert/AuthCertEdit.vue
Executable file
126
mayfly_go_web/src/views/ops/machine/authcert/AuthCertEdit.vue
Executable file
@@ -0,0 +1,126 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="500px"
|
||||||
|
:destroy-on-close="true">
|
||||||
|
<el-form ref="acForm" :rules="rules" :model="form" label-width="90px">
|
||||||
|
<el-form-item prop="name" label="名称:" required>
|
||||||
|
<el-input v-model="form.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="authMethod" label="认证方式:" required>
|
||||||
|
<el-select style="width: 100%" v-model="form.authMethod" placeholder="请选择认证方式">
|
||||||
|
<el-option key="1" label="密码" :value="1"> </el-option>
|
||||||
|
<el-option key="2" label="密钥" :value="2"> </el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form.authMethod == 1" prop="password" label="密码:">
|
||||||
|
<el-input type="password" show-password clearable v-model.trim="form.password" placeholder="请输入密码"
|
||||||
|
autocomplete="new-password">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form.authMethod == 2" prop="password" label="秘钥:">
|
||||||
|
<el-input type="textarea" :rows="5" v-model="form.password" placeholder="请将私钥文件内容拷贝至此">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form.authMethod == 2" prop="passphrase" label="秘钥密码:">
|
||||||
|
<el-input type="password" v-model="form.passphrase">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注:">
|
||||||
|
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="cancel()">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, toRefs, reactive, watch } from 'vue';
|
||||||
|
import { authCertApi } from '../api';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: [Boolean, Object],
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//定义事件
|
||||||
|
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
|
||||||
|
|
||||||
|
const acForm: any = ref(null);
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '授权凭证名称不能为空',
|
||||||
|
trigger: ['change', 'blur'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
dvisible: false,
|
||||||
|
params: [] as any,
|
||||||
|
form: {
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
authMethod: 1,
|
||||||
|
password: '',
|
||||||
|
passphrase: '',
|
||||||
|
remark: '',
|
||||||
|
},
|
||||||
|
btnLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
dvisible,
|
||||||
|
form,
|
||||||
|
btnLoading,
|
||||||
|
} = toRefs(state)
|
||||||
|
|
||||||
|
watch(props, (newValue: any) => {
|
||||||
|
state.dvisible = newValue.visible;
|
||||||
|
if (newValue.data) {
|
||||||
|
state.form = { ...newValue.data };
|
||||||
|
} else {
|
||||||
|
state.form = { authMethod: 1 } as any;
|
||||||
|
state.params = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
// 更新父组件visible prop对应的值为false
|
||||||
|
emit('update:visible', false);
|
||||||
|
// 若父组件有取消事件,则调用
|
||||||
|
emit('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
const btnOk = async () => {
|
||||||
|
acForm.value.validate(async (valid: boolean) => {
|
||||||
|
if (valid) {
|
||||||
|
state.btnLoading = true;
|
||||||
|
try {
|
||||||
|
await authCertApi.save.request(state.form);
|
||||||
|
emit('val-change', state.form);
|
||||||
|
cancel();
|
||||||
|
} finally {
|
||||||
|
state.btnLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss"></style>
|
||||||
159
mayfly_go_web/src/views/ops/machine/authcert/AuthCertList.vue
Executable file
159
mayfly_go_web/src/views/ops/machine/authcert/AuthCertList.vue
Executable file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="role-list">
|
||||||
|
<el-card>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" icon="plus" @click="edit(false)">添加</el-button>
|
||||||
|
<el-button :disabled="chooseId == null" @click="edit(chooseData)" type="primary" icon="edit">编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="chooseId == null" @click="deleteAc(chooseData)" type="danger" icon="delete">删除
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<div style="float: right">
|
||||||
|
<el-select v-model="query.type" placeholder="请选择标签" @clear="search" filterable clearable>
|
||||||
|
<el-option label="" value="item"> </el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-input class="ml5" placeholder="请输入凭证名称" style="width: 150px" v-model="query.name" @clear="search"
|
||||||
|
plain clearable></el-input>
|
||||||
|
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="authcerts" @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="名称" min-width="60px" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="authMethod" label="认证方式" min-width="50px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.authMethod == 1" type="success" size="small">密码</el-tag>
|
||||||
|
<el-tag v-if="scope.row.authMethod == 2" type="primary" size="small">密钥</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="remark" label="备注" min-width="100px" show-overflow-tooltip>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="creator" label="创建人" min-width="60px"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="创建时间" min-width="100px">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="modifier" label="修改者" min-width="60px" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.updateTime) }}
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<auth-cert-edit :title="editor.title" v-model:visible="editor.visible" :data="editor.authcert"
|
||||||
|
@val-change="editChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { toRefs, reactive, onMounted } from 'vue';
|
||||||
|
import AuthCertEdit from './AuthCertEdit.vue';
|
||||||
|
import { authCertApi } from '../api';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import { dateFormat } from '@/common/utils/date';
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
query: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: null,
|
||||||
|
type: null,
|
||||||
|
},
|
||||||
|
total: 0,
|
||||||
|
authcerts: [],
|
||||||
|
chooseId: null,
|
||||||
|
chooseData: null,
|
||||||
|
paramsDialog: {
|
||||||
|
visible: false,
|
||||||
|
config: null as any,
|
||||||
|
params: {},
|
||||||
|
paramsFormItem: [] as any,
|
||||||
|
},
|
||||||
|
editor: {
|
||||||
|
title: '授权凭证保存',
|
||||||
|
visible: false,
|
||||||
|
authcert: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
query,
|
||||||
|
total,
|
||||||
|
authcerts,
|
||||||
|
chooseId,
|
||||||
|
chooseData,
|
||||||
|
editor,
|
||||||
|
} = toRefs(state)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let res = await authCertApi.list.request(state.query);
|
||||||
|
state.authcerts = 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 editChange = () => {
|
||||||
|
ElMessage.success('保存成功');
|
||||||
|
state.chooseId = null;
|
||||||
|
state.chooseData = null;
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const edit = (data: any) => {
|
||||||
|
if (data) {
|
||||||
|
state.editor.authcert = data;
|
||||||
|
} else {
|
||||||
|
state.editor.authcert = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.editor.visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteAc = async (data: any) => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定删除该授权凭证?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
await authCertApi.delete.request({ id: data.id });
|
||||||
|
ElMessage.success('删除成功');
|
||||||
|
state.chooseData = null;
|
||||||
|
state.chooseId = null;
|
||||||
|
search();
|
||||||
|
} catch (err) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss"></style>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div style="width: 100%">
|
||||||
|
<el-select @change="changeValue" v-model="id" filterable placeholder="请选择授权凭证,可前往[机器管理->授权凭证]添加"
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option v-for="ac in acs" :key="ac.id" :value="ac.id" :label="ac.name">
|
||||||
|
<el-tag v-if="ac.authMethod == 1" type="success" size="small">密码</el-tag>
|
||||||
|
<el-tag v-if="ac.authMethod == 2" type="primary" size="small">密钥</el-tag>
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
{{ ac.name }}
|
||||||
|
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
{{ ac.remark }}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, toRefs, onMounted } from 'vue';
|
||||||
|
import { authCertApi } from '../api';
|
||||||
|
|
||||||
|
//定义事件
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change'])
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: [Number],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
acs: [] as any,
|
||||||
|
id: null as any,
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
acs,
|
||||||
|
id,
|
||||||
|
} = toRefs(state)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getAcs();
|
||||||
|
if (props.modelValue) {
|
||||||
|
state.id = props.modelValue;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeValue = (val: any) => {
|
||||||
|
emit('update:modelValue', val);
|
||||||
|
emit('change', val);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAcs = async () => {
|
||||||
|
const acs = await authCertApi.baseList.request({ pageSize: 100, type: 2 })
|
||||||
|
state.acs = acs.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss"></style>
|
||||||
@@ -1,34 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false"
|
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="38%"
|
||||||
width="38%" :destroy-on-close="true">
|
:destroy-on-close="true">
|
||||||
<el-form :model="form" ref="mongoForm" :rules="rules" label-width="85px">
|
<el-form :model="form" ref="mongoForm" :rules="rules" label-width="85px">
|
||||||
<el-form-item prop="tagId" label="标签:" required>
|
<el-tabs v-model="tabActiveName">
|
||||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
<el-tab-pane label="基础信息" name="basic">
|
||||||
</el-form-item>
|
<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-form-item prop="name" label="名称" required>
|
||||||
<el-input v-model.trim="form.name" placeholder="请输入名称" auto-complete="off"></el-input>
|
<el-input v-model.trim="form.name" placeholder="请输入名称" auto-complete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="uri" label="uri" required>
|
<el-form-item prop="uri" label="uri" required>
|
||||||
<el-input type="textarea" :rows="2" v-model.trim="form.uri"
|
<el-input type="textarea" :rows="2" v-model.trim="form.uri"
|
||||||
placeholder="形如 mongodb://username:password@host1:port1" auto-complete="off"></el-input>
|
placeholder="形如 mongodb://username:password@host1:port1" auto-complete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
|
<el-tab-pane label="其他配置" name="other">
|
||||||
<el-col :span="3">
|
<el-form-item prop="sshTunnelMachineId" label="SSH隧道:">
|
||||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
|
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
|
||||||
:false-label="-1"></el-checkbox>
|
</el-form-item>
|
||||||
</el-col>
|
</el-tab-pane>
|
||||||
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
|
</el-tabs>
|
||||||
<el-col :span="19" v-if="form.enableSshTunnel == 1">
|
|
||||||
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
|
|
||||||
<el-option v-for="item in sshTunnelMachineList" :key="item.id"
|
|
||||||
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -44,9 +39,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, watch, ref } from 'vue';
|
import { toRefs, reactive, watch, ref } from 'vue';
|
||||||
import { mongoApi } from './api';
|
import { mongoApi } from './api';
|
||||||
import { machineApi } from '../machine/api.ts';
|
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import TagSelect from '../component/TagSelect.vue';
|
import TagSelect from '../component/TagSelect.vue';
|
||||||
|
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -90,13 +85,12 @@ const rules = {
|
|||||||
const mongoForm: any = ref(null);
|
const mongoForm: any = ref(null);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
sshTunnelMachineList: [] as any,
|
tabActiveName: 'basic',
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
name: null,
|
name: null,
|
||||||
uri: null,
|
uri: null,
|
||||||
enableSshTunnel: -1,
|
sshTunnelMachineId: null as any,
|
||||||
sshTunnelMachineId: null,
|
|
||||||
tagId: null as any,
|
tagId: null as any,
|
||||||
tagPath: null as any,
|
tagPath: null as any,
|
||||||
},
|
},
|
||||||
@@ -105,7 +99,7 @@ const state = reactive({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
dialogVisible,
|
dialogVisible,
|
||||||
sshTunnelMachineList,
|
tabActiveName,
|
||||||
form,
|
form,
|
||||||
btnLoading,
|
btnLoading,
|
||||||
} = toRefs(state)
|
} = toRefs(state)
|
||||||
@@ -115,25 +109,21 @@ watch(props, async (newValue: any) => {
|
|||||||
if (!state.dialogVisible) {
|
if (!state.dialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.tabActiveName = 'basic';
|
||||||
if (newValue.mongo) {
|
if (newValue.mongo) {
|
||||||
state.form = { ...newValue.mongo };
|
state.form = { ...newValue.mongo };
|
||||||
} else {
|
} else {
|
||||||
state.form = { db: 0 } as any;
|
state.form = { db: 0 } as any;
|
||||||
}
|
}
|
||||||
getSshTunnelMachines();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSshTunnelMachines = async () => {
|
|
||||||
if (state.form.enableSshTunnel == 1 && state.sshTunnelMachineList.length == 0) {
|
|
||||||
const res = await machineApi.list.request({ pageNum: 1, pageSize: 100 });
|
|
||||||
state.sshTunnelMachineList = res.list;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const btnOk = async () => {
|
const btnOk = async () => {
|
||||||
mongoForm.value.validate(async (valid: boolean) => {
|
mongoForm.value.validate(async (valid: boolean) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const reqForm = { ...state.form };
|
const reqForm = { ...state.form };
|
||||||
|
if (!state.form.sshTunnelMachineId || state.form.sshTunnelMachineId <= 0) {
|
||||||
|
reqForm.sshTunnelMachineId = -1
|
||||||
|
}
|
||||||
// reqForm.uri = await RsaEncrypt(reqForm.uri);
|
// reqForm.uri = await RsaEncrypt(reqForm.uri);
|
||||||
mongoApi.saveMongo.request(reqForm).then(() => {
|
mongoApi.saveMongo.request(reqForm).then(() => {
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
@@ -157,6 +147,4 @@ const cancel = () => {
|
|||||||
emit('cancel');
|
emit('cancel');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<template #default>
|
<template #default>
|
||||||
<el-form class="instances-pop-form" label-width="50px" :size="'small'">
|
<el-form class="instances-pop-form" label-width="50px" :size="'small'">
|
||||||
<el-form-item label="名称:">{{ data.params.name }}</el-form-item>
|
<el-form-item label="名称:">{{ data.params.name }}</el-form-item>
|
||||||
|
<el-form-item label="模式:">{{ data.params.mode }}</el-form-item>
|
||||||
<el-form-item label="链接:">{{ data.params.host }}</el-form-item>
|
<el-form-item label="链接:">{{ data.params.host }}</el-form-item>
|
||||||
<el-form-item label="备注:">{{
|
<el-form-item label="备注:">{{
|
||||||
data.params.remark
|
data.params.remark
|
||||||
|
|||||||
@@ -3,59 +3,57 @@
|
|||||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false"
|
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false"
|
||||||
:destroy-on-close="true" width="38%">
|
:destroy-on-close="true" width="38%">
|
||||||
<el-form :model="form" ref="redisForm" :rules="rules" label-width="85px">
|
<el-form :model="form" ref="redisForm" :rules="rules" label-width="85px">
|
||||||
<el-form-item prop="tagId" label="标签:" required>
|
<el-tabs v-model="tabActiveName">
|
||||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
<el-tab-pane label="基础信息" name="basic">
|
||||||
</el-form-item>
|
<el-form-item prop="tagId" label="标签:" required>
|
||||||
<el-form-item prop="name" label="名称:" required>
|
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||||
<el-input v-model.trim="form.name" placeholder="请输入redis名称" auto-complete="off"></el-input>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item prop="name" label="名称:" required>
|
||||||
<el-form-item prop="mode" label="mode:" required>
|
<el-input v-model.trim="form.name" placeholder="请输入redis名称" auto-complete="off"></el-input>
|
||||||
<el-select style="width: 100%" v-model="form.mode" placeholder="请选择模式">
|
</el-form-item>
|
||||||
<el-option label="standalone" value="standalone"> </el-option>
|
<el-form-item prop="mode" label="mode:" required>
|
||||||
<el-option label="cluster" value="cluster"> </el-option>
|
<el-select style="width: 100%" v-model="form.mode" placeholder="请选择模式">
|
||||||
<el-option label="sentinel" value="sentinel"> </el-option>
|
<el-option label="standalone" value="standalone"> </el-option>
|
||||||
</el-select>
|
<el-option label="cluster" value="cluster"> </el-option>
|
||||||
</el-form-item>
|
<el-option label="sentinel" value="sentinel"> </el-option>
|
||||||
<el-form-item prop="host" label="host:" required>
|
</el-select>
|
||||||
<el-input v-model.trim="form.host"
|
</el-form-item>
|
||||||
placeholder="请输入host:port;sentinel模式为: mastername=sentinelhost:port,若集群或哨兵需设多个节点可使用','分割"
|
<el-form-item prop="host" label="host:" required>
|
||||||
auto-complete="off" type="textarea"></el-input>
|
<el-input v-model.trim="form.host"
|
||||||
</el-form-item>
|
placeholder="请输入host:port;sentinel模式为: mastername=sentinelhost:port,若集群或哨兵需设多个节点可使用','分割"
|
||||||
<el-form-item prop="password" label="密码:">
|
auto-complete="off" type="textarea"></el-input>
|
||||||
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码, 修改操作可不填"
|
</el-form-item>
|
||||||
autocomplete="new-password"><template v-if="form.id && form.id != 0" #suffix>
|
<el-form-item prop="password" label="密码:">
|
||||||
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
|
<el-input type="password" show-password v-model.trim="form.password"
|
||||||
:content="pwd">
|
placeholder="请输入密码, 修改操作可不填" autocomplete="new-password"><template
|
||||||
<template #reference>
|
v-if="form.id && form.id != 0" #suffix>
|
||||||
<el-link @click="getPwd" :underline="false" type="primary" class="mr5">原密码</el-link>
|
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
|
||||||
</template>
|
:content="pwd">
|
||||||
</el-popover>
|
<template #reference>
|
||||||
</template></el-input>
|
<el-link @click="getPwd" :underline="false" type="primary"
|
||||||
</el-form-item>
|
class="mr5">原密码</el-link>
|
||||||
<el-form-item prop="db" label="库号:" required>
|
</template>
|
||||||
<el-select @change="changeDb" v-model="dbList" multiple allow-create filterable
|
</el-popover>
|
||||||
placeholder="请选择可操作库号" style="width: 100%">
|
</template></el-input>
|
||||||
<el-option v-for="db in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]" :key="db"
|
</el-form-item>
|
||||||
:label="db" :value="db" />
|
<el-form-item prop="db" label="库号:" required>
|
||||||
</el-select>
|
<el-select @change="changeDb" v-model="dbList" multiple allow-create filterable
|
||||||
</el-form-item>
|
placeholder="请选择可操作库号" style="width: 100%">
|
||||||
<el-form-item prop="remark" label="备注:">
|
<el-option v-for="db in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]" :key="db"
|
||||||
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
:label="db" :value="db" />
|
||||||
</el-form-item>
|
</el-select>
|
||||||
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
|
</el-form-item>
|
||||||
<el-col :span="3">
|
<el-form-item prop="remark" label="备注:">
|
||||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
|
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||||
:false-label="-1"></el-checkbox>
|
</el-form-item>
|
||||||
</el-col>
|
</el-tab-pane>
|
||||||
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
|
|
||||||
<el-col :span="19" v-if="form.enableSshTunnel == 1">
|
<el-tab-pane label="其他配置" name="other">
|
||||||
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
|
<el-form-item prop="sshTunnelMachineId" label="SSH隧道:">
|
||||||
<el-option v-for="item in sshTunnelMachineList as any" :key="item.id"
|
<ssh-tunnel-select v-model="form.sshTunnelMachineId" />
|
||||||
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
|
</el-form-item>
|
||||||
</el-option>
|
</el-tab-pane>
|
||||||
</el-select>
|
</el-tabs>
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -71,10 +69,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, watch, ref } from 'vue';
|
import { toRefs, reactive, watch, ref } from 'vue';
|
||||||
import { redisApi } from './api';
|
import { redisApi } from './api';
|
||||||
import { machineApi } from '../machine/api.ts';
|
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { RsaEncrypt } from '@/common/rsa';
|
import { RsaEncrypt } from '@/common/rsa';
|
||||||
import TagSelect from '../component/TagSelect.vue';
|
import TagSelect from '../component/TagSelect.vue';
|
||||||
|
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -91,20 +89,6 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['update:visible', 'val-change', 'cancel'])
|
const emit = defineEmits(['update:visible', 'val-change', 'cancel'])
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
projectId: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择项目',
|
|
||||||
trigger: ['change', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
envId: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择环境',
|
|
||||||
trigger: ['change', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
host: [
|
host: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -131,7 +115,7 @@ const rules = {
|
|||||||
const redisForm: any = ref(null);
|
const redisForm: any = ref(null);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
sshTunnelMachineList: [],
|
tabActiveName: 'basic',
|
||||||
form: {
|
form: {
|
||||||
id: null,
|
id: null,
|
||||||
tagId: null as any,
|
tagId: null as any,
|
||||||
@@ -141,13 +125,8 @@ const state = reactive({
|
|||||||
host: '',
|
host: '',
|
||||||
password: null,
|
password: null,
|
||||||
db: '',
|
db: '',
|
||||||
project: null,
|
|
||||||
projectId: null,
|
|
||||||
envId: null,
|
|
||||||
env: null,
|
|
||||||
remark: '',
|
remark: '',
|
||||||
enableSshTunnel: null,
|
sshTunnelMachineId: -1,
|
||||||
sshTunnelMachineId: null,
|
|
||||||
},
|
},
|
||||||
dbList: [0],
|
dbList: [0],
|
||||||
pwd: '',
|
pwd: '',
|
||||||
@@ -157,7 +136,7 @@ const state = reactive({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
dialogVisible,
|
dialogVisible,
|
||||||
sshTunnelMachineList,
|
tabActiveName,
|
||||||
form,
|
form,
|
||||||
dbList,
|
dbList,
|
||||||
pwd,
|
pwd,
|
||||||
@@ -169,14 +148,14 @@ watch(props, async (newValue: any) => {
|
|||||||
if (!state.dialogVisible) {
|
if (!state.dialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.tabActiveName = 'basic';
|
||||||
if (newValue.redis) {
|
if (newValue.redis) {
|
||||||
state.form = { ...newValue.redis };
|
state.form = { ...newValue.redis };
|
||||||
convertDb(state.form.db);
|
convertDb(state.form.db);
|
||||||
} else {
|
} else {
|
||||||
state.form = { db: '0', enableSshTunnel: -1 } as any;
|
state.form = { db: '0' } as any;
|
||||||
state.dbList = [];
|
state.dbList = [];
|
||||||
}
|
}
|
||||||
getSshTunnelMachines();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const convertDb = (db: string) => {
|
const convertDb = (db: string) => {
|
||||||
@@ -190,13 +169,6 @@ const changeDb = () => {
|
|||||||
state.form.db = state.dbList.length == 0 ? '' : state.dbList.join(',');
|
state.form.db = state.dbList.length == 0 ? '' : state.dbList.join(',');
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSshTunnelMachines = async () => {
|
|
||||||
if (state.form.enableSshTunnel == 1 && state.sshTunnelMachineList.length == 0) {
|
|
||||||
const res = await machineApi.list.request({ pageNum: 1, pageSize: 100 });
|
|
||||||
state.sshTunnelMachineList = res.list;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPwd = async () => {
|
const getPwd = async () => {
|
||||||
state.pwd = await redisApi.getRedisPwd.request({ id: state.form.id });
|
state.pwd = await redisApi.getRedisPwd.request({ id: state.form.id });
|
||||||
};
|
};
|
||||||
@@ -209,6 +181,9 @@ const btnOk = async () => {
|
|||||||
ElMessage.error('sentinel模式host需为: mastername=sentinelhost:sentinelport模式');
|
ElMessage.error('sentinel模式host需为: mastername=sentinelhost:sentinelport模式');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!state.form.sshTunnelMachineId || state.form.sshTunnelMachineId <= 0) {
|
||||||
|
reqForm.sshTunnelMachineId = -1
|
||||||
|
}
|
||||||
reqForm.password = await RsaEncrypt(reqForm.password);
|
reqForm.password = await RsaEncrypt(reqForm.password);
|
||||||
redisApi.saveRedis.request(reqForm).then(() => {
|
redisApi.saveRedis.request(reqForm).then(() => {
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
@@ -232,6 +207,4 @@ const cancel = () => {
|
|||||||
emit('cancel');
|
emit('cancel');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
<el-descriptions-item :span="3" label="库">{{ detailDialog.data.db }}</el-descriptions-item>
|
<el-descriptions-item :span="3" label="库">{{ detailDialog.data.db }}</el-descriptions-item>
|
||||||
<el-descriptions-item :span="3" label="备注">{{ detailDialog.data.remark }}</el-descriptions-item>
|
<el-descriptions-item :span="3" label="备注">{{ detailDialog.data.remark }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="3" label="SSH隧道">{{ detailDialog.data.enableSshTunnel == 1 ? '是' : '否' }}
|
<el-descriptions-item :span="3" label="SSH隧道">{{ detailDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(detailDialog.data.createTime) }}
|
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(detailDialog.data.createTime) }}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog"
|
<el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog"
|
||||||
v-model="showTagDialog.visible">
|
v-model="showTagDialog.visible">
|
||||||
<el-form label-width="70px">
|
<el-form label-width="70px">
|
||||||
<el-form-item prop="project" label="标签:">
|
<el-form-item prop="tag" label="标签:">
|
||||||
<el-tree-select ref="tagTreeRef" style="width: 100%" v-model="showTagDialog.tagTreeTeams"
|
<el-tree-select ref="tagTreeRef" style="width: 100%" v-model="showTagDialog.tagTreeTeams"
|
||||||
:data="showTagDialog.tags" :default-expanded-keys="showTagDialog.tagTreeTeams" multiple
|
:data="showTagDialog.tags" :default-expanded-keys="showTagDialog.tagTreeTeams" multiple
|
||||||
:render-after-expand="true" show-checkbox check-strictly node-key="id"
|
:render-after-expand="true" show-checkbox check-strictly node-key="id"
|
||||||
|
|||||||
@@ -40,4 +40,4 @@ export const configApi = {
|
|||||||
|
|
||||||
export const logApi = {
|
export const logApi = {
|
||||||
list: Api.create("/syslogs", "get")
|
list: Api.create("/syslogs", "get")
|
||||||
}
|
}
|
||||||
@@ -18,19 +18,27 @@
|
|||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-input v-model="param.model" placeholder="model"></el-input>
|
<el-input v-model="param.model" placeholder="model"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-input v-model="param.name" placeholder="字段名"></el-input>
|
<el-input v-model="param.name" placeholder="字段名"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-input v-model="param.placeholder" placeholder="字段说明"></el-input>
|
<el-input v-model="param.placeholder" placeholder="字段说明"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
<span :span="1">
|
||||||
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
|
</span>
|
||||||
<el-col :span="2">
|
<el-col :span="2">
|
||||||
<el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button>
|
<el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -143,6 +151,4 @@ const btnOk = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<el-table-column prop="remark" 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="更新时间" min-width="100px">
|
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ dateFormat(scope.row.createTime) }}
|
{{ dateFormat(scope.row.updateTime) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="modifier" label="修改者" min-width="60px" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="modifier" label="修改者" min-width="60px" show-overflow-tooltip></el-table-column>
|
||||||
|
|||||||
@@ -19,16 +19,16 @@
|
|||||||
resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz"
|
resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz"
|
||||||
integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==
|
integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==
|
||||||
|
|
||||||
"@element-plus/icons-vue@^2.0.10":
|
|
||||||
version "2.0.10"
|
|
||||||
resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.10.tgz#60808d613c3dbdad025577022be8a972739ade21"
|
|
||||||
integrity sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ==
|
|
||||||
|
|
||||||
"@element-plus/icons-vue@^2.0.6":
|
"@element-plus/icons-vue@^2.0.6":
|
||||||
version "2.0.9"
|
version "2.0.9"
|
||||||
resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.9.tgz"
|
resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.9.tgz"
|
||||||
integrity sha512-okdrwiVeKBmW41Hkl0eMrXDjzJwhQMuKiBOu17rOszqM+LS/yBYpNQNV5Jvoh06Wc+89fMmb/uhzf8NZuDuUaQ==
|
integrity sha512-okdrwiVeKBmW41Hkl0eMrXDjzJwhQMuKiBOu17rOszqM+LS/yBYpNQNV5Jvoh06Wc+89fMmb/uhzf8NZuDuUaQ==
|
||||||
|
|
||||||
|
"@element-plus/icons-vue@^2.1.0":
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.1.0.tgz#7ad90d08a8c0d5fd3af31c4f73264ca89614397a"
|
||||||
|
integrity sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA==
|
||||||
|
|
||||||
"@esbuild/android-arm64@0.16.17":
|
"@esbuild/android-arm64@0.16.17":
|
||||||
version "0.16.17"
|
version "0.16.17"
|
||||||
resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23"
|
resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23"
|
||||||
@@ -139,21 +139,26 @@
|
|||||||
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091"
|
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091"
|
||||||
integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==
|
integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.0.5":
|
"@eslint/eslintrc@^2.0.0":
|
||||||
version "1.0.5"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmmirror.com/@eslint/eslintrc/download/@eslint/eslintrc-1.0.5.tgz"
|
resolved "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.0.0.tgz#943309d8697c52fc82c076e90c1c74fbbe69dbff"
|
||||||
integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==
|
integrity sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
espree "^9.2.0"
|
espree "^9.4.0"
|
||||||
globals "^13.9.0"
|
globals "^13.19.0"
|
||||||
ignore "^4.0.6"
|
ignore "^5.2.0"
|
||||||
import-fresh "^3.2.1"
|
import-fresh "^3.2.1"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.0"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
|
"@eslint/js@8.35.0":
|
||||||
|
version "8.35.0"
|
||||||
|
resolved "https://registry.npmmirror.com/@eslint/js/-/js-8.35.0.tgz#b7569632b0b788a0ca0e438235154e45d42813a7"
|
||||||
|
integrity sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==
|
||||||
|
|
||||||
"@floating-ui/core@^1.0.1":
|
"@floating-ui/core@^1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-1.0.1.tgz"
|
resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-1.0.1.tgz"
|
||||||
@@ -166,14 +171,19 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/core" "^1.0.1"
|
"@floating-ui/core" "^1.0.1"
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.9.2":
|
"@humanwhocodes/config-array@^0.11.8":
|
||||||
version "0.9.2"
|
version "0.11.8"
|
||||||
resolved "https://registry.npmmirror.com/@humanwhocodes/config-array/download/@humanwhocodes/config-array-0.9.2.tgz"
|
resolved "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
|
||||||
integrity sha1-aL5VxzcCMAnfxf4kXVEYG7ZHaRQ=
|
integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@humanwhocodes/object-schema" "^1.2.1"
|
"@humanwhocodes/object-schema" "^1.2.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.5"
|
||||||
|
|
||||||
|
"@humanwhocodes/module-importer@^1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
|
||||||
|
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||||
|
|
||||||
"@humanwhocodes/object-schema@^1.2.1":
|
"@humanwhocodes/object-schema@^1.2.1":
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
@@ -193,7 +203,7 @@
|
|||||||
resolved "https://registry.nlark.com/@nodelib/fs.stat/download/@nodelib/fs.stat-2.0.5.tgz?cache=0&sync_timestamp=1622792616417&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40nodelib%2Ffs.stat%2Fdownload%2F%40nodelib%2Ffs.stat-2.0.5.tgz"
|
resolved "https://registry.nlark.com/@nodelib/fs.stat/download/@nodelib/fs.stat-2.0.5.tgz?cache=0&sync_timestamp=1622792616417&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40nodelib%2Ffs.stat%2Fdownload%2F%40nodelib%2Ffs.stat-2.0.5.tgz"
|
||||||
integrity sha1-W9Jir5Tp0lvR5xsF3u1Eh2oiLos=
|
integrity sha1-W9Jir5Tp0lvR5xsF3u1Eh2oiLos=
|
||||||
|
|
||||||
"@nodelib/fs.walk@^1.2.3":
|
"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.nlark.com/@nodelib/fs.walk/download/@nodelib/fs.walk-1.2.8.tgz"
|
resolved "https://registry.nlark.com/@nodelib/fs.walk/download/@nodelib/fs.walk-1.2.8.tgz"
|
||||||
integrity sha1-6Vc36LtnRt3t9pxVaVNJTxlv5po=
|
integrity sha1-6Vc36LtnRt3t9pxVaVNJTxlv5po=
|
||||||
@@ -508,16 +518,21 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
vue-demi "*"
|
vue-demi "*"
|
||||||
|
|
||||||
acorn-jsx@^5.3.1:
|
acorn-jsx@^5.3.1, acorn-jsx@^5.3.2:
|
||||||
version "5.3.2"
|
version "5.3.2"
|
||||||
resolved "https://registry.nlark.com/acorn-jsx/download/acorn-jsx-5.3.2.tgz"
|
resolved "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||||
integrity sha1-ftW7VZCLOy8bxVxq8WU7rafweTc=
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
acorn@^8.7.0:
|
acorn@^8.7.0:
|
||||||
version "8.7.0"
|
version "8.7.0"
|
||||||
resolved "https://registry.npmmirror.com/acorn/download/acorn-8.7.0.tgz"
|
resolved "https://registry.npmmirror.com/acorn/download/acorn-8.7.0.tgz"
|
||||||
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
|
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
|
||||||
|
|
||||||
|
acorn@^8.8.0:
|
||||||
|
version "8.8.2"
|
||||||
|
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
|
||||||
|
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
|
||||||
|
|
||||||
ajv@^6.10.0, ajv@^6.12.4:
|
ajv@^6.10.0, ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.npmmirror.com/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1637522259668&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fajv%2Fdownload%2Fajv-6.12.6.tgz"
|
resolved "https://registry.npmmirror.com/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1637522259668&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fajv%2Fdownload%2Fajv-6.12.6.tgz"
|
||||||
@@ -528,11 +543,6 @@ ajv@^6.10.0, ajv@^6.12.4:
|
|||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ansi-colors@^4.1.1:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.nlark.com/ansi-colors/download/ansi-colors-4.1.1.tgz"
|
|
||||||
integrity sha1-y7muJWv3UK8eqzRPIpqif+lLo0g=
|
|
||||||
|
|
||||||
ansi-regex@^5.0.1:
|
ansi-regex@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.nlark.com/ansi-regex/download/ansi-regex-5.0.1.tgz"
|
resolved "https://registry.nlark.com/ansi-regex/download/ansi-regex-5.0.1.tgz"
|
||||||
@@ -586,10 +596,10 @@ asynckit@^0.4.0:
|
|||||||
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
|
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
|
||||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
axios@^1.3.2:
|
axios@^1.3.4:
|
||||||
version "1.3.2"
|
version "1.3.4"
|
||||||
resolved "https://registry.npmmirror.com/axios/-/axios-1.3.2.tgz#7ac517f0fa3ec46e0e636223fd973713a09c72b3"
|
resolved "https://registry.npmmirror.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
|
||||||
integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==
|
integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.15.0"
|
follow-redirects "^1.15.0"
|
||||||
form-data "^4.0.0"
|
form-data "^4.0.0"
|
||||||
@@ -782,10 +792,10 @@ echarts@^5.4.0:
|
|||||||
tslib "2.3.0"
|
tslib "2.3.0"
|
||||||
zrender "5.4.0"
|
zrender "5.4.0"
|
||||||
|
|
||||||
element-plus@^2.2.32:
|
element-plus@^2.2.33:
|
||||||
version "2.2.32"
|
version "2.2.33"
|
||||||
resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.32.tgz#e7e6757a66ed158363a86b5c9fc10dfbc0096783"
|
resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.33.tgz#30fe0db427dba42eb60a0ad8177f2d5c90435a92"
|
||||||
integrity sha512-DTJMhYOy6MApbmh6z/95hPTK5WrBiNHGzV4IN+uEkup1WoimQ+Qyt8RxKdTe/X1LWEJ8YgWv/Cl8P4ocrt5z5g==
|
integrity sha512-E/PmMnv4+4I9Ue0ZDfx2gGgGj4iBlTCWcES3o4jxfYjayFlcQO3UvElJzhaJZ8vDm9yfNN7t2w/xYOhsSYCNNg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ctrl/tinycolor" "^3.4.1"
|
"@ctrl/tinycolor" "^3.4.1"
|
||||||
"@element-plus/icons-vue" "^2.0.6"
|
"@element-plus/icons-vue" "^2.0.6"
|
||||||
@@ -803,13 +813,6 @@ element-plus@^2.2.32:
|
|||||||
memoize-one "^6.0.0"
|
memoize-one "^6.0.0"
|
||||||
normalize-wheel-es "^1.2.0"
|
normalize-wheel-es "^1.2.0"
|
||||||
|
|
||||||
enquirer@^2.3.5:
|
|
||||||
version "2.3.6"
|
|
||||||
resolved "https://registry.npm.taobao.org/enquirer/download/enquirer-2.3.6.tgz"
|
|
||||||
integrity sha1-Kn/l3WNKHkElqXXsmU/1RW3Dc00=
|
|
||||||
dependencies:
|
|
||||||
ansi-colors "^4.1.1"
|
|
||||||
|
|
||||||
esbuild@^0.16.14:
|
esbuild@^0.16.14:
|
||||||
version "0.16.17"
|
version "0.16.17"
|
||||||
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259"
|
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259"
|
||||||
@@ -874,10 +877,10 @@ eslint-scope@^6.0.0:
|
|||||||
esrecurse "^4.3.0"
|
esrecurse "^4.3.0"
|
||||||
estraverse "^5.2.0"
|
estraverse "^5.2.0"
|
||||||
|
|
||||||
eslint-scope@^7.1.0:
|
eslint-scope@^7.1.1:
|
||||||
version "7.1.0"
|
version "7.1.1"
|
||||||
resolved "https://registry.npmmirror.com/eslint-scope/download/eslint-scope-7.1.0.tgz?cache=0&sync_timestamp=1637466831846&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-scope%2Fdownload%2Feslint-scope-7.1.0.tgz"
|
resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
|
||||||
integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==
|
integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
|
||||||
dependencies:
|
dependencies:
|
||||||
esrecurse "^4.3.0"
|
esrecurse "^4.3.0"
|
||||||
estraverse "^5.2.0"
|
estraverse "^5.2.0"
|
||||||
@@ -899,51 +902,58 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0:
|
|||||||
resolved "https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-3.1.0.tgz?cache=0&sync_timestamp=1636378395014&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-3.1.0.tgz"
|
resolved "https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-3.1.0.tgz?cache=0&sync_timestamp=1636378395014&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-3.1.0.tgz"
|
||||||
integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==
|
integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==
|
||||||
|
|
||||||
eslint@^8.5.0:
|
eslint-visitor-keys@^3.3.0:
|
||||||
version "8.6.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.npmmirror.com/eslint/download/eslint-8.6.0.tgz"
|
resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
||||||
integrity sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==
|
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||||
|
|
||||||
|
eslint@^8.35.0:
|
||||||
|
version "8.35.0"
|
||||||
|
resolved "https://registry.npmmirror.com/eslint/-/eslint-8.35.0.tgz#fffad7c7e326bae606f0e8f436a6158566d42323"
|
||||||
|
integrity sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc" "^1.0.5"
|
"@eslint/eslintrc" "^2.0.0"
|
||||||
"@humanwhocodes/config-array" "^0.9.2"
|
"@eslint/js" "8.35.0"
|
||||||
|
"@humanwhocodes/config-array" "^0.11.8"
|
||||||
|
"@humanwhocodes/module-importer" "^1.0.1"
|
||||||
|
"@nodelib/fs.walk" "^1.2.8"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
cross-spawn "^7.0.2"
|
cross-spawn "^7.0.2"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
doctrine "^3.0.0"
|
doctrine "^3.0.0"
|
||||||
enquirer "^2.3.5"
|
|
||||||
escape-string-regexp "^4.0.0"
|
escape-string-regexp "^4.0.0"
|
||||||
eslint-scope "^7.1.0"
|
eslint-scope "^7.1.1"
|
||||||
eslint-utils "^3.0.0"
|
eslint-utils "^3.0.0"
|
||||||
eslint-visitor-keys "^3.1.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
espree "^9.3.0"
|
espree "^9.4.0"
|
||||||
esquery "^1.4.0"
|
esquery "^1.4.2"
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
fast-deep-equal "^3.1.3"
|
fast-deep-equal "^3.1.3"
|
||||||
file-entry-cache "^6.0.1"
|
file-entry-cache "^6.0.1"
|
||||||
functional-red-black-tree "^1.0.1"
|
find-up "^5.0.0"
|
||||||
glob-parent "^6.0.1"
|
glob-parent "^6.0.2"
|
||||||
globals "^13.6.0"
|
globals "^13.19.0"
|
||||||
ignore "^4.0.6"
|
grapheme-splitter "^1.0.4"
|
||||||
|
ignore "^5.2.0"
|
||||||
import-fresh "^3.0.0"
|
import-fresh "^3.0.0"
|
||||||
imurmurhash "^0.1.4"
|
imurmurhash "^0.1.4"
|
||||||
is-glob "^4.0.0"
|
is-glob "^4.0.0"
|
||||||
|
is-path-inside "^3.0.3"
|
||||||
|
js-sdsl "^4.1.4"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.0"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
levn "^0.4.1"
|
levn "^0.4.1"
|
||||||
lodash.merge "^4.6.2"
|
lodash.merge "^4.6.2"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.1.2"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
optionator "^0.9.1"
|
optionator "^0.9.1"
|
||||||
progress "^2.0.0"
|
|
||||||
regexpp "^3.2.0"
|
regexpp "^3.2.0"
|
||||||
semver "^7.2.1"
|
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
strip-json-comments "^3.1.0"
|
strip-json-comments "^3.1.0"
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
v8-compile-cache "^2.0.3"
|
|
||||||
|
|
||||||
espree@^9.0.0, espree@^9.2.0, espree@^9.3.0:
|
espree@^9.0.0:
|
||||||
version "9.3.0"
|
version "9.3.0"
|
||||||
resolved "https://registry.npmmirror.com/espree/download/espree-9.3.0.tgz"
|
resolved "https://registry.npmmirror.com/espree/download/espree-9.3.0.tgz"
|
||||||
integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==
|
integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==
|
||||||
@@ -952,6 +962,15 @@ espree@^9.0.0, espree@^9.2.0, espree@^9.3.0:
|
|||||||
acorn-jsx "^5.3.1"
|
acorn-jsx "^5.3.1"
|
||||||
eslint-visitor-keys "^3.1.0"
|
eslint-visitor-keys "^3.1.0"
|
||||||
|
|
||||||
|
espree@^9.4.0:
|
||||||
|
version "9.4.1"
|
||||||
|
resolved "https://registry.npmmirror.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd"
|
||||||
|
integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==
|
||||||
|
dependencies:
|
||||||
|
acorn "^8.8.0"
|
||||||
|
acorn-jsx "^5.3.2"
|
||||||
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
|
||||||
esquery@^1.4.0:
|
esquery@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.nlark.com/esquery/download/esquery-1.4.0.tgz"
|
resolved "https://registry.nlark.com/esquery/download/esquery-1.4.0.tgz"
|
||||||
@@ -959,6 +978,13 @@ esquery@^1.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
estraverse "^5.1.0"
|
estraverse "^5.1.0"
|
||||||
|
|
||||||
|
esquery@^1.4.2:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
|
||||||
|
integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
|
||||||
|
dependencies:
|
||||||
|
estraverse "^5.1.0"
|
||||||
|
|
||||||
esrecurse@^4.3.0:
|
esrecurse@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.npm.taobao.org/esrecurse/download/esrecurse-4.3.0.tgz"
|
resolved "https://registry.npm.taobao.org/esrecurse/download/esrecurse-4.3.0.tgz"
|
||||||
@@ -1038,6 +1064,14 @@ fill-range@^7.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
to-regex-range "^5.0.1"
|
to-regex-range "^5.0.1"
|
||||||
|
|
||||||
|
find-up@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
|
||||||
|
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
|
||||||
|
dependencies:
|
||||||
|
locate-path "^6.0.0"
|
||||||
|
path-exists "^4.0.0"
|
||||||
|
|
||||||
flat-cache@^3.0.4:
|
flat-cache@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.nlark.com/flat-cache/download/flat-cache-3.0.4.tgz"
|
resolved "https://registry.nlark.com/flat-cache/download/flat-cache-3.0.4.tgz"
|
||||||
@@ -1092,10 +1126,10 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
glob-parent@^6.0.1:
|
glob-parent@^6.0.2:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
resolved "https://registry.npmmirror.com/glob-parent/download/glob-parent-6.0.2.tgz?cache=0&sync_timestamp=1632953697891&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fglob-parent%2Fdownload%2Fglob-parent-6.0.2.tgz"
|
resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
|
||||||
integrity sha1-bSN9mQg5UMeSkPJMdkKj3poo+eM=
|
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.3"
|
is-glob "^4.0.3"
|
||||||
|
|
||||||
@@ -1111,10 +1145,10 @@ glob@^7.1.3:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
globals@^13.6.0, globals@^13.9.0:
|
globals@^13.19.0:
|
||||||
version "13.12.0"
|
version "13.20.0"
|
||||||
resolved "https://registry.npmmirror.com/globals/download/globals-13.12.0.tgz"
|
resolved "https://registry.npmmirror.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
|
||||||
integrity sha1-TXM3YDBCMKAILtluIeXFZfiYCJ4=
|
integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.20.2"
|
type-fest "^0.20.2"
|
||||||
|
|
||||||
@@ -1137,6 +1171,11 @@ good-listener@^1.2.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
delegate "^3.1.2"
|
delegate "^3.1.2"
|
||||||
|
|
||||||
|
grapheme-splitter@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.npmmirror.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
|
||||||
|
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
|
||||||
|
|
||||||
has-flag@^4.0.0:
|
has-flag@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.nlark.com/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1626715907927&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz"
|
resolved "https://registry.nlark.com/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1626715907927&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz"
|
||||||
@@ -1149,16 +1188,16 @@ has@^1.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
|
|
||||||
ignore@^4.0.6:
|
|
||||||
version "4.0.6"
|
|
||||||
resolved "https://registry.npmmirror.com/ignore/download/ignore-4.0.6.tgz?cache=0&sync_timestamp=1635926740448&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fignore%2Fdownload%2Fignore-4.0.6.tgz"
|
|
||||||
integrity sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=
|
|
||||||
|
|
||||||
ignore@^5.1.4, ignore@^5.1.8:
|
ignore@^5.1.4, ignore@^5.1.8:
|
||||||
version "5.1.9"
|
version "5.1.9"
|
||||||
resolved "https://registry.npmmirror.com/ignore/download/ignore-5.1.9.tgz?cache=0&sync_timestamp=1635926740448&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fignore%2Fdownload%2Fignore-5.1.9.tgz"
|
resolved "https://registry.npmmirror.com/ignore/download/ignore-5.1.9.tgz?cache=0&sync_timestamp=1635926740448&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fignore%2Fdownload%2Fignore-5.1.9.tgz"
|
||||||
integrity sha1-nsGly+jhRG7GDUQgBg1Dqm5zgvs=
|
integrity sha1-nsGly+jhRG7GDUQgBg1Dqm5zgvs=
|
||||||
|
|
||||||
|
ignore@^5.2.0:
|
||||||
|
version "5.2.4"
|
||||||
|
resolved "https://registry.npmmirror.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
|
||||||
|
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
|
||||||
|
|
||||||
immutable@^4.0.0:
|
immutable@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmmirror.com/immutable/download/immutable-4.0.0.tgz?cache=0&sync_timestamp=1633650644342&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fimmutable%2Fdownload%2Fimmutable-4.0.0.tgz"
|
resolved "https://registry.npmmirror.com/immutable/download/immutable-4.0.0.tgz?cache=0&sync_timestamp=1633650644342&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fimmutable%2Fdownload%2Fimmutable-4.0.0.tgz"
|
||||||
@@ -1221,11 +1260,21 @@ is-number@^7.0.0:
|
|||||||
resolved "https://registry.nlark.com/is-number/download/is-number-7.0.0.tgz"
|
resolved "https://registry.nlark.com/is-number/download/is-number-7.0.0.tgz"
|
||||||
integrity sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=
|
integrity sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=
|
||||||
|
|
||||||
|
is-path-inside@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||||
|
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||||
|
|
||||||
isexe@^2.0.0:
|
isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz"
|
resolved "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz"
|
||||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||||
|
|
||||||
|
js-sdsl@^4.1.4:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
|
||||||
|
integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
|
||||||
|
|
||||||
js-yaml@^4.1.0:
|
js-yaml@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.npmmirror.com/js-yaml/download/js-yaml-4.1.0.tgz"
|
resolved "https://registry.npmmirror.com/js-yaml/download/js-yaml-4.1.0.tgz"
|
||||||
@@ -1233,10 +1282,10 @@ js-yaml@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
argparse "^2.0.1"
|
argparse "^2.0.1"
|
||||||
|
|
||||||
jsencrypt@^3.2.1:
|
jsencrypt@^3.3.1:
|
||||||
version "3.2.1"
|
version "3.3.2"
|
||||||
resolved "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.2.1.tgz"
|
resolved "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz#b0f1a2278810c7ba1cb8957af11195354622df7c"
|
||||||
integrity sha512-k1sD5QV0KPn+D8uG9AdGzTQuamt82QZ3A3l6f7TRwMU6Oi2Vg0BsL+wZIQBONcraO1pc78ExMdvmBBJ8WhNYUA==
|
integrity sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==
|
||||||
|
|
||||||
json-schema-traverse@^0.4.1:
|
json-schema-traverse@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
@@ -1261,6 +1310,13 @@ levn@^0.4.1:
|
|||||||
prelude-ls "^1.2.1"
|
prelude-ls "^1.2.1"
|
||||||
type-check "~0.4.0"
|
type-check "~0.4.0"
|
||||||
|
|
||||||
|
locate-path@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
|
||||||
|
integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
|
||||||
|
dependencies:
|
||||||
|
p-locate "^5.0.0"
|
||||||
|
|
||||||
lodash-es@^4.17.21:
|
lodash-es@^4.17.21:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz"
|
resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz"
|
||||||
@@ -1332,15 +1388,22 @@ minimatch@^3.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
minimatch@^3.0.5, minimatch@^3.1.2:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
mitt@^3.0.0:
|
mitt@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmmirror.com/mitt/download/mitt-3.0.0.tgz"
|
resolved "https://registry.npmmirror.com/mitt/download/mitt-3.0.0.tgz"
|
||||||
integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
|
integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
|
||||||
|
|
||||||
monaco-editor@^0.35.0:
|
monaco-editor@^0.36.1:
|
||||||
version "0.35.0"
|
version "0.36.1"
|
||||||
resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.35.0.tgz#49c4220c815262a900dacf0ae8a59bef66efab8b"
|
resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.36.1.tgz#aad528c815605307473a1634612946921d8079b5"
|
||||||
integrity sha512-BJfkAZ0EJ7JgrgWzqjfBNP9hPSS8NlfECEDMEIIiozV2UaPq22yeuOjgbd3TwMh3anH0krWZirXZfn8KUSxiOA==
|
integrity sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg==
|
||||||
|
|
||||||
monaco-sql-languages@^0.11.0:
|
monaco-sql-languages@^0.11.0:
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
@@ -1430,6 +1493,20 @@ optionator@^0.9.1:
|
|||||||
type-check "^0.4.0"
|
type-check "^0.4.0"
|
||||||
word-wrap "^1.2.3"
|
word-wrap "^1.2.3"
|
||||||
|
|
||||||
|
p-limit@^3.0.2:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
|
||||||
|
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
|
||||||
|
dependencies:
|
||||||
|
yocto-queue "^0.1.0"
|
||||||
|
|
||||||
|
p-locate@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
|
||||||
|
integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
|
||||||
|
dependencies:
|
||||||
|
p-limit "^3.0.2"
|
||||||
|
|
||||||
parent-module@^1.0.0:
|
parent-module@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmmirror.com/parent-module/download/parent-module-1.0.1.tgz"
|
resolved "https://registry.npmmirror.com/parent-module/download/parent-module-1.0.1.tgz"
|
||||||
@@ -1437,6 +1514,11 @@ parent-module@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
callsites "^3.0.0"
|
||||||
|
|
||||||
|
path-exists@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||||
|
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||||
|
|
||||||
path-is-absolute@^1.0.0:
|
path-is-absolute@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.nlark.com/path-is-absolute/download/path-is-absolute-1.0.1.tgz"
|
resolved "https://registry.nlark.com/path-is-absolute/download/path-is-absolute-1.0.1.tgz"
|
||||||
@@ -1495,11 +1577,6 @@ prettier@^2.3.0:
|
|||||||
resolved "https://registry.npmmirror.com/prettier/download/prettier-2.5.1.tgz"
|
resolved "https://registry.npmmirror.com/prettier/download/prettier-2.5.1.tgz"
|
||||||
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
|
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
|
||||||
|
|
||||||
progress@^2.0.0:
|
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.nlark.com/progress/download/progress-2.0.3.tgz"
|
|
||||||
integrity sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=
|
|
||||||
|
|
||||||
proxy-from-env@^1.1.0:
|
proxy-from-env@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
||||||
@@ -1617,7 +1694,7 @@ select@^1.1.2:
|
|||||||
resolved "https://registry.nlark.com/select/download/select-1.1.2.tgz"
|
resolved "https://registry.nlark.com/select/download/select-1.1.2.tgz"
|
||||||
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
|
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
|
||||||
|
|
||||||
semver@^7.2.1, semver@^7.3.5:
|
semver@^7.3.5:
|
||||||
version "7.3.5"
|
version "7.3.5"
|
||||||
resolved "https://registry.nlark.com/semver/download/semver-7.3.5.tgz?cache=0&sync_timestamp=1618846864940&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-7.3.5.tgz"
|
resolved "https://registry.nlark.com/semver/download/semver-7.3.5.tgz?cache=0&sync_timestamp=1618846864940&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-7.3.5.tgz"
|
||||||
integrity sha1-C2Ich5NI2JmOSw5L6Us/EuYBjvc=
|
integrity sha1-C2Ich5NI2JmOSw5L6Us/EuYBjvc=
|
||||||
@@ -1751,10 +1828,10 @@ type-fest@^0.20.2:
|
|||||||
resolved "https://registry.npmmirror.com/type-fest/download/type-fest-0.20.2.tgz"
|
resolved "https://registry.npmmirror.com/type-fest/download/type-fest-0.20.2.tgz"
|
||||||
integrity sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=
|
integrity sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=
|
||||||
|
|
||||||
typescript@^4.7.4:
|
typescript@^4.9.5:
|
||||||
version "4.7.4"
|
version "4.9.5"
|
||||||
resolved "https://registry.npmmirror.com/typescript/-/typescript-4.7.4.tgz"
|
resolved "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
|
||||||
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
|
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
|
||||||
|
|
||||||
uri-js@^4.2.2:
|
uri-js@^4.2.2:
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
@@ -1763,15 +1840,10 @@ uri-js@^4.2.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
v8-compile-cache@^2.0.3:
|
vite@^4.1.4:
|
||||||
version "2.3.0"
|
version "4.1.4"
|
||||||
resolved "https://registry.nlark.com/v8-compile-cache/download/v8-compile-cache-2.3.0.tgz"
|
resolved "https://registry.npmmirror.com/vite/-/vite-4.1.4.tgz#170d93bcff97e0ebc09764c053eebe130bfe6ca0"
|
||||||
integrity sha1-LeGWGMZtwkfc+2+ZM4A12CRaLO4=
|
integrity sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==
|
||||||
|
|
||||||
vite@^4.1.1:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.npmmirror.com/vite/-/vite-4.1.1.tgz#3b18b81a4e85ce3df5cbdbf4c687d93ebf402e6b"
|
|
||||||
integrity sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.16.14"
|
esbuild "^0.16.14"
|
||||||
postcss "^8.4.21"
|
postcss "^8.4.21"
|
||||||
@@ -1862,6 +1934,11 @@ yallist@^4.0.0:
|
|||||||
resolved "https://registry.nlark.com/yallist/download/yallist-4.0.0.tgz"
|
resolved "https://registry.nlark.com/yallist/download/yallist-4.0.0.tgz"
|
||||||
integrity sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=
|
integrity sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=
|
||||||
|
|
||||||
|
yocto-queue@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
|
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||||
|
|
||||||
zrender@5.4.0:
|
zrender@5.4.0:
|
||||||
version "5.4.0"
|
version "5.4.0"
|
||||||
resolved "https://registry.npmmirror.com/zrender/-/zrender-5.4.0.tgz#d4f76e527b2e3bbd7add2bdaf27a16af85785576"
|
resolved "https://registry.npmmirror.com/zrender/-/zrender-5.4.0.tgz#d4f76e527b2e3bbd7add2bdaf27a16af85785576"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/go-sql-driver/mysql v1.7.0
|
github.com/go-sql-driver/mysql v1.7.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.3
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/lib/pq v1.10.7
|
github.com/lib/pq v1.10.7
|
||||||
github.com/mojocn/base64Captcha v1.3.5 // 验证码
|
github.com/mojocn/base64Captcha v1.3.5 // 验证码
|
||||||
@@ -15,11 +15,11 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
||||||
go.mongodb.org/mongo-driver v1.11.1 // mongo
|
go.mongodb.org/mongo-driver v1.11.1 // mongo
|
||||||
golang.org/x/crypto v0.6.0 // ssh
|
golang.org/x/crypto v0.7.0 // ssh
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
// gorm
|
// gorm
|
||||||
gorm.io/driver/mysql v1.4.5
|
gorm.io/driver/mysql v1.4.5
|
||||||
gorm.io/gorm v1.24.3
|
gorm.io/gorm v1.24.6
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -55,10 +55,10 @@ require (
|
|||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
||||||
golang.org/x/net v0.7.0 // indirect
|
golang.org/x/net v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
package form
|
package form
|
||||||
|
|
||||||
type DbForm struct {
|
type DbForm struct {
|
||||||
Id uint64
|
Id uint64
|
||||||
Name string `binding:"required" json:"name"`
|
Name string `binding:"required" json:"name"`
|
||||||
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
||||||
Host string `binding:"required" json:"host"`
|
Host string `binding:"required" json:"host"`
|
||||||
Port int `binding:"required" json:"port"`
|
Port int `binding:"required" json:"port"`
|
||||||
Username string `binding:"required" json:"username"`
|
Username string `binding:"required" json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Params string `json:"params"`
|
Params string `json:"params"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
TagId uint64 `json:"tagId"`
|
TagId uint64 `json:"tagId"`
|
||||||
TagPath string `json:"tagPath"`
|
TagPath string `json:"tagPath"`
|
||||||
|
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||||
EnableSshTunnel int8 `json:"enableSshTunnel"`
|
|
||||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DbSqlSaveForm struct {
|
type DbSqlSaveForm struct {
|
||||||
|
|||||||
@@ -23,6 +23,5 @@ type SelectDataDbVO struct {
|
|||||||
Modifier *string `json:"modifier"`
|
Modifier *string `json:"modifier"`
|
||||||
ModifierId *int64 `json:"modifierId"`
|
ModifierId *int64 `json:"modifierId"`
|
||||||
|
|
||||||
EnableSshTunnel *int8 `json:"enableSshTunnel"`
|
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||||
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (d *dbAppImpl) Save(dbEntity *entity.Db) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查找是否存在该库
|
// 查找是否存在该库
|
||||||
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, TagId: dbEntity.TagId}
|
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, Username: dbEntity.Username}
|
||||||
err := d.GetDbBy(oldDb)
|
err := d.GetDbBy(oldDb)
|
||||||
|
|
||||||
if dbEntity.Id == 0 {
|
if dbEntity.Id == 0 {
|
||||||
@@ -228,8 +228,7 @@ type DbInfo struct {
|
|||||||
Username string
|
Username string
|
||||||
TagPath string
|
TagPath string
|
||||||
Database string
|
Database string
|
||||||
EnableSshTunnel int8 // 是否启用ssh隧道
|
SshTunnelMachineId int
|
||||||
SshTunnelMachineId uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取记录日志的描述
|
// 获取记录日志的描述
|
||||||
@@ -306,7 +305,7 @@ var dbCache = cache.NewTimedCache(constant.DbConnExpireTime, 5*time.Second).
|
|||||||
})
|
})
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
|
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
|
||||||
// 遍历所有db连接实例,若存在db实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
// 遍历所有db连接实例,若存在db实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
||||||
items := dbCache.Items()
|
items := dbCache.Items()
|
||||||
for _, v := range items {
|
for _, v := range items {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
func getMysqlDB(d *entity.Db, db string) (*sql.DB, error) {
|
func getMysqlDB(d *entity.Db, db string) (*sql.DB, error) {
|
||||||
// SSH Conect
|
// SSH Conect
|
||||||
if d.EnableSshTunnel == 1 && d.SshTunnelMachineId != 0 {
|
if d.SshTunnelMachineId > 0 {
|
||||||
sshTunnelMachine := machineapp.GetMachineApp().GetSshTunnelMachine(d.SshTunnelMachineId)
|
sshTunnelMachine := machineapp.GetMachineApp().GetSshTunnelMachine(d.SshTunnelMachineId)
|
||||||
mysql.RegisterDialContext(d.Network, func(ctx context.Context, addr string) (net.Conn, error) {
|
mysql.RegisterDialContext(d.Network, func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
return sshTunnelMachine.GetDialConn("tcp", addr)
|
return sshTunnelMachine.GetDialConn("tcp", addr)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
func getPgsqlDB(d *entity.Db, db string) (*sql.DB, error) {
|
func getPgsqlDB(d *entity.Db, db string) (*sql.DB, error) {
|
||||||
driverName := d.Type
|
driverName := d.Type
|
||||||
// SSH Conect
|
// SSH Conect
|
||||||
if d.EnableSshTunnel == 1 && d.SshTunnelMachineId != 0 {
|
if d.SshTunnelMachineId > 0 {
|
||||||
// 如果使用了隧道,则使用`postgres:ssh:隧道机器id`注册名
|
// 如果使用了隧道,则使用`postgres:ssh:隧道机器id`注册名
|
||||||
driverName = fmt.Sprintf("postgres:ssh:%d", d.SshTunnelMachineId)
|
driverName = fmt.Sprintf("postgres:ssh:%d", d.SshTunnelMachineId)
|
||||||
if !utils.ArrContains(sql.Drivers(), driverName) {
|
if !utils.ArrContains(sql.Drivers(), driverName) {
|
||||||
@@ -36,7 +36,7 @@ func getPgsqlDB(d *entity.Db, db string) (*sql.DB, error) {
|
|||||||
|
|
||||||
// pgsql dialer
|
// pgsql dialer
|
||||||
type PqSqlDialer struct {
|
type PqSqlDialer struct {
|
||||||
sshTunnelMachineId uint64
|
sshTunnelMachineId int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *PqSqlDialer) Open(name string) (driver.Conn, error) {
|
func (d *PqSqlDialer) Open(name string) (driver.Conn, error) {
|
||||||
|
|||||||
@@ -9,27 +9,25 @@ import (
|
|||||||
type Db struct {
|
type Db struct {
|
||||||
model.Model
|
model.Model
|
||||||
|
|
||||||
Name string `orm:"column(name)" json:"name"`
|
Name string `orm:"column(name)" json:"name"`
|
||||||
Type string `orm:"column(type)" json:"type"` // 类型,mysql oracle等
|
Type string `orm:"column(type)" json:"type"` // 类型,mysql oracle等
|
||||||
Host string `orm:"column(host)" json:"host"`
|
Host string `orm:"column(host)" json:"host"`
|
||||||
Port int `orm:"column(port)" json:"port"`
|
Port int `orm:"column(port)" json:"port"`
|
||||||
Network string `orm:"column(network)" json:"network"`
|
Network string `orm:"column(network)" json:"network"`
|
||||||
Username string `orm:"column(username)" json:"username"`
|
Username string `orm:"column(username)" json:"username"`
|
||||||
Password string `orm:"column(password)" json:"-"`
|
Password string `orm:"column(password)" json:"-"`
|
||||||
Database string `orm:"column(database)" json:"database"`
|
Database string `orm:"column(database)" json:"database"`
|
||||||
Params string `json:"params"`
|
Params string `json:"params"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
TagId uint64
|
TagId uint64
|
||||||
TagPath string
|
TagPath string
|
||||||
|
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
|
||||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据库连接网络, 若没有使用ssh隧道,则直接返回。否则返回拼接的网络需要注册至指定dial
|
// 获取数据库连接网络, 若没有使用ssh隧道,则直接返回。否则返回拼接的网络需要注册至指定dial
|
||||||
func (d *Db) GetNetwork() string {
|
func (d *Db) GetNetwork() string {
|
||||||
network := d.Network
|
network := d.Network
|
||||||
if d.EnableSshTunnel == 0 || d.EnableSshTunnel == -1 {
|
if d.SshTunnelMachineId <= 0 {
|
||||||
if network == "" {
|
if network == "" {
|
||||||
return "tcp"
|
return "tcp"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ type DbQuery struct {
|
|||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
TagId uint64
|
TagId uint64
|
||||||
|
|
||||||
ProjectIds []uint64
|
|
||||||
TagIds []uint64
|
TagIds []uint64
|
||||||
TagPathLike string
|
TagPathLike string
|
||||||
}
|
}
|
||||||
|
|||||||
62
server/internal/machine/api/auth_cert.go
Normal file
62
server/internal/machine/api/auth_cert.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/api/form"
|
||||||
|
"mayfly-go/internal/machine/api/vo"
|
||||||
|
"mayfly-go/internal/machine/application"
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/pkg/ginx"
|
||||||
|
"mayfly-go/pkg/req"
|
||||||
|
"mayfly-go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthCert struct {
|
||||||
|
AuthCertApp application.AuthCert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *AuthCert) BaseAuthCerts(rc *req.Ctx) {
|
||||||
|
g := rc.GinCtx
|
||||||
|
condition := &entity.AuthCert{
|
||||||
|
Name: g.Query("name"),
|
||||||
|
AuthMethod: int8(ginx.QueryInt(g, "authMethod", 0)),
|
||||||
|
}
|
||||||
|
condition.Id = uint64(ginx.QueryInt(g, "id", 0))
|
||||||
|
rc.ResData = ac.AuthCertApp.GetPageList(condition, ginx.GetPageParam(g), new([]vo.AuthCertBaseVO))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *AuthCert) AuthCerts(rc *req.Ctx) {
|
||||||
|
g := rc.GinCtx
|
||||||
|
condition := &entity.AuthCert{
|
||||||
|
Name: g.Query("name"),
|
||||||
|
AuthMethod: int8(ginx.QueryInt(g, "authMethod", 0)),
|
||||||
|
}
|
||||||
|
condition.Id = uint64(ginx.QueryInt(g, "id", 0))
|
||||||
|
|
||||||
|
res := new([]*entity.AuthCert)
|
||||||
|
pageRes := ac.AuthCertApp.GetPageList(condition, ginx.GetPageParam(g), res)
|
||||||
|
for _, r := range *res {
|
||||||
|
r.PwdDecrypt()
|
||||||
|
}
|
||||||
|
rc.ResData = pageRes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthCert) SaveAuthCert(rc *req.Ctx) {
|
||||||
|
g := rc.GinCtx
|
||||||
|
acForm := &form.AuthCertForm{}
|
||||||
|
ginx.BindJsonAndValid(g, acForm)
|
||||||
|
|
||||||
|
ac := new(entity.AuthCert)
|
||||||
|
utils.Copy(ac, acForm)
|
||||||
|
|
||||||
|
// 脱敏记录日志
|
||||||
|
acForm.Passphrase = "***"
|
||||||
|
acForm.Password = "***"
|
||||||
|
rc.ReqParam = acForm
|
||||||
|
|
||||||
|
ac.SetBaseInfo(rc.LoginAccount)
|
||||||
|
c.AuthCertApp.Save(ac)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthCert) Delete(rc *req.Ctx) {
|
||||||
|
c.AuthCertApp.DeleteById(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||||
|
}
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
package form
|
package form
|
||||||
|
|
||||||
type MachineForm struct {
|
type MachineForm struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Name string `json:"name" binding:"required"`
|
Name string `json:"name" binding:"required"`
|
||||||
Ip string `json:"ip" binding:"required"` // IP地址
|
Ip string `json:"ip" binding:"required"` // IP地址
|
||||||
Username string `json:"username" binding:"required"` // 用户名
|
Port int `json:"port" binding:"required"` // 端口号
|
||||||
AuthMethod int8 `json:"authMethod" binding:"required"`
|
|
||||||
Password string `json:"password"`
|
// 资产授权凭证信息列表
|
||||||
Port int `json:"port" binding:"required"` // 端口号
|
AuthCertId int `json:"authCertId"`
|
||||||
|
TagId uint64 `json:"tagId"`
|
||||||
|
TagPath string `json:"tagPath" binding:"required"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||||
TagId uint64 `json:"tagId"`
|
|
||||||
TagPath string `json:"tagPath"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineRunForm struct {
|
type MachineRunForm struct {
|
||||||
@@ -49,3 +51,21 @@ type MachineFileUpdateForm struct {
|
|||||||
Id uint64 `binding:"required"`
|
Id uint64 `binding:"required"`
|
||||||
Path string `binding:"required"`
|
Path string `binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 授权凭证
|
||||||
|
type AuthCertForm struct {
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
AuthMethod int8 `json:"authMethod" binding:"required"` // 1.密码 2.秘钥
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"` // 密码or私钥
|
||||||
|
Passphrase string `json:"passphrase"` // 私钥口令
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 资产授权凭证信息
|
||||||
|
type AssetAuthCertForm struct {
|
||||||
|
AuthCertId uint64 `json:"authCertId"`
|
||||||
|
TagId uint64 `json:"tagId"`
|
||||||
|
TagPath string `json:"tagPath" binding:"required"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func (m *Machine) Machines(rc *req.Ctx) {
|
|||||||
|
|
||||||
list := res.List.(*[]*vo.MachineVO)
|
list := res.List.(*[]*vo.MachineVO)
|
||||||
for _, mv := range *list {
|
for _, mv := range *list {
|
||||||
mv.HasCli = machine.HasCli(*mv.Id)
|
mv.HasCli = machine.HasCli(mv.Id)
|
||||||
}
|
}
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -63,6 +63,7 @@ func (m *Machine) MachineStats(rc *req.Ctx) {
|
|||||||
rc.ResData = stats
|
rc.ResData = stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存机器信息
|
||||||
func (m *Machine) SaveMachine(rc *req.Ctx) {
|
func (m *Machine) SaveMachine(rc *req.Ctx) {
|
||||||
g := rc.GinCtx
|
g := rc.GinCtx
|
||||||
machineForm := new(form.MachineForm)
|
machineForm := new(form.MachineForm)
|
||||||
@@ -71,27 +72,22 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
|
|||||||
me := new(entity.Machine)
|
me := new(entity.Machine)
|
||||||
utils.Copy(me, machineForm)
|
utils.Copy(me, machineForm)
|
||||||
|
|
||||||
if me.AuthMethod == entity.MachineAuthMethodPassword {
|
machineForm.Password = "******"
|
||||||
// 密码解密,并使用解密后的赋值
|
|
||||||
originPwd, err := utils.DefaultRsaDecrypt(machineForm.Password, true)
|
|
||||||
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
|
|
||||||
me.Password = originPwd
|
|
||||||
}
|
|
||||||
|
|
||||||
// 密码脱敏记录日志
|
|
||||||
machineForm.Password = "****"
|
|
||||||
rc.ReqParam = machineForm
|
rc.ReqParam = machineForm
|
||||||
|
|
||||||
me.SetBaseInfo(rc.LoginAccount)
|
me.SetBaseInfo(rc.LoginAccount)
|
||||||
m.MachineApp.Save(me)
|
m.MachineApp.Save(me)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取机器实例密码,由于数据库是加密存储,故提供该接口展示原文密码
|
func (m *Machine) TestConn(rc *req.Ctx) {
|
||||||
func (m *Machine) GetMachinePwd(rc *req.Ctx) {
|
g := rc.GinCtx
|
||||||
mid := GetMachineId(rc.GinCtx)
|
machineForm := new(form.MachineForm)
|
||||||
me := m.MachineApp.GetById(mid, "Password")
|
ginx.BindJsonAndValid(g, machineForm)
|
||||||
me.PwdDecrypt()
|
|
||||||
rc.ResData = me.Password
|
me := new(entity.Machine)
|
||||||
|
utils.Copy(me, machineForm)
|
||||||
|
|
||||||
|
m.MachineApp.TestConn(me)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Machine) ChangeStatus(rc *req.Ctx) {
|
func (m *Machine) ChangeStatus(rc *req.Ctx) {
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package vo
|
package vo
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 授权凭证基础信息
|
||||||
|
type AuthCertBaseVO struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AuthMethod int8 `json:"authMethod"`
|
||||||
|
}
|
||||||
|
|
||||||
type MachineVO struct {
|
type MachineVO struct {
|
||||||
Id *uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Name *string `json:"name"`
|
Name string `json:"name"`
|
||||||
Username *string `json:"username"`
|
Ip string `json:"ip"`
|
||||||
Ip *string `json:"ip"`
|
Port int `json:"port"`
|
||||||
Port *int `json:"port"`
|
Username string `json:"username"`
|
||||||
AuthMethod *int8 `json:"authMethod"`
|
AuthCertId int `json:"authCertId"`
|
||||||
Status *int8 `json:"status"`
|
Status *int8 `json:"status"`
|
||||||
EnableSshTunnel *int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
CreateTime *time.Time `json:"createTime"`
|
CreateTime *time.Time `json:"createTime"`
|
||||||
Creator *string `json:"creator"`
|
Creator *string `json:"creator"`
|
||||||
CreatorId *int64 `json:"creatorId"`
|
CreatorId *int64 `json:"creatorId"`
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import "mayfly-go/internal/machine/infrastructure/persistence"
|
import (
|
||||||
|
"mayfly-go/internal/machine/infrastructure/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
machineApp Machine = newMachineApp(persistence.GetMachineRepo())
|
machineFileApp MachineFile = newMachineFileApp(persistence.GetMachineFileRepo(), persistence.GetMachineRepo())
|
||||||
machineFileApp MachineFile = newMachineFileApp(persistence.GetMachineFileRepo(), persistence.GetMachineRepo())
|
|
||||||
machineScriptApp MachineScript = newMachineScriptApp(persistence.GetMachineScriptRepo(), persistence.GetMachineRepo())
|
machineScriptApp MachineScript = newMachineScriptApp(persistence.GetMachineScriptRepo(), persistence.GetMachineRepo())
|
||||||
|
|
||||||
|
authCertApp AuthCert = newAuthCertApp(persistence.GetAuthCertRepo())
|
||||||
|
|
||||||
|
machineApp Machine = newMachineApp(
|
||||||
|
persistence.GetMachineRepo(),
|
||||||
|
GetAuthCertApp(),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetMachineApp() Machine {
|
func GetMachineApp() Machine {
|
||||||
@@ -19,3 +28,7 @@ func GetMachineFileApp() MachineFile {
|
|||||||
func GetMachineScriptApp() MachineScript {
|
func GetMachineScriptApp() MachineScript {
|
||||||
return machineScriptApp
|
return machineScriptApp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAuthCertApp() AuthCert {
|
||||||
|
return authCertApp
|
||||||
|
}
|
||||||
|
|||||||
64
server/internal/machine/application/auth_cert_app.go
Normal file
64
server/internal/machine/application/auth_cert_app.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthCert interface {
|
||||||
|
GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||||
|
|
||||||
|
Save(ac *entity.AuthCert)
|
||||||
|
|
||||||
|
GetById(id uint64) *entity.AuthCert
|
||||||
|
|
||||||
|
GetByIds(ids ...uint64) []*entity.AuthCert
|
||||||
|
|
||||||
|
DeleteById(id uint64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthCertApp(authCertRepo repository.AuthCert) AuthCert {
|
||||||
|
return &authCertAppImpl{
|
||||||
|
authCertRepo: authCertRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type authCertAppImpl struct {
|
||||||
|
authCertRepo repository.AuthCert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authCertAppImpl) GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||||
|
return a.authCertRepo.GetPageList(condition, pageParam, toEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authCertAppImpl) Save(ac *entity.AuthCert) {
|
||||||
|
oldAc := &entity.AuthCert{Name: ac.Name}
|
||||||
|
err := a.authCertRepo.GetByCondition(oldAc, "Id", "Name")
|
||||||
|
|
||||||
|
ac.PwdEncrypt()
|
||||||
|
if ac.Id == 0 {
|
||||||
|
biz.IsTrue(err != nil, "该凭证名已存在")
|
||||||
|
a.authCertRepo.Insert(ac)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果存在该库,则校验修改的库是否为该库
|
||||||
|
if err == nil {
|
||||||
|
biz.IsTrue(oldAc.Id == ac.Id, "该凭证名已存在")
|
||||||
|
}
|
||||||
|
a.authCertRepo.Update(ac)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authCertAppImpl) GetById(id uint64) *entity.AuthCert {
|
||||||
|
return a.authCertRepo.GetById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authCertAppImpl) GetByIds(ids ...uint64) []*entity.AuthCert {
|
||||||
|
return a.authCertRepo.GetByIds(ids...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authCertAppImpl) DeleteById(id uint64) {
|
||||||
|
a.authCertRepo.DeleteById(id)
|
||||||
|
}
|
||||||
@@ -14,7 +14,10 @@ type Machine interface {
|
|||||||
// 根据条件获取账号信息
|
// 根据条件获取账号信息
|
||||||
GetMachine(condition *entity.Machine, cols ...string) error
|
GetMachine(condition *entity.Machine, cols ...string) error
|
||||||
|
|
||||||
Save(entity *entity.Machine)
|
Save(*entity.Machine)
|
||||||
|
|
||||||
|
// 测试机器连接
|
||||||
|
TestConn(me *entity.Machine)
|
||||||
|
|
||||||
// 调整机器状态
|
// 调整机器状态
|
||||||
ChangeStatus(id uint64, status int8)
|
ChangeStatus(id uint64, status int8)
|
||||||
@@ -33,16 +36,19 @@ type Machine interface {
|
|||||||
GetCli(id uint64) *machine.Cli
|
GetCli(id uint64) *machine.Cli
|
||||||
|
|
||||||
// 获取ssh隧道机器连接
|
// 获取ssh隧道机器连接
|
||||||
GetSshTunnelMachine(id uint64) *machine.SshTunnelMachine
|
GetSshTunnelMachine(id int) *machine.SshTunnelMachine
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMachineApp(machineRepo repository.Machine) Machine {
|
func newMachineApp(machineRepo repository.Machine, authCertApp AuthCert) Machine {
|
||||||
return &machineAppImpl{machineRepo: machineRepo}
|
return &machineAppImpl{
|
||||||
|
machineRepo: machineRepo,
|
||||||
|
authCertApp: authCertApp,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type machineAppImpl struct {
|
type machineAppImpl struct {
|
||||||
machineRepo repository.Machine
|
machineRepo repository.Machine
|
||||||
|
authCertApp AuthCert
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页获取机器信息列表
|
// 分页获取机器信息列表
|
||||||
@@ -54,36 +60,35 @@ func (m *machineAppImpl) Count(condition *entity.MachineQuery) int64 {
|
|||||||
return m.machineRepo.Count(condition)
|
return m.machineRepo.Count(condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据条件获取机器信息
|
|
||||||
func (m *machineAppImpl) Save(me *entity.Machine) {
|
func (m *machineAppImpl) Save(me *entity.Machine) {
|
||||||
// ’修改机器信息且密码不为空‘ or ‘新增’需要测试是否可连接
|
|
||||||
if (me.Id != 0 && me.Password != "") || me.Id == 0 {
|
|
||||||
biz.ErrIsNilAppendErr(machine.TestConn(*me, func(u uint64) *entity.Machine {
|
|
||||||
me := m.GetById(u)
|
|
||||||
me.PwdDecrypt()
|
|
||||||
return me
|
|
||||||
}), "该机器无法连接: %s")
|
|
||||||
}
|
|
||||||
|
|
||||||
oldMachine := &entity.Machine{Ip: me.Ip, Port: me.Port, Username: me.Username}
|
oldMachine := &entity.Machine{Ip: me.Ip, Port: me.Port, Username: me.Username}
|
||||||
err := m.GetMachine(oldMachine)
|
err := m.GetMachine(oldMachine)
|
||||||
|
|
||||||
if me.Id != 0 {
|
me.PwdEncrypt()
|
||||||
// 如果存在该库,则校验修改的库是否为该库
|
if me.Id == 0 {
|
||||||
if err == nil {
|
|
||||||
biz.IsTrue(oldMachine.Id == me.Id, "该机器信息已存在")
|
|
||||||
}
|
|
||||||
// 关闭连接
|
|
||||||
machine.DeleteCli(me.Id)
|
|
||||||
me.PwdEncrypt()
|
|
||||||
m.machineRepo.UpdateById(me)
|
|
||||||
} else {
|
|
||||||
biz.IsTrue(err != nil, "该机器信息已存在")
|
biz.IsTrue(err != nil, "该机器信息已存在")
|
||||||
// 新增机器,默认启用状态
|
// 新增机器,默认启用状态
|
||||||
me.Status = entity.MachineStatusEnable
|
me.Status = entity.MachineStatusEnable
|
||||||
me.PwdEncrypt()
|
|
||||||
m.machineRepo.Create(me)
|
m.machineRepo.Create(me)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果存在该库,则校验修改的库是否为该库
|
||||||
|
if err == nil {
|
||||||
|
biz.IsTrue(oldMachine.Id == me.Id, "该机器信息已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭连接
|
||||||
|
machine.DeleteCli(me.Id)
|
||||||
|
m.machineRepo.UpdateById(me)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineAppImpl) TestConn(me *entity.Machine) {
|
||||||
|
me.Id = 0
|
||||||
|
// 测试连接
|
||||||
|
biz.ErrIsNilAppendErr(machine.TestConn(*m.toMachineInfo(me), func(u uint64) *machine.Info {
|
||||||
|
return m.toMachineInfoById(u)
|
||||||
|
}), "该机器无法连接: %s")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *machineAppImpl) ChangeStatus(id uint64, status int8) {
|
func (m *machineAppImpl) ChangeStatus(id uint64, status int8) {
|
||||||
@@ -128,24 +133,55 @@ func (m *machineAppImpl) GetById(id uint64, cols ...string) *entity.Machine {
|
|||||||
return m.machineRepo.GetById(id, cols...)
|
return m.machineRepo.GetById(id, cols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *machineAppImpl) GetCli(id uint64) *machine.Cli {
|
func (m *machineAppImpl) GetCli(machineId uint64) *machine.Cli {
|
||||||
cli, err := machine.GetCli(id, func(machineId uint64) *entity.Machine {
|
cli, err := machine.GetCli(machineId, func(mid uint64) *machine.Info {
|
||||||
machine := m.GetById(machineId)
|
return m.toMachineInfoById(mid)
|
||||||
machine.PwdDecrypt()
|
|
||||||
biz.IsTrue(machine.Status == entity.MachineStatusEnable, "该机器已被停用")
|
|
||||||
return machine
|
|
||||||
})
|
})
|
||||||
biz.ErrIsNilAppendErr(err, "获取客户端错误: %s")
|
biz.ErrIsNilAppendErr(err, "获取客户端错误: %s")
|
||||||
return cli
|
return cli
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *machineAppImpl) GetSshTunnelMachine(id uint64) *machine.SshTunnelMachine {
|
func (m *machineAppImpl) GetSshTunnelMachine(machineId int) *machine.SshTunnelMachine {
|
||||||
sshTunnel, err := machine.GetSshTunnelMachine(id, func(machineId uint64) *entity.Machine {
|
sshTunnel, err := machine.GetSshTunnelMachine(machineId, func(mid uint64) *machine.Info {
|
||||||
machine := m.GetById(machineId)
|
return m.toMachineInfoById(mid)
|
||||||
machine.PwdDecrypt()
|
|
||||||
biz.IsTrue(machine.Status == entity.MachineStatusEnable, "该机器已被停用")
|
|
||||||
return machine
|
|
||||||
})
|
})
|
||||||
biz.ErrIsNilAppendErr(err, "获取ssh隧道连接失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取ssh隧道连接失败: %s")
|
||||||
return sshTunnel
|
return sshTunnel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成机器信息,根据授权凭证id填充用户密码等
|
||||||
|
func (m *machineAppImpl) toMachineInfoById(machineId uint64) *machine.Info {
|
||||||
|
me := m.GetById(machineId)
|
||||||
|
biz.IsTrue(me.Status == entity.MachineStatusEnable, "该机器已被停用")
|
||||||
|
|
||||||
|
return m.toMachineInfo(me)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineAppImpl) toMachineInfo(me *entity.Machine) *machine.Info {
|
||||||
|
mi := new(machine.Info)
|
||||||
|
mi.Id = me.Id
|
||||||
|
mi.Name = me.Name
|
||||||
|
mi.Ip = me.Ip
|
||||||
|
mi.Port = me.Port
|
||||||
|
mi.Username = me.Username
|
||||||
|
mi.TagId = me.TagId
|
||||||
|
mi.TagPath = me.TagPath
|
||||||
|
mi.EnableRecorder = me.EnableRecorder
|
||||||
|
mi.SshTunnelMachineId = me.SshTunnelMachineId
|
||||||
|
|
||||||
|
if me.UseAuthCert() {
|
||||||
|
ac := m.authCertApp.GetById(uint64(me.AuthCertId))
|
||||||
|
biz.NotNil(ac, "授权凭证信息已不存在,请重新关联")
|
||||||
|
mi.AuthMethod = ac.AuthMethod
|
||||||
|
ac.PwdDecrypt()
|
||||||
|
mi.Password = ac.Password
|
||||||
|
mi.Passphrase = ac.Passphrase
|
||||||
|
} else {
|
||||||
|
mi.AuthMethod = entity.AuthCertAuthMethodPassword
|
||||||
|
if me.Id != 0 {
|
||||||
|
me.PwdDecrypt()
|
||||||
|
}
|
||||||
|
mi.Password = me.Password
|
||||||
|
}
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"mayfly-go/internal/machine/domain/entity"
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
"mayfly-go/internal/machine/domain/repository"
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
|
"mayfly-go/internal/machine/infrastructure/machine"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"os"
|
"os"
|
||||||
@@ -29,7 +30,7 @@ type MachineFile interface {
|
|||||||
Delete(id uint64)
|
Delete(id uint64)
|
||||||
|
|
||||||
// 获取文件关联的机器信息,主要用于记录日志使用
|
// 获取文件关联的机器信息,主要用于记录日志使用
|
||||||
GetMachine(fileId uint64) *entity.Machine
|
GetMachine(fileId uint64) *machine.Info
|
||||||
|
|
||||||
/** sftp 相关操作 **/
|
/** sftp 相关操作 **/
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ func (m *machineFileAppImpl) getSftpCli(machineId uint64) *sftp.Client {
|
|||||||
return GetMachineApp().GetCli(machineId).GetSftpCli()
|
return GetMachineApp().GetCli(machineId).GetSftpCli()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *machineFileAppImpl) GetMachine(fileId uint64) *entity.Machine {
|
func (m *machineFileAppImpl) GetMachine(fileId uint64) *machine.Info {
|
||||||
return GetMachineApp().GetCli(m.GetById(fileId).MachineId).GetMachine()
|
return GetMachineApp().GetCli(m.GetById(fileId).MachineId).GetMachine()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
server/internal/machine/domain/entity/auth_cert.go
Normal file
41
server/internal/machine/domain/entity/auth_cert.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/common/utils"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 授权凭证
|
||||||
|
type AuthCert struct {
|
||||||
|
model.Model
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
AuthMethod int8 `json:"authMethod"` // 1.密码 2.秘钥
|
||||||
|
Password string `json:"password"` // 密码or私钥
|
||||||
|
Passphrase string `json:"passphrase"` // 私钥口令
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthCert) TableName() string {
|
||||||
|
return "t_auth_cert"
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthCertAuthMethodPassword int8 = 1 // 密码
|
||||||
|
MachineAuthMethodPublicKey int8 = 2 // 密钥
|
||||||
|
|
||||||
|
AuthCertTypePrivate int8 = 1
|
||||||
|
AuthCertTypePublic int8 = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// 密码加密
|
||||||
|
func (ac *AuthCert) PwdEncrypt() {
|
||||||
|
ac.Password = utils.PwdAesEncrypt(ac.Password)
|
||||||
|
ac.Passphrase = utils.PwdAesEncrypt(ac.Passphrase)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码解密
|
||||||
|
func (ac *AuthCert) PwdDecrypt() {
|
||||||
|
ac.Password = utils.PwdAesDecrypt(ac.Password)
|
||||||
|
ac.Passphrase = utils.PwdAesDecrypt(ac.Passphrase)
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"mayfly-go/internal/common/utils"
|
"mayfly-go/internal/common/utils"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
)
|
)
|
||||||
@@ -11,24 +10,21 @@ type Machine struct {
|
|||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Ip string `json:"ip"` // IP地址
|
Ip string `json:"ip"` // IP地址
|
||||||
|
Port int `json:"port"` // 端口号
|
||||||
Username string `json:"username"` // 用户名
|
Username string `json:"username"` // 用户名
|
||||||
AuthMethod int8 `json:"authMethod"` // 授权认证方式
|
Password string `json:"password"` // 密码
|
||||||
Password string `json:"-"`
|
AuthCertId int `json:"authCertId"` // 授权凭证id
|
||||||
Port int `json:"port"` // 端口号
|
TagId uint64
|
||||||
|
TagPath string
|
||||||
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||||
Remark string `json:"remark"` // 备注
|
Remark string `json:"remark"` // 备注
|
||||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||||
TagId uint64 `json:"tagId"`
|
|
||||||
TagPath string `json:"tagPath"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MachineStatusEnable int8 = 1 // 启用状态
|
MachineStatusEnable int8 = 1 // 启用状态
|
||||||
MachineStatusDisable int8 = -1 // 禁用状态
|
MachineStatusDisable int8 = -1 // 禁用状态
|
||||||
MachineAuthMethodPassword int8 = 1 // 密码登录
|
|
||||||
MachineAuthMethodPublicKey int8 = 2 // 公钥免密登录
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *Machine) PwdEncrypt() {
|
func (m *Machine) PwdEncrypt() {
|
||||||
@@ -41,7 +37,6 @@ func (m *Machine) PwdDecrypt() {
|
|||||||
m.Password = utils.PwdAesDecrypt(m.Password)
|
m.Password = utils.PwdAesDecrypt(m.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取记录日志的描述
|
func (m *Machine) UseAuthCert() bool {
|
||||||
func (m *Machine) GetLogDesc() string {
|
return m.AuthCertId > 0
|
||||||
return fmt.Sprintf("Machine[id=%d, tag=%s, name=%s, ip=%s:%d]", m.Id, m.TagPath, m.Name, m.Ip, m.Port)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import "mayfly-go/pkg/model"
|
|||||||
|
|
||||||
type MachineQuery struct {
|
type MachineQuery struct {
|
||||||
model.Model
|
model.Model
|
||||||
ProjectId uint64 `json:"projectId"`
|
|
||||||
ProjectName string `json:"projectName"`
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Ip string `json:"ip"` // IP地址
|
Ip string `json:"ip"` // IP地址
|
||||||
Username string `json:"username"` // 用户名
|
Username string `json:"username"` // 用户名
|
||||||
@@ -14,11 +12,9 @@ type MachineQuery struct {
|
|||||||
Port int `json:"port"` // 端口号
|
Port int `json:"port"` // 端口号
|
||||||
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||||
Remark string `json:"remark"` // 备注
|
Remark string `json:"remark"` // 备注
|
||||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
|
||||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||||
|
|
||||||
ProjectIds []uint64
|
|
||||||
TagId uint64
|
TagId uint64
|
||||||
TagPathLike string
|
TagPathLike string
|
||||||
TagIds []uint64
|
TagIds []uint64
|
||||||
|
|||||||
22
server/internal/machine/domain/repository/auth_cert.go
Normal file
22
server/internal/machine/domain/repository/auth_cert.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthCert interface {
|
||||||
|
GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||||
|
|
||||||
|
Insert(ac *entity.AuthCert)
|
||||||
|
|
||||||
|
Update(ac *entity.AuthCert)
|
||||||
|
|
||||||
|
GetById(id uint64) *entity.AuthCert
|
||||||
|
|
||||||
|
GetByIds(ids ...uint64) []*entity.AuthCert
|
||||||
|
|
||||||
|
GetByCondition(condition *entity.AuthCert, cols ...string) error
|
||||||
|
|
||||||
|
DeleteById(id uint64)
|
||||||
|
}
|
||||||
@@ -16,15 +16,42 @@ import (
|
|||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 机器信息
|
||||||
|
type Info struct {
|
||||||
|
Id uint64
|
||||||
|
Name string `json:"name"`
|
||||||
|
Ip string `json:"ip"` // IP地址
|
||||||
|
Port int `json:"port"` // 端口号
|
||||||
|
|
||||||
|
AuthMethod int8 `json:"authMethod"` // 授权认证方式
|
||||||
|
Username string `json:"username"` // 用户名
|
||||||
|
Password string `json:"-"`
|
||||||
|
Passphrase string `json:"-"` // 私钥口令
|
||||||
|
|
||||||
|
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||||
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
|
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
|
||||||
|
TagId uint64 `json:"tagId"`
|
||||||
|
TagPath string `json:"tagPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Info) UseSshTunnel() bool {
|
||||||
|
return m.SshTunnelMachineId > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取记录日志的描述
|
||||||
|
func (m *Info) GetLogDesc() string {
|
||||||
|
return fmt.Sprintf("Machine[id=%d, tag=%s, name=%s, ip=%s:%d]", m.Id, m.TagPath, m.Name, m.Ip, m.Port)
|
||||||
|
}
|
||||||
|
|
||||||
// 客户端信息
|
// 客户端信息
|
||||||
type Cli struct {
|
type Cli struct {
|
||||||
machine *entity.Machine
|
machine *Info
|
||||||
|
|
||||||
client *ssh.Client // ssh客户端
|
client *ssh.Client // ssh客户端
|
||||||
sftpClient *sftp.Client // sftp客户端
|
sftpClient *sftp.Client // sftp客户端
|
||||||
|
|
||||||
enableSshTunnel int8
|
sshTunnelMachineId int
|
||||||
sshTunnelMachineId uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接
|
// 连接
|
||||||
@@ -54,7 +81,7 @@ func (c *Cli) Close() {
|
|||||||
c.sftpClient.Close()
|
c.sftpClient.Close()
|
||||||
c.sftpClient = nil
|
c.sftpClient = nil
|
||||||
}
|
}
|
||||||
if c.enableSshTunnel == 1 {
|
if c.sshTunnelMachineId > 0 {
|
||||||
CloseSshTunnelMachine(c.sshTunnelMachineId, c.machine.Id)
|
CloseSshTunnelMachine(c.sshTunnelMachineId, c.machine.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +133,7 @@ func (c *Cli) Run(shell string) (string, error) {
|
|||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cli) GetMachine() *entity.Machine {
|
func (c *Cli) GetMachine() *Info {
|
||||||
return c.machine
|
return c.machine
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +145,7 @@ var cliCache = cache.NewTimedCache(constant.MachineConnExpireTime, 5*time.Second
|
|||||||
})
|
})
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
|
AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
|
||||||
// 遍历所有机器连接实例,若存在机器连接实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
// 遍历所有机器连接实例,若存在机器连接实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
||||||
items := cliCache.Items()
|
items := cliCache.Items()
|
||||||
for _, v := range items {
|
for _, v := range items {
|
||||||
@@ -144,7 +171,7 @@ func DeleteCli(id uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
|
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
|
||||||
func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, error) {
|
func GetCli(machineId uint64, getMachine func(uint64) *Info) (*Cli, error) {
|
||||||
cli, err := cliCache.ComputeIfAbsent(machineId, func(_ interface{}) (interface{}, error) {
|
cli, err := cliCache.ComputeIfAbsent(machineId, func(_ interface{}) (interface{}, error) {
|
||||||
me := getMachine(machineId)
|
me := getMachine(machineId)
|
||||||
err := IfUseSshTunnelChangeIpPort(me, getMachine)
|
err := IfUseSshTunnelChangeIpPort(me, getMachine)
|
||||||
@@ -156,7 +183,6 @@ func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, er
|
|||||||
CloseSshTunnelMachine(me.SshTunnelMachineId, me.Id)
|
CloseSshTunnelMachine(me.SshTunnelMachineId, me.Id)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.enableSshTunnel = me.EnableSshTunnel
|
|
||||||
c.sshTunnelMachineId = me.SshTunnelMachineId
|
c.sshTunnelMachineId = me.SshTunnelMachineId
|
||||||
return c, nil
|
return c, nil
|
||||||
})
|
})
|
||||||
@@ -168,7 +194,7 @@ func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 测试连接,使用传值的方式,而非引用。因为如果使用了ssh隧道,则ip和端口会变为本地映射地址与端口
|
// 测试连接,使用传值的方式,而非引用。因为如果使用了ssh隧道,则ip和端口会变为本地映射地址与端口
|
||||||
func TestConn(me entity.Machine, getSshTunnelMachine func(uint64) *entity.Machine) error {
|
func TestConn(me Info, getSshTunnelMachine func(uint64) *Info) error {
|
||||||
originId := me.Id
|
originId := me.Id
|
||||||
if originId == 0 {
|
if originId == 0 {
|
||||||
// 随机设置一个ip,如果使用了隧道则用于临时保存隧道
|
// 随机设置一个ip,如果使用了隧道则用于临时保存隧道
|
||||||
@@ -177,7 +203,7 @@ func TestConn(me entity.Machine, getSshTunnelMachine func(uint64) *entity.Machin
|
|||||||
|
|
||||||
err := IfUseSshTunnelChangeIpPort(&me, getSshTunnelMachine)
|
err := IfUseSshTunnelChangeIpPort(&me, getSshTunnelMachine)
|
||||||
biz.ErrIsNilAppendErr(err, "ssh隧道连接失败: %s")
|
biz.ErrIsNilAppendErr(err, "ssh隧道连接失败: %s")
|
||||||
if me.EnableSshTunnel == 1 {
|
if me.UseSshTunnel() {
|
||||||
defer CloseSshTunnelMachine(me.SshTunnelMachineId, me.Id)
|
defer CloseSshTunnelMachine(me.SshTunnelMachineId, me.Id)
|
||||||
}
|
}
|
||||||
sshClient, err := GetSshClient(&me)
|
sshClient, err := GetSshClient(&me)
|
||||||
@@ -189,11 +215,11 @@ func TestConn(me entity.Machine, getSshTunnelMachine func(uint64) *entity.Machin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
||||||
func IfUseSshTunnelChangeIpPort(me *entity.Machine, getMachine func(uint64) *entity.Machine) error {
|
func IfUseSshTunnelChangeIpPort(me *Info, getMachine func(uint64) *Info) error {
|
||||||
if me.EnableSshTunnel != 1 {
|
if !me.UseSshTunnel() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sshTunnelMachine, err := GetSshTunnelMachine(me.SshTunnelMachineId, func(u uint64) *entity.Machine {
|
sshTunnelMachine, err := GetSshTunnelMachine(me.SshTunnelMachineId, func(u uint64) *Info {
|
||||||
return getMachine(u)
|
return getMachine(u)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -209,26 +235,34 @@ func IfUseSshTunnelChangeIpPort(me *entity.Machine, getMachine func(uint64) *ent
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSshClient(m *entity.Machine) (*ssh.Client, error) {
|
func GetSshClient(m *Info) (*ssh.Client, error) {
|
||||||
config := ssh.ClientConfig{
|
config := &ssh.ClientConfig{
|
||||||
User: m.Username,
|
User: m.Username,
|
||||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
if m.AuthMethod == entity.MachineAuthMethodPassword {
|
|
||||||
|
if m.AuthMethod == entity.AuthCertAuthMethodPassword {
|
||||||
config.Auth = []ssh.AuthMethod{ssh.Password(m.Password)}
|
config.Auth = []ssh.AuthMethod{ssh.Password(m.Password)}
|
||||||
} else if m.AuthMethod == entity.MachineAuthMethodPublicKey {
|
} else if m.AuthMethod == entity.MachineAuthMethodPublicKey {
|
||||||
if signer, err := ssh.ParsePrivateKey([]byte(m.Password)); err != nil {
|
var key ssh.Signer
|
||||||
return nil, err
|
var err error
|
||||||
|
|
||||||
|
if len(m.Passphrase) > 0 {
|
||||||
|
key, err = ssh.ParsePrivateKeyWithPassphrase([]byte(m.Password), []byte(m.Passphrase))
|
||||||
} else {
|
} else {
|
||||||
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
|
key, err = ssh.ParsePrivateKey([]byte(m.Password))
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(key)}
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
||||||
sshClient, err := ssh.Dial("tcp", addr, &config)
|
sshClient, err := ssh.Dial("tcp", addr, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -236,7 +270,7 @@ func GetSshClient(m *entity.Machine) (*ssh.Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据机器信息创建客户端对象
|
// 根据机器信息创建客户端对象
|
||||||
func newClient(machine *entity.Machine) (*Cli, error) {
|
func newClient(machine *Info) (*Cli, error) {
|
||||||
if machine == nil {
|
if machine == nil {
|
||||||
return nil, errors.New("机器不存在")
|
return nil, errors.New("机器不存在")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package machine
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mayfly-go/internal/machine/domain/entity"
|
|
||||||
"mayfly-go/pkg/global"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/scheduler"
|
"mayfly-go/pkg/scheduler"
|
||||||
"mayfly-go/pkg/utils"
|
"mayfly-go/pkg/utils"
|
||||||
@@ -15,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sshTunnelMachines map[uint64]*SshTunnelMachine = make(map[uint64]*SshTunnelMachine)
|
sshTunnelMachines map[int]*SshTunnelMachine = make(map[int]*SshTunnelMachine)
|
||||||
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 检查ssh隧道机器是否有被使用
|
// 检查ssh隧道机器是否有被使用
|
||||||
type CheckSshTunnelMachineHasUseFunc func(uint64) bool
|
type CheckSshTunnelMachineHasUseFunc func(int) bool
|
||||||
|
|
||||||
func startCheckUse() {
|
func startCheckUse() {
|
||||||
global.Log.Info("开启定时检测ssh隧道机器是否还有被使用")
|
global.Log.Info("开启定时检测ssh隧道机器是否还有被使用")
|
||||||
@@ -66,7 +65,7 @@ func AddCheckSshTunnelMachineUseFunc(checkFunc CheckSshTunnelMachineHasUseFunc)
|
|||||||
|
|
||||||
// ssh隧道机器
|
// ssh隧道机器
|
||||||
type SshTunnelMachine struct {
|
type SshTunnelMachine struct {
|
||||||
machineId uint64 // 隧道机器id
|
machineId int // 隧道机器id
|
||||||
SshClient *ssh.Client
|
SshClient *ssh.Client
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
tunnels map[uint64]*Tunnel // 机器id -> 隧道
|
tunnels map[uint64]*Tunnel // 机器id -> 隧道
|
||||||
@@ -137,7 +136,7 @@ func (stm *SshTunnelMachine) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取ssh隧道机器,方便统一管理充当ssh隧道的机器,避免创建多个ssh client
|
// 获取ssh隧道机器,方便统一管理充当ssh隧道的机器,避免创建多个ssh client
|
||||||
func GetSshTunnelMachine(machineId uint64, getMachine func(uint64) *entity.Machine) (*SshTunnelMachine, error) {
|
func GetSshTunnelMachine(machineId int, getMachine func(uint64) *Info) (*SshTunnelMachine, error) {
|
||||||
sshTunnelMachine := sshTunnelMachines[machineId]
|
sshTunnelMachine := sshTunnelMachines[machineId]
|
||||||
if sshTunnelMachine != nil {
|
if sshTunnelMachine != nil {
|
||||||
return sshTunnelMachine, nil
|
return sshTunnelMachine, nil
|
||||||
@@ -146,7 +145,7 @@ func GetSshTunnelMachine(machineId uint64, getMachine func(uint64) *entity.Machi
|
|||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
me := getMachine(machineId)
|
me := getMachine(uint64(machineId))
|
||||||
sshClient, err := GetSshClient(me)
|
sshClient, err := GetSshClient(me)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -165,7 +164,7 @@ func GetSshTunnelMachine(machineId uint64, getMachine func(uint64) *entity.Machi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关闭ssh隧道机器的指定隧道
|
// 关闭ssh隧道机器的指定隧道
|
||||||
func CloseSshTunnelMachine(machineId uint64, tunnelId uint64) {
|
func CloseSshTunnelMachine(machineId int, tunnelId uint64) {
|
||||||
sshTunnelMachine := sshTunnelMachines[machineId]
|
sshTunnelMachine := sshTunnelMachines[machineId]
|
||||||
if sshTunnelMachine == nil {
|
if sshTunnelMachine == nil {
|
||||||
return
|
return
|
||||||
@@ -182,7 +181,7 @@ func CloseSshTunnelMachine(machineId uint64, tunnelId uint64) {
|
|||||||
|
|
||||||
type Tunnel struct {
|
type Tunnel struct {
|
||||||
id uint64 // 唯一标识
|
id uint64 // 唯一标识
|
||||||
machineId uint64 // 隧道机器id
|
machineId int // 隧道机器id
|
||||||
localHost string // 本地监听地址
|
localHost string // 本地监听地址
|
||||||
localPort int // 本地端口
|
localPort int // 本地端口
|
||||||
remoteHost string // 远程连接地址
|
remoteHost string // 远程连接地址
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type authCertRepoImpl struct{}
|
||||||
|
|
||||||
|
func newAuthCertRepo() repository.AuthCert {
|
||||||
|
return new(authCertRepoImpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||||
|
return model.GetPage(pageParam, condition, condition, toEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) Insert(ac *entity.AuthCert) {
|
||||||
|
biz.ErrIsNil(model.Insert(ac), "新增授权凭证失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) Update(ac *entity.AuthCert) {
|
||||||
|
biz.ErrIsNil(model.UpdateById(ac), "更新授权凭证失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) GetById(id uint64) *entity.AuthCert {
|
||||||
|
ac := new(entity.AuthCert)
|
||||||
|
err := model.GetById(ac, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ac
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) GetByIds(ids ...uint64) []*entity.AuthCert {
|
||||||
|
acs := new([]*entity.AuthCert)
|
||||||
|
model.GetByIdIn(new(entity.AuthCert), acs, ids)
|
||||||
|
return *acs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) GetByCondition(condition *entity.AuthCert, cols ...string) error {
|
||||||
|
return model.GetBy(condition, cols...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *authCertRepoImpl) DeleteById(id uint64) {
|
||||||
|
model.DeleteById(new(entity.AuthCert), id)
|
||||||
|
}
|
||||||
@@ -6,8 +6,6 @@ import (
|
|||||||
"mayfly-go/internal/machine/domain/repository"
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/utils"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type machineRepoImpl struct{}
|
type machineRepoImpl struct{}
|
||||||
@@ -30,7 +28,8 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
|
|||||||
values = append(values, "%"+condition.Name+"%")
|
values = append(values, "%"+condition.Name+"%")
|
||||||
}
|
}
|
||||||
if len(condition.TagIds) > 0 {
|
if len(condition.TagIds) > 0 {
|
||||||
sql = fmt.Sprintf("%s AND m.tag_id IN (%s) ", sql, strings.Join(utils.NumberArr2StrArr(condition.TagIds), ","))
|
sql = fmt.Sprintf("%s AND m.tag_id IN ? ", sql)
|
||||||
|
values = append(values, condition.TagIds)
|
||||||
}
|
}
|
||||||
if condition.TagPathLike != "" {
|
if condition.TagPathLike != "" {
|
||||||
sql = sql + " AND m.tag_path LIKE ?"
|
sql = sql + " AND m.tag_path LIKE ?"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ var (
|
|||||||
machineRepo repository.Machine = newMachineRepo()
|
machineRepo repository.Machine = newMachineRepo()
|
||||||
machineFileRepo repository.MachineFile = newMachineFileRepo()
|
machineFileRepo repository.MachineFile = newMachineFileRepo()
|
||||||
machineScriptRepo repository.MachineScript = newMachineScriptRepo()
|
machineScriptRepo repository.MachineScript = newMachineScriptRepo()
|
||||||
|
authCertRepo = newAuthCertRepo()
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetMachineRepo() repository.Machine {
|
func GetMachineRepo() repository.Machine {
|
||||||
@@ -19,3 +20,7 @@ func GetMachineFileRepo() repository.MachineFile {
|
|||||||
func GetMachineScriptRepo() repository.MachineScript {
|
func GetMachineScriptRepo() repository.MachineScript {
|
||||||
return machineScriptRepo
|
return machineScriptRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAuthCertRepo() repository.AuthCert {
|
||||||
|
return authCertRepo
|
||||||
|
}
|
||||||
|
|||||||
45
server/internal/machine/router/auth_cert.go
Normal file
45
server/internal/machine/router/auth_cert.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/api"
|
||||||
|
"mayfly-go/internal/machine/application"
|
||||||
|
"mayfly-go/pkg/req"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitAuthCertRouter(router *gin.RouterGroup) {
|
||||||
|
r := &api.AuthCert{AuthCertApp: application.GetAuthCertApp()}
|
||||||
|
db := router.Group("sys/authcerts")
|
||||||
|
{
|
||||||
|
listAcP := req.NewPermission("authcert")
|
||||||
|
db.GET("", func(c *gin.Context) {
|
||||||
|
req.NewCtxWithGin(c).
|
||||||
|
WithRequiredPermission(listAcP).
|
||||||
|
Handle(r.AuthCerts)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 基础授权凭证信息,不包含密码等
|
||||||
|
db.GET("base", func(c *gin.Context) {
|
||||||
|
req.NewCtxWithGin(c).Handle(r.BaseAuthCerts)
|
||||||
|
})
|
||||||
|
|
||||||
|
saveAc := req.NewLogInfo("保存授权凭证").WithSave(true)
|
||||||
|
saveAcP := req.NewPermission("authcert:save")
|
||||||
|
db.POST("", func(c *gin.Context) {
|
||||||
|
req.NewCtxWithGin(c).
|
||||||
|
WithLog(saveAc).
|
||||||
|
WithRequiredPermission(saveAcP).
|
||||||
|
Handle(r.SaveAuthCert)
|
||||||
|
})
|
||||||
|
|
||||||
|
deleteAc := req.NewLogInfo("删除授权凭证").WithSave(true)
|
||||||
|
deleteAcP := req.NewPermission("authcert:del")
|
||||||
|
db.DELETE(":id", func(c *gin.Context) {
|
||||||
|
req.NewCtxWithGin(c).
|
||||||
|
WithLog(deleteAc).
|
||||||
|
WithRequiredPermission(deleteAcP).
|
||||||
|
Handle(r.Delete)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,10 +21,6 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
|||||||
req.NewCtxWithGin(c).Handle(m.Machines)
|
req.NewCtxWithGin(c).Handle(m.Machines)
|
||||||
})
|
})
|
||||||
|
|
||||||
machines.GET(":machineId/pwd", func(c *gin.Context) {
|
|
||||||
req.NewCtxWithGin(c).Handle(m.GetMachinePwd)
|
|
||||||
})
|
|
||||||
|
|
||||||
machines.GET(":machineId/stats", func(c *gin.Context) {
|
machines.GET(":machineId/stats", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).Handle(m.MachineStats)
|
req.NewCtxWithGin(c).Handle(m.MachineStats)
|
||||||
})
|
})
|
||||||
@@ -52,6 +48,11 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
|||||||
Handle(m.SaveMachine)
|
Handle(m.SaveMachine)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
machines.POST("test-conn", func(c *gin.Context) {
|
||||||
|
req.NewCtxWithGin(c).
|
||||||
|
Handle(m.TestConn)
|
||||||
|
})
|
||||||
|
|
||||||
changeStatus := req.NewLogInfo("调整机器状态").WithSave(true)
|
changeStatus := req.NewLogInfo("调整机器状态").WithSave(true)
|
||||||
machines.PUT(":machineId/:status", func(c *gin.Context) {
|
machines.PUT(":machineId/:status", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).
|
req.NewCtxWithGin(c).
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ func Init(router *gin.RouterGroup) {
|
|||||||
InitMachineRouter(router)
|
InitMachineRouter(router)
|
||||||
InitMachineFileRouter(router)
|
InitMachineFileRouter(router)
|
||||||
InitMachineScriptRouter(router)
|
InitMachineScriptRouter(router)
|
||||||
|
InitAuthCertRouter(router)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package form
|
|||||||
type Mongo struct {
|
type Mongo struct {
|
||||||
Id uint64
|
Id uint64
|
||||||
Uri string `binding:"required" json:"uri"`
|
Uri string `binding:"required" json:"uri"`
|
||||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
Name string `binding:"required" json:"name"`
|
Name string `binding:"required" json:"name"`
|
||||||
TagId uint64 `binding:"required" json:"tagId"`
|
TagId uint64 `binding:"required" json:"tagId"`
|
||||||
TagPath string `json:"tagPath"`
|
TagPath string `json:"tagPath"`
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ var mongoCliCache = cache.NewTimedCache(constant.MongoConnExpireTime, 5*time.Sec
|
|||||||
})
|
})
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
|
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
|
||||||
// 遍历所有mongo连接实例,若存在redis实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
// 遍历所有mongo连接实例,若存在redis实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
||||||
items := mongoCliCache.Items()
|
items := mongoCliCache.Items()
|
||||||
for _, v := range items {
|
for _, v := range items {
|
||||||
@@ -144,7 +144,7 @@ type MongoInfo struct {
|
|||||||
Id uint64
|
Id uint64
|
||||||
Name string
|
Name string
|
||||||
TagPath string
|
TagPath string
|
||||||
SshTunnelMachineId uint64 // ssh隧道机器id
|
SshTunnelMachineId int // ssh隧道机器id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MongoInfo) GetLogDesc() string {
|
func (m *MongoInfo) GetLogDesc() string {
|
||||||
@@ -177,7 +177,7 @@ func connect(me *entity.Mongo) (*MongoInstance, error) {
|
|||||||
mongoOptions := options.Client().ApplyURI(me.Uri).
|
mongoOptions := options.Client().ApplyURI(me.Uri).
|
||||||
SetMaxPoolSize(1)
|
SetMaxPoolSize(1)
|
||||||
// 启用ssh隧道则连接隧道机器
|
// 启用ssh隧道则连接隧道机器
|
||||||
if me.EnableSshTunnel == 1 {
|
if me.SshTunnelMachineId > 0 {
|
||||||
mongoOptions.SetDialer(&MongoSshDialer{machineId: me.SshTunnelMachineId})
|
mongoOptions.SetDialer(&MongoSshDialer{machineId: me.SshTunnelMachineId})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ func toMongiInfo(me *entity.Mongo) *MongoInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MongoSshDialer struct {
|
type MongoSshDialer struct {
|
||||||
machineId uint64
|
machineId int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *MongoSshDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (sd *MongoSshDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ type Mongo struct {
|
|||||||
|
|
||||||
Name string `orm:"column(name)" json:"name"`
|
Name string `orm:"column(name)" json:"name"`
|
||||||
Uri string `orm:"column(uri)" json:"uri"`
|
Uri string `orm:"column(uri)" json:"uri"`
|
||||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
TagId uint64 `json:"tagId"`
|
TagId uint64 `json:"tagId"`
|
||||||
TagPath string `json:"tagPath"`
|
TagPath string `json:"tagPath"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ type MongoQuery struct {
|
|||||||
|
|
||||||
Name string
|
Name string
|
||||||
Uri string
|
Uri string
|
||||||
EnableSshTunnel int8 // 是否启用ssh隧道
|
|
||||||
SshTunnelMachineId uint64 // ssh隧道机器id
|
SshTunnelMachineId uint64 // ssh隧道机器id
|
||||||
TagId uint64 `json:"tagId"`
|
TagId uint64 `json:"tagId"`
|
||||||
TagPath string `json:"tagPath"`
|
TagPath string `json:"tagPath"`
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ type Redis struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Db string `json:"db"`
|
Db string `json:"db"`
|
||||||
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
TagId uint64 `binding:"required" json:"tagId"`
|
TagId uint64 `binding:"required" json:"tagId"`
|
||||||
TagPath string `json:"tagPath"`
|
TagPath string `json:"tagPath"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ type Redis struct {
|
|||||||
Host *string `json:"host"`
|
Host *string `json:"host"`
|
||||||
Db string `json:"db"`
|
Db string `json:"db"`
|
||||||
Mode *string `json:"mode"`
|
Mode *string `json:"mode"`
|
||||||
EnableSshTunnel *int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
Remark *string `json:"remark"`
|
Remark *string `json:"remark"`
|
||||||
TagId *uint64 `json:"tagId"`
|
TagId *uint64 `json:"tagId"`
|
||||||
TagPath *string `json:"tagPath"`
|
TagPath *string `json:"tagPath"`
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ func getRedisCient(re *entity.Redis, db int) *RedisInstance {
|
|||||||
ReadTimeout: -1, // Disable timeouts, because SSH does not support deadlines.
|
ReadTimeout: -1, // Disable timeouts, because SSH does not support deadlines.
|
||||||
WriteTimeout: -1,
|
WriteTimeout: -1,
|
||||||
}
|
}
|
||||||
if re.EnableSshTunnel == 1 {
|
if re.SshTunnelMachineId > 0 {
|
||||||
redisOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
|
redisOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
|
||||||
}
|
}
|
||||||
ri.Cli = redis.NewClient(redisOptions)
|
ri.Cli = redis.NewClient(redisOptions)
|
||||||
@@ -204,7 +204,7 @@ func getRedisClusterClient(re *entity.Redis) *RedisInstance {
|
|||||||
Password: re.Password,
|
Password: re.Password,
|
||||||
DialTimeout: 8 * time.Second,
|
DialTimeout: 8 * time.Second,
|
||||||
}
|
}
|
||||||
if re.EnableSshTunnel == 1 {
|
if re.SshTunnelMachineId > 0 {
|
||||||
redisClusterOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
|
redisClusterOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
|
||||||
}
|
}
|
||||||
ri.ClusterCli = redis.NewClusterClient(redisClusterOptions)
|
ri.ClusterCli = redis.NewClusterClient(redisClusterOptions)
|
||||||
@@ -224,14 +224,14 @@ func getRedisSentinelCient(re *entity.Redis, db int) *RedisInstance {
|
|||||||
ReadTimeout: -1, // Disable timeouts, because SSH does not support deadlines.
|
ReadTimeout: -1, // Disable timeouts, because SSH does not support deadlines.
|
||||||
WriteTimeout: -1,
|
WriteTimeout: -1,
|
||||||
}
|
}
|
||||||
if re.EnableSshTunnel == 1 {
|
if re.SshTunnelMachineId > 0 {
|
||||||
sentinelOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
|
sentinelOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
|
||||||
}
|
}
|
||||||
ri.Cli = redis.NewFailoverClient(sentinelOptions)
|
ri.Cli = redis.NewFailoverClient(sentinelOptions)
|
||||||
return ri
|
return ri
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRedisDialer(machineId uint64) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
func getRedisDialer(machineId int) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
sshTunnel := machineapp.GetMachineApp().GetSshTunnelMachine(machineId)
|
sshTunnel := machineapp.GetMachineApp().GetSshTunnelMachine(machineId)
|
||||||
return func(_ context.Context, network, addr string) (net.Conn, error) {
|
return func(_ context.Context, network, addr string) (net.Conn, error) {
|
||||||
if sshConn, err := sshTunnel.GetDialConn(network, addr); err == nil {
|
if sshConn, err := sshTunnel.GetDialConn(network, addr); err == nil {
|
||||||
@@ -259,7 +259,7 @@ func CloseRedis(id uint64, db int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
|
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
|
||||||
// 遍历所有redis连接实例,若存在redis实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
// 遍历所有redis连接实例,若存在redis实例使用该ssh隧道机器,则返回true,表示还在使用中...
|
||||||
items := redisCache.Items()
|
items := redisCache.Items()
|
||||||
for _, v := range items {
|
for _, v := range items {
|
||||||
@@ -303,7 +303,7 @@ type RedisInfo struct {
|
|||||||
Mode string
|
Mode string
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
SshTunnelMachineId uint64
|
SshTunnelMachineId int
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取记录日志的描述
|
// 获取记录日志的描述
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ type RedisQuery struct {
|
|||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Password string `orm:"column(password)" json:"-"`
|
Password string `orm:"column(password)" json:"-"`
|
||||||
Db string `orm:"column(database)" json:"db"`
|
Db string `orm:"column(database)" json:"db"`
|
||||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
Remark string
|
Remark string
|
||||||
TagId uint64
|
TagId uint64
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ type Redis struct {
|
|||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Password string `orm:"column(password)" json:"-"`
|
Password string `orm:"column(password)" json:"-"`
|
||||||
Db string `orm:"column(database)" json:"db"`
|
Db string `orm:"column(database)" json:"db"`
|
||||||
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
|
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||||
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
|
||||||
Remark string
|
Remark string
|
||||||
TagId uint64
|
TagId uint64
|
||||||
TagPath string
|
TagPath string
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import "mayfly-go/internal/sys/infrastructure/persistence"
|
import (
|
||||||
|
"mayfly-go/internal/sys/infrastructure/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
accountApp = newAccountApp(persistence.GetAccountRepo())
|
accountApp = newAccountApp(persistence.GetAccountRepo())
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ func (p *TagTree) ListByQuery(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *TagTree) SaveTagTree(rc *req.Ctx) {
|
func (p *TagTree) SaveTagTree(rc *req.Ctx) {
|
||||||
projectTree := &entity.TagTree{}
|
tagTree := &entity.TagTree{}
|
||||||
ginx.BindJsonAndValid(rc.GinCtx, projectTree)
|
ginx.BindJsonAndValid(rc.GinCtx, tagTree)
|
||||||
|
|
||||||
loginAccount := rc.LoginAccount
|
loginAccount := rc.LoginAccount
|
||||||
projectTree.SetBaseInfo(loginAccount)
|
tagTree.SetBaseInfo(loginAccount)
|
||||||
p.TagTreeApp.Save(projectTree)
|
p.TagTreeApp.Save(tagTree)
|
||||||
|
|
||||||
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, codePath: %s", projectTree.Id, projectTree.Name, projectTree.CodePath)
|
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, codePath: %s", tagTree.Id, tagTree.Name, tagTree.CodePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TagTree) DelTagTree(rc *req.Ctx) {
|
func (p *TagTree) DelTagTree(rc *req.Ctx) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type TagTree interface {
|
|||||||
|
|
||||||
GetById(id uint64) *entity.TagTree
|
GetById(id uint64) *entity.TagTree
|
||||||
|
|
||||||
Save(project *entity.TagTree)
|
Save(tt *entity.TagTree)
|
||||||
|
|
||||||
Delete(id uint64)
|
Delete(id uint64)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type Team interface {
|
|||||||
// 分页获取项目团队信息列表
|
// 分页获取项目团队信息列表
|
||||||
GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||||
|
|
||||||
Save(projectTeam *entity.Team)
|
Save(team *entity.Team)
|
||||||
|
|
||||||
Delete(id uint64)
|
Delete(id uint64)
|
||||||
|
|
||||||
@@ -19,99 +19,96 @@ type Team interface {
|
|||||||
|
|
||||||
GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult
|
GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult
|
||||||
|
|
||||||
SaveMember(projectTeamMember *entity.TeamMember)
|
SaveMember(tagTeamMember *entity.TeamMember)
|
||||||
|
|
||||||
DeleteMember(teamId, accountId uint64)
|
DeleteMember(teamId, accountId uint64)
|
||||||
|
|
||||||
IsExistMember(teamId, accounId uint64) bool
|
IsExistMember(teamId, accounId uint64) bool
|
||||||
|
|
||||||
// 账号是否有权限访问该项目关联的资源信息
|
|
||||||
// CanAccess(accountId, projectId uint64) error
|
|
||||||
|
|
||||||
//--------------- 关联项目相关接口 ---------------
|
//--------------- 关联项目相关接口 ---------------
|
||||||
|
|
||||||
ListTagIds(teamId uint64) []uint64
|
ListTagIds(teamId uint64) []uint64
|
||||||
|
|
||||||
SaveTag(tagTeam *entity.TagTreeTeam)
|
SaveTag(tagTeam *entity.TagTreeTeam)
|
||||||
|
|
||||||
DeleteTag(teamId, projectId uint64)
|
DeleteTag(teamId, tagId uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTeamApp(projectTeamRepo repository.Team,
|
func newTeamApp(teamRepo repository.Team,
|
||||||
projectTeamMemberRepo repository.TeamMember,
|
teamMemberRepo repository.TeamMember,
|
||||||
tagTreeTeamRepo repository.TagTreeTeam,
|
tagTreeTeamRepo repository.TagTreeTeam,
|
||||||
) Team {
|
) Team {
|
||||||
return &projectTeamAppImpl{
|
return &teamAppImpl{
|
||||||
projectTeamRepo: projectTeamRepo,
|
teamRepo: teamRepo,
|
||||||
projectTeamMemberRepo: projectTeamMemberRepo,
|
teamMemberRepo: teamMemberRepo,
|
||||||
tagTreeTeamRepo: tagTreeTeamRepo,
|
tagTreeTeamRepo: tagTreeTeamRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type projectTeamAppImpl struct {
|
type teamAppImpl struct {
|
||||||
projectTeamRepo repository.Team
|
teamRepo repository.Team
|
||||||
projectTeamMemberRepo repository.TeamMember
|
teamMemberRepo repository.TeamMember
|
||||||
tagTreeTeamRepo repository.TagTreeTeam
|
tagTreeTeamRepo repository.TagTreeTeam
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *projectTeamAppImpl) GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
func (p *teamAppImpl) GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||||
return p.projectTeamRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
return p.teamRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *projectTeamAppImpl) Save(projectTeam *entity.Team) {
|
func (p *teamAppImpl) Save(team *entity.Team) {
|
||||||
if projectTeam.Id == 0 {
|
if team.Id == 0 {
|
||||||
p.projectTeamRepo.Insert(projectTeam)
|
p.teamRepo.Insert(team)
|
||||||
} else {
|
} else {
|
||||||
p.projectTeamRepo.UpdateById(projectTeam)
|
p.teamRepo.UpdateById(team)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *projectTeamAppImpl) Delete(id uint64) {
|
func (p *teamAppImpl) Delete(id uint64) {
|
||||||
p.projectTeamRepo.Delete(id)
|
p.teamRepo.Delete(id)
|
||||||
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: id})
|
p.teamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: id})
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------- 团队成员相关接口 ---------------
|
// --------------- 团队成员相关接口 ---------------
|
||||||
|
|
||||||
func (p *projectTeamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult {
|
func (p *teamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult {
|
||||||
return p.projectTeamMemberRepo.GetPageList(condition, pageParam, toEntity)
|
return p.teamMemberRepo.GetPageList(condition, pageParam, toEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存团队成员信息
|
// 保存团队成员信息
|
||||||
func (p *projectTeamAppImpl) SaveMember(projectTeamMember *entity.TeamMember) {
|
func (p *teamAppImpl) SaveMember(teamMember *entity.TeamMember) {
|
||||||
projectTeamMember.Id = 0
|
teamMember.Id = 0
|
||||||
biz.IsTrue(!p.projectTeamMemberRepo.IsExist(projectTeamMember.TeamId, projectTeamMember.AccountId), "该成员已存在")
|
biz.IsTrue(!p.teamMemberRepo.IsExist(teamMember.TeamId, teamMember.AccountId), "该成员已存在")
|
||||||
p.projectTeamMemberRepo.Save(projectTeamMember)
|
p.teamMemberRepo.Save(teamMember)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除团队成员信息
|
// 删除团队成员信息
|
||||||
func (p *projectTeamAppImpl) DeleteMember(teamId, accountId uint64) {
|
func (p *teamAppImpl) DeleteMember(teamId, accountId uint64) {
|
||||||
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: teamId, AccountId: accountId})
|
p.teamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: teamId, AccountId: accountId})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *projectTeamAppImpl) IsExistMember(teamId, accounId uint64) bool {
|
func (p *teamAppImpl) IsExistMember(teamId, accounId uint64) bool {
|
||||||
return p.projectTeamMemberRepo.IsExist(teamId, accounId)
|
return p.teamMemberRepo.IsExist(teamId, accounId)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------- 关联项目相关接口 ---------------
|
//--------------- 关联项目相关接口 ---------------
|
||||||
|
|
||||||
func (p *projectTeamAppImpl) ListTagIds(teamId uint64) []uint64 {
|
func (p *teamAppImpl) ListTagIds(teamId uint64) []uint64 {
|
||||||
projects := &[]entity.TagTreeTeam{}
|
tags := &[]entity.TagTreeTeam{}
|
||||||
p.tagTreeTeamRepo.ListProject(&entity.TagTreeTeam{TeamId: teamId}, projects)
|
p.tagTreeTeamRepo.ListTag(&entity.TagTreeTeam{TeamId: teamId}, tags)
|
||||||
ids := make([]uint64, 0)
|
ids := make([]uint64, 0)
|
||||||
for _, v := range *projects {
|
for _, v := range *tags {
|
||||||
ids = append(ids, v.TagId)
|
ids = append(ids, v.TagId)
|
||||||
}
|
}
|
||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存关联项目信息
|
// 保存关联项目信息
|
||||||
func (p *projectTeamAppImpl) SaveTag(projectTreeTeam *entity.TagTreeTeam) {
|
func (p *teamAppImpl) SaveTag(tagTreeTeam *entity.TagTreeTeam) {
|
||||||
projectTreeTeam.Id = 0
|
tagTreeTeam.Id = 0
|
||||||
p.tagTreeTeamRepo.Save(projectTreeTeam)
|
p.tagTreeTeamRepo.Save(tagTreeTeam)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除关联项目信息
|
// 删除关联项目信息
|
||||||
func (p *projectTeamAppImpl) DeleteTag(teamId, tagId uint64) {
|
func (p *teamAppImpl) DeleteTag(teamId, tagId uint64) {
|
||||||
p.tagTreeTeamRepo.DeleteBy(&entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
|
p.tagTreeTeamRepo.DeleteBy(&entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import "mayfly-go/internal/tag/domain/entity"
|
|||||||
|
|
||||||
type TagTreeTeam interface {
|
type TagTreeTeam interface {
|
||||||
|
|
||||||
// 获取团队项目信息列表
|
// 获取团队标签信息列表
|
||||||
ListProject(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string)
|
ListTag(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string)
|
||||||
|
|
||||||
Save(mp *entity.TagTreeTeam)
|
Save(mp *entity.TagTreeTeam)
|
||||||
|
|
||||||
|
|||||||
@@ -64,12 +64,12 @@ func (a *tagTreeRepoImpl) GetBy(condition *entity.TagTree, cols ...string) error
|
|||||||
return model.GetBy(condition, cols...)
|
return model.GetBy(condition, cols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeRepoImpl) Insert(project *entity.TagTree) {
|
func (p *tagTreeRepoImpl) Insert(tagTree *entity.TagTree) {
|
||||||
biz.ErrIsNil(model.Insert(project), "新增标签失败")
|
biz.ErrIsNil(model.Insert(tagTree), "新增标签失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeRepoImpl) UpdateById(project *entity.TagTree) {
|
func (p *tagTreeRepoImpl) UpdateById(tagTree *entity.TagTree) {
|
||||||
biz.ErrIsNil(model.UpdateById(project), "更新标签失败")
|
biz.ErrIsNil(model.UpdateById(tagTree), "更新标签失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeRepoImpl) Delete(id uint64) {
|
func (p *tagTreeRepoImpl) Delete(id uint64) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func newTagTreeTeamRepo() repository.TagTreeTeam {
|
|||||||
return new(tagTreeTeamRepoImpl)
|
return new(tagTreeTeamRepoImpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeTeamRepoImpl) ListProject(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string) {
|
func (p *tagTreeTeamRepoImpl) ListTag(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string) {
|
||||||
model.ListByOrder(condition, toEntity, orderBy...)
|
model.ListByOrder(condition, toEntity, orderBy...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,18 +17,18 @@ func (p *teamRepoImpl) GetPageList(condition *entity.Team, pageParam *model.Page
|
|||||||
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *teamRepoImpl) Insert(projectTeam *entity.Team) {
|
func (p *teamRepoImpl) Insert(team *entity.Team) {
|
||||||
biz.ErrIsNil(model.Insert(projectTeam), "新增团队失败")
|
biz.ErrIsNil(model.Insert(team), "新增团队失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *teamRepoImpl) UpdateById(projectTeam *entity.Team) {
|
func (p *teamRepoImpl) UpdateById(team *entity.Team) {
|
||||||
biz.ErrIsNil(model.UpdateById(projectTeam), "更新团队失败")
|
biz.ErrIsNil(model.UpdateById(team), "更新团队失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *teamRepoImpl) Delete(id uint64) {
|
func (p *teamRepoImpl) Delete(id uint64) {
|
||||||
model.DeleteById(new(entity.Team), id)
|
model.DeleteById(new(entity.Team), id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *teamRepoImpl) DeleteBy(projectTeam *entity.Team) {
|
func (p *teamRepoImpl) DeleteBy(team *entity.Team) {
|
||||||
model.DeleteByCondition(projectTeam)
|
model.DeleteByCondition(team)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,37 +13,37 @@ func InitTagTreeRouter(router *gin.RouterGroup) {
|
|||||||
TagTreeApp: application.GetTagTreeApp(),
|
TagTreeApp: application.GetTagTreeApp(),
|
||||||
}
|
}
|
||||||
|
|
||||||
project := router.Group("/tag-trees")
|
tagTree := router.Group("/tag-trees")
|
||||||
{
|
{
|
||||||
// 获取标签树列表
|
// 获取标签树列表
|
||||||
project.GET("", func(c *gin.Context) {
|
tagTree.GET("", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).Handle(m.GetTagTree)
|
req.NewCtxWithGin(c).Handle(m.GetTagTree)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 根据条件获取标签
|
// 根据条件获取标签
|
||||||
project.GET("query", func(c *gin.Context) {
|
tagTree.GET("query", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).Handle(m.ListByQuery)
|
req.NewCtxWithGin(c).Handle(m.ListByQuery)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取登录账号拥有的标签信息
|
// 获取登录账号拥有的标签信息
|
||||||
project.GET("account-has", func(c *gin.Context) {
|
tagTree.GET("account-has", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).Handle(m.GetAccountTags)
|
req.NewCtxWithGin(c).Handle(m.GetAccountTags)
|
||||||
})
|
})
|
||||||
|
|
||||||
saveProjectTreeLog := req.NewLogInfo("标签树-保存信息").WithSave(true)
|
saveTagTreeLog := req.NewLogInfo("标签树-保存信息").WithSave(true)
|
||||||
savePP := req.NewPermission("tag:save")
|
savePP := req.NewPermission("tag:save")
|
||||||
// 保存项目树下的环境信息
|
// 保存项目树下的环境信息
|
||||||
project.POST("", func(c *gin.Context) {
|
tagTree.POST("", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).WithLog(saveProjectTreeLog).
|
req.NewCtxWithGin(c).WithLog(saveTagTreeLog).
|
||||||
WithRequiredPermission(savePP).
|
WithRequiredPermission(savePP).
|
||||||
Handle(m.SaveTagTree)
|
Handle(m.SaveTagTree)
|
||||||
})
|
})
|
||||||
|
|
||||||
delProjectLog := req.NewLogInfo("标签树-删除信息").WithSave(true)
|
delTagLog := req.NewLogInfo("标签树-删除信息").WithSave(true)
|
||||||
delPP := req.NewPermission("tag:del")
|
delPP := req.NewPermission("tag:del")
|
||||||
// 删除标签
|
// 删除标签
|
||||||
project.DELETE(":id", func(c *gin.Context) {
|
tagTree.DELETE(":id", func(c *gin.Context) {
|
||||||
req.NewCtxWithGin(c).WithLog(delProjectLog).
|
req.NewCtxWithGin(c).WithLog(delTagLog).
|
||||||
WithRequiredPermission(delPP).
|
WithRequiredPermission(delPP).
|
||||||
Handle(m.DelTagTree)
|
Handle(m.DelTagTree)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ CREATE TABLE `t_db` (
|
|||||||
`database` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库,空格分割多个数据库',
|
`database` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库,空格分割多个数据库',
|
||||||
`params` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '其他连接参数',
|
`params` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '其他连接参数',
|
||||||
`network` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
`network` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
|
|
||||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||||
`remark` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注,描述等',
|
`remark` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注,描述等',
|
||||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||||
@@ -105,6 +104,23 @@ CREATE TABLE `t_db_sql_exec` (
|
|||||||
BEGIN;
|
BEGIN;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `t_auth_cert`;
|
||||||
|
CREATE TABLE `t_auth_cert` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
|
`auth_method` tinyint NOT NULL COMMENT '1.密码 2.秘钥',
|
||||||
|
`password` varchar(4200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码or私钥',
|
||||||
|
`passphrase` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '私钥口令',
|
||||||
|
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
|
`create_time` datetime NOT NULL,
|
||||||
|
`creator` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`creator_id` bigint NOT NULL,
|
||||||
|
`update_time` datetime NOT NULL,
|
||||||
|
`modifier` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`modifier_id` bigint NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='授权凭证';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Table structure for t_machine
|
-- Table structure for t_machine
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -116,8 +132,8 @@ CREATE TABLE `t_machine` (
|
|||||||
`port` int(12) NOT NULL,
|
`port` int(12) NOT NULL,
|
||||||
`username` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
`username` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
`auth_method` tinyint(2) DEFAULT NULL COMMENT '1.密码登录2.publickey登录',
|
`auth_method` tinyint(2) DEFAULT NULL COMMENT '1.密码登录2.publickey登录',
|
||||||
`password` varchar(3200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
|
`auth_cert_id` bigint(20) DEFAULT NULL COMMENT '授权凭证id',
|
||||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||||
`enable_recorder` tinyint(2) DEFAULT NULL COMMENT '是否启用终端回放记录',
|
`enable_recorder` tinyint(2) DEFAULT NULL COMMENT '是否启用终端回放记录',
|
||||||
`status` tinyint(2) NOT NULL COMMENT '状态: 1:启用; -1:禁用',
|
`status` tinyint(2) NOT NULL COMMENT '状态: 1:启用; -1:禁用',
|
||||||
@@ -226,7 +242,6 @@ CREATE TABLE `t_mongo` (
|
|||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`name` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
|
`name` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
|
||||||
`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '连接uri',
|
`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '连接uri',
|
||||||
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
|
|
||||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||||
`tag_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '标签路径',
|
`tag_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '标签路径',
|
||||||
@@ -256,7 +271,6 @@ CREATE TABLE `t_redis` (
|
|||||||
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`db` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '库号: 多个库用,分割',
|
`db` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '库号: 多个库用,分割',
|
||||||
`mode` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
`mode` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
|
|
||||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||||
`remark` varchar(125) COLLATE utf8mb4_bin DEFAULT NULL,
|
`remark` varchar(125) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||||
@@ -498,6 +512,10 @@ INSERT INTO `t_sys_resource` VALUES (99, 95, 2, 1, '删除团队', 'team:del', 2
|
|||||||
INSERT INTO `t_sys_resource` VALUES (100, 95, 2, 1, '新增团队成员', 'team:member:save', 3, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:27', '2022-10-26 13:59:27');
|
INSERT INTO `t_sys_resource` VALUES (100, 95, 2, 1, '新增团队成员', 'team:member:save', 3, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:27', '2022-10-26 13:59:27');
|
||||||
INSERT INTO `t_sys_resource` VALUES (101, 95, 2, 1, '移除团队成员', 'team:member:del', 4, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:43', '2022-10-26 13:59:43');
|
INSERT INTO `t_sys_resource` VALUES (101, 95, 2, 1, '移除团队成员', 'team:member:del', 4, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:43', '2022-10-26 13:59:43');
|
||||||
INSERT INTO `t_sys_resource` VALUES (102, 95, 2, 1, '保存团队标签', 'team:tag:save', 5, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:57', '2022-10-26 13:59:57');
|
INSERT INTO `t_sys_resource` VALUES (102, 95, 2, 1, '保存团队标签', 'team:tag:save', 5, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:57', '2022-10-26 13:59:57');
|
||||||
|
INSERT INTO `t_sys_resource` VALUES (103, 2, 1, 1, '授权凭证', 'authcerts', 6, '{"component":"AuthCertList","icon":"Unlock","isKeepAlive":true,"routeName":"AuthCertList"}', 1, 'admin', 1, 'admin', '2023-02-23 11:36:26', '2023-02-23 14:40:23');
|
||||||
|
INSERT INTO `t_sys_resource` VALUES (104, 103, 2, 1, '基本权限', 'authcert', 1, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:37:24', '2023-02-23 11:37:24');
|
||||||
|
INSERT INTO `t_sys_resource` VALUES (105, 103, 2, 1, '保存权限', 'authcert:save', 2, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:37:54', '2023-02-23 11:37:54');
|
||||||
|
INSERT INTO `t_sys_resource` VALUES (106, 103, 2, 1, '删除权限', 'authcert:del', 3, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:38:09', '2023-02-23 11:38:09');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -702,6 +720,10 @@ INSERT INTO `t_sys_role_resource` VALUES (522, 1, 99, 1, 'admin', '2022-10-26 20
|
|||||||
INSERT INTO `t_sys_role_resource` VALUES (523, 1, 100, 1, 'admin', '2022-10-26 20:03:14');
|
INSERT INTO `t_sys_role_resource` VALUES (523, 1, 100, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
INSERT INTO `t_sys_role_resource` VALUES (524, 1, 101, 1, 'admin', '2022-10-26 20:03:14');
|
INSERT INTO `t_sys_role_resource` VALUES (524, 1, 101, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
INSERT INTO `t_sys_role_resource` VALUES (525, 1, 102, 1, 'admin', '2022-10-26 20:03:14');
|
INSERT INTO `t_sys_role_resource` VALUES (525, 1, 102, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
|
INSERT INTO `t_sys_role_resource` VALUES (526, 1, 103, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
|
INSERT INTO `t_sys_role_resource` VALUES (527, 1, 104, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
|
INSERT INTO `t_sys_role_resource` VALUES (528, 1, 105, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
|
INSERT INTO `t_sys_role_resource` VALUES (529, 1, 106, 1, 'admin', '2022-10-26 20:03:14');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ErrIsNil(err error, msg string, params ...interface{}) {
|
func ErrIsNil(err error, msg string, params ...any) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Log.Error(msg + ": " + err.Error())
|
global.Log.Error(msg + ": " + err.Error())
|
||||||
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
||||||
@@ -31,7 +31,7 @@ func IsNil(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsTrue(exp bool, msg string, params ...interface{}) {
|
func IsTrue(exp bool, msg string, params ...any) {
|
||||||
if !exp {
|
if !exp {
|
||||||
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
||||||
}
|
}
|
||||||
@@ -43,21 +43,21 @@ func IsTrueBy(exp bool, err BizError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotEmpty(str string, msg string, params ...interface{}) {
|
func NotEmpty(str string, msg string, params ...any) {
|
||||||
if str == "" {
|
if str == "" {
|
||||||
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotNil(data interface{}, msg string) {
|
func NotNil(data interface{}, msg string, params ...any) {
|
||||||
if reflect.ValueOf(data).IsNil() {
|
if reflect.ValueOf(data).IsNil() {
|
||||||
panic(NewBizErr(msg))
|
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotBlank(data interface{}, msg string) {
|
func NotBlank(data interface{}, msg string, params ...any) {
|
||||||
if utils.IsBlank(data) {
|
if utils.IsBlank(data) {
|
||||||
panic(NewBizErr(msg))
|
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "fmt"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
AppName = "mayfly-go"
|
AppName = "mayfly-go"
|
||||||
Version = "v1.4.0"
|
Version = "v1.4.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAppInfo() string {
|
func GetAppInfo() string {
|
||||||
|
|||||||
Reference in New Issue
Block a user