feat: linux支持ssh隧道访问&其他优化

This commit is contained in:
meilin.huang
2022-07-23 16:41:04 +08:00
parent f0540559bb
commit 76d6fc3ba5
26 changed files with 2003 additions and 1556 deletions

View File

@@ -13,7 +13,7 @@
"countup.js": "^2.0.7",
"cropperjs": "^1.5.11",
"echarts": "^5.3.3",
"element-plus": "^2.2.9",
"element-plus": "^2.2.10",
"jsencrypt": "^3.2.1",
"jsoneditor": "^9.9.0",
"lodash": "^4.17.21",

View File

@@ -947,12 +947,6 @@
.el-select-dropdown .el-scrollbar__wrap {
overflow-x: scroll !important;
}
.el-select-dropdown__wrap {
max-height: 274px !important; /*修复Select 选择器高度问题*/
}
.el-cascader-menu__wrap.el-scrollbar__wrap {
height: 204px !important; /*修复Cascader 级联选择器高度问题*/
}
/* Drawer 抽屉
------------------------------- */

View File

@@ -47,28 +47,20 @@
<el-input v-model="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2"></el-input>
</el-form-item>
<el-form-item prop="database" label="数据库名:" required>
<el-tag
v-for="db in databaseList"
:key="db"
class="ml5 mt5"
type="success"
effect="plain"
closable
:disable-transitions="false"
@close="handleClose(db)"
<el-select
@change="changeDatabase"
@focus="getAllDatabase"
v-model="databaseList"
multiple
collapse-tags
collapse-tags-tooltip
filterable
allow-create
placeholder="请确保数据库实例信息填写完整后选择数据库"
style="width: 100%"
>
{{ db }}
</el-tag>
<el-input
v-if="inputDbVisible"
ref="InputDbRef"
v-model="inputDbValue"
style="width: 120px; margin-left: 5px; margin-top: 5px"
size="small"
@keyup.enter="handleInputDbConfirm"
@blur="handleInputDbConfirm"
/>
<el-button v-else class="ml5 mt5" size="small" @click="showInputDb"> + 添加数据库 </el-button>
<el-option v-for="db in allDatabases" :key="db" :label="db" :value="db" />
</el-select>
</el-form-item>
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
@@ -101,12 +93,11 @@
</template>
<script lang="ts">
import { toRefs, reactive, nextTick, watch, defineComponent, ref } from 'vue';
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
import { dbApi } from './api';
import { projectApi } from '../project/api.ts';
import { machineApi } from '../machine/api.ts';
import { ElMessage } from 'element-plus';
import type { ElInput } from 'element-plus';
import { notBlank } from '@/common/assert';
import { RsaEncrypt } from '@/common/rsa';
@@ -128,16 +119,14 @@ export default defineComponent({
},
setup(props: any, { emit }) {
const dbForm: any = ref(null);
const InputDbRef = ref<InstanceType<typeof ElInput>>();
const state = reactive({
dialogVisible: false,
projects: [],
envs: [],
allDatabases: [] as any,
databaseList: [] as any,
sshTunnelMachineList: [],
inputDbVisible: false,
inputDbValue: '',
form: {
id: null,
name: null,
@@ -226,27 +215,6 @@ export default defineComponent({
getSshTunnelMachines();
});
const handleClose = (db: string) => {
state.databaseList.splice(state.databaseList.indexOf(db), 1);
changeDatabase();
};
const showInputDb = () => {
state.inputDbVisible = true;
nextTick(() => {
InputDbRef.value!.input!.focus();
});
};
const handleInputDbConfirm = () => {
if (state.inputDbValue) {
state.databaseList.push(state.inputDbValue);
changeDatabase();
}
state.inputDbVisible = false;
state.inputDbValue = '';
};
/**
* 改变表单中的数据库字段,方便表单错误提示。如全部删光,可提示请添加数据库
*/
@@ -285,6 +253,15 @@ export default defineComponent({
}
};
const getAllDatabase = async () => {
if (state.allDatabases.length != 0) {
return;
}
const reqForm = { ...state.form };
reqForm.password = await RsaEncrypt(reqForm.password);
state.allDatabases = await dbApi.getAllDatabase.request(reqForm);
};
const btnOk = async () => {
if (!state.form.id) {
notBlank(state.form.password, '新增操作,密码不可为空');
@@ -293,7 +270,6 @@ export default defineComponent({
if (valid) {
const reqForm = { ...state.form };
reqForm.password = await RsaEncrypt(reqForm.password);
// reqForm.ssh_pass = await RsaEncrypt(reqForm.ssh_pass);
dbApi.saveDb.request(reqForm).then(() => {
ElMessage.success('保存成功');
emit('val-change', state.form);
@@ -312,9 +288,8 @@ export default defineComponent({
};
const resetInputDb = () => {
state.inputDbVisible = false;
state.databaseList = [];
state.inputDbValue = '';
state.allDatabases = [];
};
const cancel = () => {
@@ -328,10 +303,8 @@ export default defineComponent({
return {
...toRefs(state),
dbForm,
InputDbRef,
handleClose,
showInputDb,
handleInputDbConfirm,
getAllDatabase,
changeDatabase,
getSshTunnelMachines,
changeProject,
changeEnv,

View File

@@ -502,6 +502,8 @@ export default defineComponent({
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: row.id, db });
state.dbId = row.id;
state.db = db;
} catch (e) {
state.tableInfoDialog.visible = false;
} finally {
state.tableInfoDialog.loading = false;
}

View File

@@ -4,6 +4,7 @@ export const dbApi = {
// 获取权限列表
dbs: Api.create("/dbs", 'get'),
saveDb: Api.create("/dbs", 'post'),
getAllDatabase: Api.create("/dbs/databases", 'post'),
deleteDb: Api.create("/dbs/{id}", 'delete'),
dumpDb: Api.create("/dbs/{id}/dump", 'post'),
tableInfos: Api.create("/dbs/{id}/t-infos", 'get'),

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true" :before-close="cancel" width="35%">
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true" :before-close="cancel" width="38%">
<el-form :model="form" ref="machineForm" :rules="rules" label-width="85px">
<el-form-item prop="projectId" label="项目:" required>
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
@@ -43,6 +43,24 @@
<el-form-item prop="remark" label="备注:">
<el-input type="textarea" v-model="form.remark"></el-input>
</el-form-item>
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
<el-col :span="3">
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1" :false-label="-1"></el-checkbox>
</el-col>
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
<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>
<template #footer>
@@ -83,6 +101,7 @@ export default defineComponent({
const state = reactive({
dialogVisible: false,
projects: [],
sshTunnelMachineList: [],
form: {
id: null,
projectId: null,
@@ -93,6 +112,8 @@ export default defineComponent({
username: '',
password: '',
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
},
btnLoading: false,
rules: {
@@ -152,8 +173,20 @@ export default defineComponent({
} else {
state.form = { port: 22, authMethod: 1 } 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 getSshTunnelMachine = (machineId: any) => {
return state.sshTunnelMachineList.find((x: any) => x.id == machineId);
};
const changeProject = (projectId: number) => {
for (let p of state.projects as any) {
if (p.id == projectId) {
@@ -168,7 +201,15 @@ export default defineComponent({
}
machineForm.value.validate(async (valid: boolean) => {
if (valid) {
const reqForm = { ...state.form };
const form: any = state.form;
if (form.enableSshTunnel == 1) {
const tunnelMachine: any = getSshTunnelMachine(form.sshTunnelMachineId);
if (tunnelMachine.ip == form.ip && tunnelMachine.port == form.port) {
ElMessage.error('隧道机器不能与本机器一致');
return;
}
}
const reqForm: any = { ...form };
if (reqForm.authMethod == 1) {
reqForm.password = await RsaEncrypt(state.form.password);
}
@@ -196,6 +237,7 @@ export default defineComponent({
return {
...toRefs(state),
machineForm,
getSshTunnelMachines,
changeProject,
btnOk,
cancel,

View File

@@ -120,7 +120,7 @@
</template>
</el-dialog>
<el-dialog width="800px" title="json编辑器" v-model="jsoneditorDialog.visible" @close="onCloseJsonEditDialog" :close-on-click-modal="false">
<el-dialog width="70%" title="json编辑器" v-model="jsoneditorDialog.visible" @close="onCloseJsonEditDialog" :close-on-click-modal="false">
<json-edit v-model="jsoneditorDialog.doc" />
</el-dialog>

File diff suppressed because it is too large Load Diff