mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 12:43:58 +00:00
connection modal converted
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
if (tabComponent) {
|
if (tabComponent) {
|
||||||
return {
|
return {
|
||||||
tabComponent,
|
tabComponent,
|
||||||
props: selectedTab.props,
|
props: selectedTab && selectedTab.props,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -38,11 +38,15 @@
|
|||||||
$: {
|
$: {
|
||||||
if (selectedTab) {
|
if (selectedTab) {
|
||||||
const { tabid } = selectedTab;
|
const { tabid } = selectedTab;
|
||||||
if (tabid && !mountedTabs[tabid])
|
if (tabid && !mountedTabs[tabid]) {
|
||||||
mountedTabs = {
|
const newTab = createTabComponent(selectedTab);
|
||||||
...mountedTabs,
|
if (newTab) {
|
||||||
[tabid]: createTabComponent(selectedTab),
|
mountedTabs = {
|
||||||
};
|
...mountedTabs,
|
||||||
|
[tabid]: newTab,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -9,8 +9,15 @@
|
|||||||
const handleConnect = () => {
|
const handleConnect = () => {
|
||||||
openedConnections.update(list => _.uniq([...list, data._id]));
|
openedConnections.update(list => _.uniq([...list, data._id]));
|
||||||
};
|
};
|
||||||
|
const handleEdit = () => {
|
||||||
|
showModal(ConnectionModal, { connection: data });
|
||||||
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
text: 'Edit',
|
||||||
|
onClick: handleEdit,
|
||||||
|
},
|
||||||
!$openedConnections.includes(data._id) && {
|
!$openedConnections.includes(data._id) && {
|
||||||
text: 'Connect',
|
text: 'Connect',
|
||||||
onClick: handleConnect,
|
onClick: handleConnect,
|
||||||
@@ -37,6 +44,8 @@
|
|||||||
import { currentDatabase, extensions, openedConnections } from '../stores';
|
import { currentDatabase, extensions, openedConnections } from '../stores';
|
||||||
import axios from '../utility/axios';
|
import axios from '../utility/axios';
|
||||||
import { filterName } from 'dbgate-datalib';
|
import { filterName } from 'dbgate-datalib';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
import ConnectionModal from '../modals/ConnectionModal.svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<input type="checkbox" {...$$restProps} />
|
<input type="checkbox" {...$$restProps} on:change />
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getFormContext } from './FormProviderCore.svelte';
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
import FromCheckboxFieldRaw from './FromTextFieldRaw.svelte';
|
import FromCheckboxFieldRaw from './FromCheckboxFieldRaw.svelte';
|
||||||
|
|
||||||
export let label;
|
export let label;
|
||||||
export let name;
|
export let name;
|
||||||
|
export let disabled;
|
||||||
export let templateProps = {};
|
export let templateProps = {};
|
||||||
|
|
||||||
const { template } = getFormContext();
|
const { template, setFieldValue, values } = getFormContext();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:component this={template} type="checkbox" {label} {...templateProps}>
|
<svelte:component
|
||||||
<FromCheckboxFieldRaw {name} {...$$restProps} />
|
this={template}
|
||||||
|
type="checkbox"
|
||||||
|
{label}
|
||||||
|
{disabled}
|
||||||
|
{...templateProps}
|
||||||
|
labelProps={disabled ? { disabled: true } : { onClick: () => setFieldValue(name, !$values[name]) }}
|
||||||
|
>
|
||||||
|
<FromCheckboxFieldRaw {name} {...$$restProps} {disabled} />
|
||||||
</svelte:component>
|
</svelte:component>
|
||||||
|
|||||||
14
packages/web/src/forms/FormElectronFileSelector.svelte
Normal file
14
packages/web/src/forms/FormElectronFileSelector.svelte
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FormElectronFileSelectorRaw from './FormElectronFileSelectorRaw.svelte';
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
|
||||||
|
export let label;
|
||||||
|
export let name;
|
||||||
|
export let templateProps = {};
|
||||||
|
|
||||||
|
const { template } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:component this={template} type="select" {label} {...templateProps}>
|
||||||
|
<FormElectronFileSelectorRaw {name} {...$$restProps} />
|
||||||
|
</svelte:component>
|
||||||
29
packages/web/src/forms/FormElectronFileSelectorRaw.svelte
Normal file
29
packages/web/src/forms/FormElectronFileSelectorRaw.svelte
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import getElectron from '../utility/getElectron';
|
||||||
|
|
||||||
|
import InlineButton from '../widgets/InlineButton.svelte';
|
||||||
|
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
|
||||||
|
export let name;
|
||||||
|
export let disabled = false;
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
|
||||||
|
function handleBrowse() {
|
||||||
|
const electron = getElectron();
|
||||||
|
if (!electron) return;
|
||||||
|
const filePaths = electron.remote.dialog.showOpenDialogSync(electron.remote.getCurrentWindow(), {
|
||||||
|
defaultPath: values[name],
|
||||||
|
properties: ['showHiddenFiles'],
|
||||||
|
});
|
||||||
|
const filePath = filePaths && filePaths[0];
|
||||||
|
if (filePath) setFieldValue(name, filePath);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
<TextField {...$$restProps} value={$values[name]} onClick={handleBrowse} readOnly {disabled} />
|
||||||
|
<InlineButton on:click={handleBrowse} {disabled}>Browse</InlineButton>
|
||||||
|
</div>
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getFormContext } from './FormProviderCore.svelte';
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
import TextField from './TextField.svelte';
|
import CheckboxField from './CheckboxField.svelte';
|
||||||
|
|
||||||
export let name;
|
export let name;
|
||||||
|
|
||||||
const { values, setFieldValue } = getFormContext();
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
function handleChange(e) {
|
||||||
|
setFieldValue(name, e.target['checked']);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TextField {...$$restProps} value={$values[name]} on:change={e => setFieldValue(name, e.target['checked'])} />
|
<CheckboxField {...$$restProps} checked={$values[name]} on:change={handleChange} />
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
export let options = [];
|
export let options = [];
|
||||||
export let value;
|
export let value;
|
||||||
|
|
||||||
$: console.log('FIELD', $$props.value);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<select {...$$restProps} on:change>
|
<select {...$$restProps} on:change>
|
||||||
{#each options as x (x.value)}
|
{#each _.compact(options) as x (x.value)}
|
||||||
<option value={x.value} selected={value == x.value}>
|
<option value={x.value} selected={value == x.value}>
|
||||||
{x.label}
|
{x.label}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -3,20 +3,52 @@
|
|||||||
import FormButton from '../forms/FormButton.svelte';
|
import FormButton from '../forms/FormButton.svelte';
|
||||||
import FormProvider from '../forms/FormProvider.svelte';
|
import FormProvider from '../forms/FormProvider.svelte';
|
||||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import axios from '../utility/axios';
|
||||||
import TabControl from '../widgets/TabControl.svelte';
|
import TabControl from '../widgets/TabControl.svelte';
|
||||||
import ConnectionModalDriverFields from './ConnectionModalDriverFields.svelte';
|
import ConnectionModalDriverFields from './ConnectionModalDriverFields.svelte';
|
||||||
|
import ConnectionModalSshTunnelFields from './ConnectionModalSshTunnelFields.svelte';
|
||||||
|
import ConnectionModalSslFields from './ConnectionModalSslFields.svelte';
|
||||||
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
||||||
|
|
||||||
import ModalBase from './ModalBase.svelte';
|
import ModalBase from './ModalBase.svelte';
|
||||||
|
import { closeModal } from './modalTools';
|
||||||
|
|
||||||
export let connection;
|
export let connection;
|
||||||
|
export let modalId;
|
||||||
|
|
||||||
|
let isTesting;
|
||||||
|
let sqlConnectResult;
|
||||||
|
|
||||||
|
const testIdRef = { current: 0 };
|
||||||
|
|
||||||
|
async function handleTest(e) {
|
||||||
|
isTesting = true;
|
||||||
|
testIdRef.current += 1;
|
||||||
|
const testid = testIdRef.current;
|
||||||
|
const resp = await axios.post('connections/test', e.detail);
|
||||||
|
if (testIdRef.current != testid) return;
|
||||||
|
|
||||||
|
isTesting = false;
|
||||||
|
sqlConnectResult = resp.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelTest() {
|
||||||
|
testIdRef.current += 1; // invalidate current test
|
||||||
|
isTesting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit(e) {
|
||||||
|
axios.post('connections/save', e.detail);
|
||||||
|
closeModal(modalId);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProvider
|
<FormProvider
|
||||||
template={FormFieldTemplateLarge}
|
template={FormFieldTemplateLarge}
|
||||||
initialValues={connection || { server: 'localhost', engine: 'mssql@dbgate-plugin-mssql' }}
|
initialValues={connection || { server: 'localhost', engine: 'mssql@dbgate-plugin-mssql' }}
|
||||||
>
|
>
|
||||||
<ModalBase {...$$restProps} noPadding>
|
<ModalBase {...$$restProps} {modalId} noPadding>
|
||||||
<div slot="header">Add connection</div>
|
<div slot="header">Add connection</div>
|
||||||
|
|
||||||
<TabControl
|
<TabControl
|
||||||
@@ -28,17 +60,42 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'SSH Tunnel',
|
label: 'SSH Tunnel',
|
||||||
slot: 1,
|
component: ConnectionModalSshTunnelFields,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SSL',
|
||||||
|
component: ConnectionModalSslFields,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
/>
|
||||||
<div slot="1">SSH</div>
|
|
||||||
</TabControl>
|
|
||||||
|
|
||||||
<div slot="footer" class="flex">
|
<div slot="footer" class="flex">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<FormButton value="Test" />
|
{#if isTesting}
|
||||||
<FormSubmit value="Save" on:click={v => console.log('SAVE', v.detail)} />
|
<FormButton value="Cancel test" on:click={handleCancelTest} />
|
||||||
|
{:else}
|
||||||
|
<FormButton value="Test" on:click={handleTest} />
|
||||||
|
{/if}
|
||||||
|
<FormSubmit value="Save" on:click={handleSubmit} />
|
||||||
|
</div>
|
||||||
|
<div class="test-result">
|
||||||
|
{#if !isTesting && sqlConnectResult && sqlConnectResult.msgtype == 'connected'}
|
||||||
|
<div>
|
||||||
|
Connected: <FontIcon icon="img ok" />
|
||||||
|
{sqlConnectResult.version}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if !isTesting && sqlConnectResult && sqlConnectResult.msgtype == 'error'}
|
||||||
|
<div>
|
||||||
|
Connect failed: <FontIcon icon="img error" />
|
||||||
|
{sqlConnectResult.error}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if isTesting}
|
||||||
|
<div>
|
||||||
|
<FontIcon icon="icon loading" /> Testing connection
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalBase>
|
</ModalBase>
|
||||||
@@ -48,4 +105,12 @@
|
|||||||
.buttons {
|
.buttons {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.test-result {
|
||||||
|
margin-left: 10px;
|
||||||
|
align-self: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
107
packages/web/src/modals/ConnectionModalSshTunnelFields.svelte
Normal file
107
packages/web/src/modals/ConnectionModalSshTunnelFields.svelte
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FormElectronFileSelector from '../forms/FormElectronFileSelector.svelte';
|
||||||
|
|
||||||
|
import FormPasswordField from '../forms/FormPasswordField.svelte';
|
||||||
|
|
||||||
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||||
|
|
||||||
|
import FormTextField from '../forms/FormTextField.svelte';
|
||||||
|
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||||
|
import getElectron from '../utility/getElectron';
|
||||||
|
import { usePlatformInfo } from '../utility/metadataLoaders';
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
const electron = getElectron();
|
||||||
|
|
||||||
|
$: useSshTunnel = $values.useSshTunnel;
|
||||||
|
$: platformInfo = usePlatformInfo();
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (!$values.sshMode) setFieldValue('sshMode', 'userPassword');
|
||||||
|
if (!$values.sshPort) setFieldValue('sshPort', '22');
|
||||||
|
if (!$values.sshKeyfile && $platformInfo) setFieldValue('sshKeyfile', $platformInfo.defaultKeyFile);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormCheckboxField label="Use SSH tunnel" name="useSshTunnel" />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-9 mr-1">
|
||||||
|
<FormTextField label="Host" name="sshHost" disabled={!useSshTunnel} templateProps={{ noMargin: true }} />
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<FormTextField label="Port" name="sshPort" disabled={!useSshTunnel} templateProps={{ noMargin: true }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FormTextField label="Bastion host (Jump host)" name="sshBastionHost" disabled={!useSshTunnel} />
|
||||||
|
|
||||||
|
<FormSelectField
|
||||||
|
label="SSH Authentication"
|
||||||
|
name="sshMode"
|
||||||
|
disabled={!useSshTunnel}
|
||||||
|
options={[
|
||||||
|
{ value: 'userPassword', label: 'Username & password' },
|
||||||
|
{ value: 'agent', label: 'SSH agent' },
|
||||||
|
electron && { value: 'keyFile', label: 'Key file' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if $values.sshMode != 'userPassword'}
|
||||||
|
<FormTextField label="Login" name="sshLogin" disabled={!useSshTunnel} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $values.sshMode == 'userPassword'}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 mr-1">
|
||||||
|
<FormTextField label="Login" name="sshLogin" disabled={!useSshTunnel} templateProps={{ noMargin: true }} />
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<FormPasswordField
|
||||||
|
label="Password"
|
||||||
|
name="sshPassword"
|
||||||
|
disabled={!useSshTunnel}
|
||||||
|
templateProps={{ noMargin: true }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $values.sshMode == 'keyFile'}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 mr-1">
|
||||||
|
<FormElectronFileSelector
|
||||||
|
label="Private key file"
|
||||||
|
name="sshKeyfile"
|
||||||
|
disabled={!useSshTunnel}
|
||||||
|
templateProps={{ noMargin: true }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<FormPasswordField
|
||||||
|
label="Key file passphrase"
|
||||||
|
name="sshKeyfilePassword"
|
||||||
|
disabled={!useSshTunnel}
|
||||||
|
templateProps={{ noMargin: true }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if useSshTunnel && $values.sshMode == 'agent'}
|
||||||
|
<div class="ml-3 mb-3">
|
||||||
|
{#if $platformInfo && $platformInfo.sshAuthSock}
|
||||||
|
<FontIcon icon="img ok" /> SSH Agent found
|
||||||
|
{:else}
|
||||||
|
<FontIcon icon="img error" /> SSH Agent not found
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.row {
|
||||||
|
margin: var(--dim-large-form-margin);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
19
packages/web/src/modals/ConnectionModalSslFields.svelte
Normal file
19
packages/web/src/modals/ConnectionModalSslFields.svelte
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FormElectronFileSelector from '../forms/FormElectronFileSelector.svelte';
|
||||||
|
|
||||||
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
|
||||||
|
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||||
|
import getElectron from '../utility/getElectron';
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
const electron = getElectron();
|
||||||
|
|
||||||
|
$: useSsl = $values.useSsl;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormCheckboxField label="Use SSL" name="useSsl" />
|
||||||
|
<FormElectronFileSelector label="CA Cert (optional)" name="sslCaFile" disabled={!useSsl || !electron} />
|
||||||
|
<FormElectronFileSelector label="Certificate (optional)" name="sslCertFile" disabled={!useSsl || !electron} />
|
||||||
|
<FormElectronFileSelector label="Key file (optional)" name="sslKeyFile" disabled={!useSsl || !electron} />
|
||||||
|
<FormCheckboxField label="Reject unauthorized" name="sslRejectUnauthorized" disabled={!useSsl} />
|
||||||
@@ -335,10 +335,10 @@ export function useConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPlatformInfo() {
|
export function getPlatformInfo() {
|
||||||
return getCore(platformInfoLoader, {}) || {};
|
return getCore(platformInfoLoader, {});
|
||||||
}
|
}
|
||||||
export function usePlatformInfo() {
|
export function usePlatformInfo() {
|
||||||
return useCore(platformInfoLoader, {}) || {};
|
return useCore(platformInfoLoader, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getArchiveFiles(args) {
|
export function getArchiveFiles(args) {
|
||||||
|
|||||||
@@ -210,6 +210,7 @@
|
|||||||
}
|
}
|
||||||
.file-name {
|
.file-name {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.close-button {
|
.close-button {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|||||||
Reference in New Issue
Block a user