mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 02:06:01 +00:00
480 lines
15 KiB
Svelte
480 lines
15 KiB
Svelte
<script context="module">
|
|
export const extractKey = data => data._id;
|
|
export const createMatcher =
|
|
(filter, cfg = DEFAULT_CONNECTION_SEARCH_SETTINGS) =>
|
|
props => {
|
|
const { _id, displayName, server, user, engine } = props;
|
|
const databases = getLocalStorage(`database_list_${_id}`) || [];
|
|
const match = (engine || '').match(/^([^@]*)@/);
|
|
const engineDisplay = match ? match[1] : engine;
|
|
|
|
return filterNameCompoud(
|
|
filter,
|
|
[
|
|
cfg.displayName ? displayName : null,
|
|
cfg.server ? server : null,
|
|
cfg.user ? user : null,
|
|
cfg.engine ? engineDisplay : null,
|
|
],
|
|
cfg.database ? databases.map(x => x.name) : []
|
|
);
|
|
};
|
|
export function openConnection(connection, disableExpand = false) {
|
|
if (connection.singleDatabase) {
|
|
if (getOpenedSingleDatabaseConnections().includes(connection._id)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (getOpenedConnections().includes(connection._id)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const config = getCurrentConfig();
|
|
if (connection.singleDatabase) {
|
|
switchCurrentDatabase({ connection, name: connection.defaultDatabase });
|
|
apiCall('database-connections/refresh', {
|
|
conid: connection._id,
|
|
database: connection.defaultDatabase,
|
|
keepOpen: true,
|
|
});
|
|
openedSingleDatabaseConnections.update(x => _.uniq([...x, connection._id]));
|
|
} else {
|
|
openedConnections.update(x => _.uniq([...x, connection._id]));
|
|
apiCall('server-connections/refresh', {
|
|
conid: connection._id,
|
|
keepOpen: true,
|
|
});
|
|
|
|
if (!disableExpand) {
|
|
expandedConnections.update(x => _.uniq([...x, connection._id]));
|
|
}
|
|
|
|
if (connection.defaultDatabase) {
|
|
switchCurrentDatabase({ connection, name: connection.defaultDatabase });
|
|
}
|
|
|
|
// if (!config.runAsPortal && getCurrentSettings()['defaultAction.connectionClick'] != 'connect') {
|
|
// expandedConnections.update(x => _.uniq([...x, connection._id]));
|
|
// }
|
|
}
|
|
// closeMultipleTabs(x => x.tabComponent == 'ConnectionTab' && x.props?.conid == connection._id, true);
|
|
}
|
|
export function disconnectServerConnection(conid, showConfirmation = true) {
|
|
const closeCondition = x => x.props?.conid == conid && x.tabComponent != 'ConnectionTab' && x.closedTime == null;
|
|
|
|
if (showConfirmation) {
|
|
const count = getOpenedTabs().filter(closeCondition).length;
|
|
if (count > 0) {
|
|
showModal(ConfirmModal, {
|
|
message: _t('connection.closeConfirm', {
|
|
defaultMessage: 'Closing connection will close {count} opened tabs, continue?',
|
|
values: { count },
|
|
}),
|
|
onConfirm: () => disconnectServerConnection(conid, false),
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
const electron = getElectron();
|
|
const currentDb = getCurrentDatabase();
|
|
openedConnections.update(list => list.filter(x => x != conid));
|
|
removeVolatileMapping(conid);
|
|
if (electron) {
|
|
apiCall('server-connections/disconnect', { conid });
|
|
}
|
|
if (currentDb?.connection?._id == conid) {
|
|
if (electron) {
|
|
apiCall('database-connections/disconnect', { conid, database: currentDb.name });
|
|
}
|
|
switchCurrentDatabase(null);
|
|
}
|
|
closeMultipleTabs(closeCondition);
|
|
// if (data.unsaved) {
|
|
// openNewTab({
|
|
// title: 'New Connection',
|
|
// icon: 'img connection',
|
|
// tabComponent: 'ConnectionTab',
|
|
// props: {
|
|
// conid: data._id,
|
|
// },
|
|
// });
|
|
// }
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import _ from 'lodash';
|
|
import AppObjectCore from './AppObjectCore.svelte';
|
|
import {
|
|
cloudSigninTokenHolder,
|
|
currentDatabase,
|
|
DEFAULT_CONNECTION_SEARCH_SETTINGS,
|
|
expandedConnections,
|
|
extensions,
|
|
focusedConnectionOrDatabase,
|
|
getCurrentConfig,
|
|
getCurrentDatabase,
|
|
getCurrentSettings,
|
|
getOpenedConnections,
|
|
getOpenedSingleDatabaseConnections,
|
|
getOpenedTabs,
|
|
openedConnections,
|
|
openedSingleDatabaseConnections,
|
|
pinnedDatabases,
|
|
} from '../stores';
|
|
import { filterName, filterNameCompoud } from 'dbgate-tools';
|
|
import { showModal } from '../modals/modalTools';
|
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
|
import InputTextModal from '../modals/InputTextModal.svelte';
|
|
import openNewTab from '../utility/openNewTab';
|
|
import { getDatabaseMenuItems } from './DatabaseAppObject.svelte';
|
|
import getElectron from '../utility/getElectron';
|
|
import { getDatabaseList, useAllApps } from '../utility/metadataLoaders';
|
|
import { getLocalStorage } from '../utility/storageCache';
|
|
import { apiCall, removeVolatileMapping } from '../utility/api';
|
|
import { closeMultipleTabs } from '../tabpanel/TabsPanel.svelte';
|
|
import AboutModal from '../modals/AboutModal.svelte';
|
|
import { tick } from 'svelte';
|
|
import { getConnectionLabel } from 'dbgate-tools';
|
|
import hasPermission from '../utility/hasPermission';
|
|
import { switchCurrentDatabase } from '../utility/common';
|
|
import { getConnectionClickActionSetting } from '../settings/settingsTools';
|
|
import { _t } from '../translations';
|
|
import { isProApp } from '../utility/proTools';
|
|
|
|
export let data;
|
|
export let passProps;
|
|
|
|
let statusIcon = null;
|
|
let statusTitle = null;
|
|
let extInfo = null;
|
|
let engineStatusIcon = null;
|
|
let engineStatusTitle = null;
|
|
|
|
$: isPinned = data.singleDatabase && !!$pinnedDatabases.find(x => x?.connection?._id == data?._id);
|
|
|
|
const electron = getElectron();
|
|
|
|
const handleConnect = (disableExpand = false) => {
|
|
openConnection(data, disableExpand);
|
|
};
|
|
|
|
const handleOpenConnectionTab = () => {
|
|
openNewTab({
|
|
title: getConnectionLabel(data),
|
|
icon: data._id.startsWith('cloud://') ? 'img cloud-connection' : 'img connection',
|
|
tabComponent: 'ConnectionTab',
|
|
props: {
|
|
conid: data._id,
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleDoubleClick = async () => {
|
|
// const config = getCurrentConfig();
|
|
// if (config.runAsPortal) {
|
|
// await tick();
|
|
// handleConnect(true);
|
|
// return;
|
|
// }
|
|
|
|
if ($openedSingleDatabaseConnections.includes(data._id)) {
|
|
switchCurrentDatabase({ connection: data, name: data.defaultDatabase });
|
|
return;
|
|
}
|
|
if ($openedConnections.includes(data._id)) {
|
|
return;
|
|
}
|
|
await tick();
|
|
handleConnect(true);
|
|
|
|
// if (getCurrentSettings()['defaultAction.connectionClick'] == 'openDetails') {
|
|
// handleOpenConnectionTab();
|
|
// } else {
|
|
// await tick();
|
|
// handleConnect();
|
|
// }
|
|
};
|
|
|
|
const handleClick = async e => {
|
|
// focusedConnectionOrDatabase.set({
|
|
// conid: data?._id,
|
|
// connection: data,
|
|
// database: data.singleDatabase ? data.defaultDatabase : null,
|
|
// });
|
|
|
|
const config = getCurrentConfig();
|
|
|
|
const connectionClickAction = getConnectionClickActionSetting();
|
|
if (connectionClickAction == 'openDetails') {
|
|
if (config.runAsPortal == false && !config.storageDatabase) {
|
|
openNewTab({
|
|
title: getConnectionLabel(data),
|
|
icon: 'img connection',
|
|
tabComponent: 'ConnectionTab',
|
|
tabPreviewMode: true,
|
|
props: {
|
|
conid: data._id,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
if (connectionClickAction == 'connect') {
|
|
await tick();
|
|
handleConnect();
|
|
}
|
|
};
|
|
|
|
const handleMouseDown = () => {
|
|
focusedConnectionOrDatabase.set({
|
|
conid: data?._id,
|
|
connection: data,
|
|
database: data.singleDatabase ? data.defaultDatabase : null,
|
|
});
|
|
};
|
|
|
|
const handleRestoreDatabase = () => {
|
|
openNewTab({
|
|
title: 'Restore #',
|
|
icon: 'img db-restore',
|
|
tabComponent: 'RestoreDatabaseTab',
|
|
props: {
|
|
conid: data._id,
|
|
},
|
|
});
|
|
};
|
|
|
|
const getContextMenu = () => {
|
|
const driver = $extensions.drivers.find(x => x.engine == data.engine);
|
|
const config = getCurrentConfig();
|
|
const handleRefresh = () => {
|
|
apiCall('server-connections/refresh', { conid: data._id });
|
|
};
|
|
const handleDisconnect = () => {
|
|
disconnectServerConnection(data._id);
|
|
};
|
|
const handleDelete = () => {
|
|
showModal(ConfirmModal, {
|
|
message: _t('connection.deleteConfirm', {
|
|
defaultMessage: 'Really delete connection {name}?',
|
|
values: { name: getConnectionLabel(data) },
|
|
}),
|
|
onConfirm: () => apiCall('connections/delete', data),
|
|
});
|
|
};
|
|
const handleDuplicate = () => {
|
|
if (data._id.startsWith('cloud://')) {
|
|
apiCall('cloud/duplicate-connection', { conid: data._id });
|
|
} else {
|
|
apiCall('connections/save', {
|
|
...data,
|
|
_id: undefined,
|
|
displayName: `${getConnectionLabel(data)} - copy`,
|
|
});
|
|
}
|
|
};
|
|
const handleCreateDatabase = () => {
|
|
showModal(InputTextModal, {
|
|
header: _t('connection.createDatabase', { defaultMessage: 'Create database' }),
|
|
value: 'newdb',
|
|
label: _t('connection.database', { defaultMessage: 'Database name' }),
|
|
onConfirm: name =>
|
|
apiCall('server-connections/create-database', {
|
|
conid: data._id,
|
|
name,
|
|
}),
|
|
});
|
|
};
|
|
const handleServerSummary = () => {
|
|
openNewTab({
|
|
title: getConnectionLabel(data),
|
|
icon: 'img server',
|
|
tabComponent: 'ServerSummaryTab',
|
|
props: {
|
|
conid: data._id,
|
|
},
|
|
});
|
|
};
|
|
const handleNewQuery = () => {
|
|
const tooltip = `${getConnectionLabel(data)}`;
|
|
openNewTab({
|
|
title: 'Query #',
|
|
icon: 'img sql-file',
|
|
tooltip,
|
|
tabComponent: 'QueryTab',
|
|
focused: true,
|
|
props: {
|
|
conid: data._id,
|
|
},
|
|
});
|
|
};
|
|
|
|
return [
|
|
!data.singleDatabase && [
|
|
!$openedConnections.includes(data._id) && {
|
|
text: _t('connection.connect', { defaultMessage: 'Connect' }),
|
|
onClick: handleConnect,
|
|
isBold: true,
|
|
},
|
|
$openedConnections.includes(data._id) && {
|
|
text: _t('connection.disconnect', { defaultMessage: 'Disconnect' }),
|
|
onClick: handleDisconnect,
|
|
},
|
|
],
|
|
{ divider: true },
|
|
config.runAsPortal == false &&
|
|
!config.storageDatabase && [
|
|
{
|
|
text: $openedConnections.includes(data._id)
|
|
? _t('connection.viewDetails', { defaultMessage: 'View details' })
|
|
: _t('connection.edit', { defaultMessage: 'Edit' }),
|
|
onClick: handleOpenConnectionTab,
|
|
},
|
|
!$openedConnections.includes(data._id) && {
|
|
text: _t('connection.delete', { defaultMessage: 'Delete' }),
|
|
onClick: handleDelete,
|
|
},
|
|
{
|
|
text: _t('connection.duplicate', { defaultMessage: 'Duplicate' }),
|
|
onClick: handleDuplicate,
|
|
},
|
|
$cloudSigninTokenHolder &&
|
|
passProps?.cloudContentList?.length > 0 && {
|
|
text: _t('connection.copyToCloudFolder', { defaultMessage: 'Copy to cloud folder' }),
|
|
submenu: passProps?.cloudContentList
|
|
?.filter(x => x.role == 'write' || x.role == 'admin')
|
|
?.map(fld => ({
|
|
text: fld.name,
|
|
onClick: () => {
|
|
apiCall('cloud/copy-connection-cloud', { conid: data._id, folid: fld.folid });
|
|
},
|
|
})),
|
|
},
|
|
],
|
|
{ divider: true },
|
|
!data.singleDatabase && [
|
|
hasPermission(`dbops/query`) && {
|
|
onClick: handleNewQuery,
|
|
text: _t('connection.newQuery', { defaultMessage: 'New Query (server)' }),
|
|
isNewQuery: true,
|
|
},
|
|
$openedConnections.includes(data._id) &&
|
|
data.status && {
|
|
text: _t('connection.refresh', { defaultMessage: 'Refresh' }),
|
|
onClick: handleRefresh,
|
|
},
|
|
hasPermission(`dbops/createdb`) &&
|
|
$openedConnections.includes(data._id) &&
|
|
driver?.supportedCreateDatabase &&
|
|
!data.isReadOnly && {
|
|
text: _t('connection.createDatabase', { defaultMessage: 'Create database' }),
|
|
onClick: handleCreateDatabase,
|
|
},
|
|
driver?.supportsServerSummary && {
|
|
text: _t('connection.serverSummary', { defaultMessage: 'Server summary' }),
|
|
onClick: handleServerSummary,
|
|
},
|
|
],
|
|
data.singleDatabase && [
|
|
{ divider: true },
|
|
getDatabaseMenuItems(
|
|
data,
|
|
data.defaultDatabase,
|
|
$extensions,
|
|
$currentDatabase,
|
|
$apps,
|
|
$openedSingleDatabaseConnections,
|
|
data.databasePermissionRole
|
|
),
|
|
],
|
|
|
|
driver?.supportsDatabaseRestore &&
|
|
isProApp() &&
|
|
hasPermission(`dbops/sql-dump/import`) &&
|
|
!data.isReadOnly && { onClick: handleRestoreDatabase, text: 'Restore database backup' },
|
|
];
|
|
};
|
|
|
|
$: {
|
|
if ($extensions.drivers.find(x => x.engine == data.engine)) {
|
|
const match = (data.engine || '').match(/^([^@]*)@/);
|
|
extInfo = match ? match[1] : data.engine;
|
|
engineStatusIcon = null;
|
|
engineStatusTitle = null;
|
|
} else {
|
|
extInfo = data.engine;
|
|
engineStatusIcon = 'img warn';
|
|
engineStatusTitle = _t('connection.engineDriverNotFound', {
|
|
defaultMessage:
|
|
'Engine driver {engine} not found, review installed plugins and change engine in edit connection dialog',
|
|
values: { engine: data.engine },
|
|
});
|
|
}
|
|
}
|
|
|
|
$: {
|
|
const { _id, status } = data;
|
|
if ($openedConnections.includes(_id)) {
|
|
if (!status) statusIcon = 'icon loading';
|
|
else if (status.name == 'pending') statusIcon = 'icon loading';
|
|
else if (status.name == 'ok') statusIcon = 'img ok';
|
|
else statusIcon = 'img error';
|
|
if (status && status.name == 'error') {
|
|
statusTitle = status.message;
|
|
}
|
|
} else {
|
|
statusIcon = null;
|
|
statusTitle = null;
|
|
}
|
|
}
|
|
|
|
$: apps = useAllApps();
|
|
</script>
|
|
|
|
<AppObjectCore
|
|
{...$$restProps}
|
|
{data}
|
|
title={getConnectionLabel(data, { showUnsaved: true })}
|
|
icon={data._id.startsWith('cloud://') ? 'img cloud-connection' : data.singleDatabase ? 'img database' : 'img server'}
|
|
isBold={data.singleDatabase
|
|
? $currentDatabase?.connection?._id == data._id && $currentDatabase?.name == data.defaultDatabase
|
|
: $currentDatabase?.connection?._id == data._id}
|
|
statusIcon={statusIcon || engineStatusIcon}
|
|
statusTitle={statusTitle || engineStatusTitle}
|
|
statusTitleToCopy={statusTitle || engineStatusTitle}
|
|
statusIconBefore={data.isReadOnly ? 'icon lock' : null}
|
|
{extInfo}
|
|
colorMark={passProps?.connectionColorFactory && passProps?.connectionColorFactory({ conid: data._id })}
|
|
menu={getContextMenu}
|
|
on:click={handleClick}
|
|
on:mousedown={handleMouseDown}
|
|
on:dblclick
|
|
on:expand
|
|
on:dblclick={handleDoubleClick}
|
|
on:middleclick={() => {
|
|
_.flattenDeep(getContextMenu())
|
|
.find(x => x.isNewQuery)
|
|
.onClick();
|
|
}}
|
|
onPin={!isPinned && data.singleDatabase
|
|
? () =>
|
|
pinnedDatabases.update(list => [
|
|
...list,
|
|
{
|
|
name: data.defaultDatabase,
|
|
connection: data,
|
|
},
|
|
])
|
|
: null}
|
|
onUnpin={isPinned && data.singleDatabase
|
|
? () => pinnedDatabases.update(list => list.filter(x => x?.connection?._id != data?._id))
|
|
: null}
|
|
isChoosed={data._id == $focusedConnectionOrDatabase?.conid &&
|
|
(data.singleDatabase
|
|
? $focusedConnectionOrDatabase?.database == data.defaultDatabase
|
|
: !$focusedConnectionOrDatabase?.database)}
|
|
disableBoldScroll={!!$focusedConnectionOrDatabase}
|
|
/>
|