mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
Merge branch 'summary'
This commit is contained in:
commit
a1032138da
@ -1,6 +1,7 @@
|
|||||||
const connections = require('./connections');
|
const connections = require('./connections');
|
||||||
const socket = require('../utility/socket');
|
const socket = require('../utility/socket');
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
|
const uuidv1 = require('uuid/v1');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const AsyncLock = require('async-lock');
|
const AsyncLock = require('async-lock');
|
||||||
const { handleProcessCommunication } = require('../utility/processComm');
|
const { handleProcessCommunication } = require('../utility/processComm');
|
||||||
@ -13,6 +14,7 @@ module.exports = {
|
|||||||
opened: [],
|
opened: [],
|
||||||
closed: {},
|
closed: {},
|
||||||
lastPinged: {},
|
lastPinged: {},
|
||||||
|
requests: {},
|
||||||
|
|
||||||
handle_databases(conid, { databases }) {
|
handle_databases(conid, { databases }) {
|
||||||
const existing = this.opened.find(x => x.conid == conid);
|
const existing = this.opened.find(x => x.conid == conid);
|
||||||
@ -33,6 +35,11 @@ module.exports = {
|
|||||||
socket.emitChanged(`server-status-changed`);
|
socket.emitChanged(`server-status-changed`);
|
||||||
},
|
},
|
||||||
handle_ping() {},
|
handle_ping() {},
|
||||||
|
handle_response(conid, { msgid, ...response }) {
|
||||||
|
const [resolve, reject] = this.requests[msgid];
|
||||||
|
resolve(response);
|
||||||
|
delete this.requests[msgid];
|
||||||
|
},
|
||||||
|
|
||||||
async ensureOpened(conid) {
|
async ensureOpened(conid) {
|
||||||
const res = await lock.acquire(conid, async () => {
|
const res = await lock.acquire(conid, async () => {
|
||||||
@ -161,4 +168,41 @@ module.exports = {
|
|||||||
opened.subprocess.send({ msgtype: 'dropDatabase', name });
|
opened.subprocess.send({ msgtype: 'dropDatabase', name });
|
||||||
return { status: 'ok' };
|
return { status: 'ok' };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sendRequest(conn, message) {
|
||||||
|
const msgid = uuidv1();
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
this.requests[msgid] = [resolve, reject];
|
||||||
|
conn.subprocess.send({ msgid, ...message });
|
||||||
|
});
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadDataCore(msgtype, { conid, ...args }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
|
const opened = await this.ensureOpened(conid);
|
||||||
|
const res = await this.sendRequest(opened, { msgtype, ...args });
|
||||||
|
if (res.errorMessage) {
|
||||||
|
console.error(res.errorMessage);
|
||||||
|
|
||||||
|
return {
|
||||||
|
errorMessage: res.errorMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return res.result || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
serverSummary_meta: true,
|
||||||
|
async serverSummary({ conid }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
|
return this.loadDataCore('serverSummary', { conid });
|
||||||
|
},
|
||||||
|
|
||||||
|
summaryCommand_meta: true,
|
||||||
|
async summaryCommand({ conid, command, row }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
|
const opened = await this.ensureOpened(conid);
|
||||||
|
if (opened.connection.isReadOnly) return false;
|
||||||
|
return this.loadDataCore('summaryCommand', { conid, command, row });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ let storedConnection;
|
|||||||
let lastDatabases = null;
|
let lastDatabases = null;
|
||||||
let lastStatus = null;
|
let lastStatus = null;
|
||||||
let lastPing = null;
|
let lastPing = null;
|
||||||
|
let afterConnectCallbacks = [];
|
||||||
|
|
||||||
async function handleRefresh() {
|
async function handleRefresh() {
|
||||||
const driver = requireEngineDriver(storedConnection);
|
const driver = requireEngineDriver(storedConnection);
|
||||||
@ -74,6 +75,18 @@ async function handleConnect(connection) {
|
|||||||
// console.error(err);
|
// console.error(err);
|
||||||
setTimeout(() => process.exit(1), 1000);
|
setTimeout(() => process.exit(1), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const [resolve] of afterConnectCallbacks) {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
afterConnectCallbacks = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitConnected() {
|
||||||
|
if (systemConnection) return Promise.resolve();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
afterConnectCallbacks.push([resolve, reject]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePing() {
|
function handlePing() {
|
||||||
@ -94,9 +107,30 @@ async function handleDatabaseOp(op, { name }) {
|
|||||||
await handleRefresh();
|
await handleRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleDriverDataCore(msgid, callMethod) {
|
||||||
|
await waitConnected();
|
||||||
|
const driver = requireEngineDriver(storedConnection);
|
||||||
|
try {
|
||||||
|
const result = await callMethod(driver);
|
||||||
|
process.send({ msgtype: 'response', msgid, result });
|
||||||
|
} catch (err) {
|
||||||
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleServerSummary({ msgid }) {
|
||||||
|
return handleDriverDataCore(msgid, driver => driver.serverSummary(systemConnection));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSummaryCommand({ msgid, command, row }) {
|
||||||
|
return handleDriverDataCore(msgid, driver => driver.summaryCommand(systemConnection, command, row));
|
||||||
|
}
|
||||||
|
|
||||||
const messageHandlers = {
|
const messageHandlers = {
|
||||||
connect: handleConnect,
|
connect: handleConnect,
|
||||||
ping: handlePing,
|
ping: handlePing,
|
||||||
|
serverSummary: handleServerSummary,
|
||||||
|
summaryCommand: handleSummaryCommand,
|
||||||
createDatabase: props => handleDatabaseOp('createDatabase', props),
|
createDatabase: props => handleDatabaseOp('createDatabase', props),
|
||||||
dropDatabase: props => handleDatabaseOp('dropDatabase', props),
|
dropDatabase: props => handleDatabaseOp('dropDatabase', props),
|
||||||
};
|
};
|
||||||
|
16
packages/types/engines.d.ts
vendored
16
packages/types/engines.d.ts
vendored
@ -55,6 +55,17 @@ export interface SqlBackupDumper {
|
|||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SummaryColumn {
|
||||||
|
fieldName: string;
|
||||||
|
header: string;
|
||||||
|
dataType: 'string' | 'number' | 'bytes';
|
||||||
|
}
|
||||||
|
export interface ServerSummaryDatabase {}
|
||||||
|
export interface ServerSummary {
|
||||||
|
columns: SummaryColumn[];
|
||||||
|
databases: ServerSummaryDatabase[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface EngineDriver {
|
export interface EngineDriver {
|
||||||
engine: string;
|
engine: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -65,6 +76,7 @@ export interface EngineDriver {
|
|||||||
supportedKeyTypes: SupportedDbKeyType[];
|
supportedKeyTypes: SupportedDbKeyType[];
|
||||||
supportsDatabaseUrl?: boolean;
|
supportsDatabaseUrl?: boolean;
|
||||||
supportsDatabaseDump?: boolean;
|
supportsDatabaseDump?: boolean;
|
||||||
|
supportsServerSummary?: boolean;
|
||||||
isElectronOnly?: boolean;
|
isElectronOnly?: boolean;
|
||||||
supportedCreateDatabase?: boolean;
|
supportedCreateDatabase?: boolean;
|
||||||
showConnectionField?: (field: string, values: any) => boolean;
|
showConnectionField?: (field: string, values: any) => boolean;
|
||||||
@ -81,7 +93,7 @@ export interface EngineDriver {
|
|||||||
stream(pool: any, sql: string, options: StreamOptions);
|
stream(pool: any, sql: string, options: StreamOptions);
|
||||||
readQuery(pool: any, sql: string, structure?: TableInfo): Promise<stream.Readable>;
|
readQuery(pool: any, sql: string, structure?: TableInfo): Promise<stream.Readable>;
|
||||||
readJsonQuery(pool: any, query: any, structure?: TableInfo): Promise<stream.Readable>;
|
readJsonQuery(pool: any, query: any, structure?: TableInfo): Promise<stream.Readable>;
|
||||||
writeTable(pool: any, name: NamedObjectInfo, options: WriteTableOptions): Promise<stream.Writeable>;
|
writeTable(pool: any, name: NamedObjectInfo, options: WriteTableOptions): Promise<stream.Writable>;
|
||||||
analyseSingleObject(
|
analyseSingleObject(
|
||||||
pool: any,
|
pool: any,
|
||||||
name: NamedObjectInfo,
|
name: NamedObjectInfo,
|
||||||
@ -116,6 +128,8 @@ export interface EngineDriver {
|
|||||||
getNewObjectTemplates(): NewObjectTemplate[];
|
getNewObjectTemplates(): NewObjectTemplate[];
|
||||||
// direct call of pool method, only some methods could be supported, on only some drivers
|
// direct call of pool method, only some methods could be supported, on only some drivers
|
||||||
callMethod(pool, method, args);
|
callMethod(pool, method, args);
|
||||||
|
serverSummary(pool): Promise<ServerSummary>;
|
||||||
|
summaryCommand(pool, command, row): Promise<void>;
|
||||||
|
|
||||||
analyserClass?: any;
|
analyserClass?: any;
|
||||||
dumperClass?: any;
|
dumperClass?: any;
|
||||||
|
@ -195,6 +195,16 @@ import { tick } from 'svelte';
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const handleServerSummary = () => {
|
||||||
|
openNewTab({
|
||||||
|
title: getConnectionLabel(data),
|
||||||
|
icon: 'img server',
|
||||||
|
tabComponent: 'ServerSummaryTab',
|
||||||
|
props: {
|
||||||
|
conid: data._id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
const handleNewQuery = () => {
|
const handleNewQuery = () => {
|
||||||
const tooltip = `${getConnectionLabel(data)}`;
|
const tooltip = `${getConnectionLabel(data)}`;
|
||||||
openNewTab({
|
openNewTab({
|
||||||
@ -244,6 +254,11 @@ import { tick } from 'svelte';
|
|||||||
text: 'Create database',
|
text: 'Create database',
|
||||||
onClick: handleCreateDatabase,
|
onClick: handleCreateDatabase,
|
||||||
},
|
},
|
||||||
|
$openedConnections.includes(data._id) &&
|
||||||
|
driver?.supportsServerSummary && {
|
||||||
|
text: 'Server summary',
|
||||||
|
onClick: handleServerSummary,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
data.singleDatabase && [
|
data.singleDatabase && [
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import _ from 'lodash';
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
|
||||||
import Link from './Link.svelte';
|
import Link from './Link.svelte';
|
||||||
|
|
||||||
import TableControl from './TableControl.svelte';
|
import TableControl from './TableControl.svelte';
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
@ -10,6 +9,7 @@
|
|||||||
export let columns;
|
export let columns;
|
||||||
export let showIfEmpty = false;
|
export let showIfEmpty = false;
|
||||||
export let emptyMessage = null;
|
export let emptyMessage = null;
|
||||||
|
export let hideDisplayName = false;
|
||||||
export let clickable;
|
export let clickable;
|
||||||
export let onAddNew;
|
export let onAddNew;
|
||||||
</script>
|
</script>
|
||||||
@ -31,43 +31,43 @@
|
|||||||
<div class="body">
|
<div class="body">
|
||||||
<TableControl
|
<TableControl
|
||||||
rows={collection || []}
|
rows={collection || []}
|
||||||
columns={[
|
columns={_.compact([
|
||||||
{
|
!hideDisplayName && {
|
||||||
fieldName: 'displayName',
|
fieldName: 'displayName',
|
||||||
header: 'Name',
|
header: 'Name',
|
||||||
slot: -1,
|
slot: -1,
|
||||||
},
|
},
|
||||||
...columns,
|
...columns,
|
||||||
]}
|
])}
|
||||||
{clickable}
|
{clickable}
|
||||||
on:clickrow
|
on:clickrow
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="-1" let:row>
|
<svelte:fragment slot="-1" let:row let:col>
|
||||||
<slot name="name" {row} />
|
<slot name="name" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="0" let:row>
|
<svelte:fragment slot="0" let:row let:col>
|
||||||
<slot name="0" {row} />
|
<slot name="0" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="1" let:row>
|
<svelte:fragment slot="1" let:row let:col>
|
||||||
<slot name="1" {row} />
|
<slot name="1" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="2" let:row>
|
<svelte:fragment slot="2" let:row let:col>
|
||||||
<slot name="2" {row} />
|
<slot name="2" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="3" let:row>
|
<svelte:fragment slot="3" let:row let:col>
|
||||||
<slot name="3" {row} />
|
<slot name="3" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="4" let:row>
|
<svelte:fragment slot="4" let:row let:col>
|
||||||
<slot name="4" {row} />
|
<slot name="4" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="5" let:row>
|
<svelte:fragment slot="5" let:row let:col>
|
||||||
<slot name="5" {row} />
|
<slot name="5" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="6" let:row>
|
<svelte:fragment slot="6" let:row let:col>
|
||||||
<slot name="6" {row} />
|
<slot name="6" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="7" let:row>
|
<svelte:fragment slot="7" let:row let:col>
|
||||||
<slot name="7" {row} />
|
<slot name="7" {row} {col} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</TableControl>
|
</TableControl>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,17 +86,17 @@
|
|||||||
{:else if col.formatter}
|
{:else if col.formatter}
|
||||||
{col.formatter(row)}
|
{col.formatter(row)}
|
||||||
{:else if col.slot != null}
|
{:else if col.slot != null}
|
||||||
{#if col.slot == -1}<slot name="-1" {row} {index} />
|
{#if col.slot == -1}<slot name="-1" {row} {col} {index} />
|
||||||
{:else if col.slot == 0}<slot name="0" {row} {index} {...rowProps} />
|
{:else if col.slot == 0}<slot name="0" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 1}<slot name="1" {row} {index} {...rowProps} />
|
{:else if col.slot == 1}<slot name="1" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 2}<slot name="2" {row} {index} {...rowProps} />
|
{:else if col.slot == 2}<slot name="2" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 3}<slot name="3" {row} {index} {...rowProps} />
|
{:else if col.slot == 3}<slot name="3" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 4}<slot name="4" {row} {index} {...rowProps} />
|
{:else if col.slot == 4}<slot name="4" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 5}<slot name="5" {row} {index} {...rowProps} />
|
{:else if col.slot == 5}<slot name="5" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 6}<slot name="6" {row} {index} {...rowProps} />
|
{:else if col.slot == 6}<slot name="6" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 7}<slot name="7" {row} {index} {...rowProps} />
|
{:else if col.slot == 7}<slot name="7" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 8}<slot name="8" {row} {index} {...rowProps} />
|
{:else if col.slot == 8}<slot name="8" {row} {col} {index} {...rowProps} />
|
||||||
{:else if col.slot == 9}<slot name="9" {row} {index} {...rowProps} />
|
{:else if col.slot == 9}<slot name="9" {row} {col} {index} {...rowProps} />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
{row[col.fieldName] || ''}
|
{row[col.fieldName] || ''}
|
||||||
|
103
packages/web/src/tabs/ServerSummaryTab.svelte
Normal file
103
packages/web/src/tabs/ServerSummaryTab.svelte
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<script lang="ts" context="module">
|
||||||
|
const getCurrentEditor = () => getActiveComponent('ServerSummaryTab');
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'serverSummary.refresh',
|
||||||
|
category: 'Server sumnmary',
|
||||||
|
name: 'Refresh',
|
||||||
|
keyText: 'F5 | CtrlOrCommand+R',
|
||||||
|
toolbar: true,
|
||||||
|
isRelatedToTab: true,
|
||||||
|
icon: 'icon reload',
|
||||||
|
onClick: () => getCurrentEditor().refresh(),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||||
|
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||||
|
import registerCommand from '../commands/registerCommand';
|
||||||
|
import Link from '../elements/Link.svelte';
|
||||||
|
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||||
|
|
||||||
|
import ObjectListControl from '../elements/ObjectListControl.svelte';
|
||||||
|
import { apiCall } from '../utility/api';
|
||||||
|
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||||
|
import formatFileSize from '../utility/formatFileSize';
|
||||||
|
import openNewTab from '../utility/openNewTab';
|
||||||
|
|
||||||
|
export let conid;
|
||||||
|
|
||||||
|
let refreshToken = 0;
|
||||||
|
|
||||||
|
export const activator = createActivator('ServerSummaryTab', true);
|
||||||
|
|
||||||
|
export function refresh() {
|
||||||
|
refreshToken += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runAction(action, row) {
|
||||||
|
const { command, openQuery } = action;
|
||||||
|
if (command) {
|
||||||
|
await apiCall('server-connections/summary-command', { conid, refreshToken, command, row });
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
if (openQuery) {
|
||||||
|
openNewTab({
|
||||||
|
title: action.tabTitle || row.name,
|
||||||
|
icon: 'img query-data',
|
||||||
|
tabComponent: 'QueryDataTab',
|
||||||
|
props: {
|
||||||
|
conid,
|
||||||
|
database: row.name,
|
||||||
|
sql: openQuery,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ToolStripContainer>
|
||||||
|
{#await apiCall('server-connections/server-summary', { conid, refreshToken })}
|
||||||
|
<LoadingInfo message="Loading server details" wrapper />
|
||||||
|
{:then summary}
|
||||||
|
<div class="wrapper">
|
||||||
|
<ObjectListControl
|
||||||
|
collection={summary.databases}
|
||||||
|
hideDisplayName
|
||||||
|
title={`Databases (${summary.databases.length})`}
|
||||||
|
emptyMessage={'No databases'}
|
||||||
|
columns={summary.columns.map(col => ({
|
||||||
|
...col,
|
||||||
|
slot: col.columnType == 'bytes' ? 1 : col.columnType == 'actions' ? 2 : null,
|
||||||
|
}))}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="1" let:row let:col>{formatFileSize(row?.[col.fieldName])}</svelte:fragment>
|
||||||
|
<svelte:fragment slot="2" let:row let:col>
|
||||||
|
{#each col.actions as action, index}
|
||||||
|
{#if index > 0}
|
||||||
|
<span> | </span>
|
||||||
|
{/if}
|
||||||
|
<Link onClick={() => runAction(action, row)}>{action.header}</Link>
|
||||||
|
{/each}
|
||||||
|
</svelte:fragment>
|
||||||
|
</ObjectListControl>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
<svelte:fragment slot="toolstrip">
|
||||||
|
<ToolStripCommandButton command="serverSummary.refresh" />
|
||||||
|
</svelte:fragment>
|
||||||
|
</ToolStripContainer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--theme-bg-0);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
@ -26,6 +26,7 @@ import * as QueryDataTab from './QueryDataTab.svelte';
|
|||||||
import * as ConnectionTab from './ConnectionTab.svelte';
|
import * as ConnectionTab from './ConnectionTab.svelte';
|
||||||
import * as MapTab from './MapTab.svelte';
|
import * as MapTab from './MapTab.svelte';
|
||||||
import * as PerspectiveTab from './PerspectiveTab.svelte';
|
import * as PerspectiveTab from './PerspectiveTab.svelte';
|
||||||
|
import * as ServerSummaryTab from './ServerSummaryTab.svelte';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
TableDataTab,
|
TableDataTab,
|
||||||
@ -56,4 +57,5 @@ export default {
|
|||||||
ConnectionTab,
|
ConnectionTab,
|
||||||
MapTab,
|
MapTab,
|
||||||
PerspectiveTab,
|
PerspectiveTab,
|
||||||
|
ServerSummaryTab,
|
||||||
};
|
};
|
||||||
|
@ -351,6 +351,86 @@ const driver = {
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async summaryCommand(pool, command, row) {
|
||||||
|
switch (command) {
|
||||||
|
case 'profileOff':
|
||||||
|
await pool.db(row.name).command({ profile: 0 });
|
||||||
|
return;
|
||||||
|
case 'profileFiltered':
|
||||||
|
await pool.db(row.name).command({ profile: 1, slowms: 100 });
|
||||||
|
return;
|
||||||
|
case 'profileAll':
|
||||||
|
await pool.db(row.name).command({ profile: 2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async serverSummary(pool) {
|
||||||
|
const res = await pool.__getDatabase().admin().listDatabases();
|
||||||
|
const profiling = await Promise.all(res.databases.map((x) => pool.db(x.name).command({ profile: -1 })));
|
||||||
|
|
||||||
|
function formatProfiling(info) {
|
||||||
|
switch (info.was) {
|
||||||
|
case 0:
|
||||||
|
return 'No profiling';
|
||||||
|
case 1:
|
||||||
|
return `Filtered (>${info.slowms} ms)`;
|
||||||
|
case 2:
|
||||||
|
return 'Profile all';
|
||||||
|
default:
|
||||||
|
return '???';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
columnType: 'string',
|
||||||
|
header: 'Name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'sizeOnDisk',
|
||||||
|
columnType: 'bytes',
|
||||||
|
header: 'Size',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'profiling',
|
||||||
|
columnType: 'string',
|
||||||
|
header: 'Profiling',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'setProfile',
|
||||||
|
columnType: 'actions',
|
||||||
|
header: 'Profiling actions',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
header: 'Off',
|
||||||
|
command: 'profileOff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Filtered',
|
||||||
|
command: 'profileFiltered',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'All',
|
||||||
|
command: 'profileAll',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'View',
|
||||||
|
openQuery: "db['system.profile'].find()",
|
||||||
|
tabTitle: 'Profile data',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
databases: res.databases.map((db, i) => ({
|
||||||
|
...db,
|
||||||
|
profiling: formatProfiling(profiling[i]),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = driver;
|
module.exports = driver;
|
||||||
|
@ -32,6 +32,7 @@ const driver = {
|
|||||||
editorMode: 'javascript',
|
editorMode: 'javascript',
|
||||||
defaultPort: 27017,
|
defaultPort: 27017,
|
||||||
supportsDatabaseUrl: true,
|
supportsDatabaseUrl: true,
|
||||||
|
supportsServerSummary: true,
|
||||||
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
|
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
|
||||||
|
|
||||||
getQuerySplitterOptions: () => mongoSplitterOptions,
|
getQuerySplitterOptions: () => mongoSplitterOptions,
|
||||||
|
Loading…
Reference in New Issue
Block a user