mirror of
https://github.com/Fabio286/antares.git
synced 2025-03-25 15:50:13 +01:00
feat: SSH Tunnel functionality (#81)
* added ssh-tunnel-functionality for mysql-connections * remove autoformat-stuff * added identity for using ssh-key * added identity to mysqlclient to use sshkey * removed debug console.log * added ssh-tunnel-functionality for postgresqlclient * changed naming to sshKey for sshKey-input * refactoring code * fix lint * set dbConfig.ssl to null initially
This commit is contained in:
parent
0db5ebd7bf
commit
1801bef019
@ -102,6 +102,7 @@
|
||||
"source-map-support": "^0.5.16",
|
||||
"spectre.css": "^0.5.9",
|
||||
"sql-formatter": "^4.0.2",
|
||||
"ssh2-promise": "^0.1.7",
|
||||
"v-mask": "^2.2.4",
|
||||
"vue-i18n": "^8.24.4",
|
||||
"vuedraggable": "^2.24.3",
|
||||
|
@ -24,13 +24,23 @@ export default connections => {
|
||||
};
|
||||
}
|
||||
|
||||
const connection = ClientsFactory.getConnection({
|
||||
client: conn.client,
|
||||
params
|
||||
});
|
||||
if (conn.ssh) {
|
||||
params.ssh = {
|
||||
host: conn.sshHost,
|
||||
username: conn.sshUser,
|
||||
password: conn.sshPass,
|
||||
port: conn.sshPort ? conn.sshPort : 22,
|
||||
identity: conn.sshKey
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const connection = await ClientsFactory.getConnection({
|
||||
client: conn.client,
|
||||
params
|
||||
});
|
||||
await connection.connect();
|
||||
|
||||
await connection.select('1+1').run();
|
||||
connection.destroy();
|
||||
|
||||
@ -66,6 +76,16 @@ export default connections => {
|
||||
};
|
||||
}
|
||||
|
||||
if (conn.ssh) {
|
||||
params.ssh = {
|
||||
host: conn.sshHost,
|
||||
username: conn.sshUser,
|
||||
password: conn.sshPass,
|
||||
port: conn.sshPort ? conn.sshPort : 22,
|
||||
identity: conn.sshKey
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const connection = ClientsFactory.getConnection({
|
||||
client: conn.client,
|
||||
|
@ -12,6 +12,10 @@ export class ClientsFactory {
|
||||
* @param {String} args.params.host
|
||||
* @param {Number} args.params.port
|
||||
* @param {String} args.params.password
|
||||
* @param {String} args.params.ssh.host
|
||||
* @param {String} args.params.ssh.username
|
||||
* @param {String} args.params.ssh.password
|
||||
* @param {Number} args.params.ssh.port
|
||||
* @param {Number=} args.poolSize
|
||||
* @returns Database Connection
|
||||
* @memberof ClientsFactory
|
||||
|
@ -2,6 +2,7 @@
|
||||
import mysql from 'mysql2/promise';
|
||||
import { AntaresCore } from '../AntaresCore';
|
||||
import dataTypes from 'common/data-types/mysql';
|
||||
import * as SSH2Promise from 'ssh2-promise';
|
||||
|
||||
export class MySQLClient extends AntaresCore {
|
||||
constructor (args) {
|
||||
@ -104,11 +105,32 @@ export class MySQLClient extends AntaresCore {
|
||||
async connect () {
|
||||
delete this._params.application_name;
|
||||
|
||||
if (!this._poolSize)
|
||||
this._connection = await mysql.createConnection(this._params);
|
||||
const dbConfig = {
|
||||
host: this._params.host,
|
||||
port: this._params.port,
|
||||
user: this._params.user,
|
||||
password: this._params.password,
|
||||
ssl: null
|
||||
};
|
||||
|
||||
if (this._params.database?.length) dbConfig.database = this._params.database;
|
||||
|
||||
if (this._params.ssl) dbConfig.ssl = { ...this._params.ssl };
|
||||
|
||||
if (this._params.ssh) {
|
||||
this._ssh = new SSH2Promise({ ...this._params.ssh });
|
||||
|
||||
this._tunnel = await this._ssh.addTunnel({
|
||||
remoteAddr: this._params.host,
|
||||
remotePort: this._params.port
|
||||
});
|
||||
dbConfig.port = this._tunnel.localPort;
|
||||
}
|
||||
|
||||
if (!this._poolSize) this._connection = await mysql.createConnection(dbConfig);
|
||||
else {
|
||||
this._connection = mysql.createPool({
|
||||
...this._params,
|
||||
...dbConfig,
|
||||
connectionLimit: this._poolSize,
|
||||
typeCast: (field, next) => {
|
||||
if (field.type === 'DATETIME')
|
||||
@ -125,6 +147,7 @@ export class MySQLClient extends AntaresCore {
|
||||
*/
|
||||
destroy () {
|
||||
this._connection.end();
|
||||
if (this._ssh) this._ssh.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ import { Pool, Client, types } from 'pg';
|
||||
import { parse } from 'pgsql-ast-parser';
|
||||
import { AntaresCore } from '../AntaresCore';
|
||||
import dataTypes from 'common/data-types/postgresql';
|
||||
import * as SSH2Promise from 'ssh2-promise';
|
||||
|
||||
function pgToString (value) {
|
||||
return value.toString();
|
||||
@ -51,13 +52,35 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async connect () {
|
||||
const dbConfig = {
|
||||
host: this._params.host,
|
||||
port: this._params.port,
|
||||
user: this._params.user,
|
||||
password: this._params.password,
|
||||
ssl: null
|
||||
};
|
||||
|
||||
if (this._params.database?.length) dbConfig.database = this._params.database;
|
||||
|
||||
if (this._params.ssl) dbConfig.ssl = { ...this._params.ssl };
|
||||
|
||||
if (this._params.ssh) {
|
||||
this._ssh = new SSH2Promise({ ...this._params.ssh });
|
||||
|
||||
this._tunnel = await this._ssh.addTunnel({
|
||||
remoteAddr: this._params.host,
|
||||
remotePort: this._params.port
|
||||
});
|
||||
dbConfig.port = this._tunnel.localPort;
|
||||
}
|
||||
|
||||
if (!this._poolSize) {
|
||||
const client = new Client(this._params);
|
||||
const client = new Client(dbConfig);
|
||||
await client.connect();
|
||||
this._connection = client;
|
||||
}
|
||||
else {
|
||||
const pool = new Pool({ ...this._params, max: this._poolSize });
|
||||
const pool = new Pool({ ...dbConfig, max: this._poolSize });
|
||||
this._connection = pool;
|
||||
}
|
||||
}
|
||||
@ -67,6 +90,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
*/
|
||||
destroy () {
|
||||
this._connection.end();
|
||||
if (this._ssh) this._ssh.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,13 @@
|
||||
>
|
||||
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
:class="{'active': selectedTab === 'ssh'}"
|
||||
@click="selectTab('ssh')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.sshTunnel') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||
@ -208,7 +215,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||
@ -231,6 +237,95 @@
|
||||
:status="toast.status"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
|
||||
<div class="container">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">
|
||||
{{ $t('message.enableSsh') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
|
||||
<input type="checkbox" :checked="localConnection.ssh">
|
||||
<i class="form-icon" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssh">
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="localConnection.sshHost"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.user') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="localConnection.sshUser"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.password') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="localConnection.sshPass"
|
||||
class="form-input"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.port') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="localConnection.sshPort"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.sshKey"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('sshKey')"
|
||||
@change="pathSelection($event, 'sshKey')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<BaseToast
|
||||
class="mb-2"
|
||||
:message="toast.message"
|
||||
:status="toast.status"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer text-light">
|
||||
<button
|
||||
class="btn btn-gray mr-2"
|
||||
@ -369,6 +464,9 @@ export default {
|
||||
toggleSsl () {
|
||||
this.localConnection.ssl = !this.localConnection.ssl;
|
||||
},
|
||||
toggleSsh () {
|
||||
this.localConnection.ssh = !this.localConnection.ssh;
|
||||
},
|
||||
pathSelection (event, name) {
|
||||
const { files } = event.target;
|
||||
if (!files.length) return;
|
||||
|
@ -29,6 +29,13 @@
|
||||
>
|
||||
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
:class="{'active': selectedTab === 'ssh'}"
|
||||
@click="selectTab('ssh')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.sshTunnel') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||
@ -213,7 +220,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||
@ -236,6 +242,95 @@
|
||||
:status="toast.status"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
|
||||
<div class="container">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">
|
||||
{{ $t('message.enableSsh') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
|
||||
<input type="checkbox" :checked="connection.ssh">
|
||||
<i class="form-icon" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset class="m-0" :disabled="isTesting || !connection.ssh">
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="connection.sshHost"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.user') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="connection.sshUser"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.password') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="connection.sshPass"
|
||||
class="form-input"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.port') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="connection.sshPort"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.sshKey"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('sshKey')"
|
||||
@change="pathSelection($event, 'sshKey')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<BaseToast
|
||||
class="mb-2"
|
||||
:message="toast.message"
|
||||
:status="toast.status"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer text-light">
|
||||
@ -294,8 +389,13 @@ export default {
|
||||
cert: '',
|
||||
key: '',
|
||||
ca: '',
|
||||
ciphers: ''
|
||||
|
||||
ciphers: '',
|
||||
ssh: false,
|
||||
sshHost: '',
|
||||
sshUser: '',
|
||||
sshPass: '',
|
||||
sshKey: '',
|
||||
sshPort: 22
|
||||
},
|
||||
toast: {
|
||||
status: '',
|
||||
@ -393,6 +493,9 @@ export default {
|
||||
toggleSsl () {
|
||||
this.connection.ssl = !this.connection.ssl;
|
||||
},
|
||||
toggleSsh () {
|
||||
this.connection.ssh = !this.connection.ssh;
|
||||
},
|
||||
pathSelection (event, name) {
|
||||
const { files } = event.target;
|
||||
if (!files.length) return;
|
||||
|
@ -104,7 +104,8 @@ module.exports = {
|
||||
database: 'Datenbank',
|
||||
scratchpad: 'Scratchpad',
|
||||
array: 'Array',
|
||||
format: 'Formatierung'
|
||||
format: 'Formatierung',
|
||||
sshTunnel: 'SSH Tunnel'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Willkommen im Antares SQL Client!',
|
||||
@ -210,7 +211,8 @@ module.exports = {
|
||||
deleteSchema: 'Schema löschen',
|
||||
markdownSupported: 'Unterstützt Markdown',
|
||||
plantATree: 'Pflanze einen Baum',
|
||||
dataTabPageSize: 'Einträge pro Tab / Seite'
|
||||
dataTabPageSize: 'Einträge pro Tab / Seite',
|
||||
enableSsh: 'Aktiviere SSH'
|
||||
},
|
||||
faker: {
|
||||
address: 'Adresse',
|
||||
|
@ -106,6 +106,7 @@ module.exports = {
|
||||
array: 'Array',
|
||||
changelog: 'Changelog',
|
||||
format: 'Format',
|
||||
sshTunnel: 'SSH tunnel',
|
||||
structure: 'Structure',
|
||||
small: 'Small',
|
||||
medium: 'Medium',
|
||||
@ -219,6 +220,7 @@ module.exports = {
|
||||
markdownSupported: 'Markdown supported',
|
||||
plantATree: 'Plant a Tree',
|
||||
dataTabPageSize: 'DATA tab page size',
|
||||
enableSsh: 'Enable SSH',
|
||||
pageNumber: 'Page number',
|
||||
duplicateTable: 'Duplicate table'
|
||||
},
|
||||
|
@ -93,7 +93,8 @@ module.exports = {
|
||||
ciphers: 'Chiffrement',
|
||||
upload: 'Charger',
|
||||
browse: 'Parcourir',
|
||||
faker: 'Faker'
|
||||
faker: 'Faker',
|
||||
sshTunnel: 'SSH tunnel'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Bienvenu sur le client SQL Antares!',
|
||||
@ -183,7 +184,8 @@ module.exports = {
|
||||
preserveOnCompletion: 'Préserver à l\'achèvement',
|
||||
enableSsl: 'Activer le SSL',
|
||||
manualValue: 'Valeur manuelle',
|
||||
tableFiller: 'Remplisseur de table'
|
||||
tableFiller: 'Remplisseur de table',
|
||||
enableSsh: 'Activer le SSH'
|
||||
},
|
||||
faker: {
|
||||
address: 'Adresse',
|
||||
|
@ -105,7 +105,8 @@ module.exports = {
|
||||
scratchpad: 'Blocco appunti',
|
||||
array: 'Array',
|
||||
changelog: 'Changelog',
|
||||
format: 'Formatta'
|
||||
format: 'Formatta',
|
||||
sshTunnel: 'SSH tunnel'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Benvenuto in Antares SQL Client!',
|
||||
@ -210,7 +211,8 @@ module.exports = {
|
||||
editSchema: 'Modifica schema',
|
||||
deleteSchema: 'Elimina schema',
|
||||
markdownSupported: 'Markdown supportato',
|
||||
plantATree: 'Pianta un albero'
|
||||
plantATree: 'Pianta un albero',
|
||||
enableSsh: 'Abilita SSH'
|
||||
},
|
||||
faker: {
|
||||
address: 'Indirizzo',
|
||||
|
@ -105,7 +105,8 @@ module.exports = {
|
||||
scratchpad: 'Rascunho',
|
||||
array: 'Array',
|
||||
changelog: 'Logs de alteração',
|
||||
format: 'Formato'
|
||||
format: 'Formato',
|
||||
sshTunnel: 'SSH túnel'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Bem vindo ao Antares SQL Client!',
|
||||
@ -210,7 +211,8 @@ module.exports = {
|
||||
editSchema: 'Editar schema',
|
||||
deleteSchema: 'Apagar schema',
|
||||
markdownSupported: 'Markdown suportado',
|
||||
plantATree: 'Plante uma árvore'
|
||||
plantATree: 'Plante uma árvore',
|
||||
enableSsh: 'Habilitar SSH'
|
||||
},
|
||||
faker: {
|
||||
address: 'Endereço',
|
||||
|
@ -215,8 +215,10 @@
|
||||
}
|
||||
|
||||
.bg-checkered {
|
||||
background-image: linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
|
||||
linear-gradient(to right, black 50%, white 50%), linear-gradient(to bottom, black 50%, white 50%);
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
|
||||
linear-gradient(to right, black 50%, white 50%),
|
||||
linear-gradient(to bottom, black 50%, white 50%);
|
||||
background-blend-mode: normal, difference, normal;
|
||||
background-size: 2em 2em;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user