mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-19 00:16:00 +00:00
207 lines
6.2 KiB
Svelte
207 lines
6.2 KiB
Svelte
<script lang="ts">
|
|
import { writable } from 'svelte/store';
|
|
import { DatabaseProcess } from 'dbgate-types';
|
|
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
|
import TableControl from '../elements/TableControl.svelte';
|
|
import { _t } from '../translations';
|
|
import CtaButton from '../buttons/CtaButton.svelte';
|
|
import { apiCall } from '../utility/api';
|
|
import { onMount } from 'svelte';
|
|
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
|
|
import { showModal } from '../modals/modalTools';
|
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
|
import SqlEditor from '../query/SqlEditor.svelte';
|
|
|
|
export let conid;
|
|
export let isSummaryOpened: boolean = false;
|
|
export let processes: DatabaseProcess[] = [];
|
|
export let refreshInterval: number = 1000;
|
|
export let tabVisible: boolean = false;
|
|
|
|
let selectedProcess: DatabaseProcess | null = null;
|
|
const filters = writable({});
|
|
|
|
let internalProcesses = [...processes];
|
|
|
|
async function refreshProcesses() {
|
|
const data = await apiCall('server-connections/list-database-processes', { conid });
|
|
internalProcesses = data.result;
|
|
}
|
|
|
|
async function killProcess(processId: number) {
|
|
const result = await apiCall('server-connections/kill-database-process', {
|
|
pid: processId,
|
|
conid,
|
|
});
|
|
|
|
if (result.errorMessage || result.error) {
|
|
showSnackbarError(
|
|
_t('summaryProcesses.killError', {
|
|
defaultMessage: 'Error while killing process {processId}: {errorMessage}',
|
|
values: { processId, errorMessage: result.errorMessage || result.error },
|
|
})
|
|
);
|
|
} else {
|
|
showSnackbarSuccess(
|
|
_t('summaryProcesses.killSuccess', {
|
|
defaultMessage: 'Process {processId} killed successfully',
|
|
values: { processId },
|
|
})
|
|
);
|
|
}
|
|
|
|
refreshProcesses();
|
|
}
|
|
|
|
async function killProcessWithConfirm(processId: number) {
|
|
showModal(ConfirmModal, {
|
|
message: _t('summaryProcesses.killConfirm', {
|
|
defaultMessage: 'Are you sure you want to kill process {processId}?',
|
|
values: { processId },
|
|
}),
|
|
onConfirm: async () => {
|
|
await killProcess(processId);
|
|
},
|
|
});
|
|
}
|
|
|
|
function formatRunningTime(seconds: number): string {
|
|
if (!seconds) return '-';
|
|
if (seconds < 60) return `${seconds.toFixed(3)}s`;
|
|
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${(seconds % 60).toFixed(3)}s`;
|
|
return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
|
|
}
|
|
|
|
onMount(() => {
|
|
const intervalId = setInterval(() => {
|
|
if (!tabVisible || !isSummaryOpened) return;
|
|
|
|
refreshProcesses();
|
|
}, refreshInterval);
|
|
|
|
return () => clearInterval(intervalId);
|
|
});
|
|
</script>
|
|
|
|
<div class="wrapper">
|
|
<VerticalSplitter initialValue="70%" isSplitter={!!selectedProcess}>
|
|
<svelte:fragment slot="1">
|
|
<div class="child1-wrapper">
|
|
<TableControl
|
|
clickable
|
|
on:clickrow={e => {
|
|
selectedProcess = e.detail;
|
|
}}
|
|
{filters}
|
|
stickyHeader
|
|
rows={internalProcesses}
|
|
columns={[
|
|
{
|
|
sortable: true,
|
|
filterable: true,
|
|
header: _t('summaryProcesses.processId', { defaultMessage: 'Process ID' }),
|
|
fieldName: 'processId',
|
|
slot: 1,
|
|
},
|
|
{
|
|
sortable: true,
|
|
filterable: true,
|
|
header: _t('summaryProcesses.connectionId', { defaultMessage: 'Connection ID' }),
|
|
fieldName: 'connectionId',
|
|
},
|
|
{
|
|
sortable: true,
|
|
filterable: true,
|
|
header: _t('summaryProcesses.client', { defaultMessage: 'Client' }),
|
|
fieldName: 'client',
|
|
},
|
|
{
|
|
filterable: true,
|
|
header: _t('summaryProcesses.operation', { defaultMessage: 'Operation' }),
|
|
fieldName: 'operation',
|
|
},
|
|
{
|
|
sortable: true,
|
|
filterable: true,
|
|
header: _t('summaryProcesses.namespace', { defaultMessage: 'Namespace' }),
|
|
fieldName: 'namespace',
|
|
},
|
|
{
|
|
sortable: true,
|
|
header: _t('summaryProcesses.runningTime', { defaultMessage: 'Running Time' }),
|
|
fieldName: 'runningTime',
|
|
slot: 2,
|
|
},
|
|
{
|
|
sortable: true,
|
|
filterable: true,
|
|
header: _t('summaryProcesses.state', { defaultMessage: 'State' }),
|
|
fieldName: 'state',
|
|
},
|
|
{
|
|
sortable: true,
|
|
header: _t('summaryProcesses.waitingFor', { defaultMessage: 'Waiting For' }),
|
|
fieldName: 'waitingFor',
|
|
slot: 3,
|
|
},
|
|
{
|
|
header: _t('summaryProcesses.actions', { defaultMessage: 'Actions' }),
|
|
fieldName: 'processId',
|
|
slot: 0,
|
|
},
|
|
]}
|
|
>
|
|
<svelte:fragment slot="0" let:row>
|
|
<CtaButton on:click={() => killProcessWithConfirm(row.processId)}>
|
|
{_t('common.kill', { defaultMessage: 'Kill' })}
|
|
</CtaButton>
|
|
</svelte:fragment>
|
|
|
|
<svelte:fragment slot="1" let:row>
|
|
<code>{row.processId}</code>
|
|
</svelte:fragment>
|
|
|
|
<svelte:fragment slot="2" let:row>
|
|
<span>{formatRunningTime(row.runningTime)}</span>
|
|
</svelte:fragment>
|
|
|
|
<svelte:fragment slot="3" let:row>
|
|
<span class:waiting={row.waitingFor}>{row.waitingFor ? 'Yes' : 'No'}</span>
|
|
</svelte:fragment>
|
|
</TableControl>
|
|
</div>
|
|
</svelte:fragment>
|
|
<svelte:fragment slot="2">
|
|
{#if !!selectedProcess}
|
|
<SqlEditor value={selectedProcess.operation ?? ''} readOnly />
|
|
{/if}
|
|
</svelte:fragment>
|
|
</VerticalSplitter>
|
|
</div>
|
|
|
|
<style>
|
|
.wrapper {
|
|
flex-grow: 1;
|
|
overflow-y: auto;
|
|
max-height: 100%;
|
|
}
|
|
|
|
.child1-wrapper {
|
|
width: 100%;
|
|
max-height: 100%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
code {
|
|
font-family: monospace;
|
|
background: var(--theme-bg-1);
|
|
padding: 2px 4px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.waiting {
|
|
color: var(--theme-font-warning);
|
|
font-weight: bold;
|
|
}
|
|
</style>
|