SYNC: Merge branch 'feature/backup-restore'

This commit is contained in:
Jan Prochazka
2025-03-14 10:45:30 +01:00
committed by Diflow
parent ba9e124527
commit 3a75ad61f3
31 changed files with 842 additions and 411 deletions

View File

@@ -1,117 +0,0 @@
<script lang="ts">
import { onMount, tick } from 'svelte';
import { format as dateFormat } from 'date-fns';
import FormStyledButton from '../buttons/FormStyledButton.svelte';
import UploadButton from '../buttons/UploadButton.svelte';
import FormProvider from '../forms/FormProvider.svelte';
import FormSubmit from '../forms/FormSubmit.svelte';
import { exportSqlDump, importSqlDump } from '../utility/exportFileTools';
import getElectron from '../utility/getElectron';
import { setUploadListener } from '../utility/uploadFiles';
import ChangeDownloadUrlModal from './ChangeDownloadUrlModal.svelte';
import ModalBase from './ModalBase.svelte';
import { closeCurrentModal, showModal } from './modalTools';
import InputTextModal from './InputTextModal.svelte';
import { apiCall } from '../utility/api';
import { getConnectionLabel } from 'dbgate-tools';
export let connection;
let outputLabel;
let outputFile;
let pureFileName = null;
function getDefaultFileName() {
return `${connection.database}-${dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss')}.sql`;
}
onMount(async () => {
const file = getDefaultFileName();
setFilesFolderResult(file);
});
const setFilesFolderResult = async file => {
const resp = await apiCall('files/get-file-real-path', { folder: 'sql', file });
if (!resp) return;
outputLabel = `SQL Files folder: ${file}`;
outputFile = resp;
pureFileName = null;
};
const handleSubmit = async values => {
const { value } = values;
closeCurrentModal();
exportSqlDump(outputFile, connection, connection.database, pureFileName);
};
const electron = getElectron();
const handleFilesFolder = () => {
showModal(InputTextModal, {
value: getDefaultFileName(),
label: 'New file name',
header: 'Backup/dump database',
onConfirm: async file => {
await tick();
setFilesFolderResult(file);
},
});
};
const handleBrowse = async () => {
const electron = getElectron();
const file = await electron.showSaveDialog({
properties: ['showOverwriteConfirmation'],
filters: [
{ name: 'SQL Files', extensions: ['sql'] },
{ name: 'All Files', extensions: ['*'] },
],
defaultPath: outputFile,
});
if (file) {
const path = window.require('path');
outputFile = file;
outputLabel = path.parse(outputFile).name;
pureFileName = null;
}
};
const handleDownload = async () => {
const resp = await apiCall('files/generate-uploads-file', { extension: 'sql' });
outputFile = resp.filePath;
outputLabel = 'Download';
pureFileName = resp.fileName;
};
</script>
<FormProvider>
<ModalBase {...$$restProps}>
<svelte:fragment slot="header">Export database dump</svelte:fragment>
<div class="m-3">
<strong>Source:</strong>
{getConnectionLabel(connection)}
{#if connection.database}
({connection.database})
{/if}
</div>
<div class="ml-3 mr-3 mt-3"><strong>Target:</strong> {outputLabel}</div>
<div class="flex ml-3">
{#if electron}
<FormStyledButton type="button" value="Browse" on:click={handleBrowse} />
{:else}
<FormStyledButton type="button" value="Set download" on:click={handleDownload} />
{/if}
<FormStyledButton type="button" value="Files folder" on:click={handleFilesFolder} />
</div>
<svelte:fragment slot="footer">
<FormSubmit value="Run export" on:click={e => handleSubmit(e.detail)} disabled={!outputFile} />
<FormStyledButton type="button" value="Cancel" on:click={closeCurrentModal} />
</svelte:fragment>
</ModalBase>
</FormProvider>

View File

@@ -1,130 +0,0 @@
<script lang="ts">
import { onMount, tick } from 'svelte';
import FormStyledButton from '../buttons/FormStyledButton.svelte';
import UploadButton from '../buttons/UploadButton.svelte';
import FormProvider from '../forms/FormProvider.svelte';
import FormSubmit from '../forms/FormSubmit.svelte';
import { currentDropDownMenu } from '../stores';
import { apiCall } from '../utility/api';
import { importSqlDump } from '../utility/exportFileTools';
import getElectron from '../utility/getElectron';
import { setUploadListener } from '../utility/uploadFiles';
import ChangeDownloadUrlModal from './ChangeDownloadUrlModal.svelte';
import ModalBase from './ModalBase.svelte';
import { closeCurrentModal, showModal } from './modalTools';
import { getConnectionLabel } from 'dbgate-tools';
export let connection;
let inputLabel = '(not selected)';
let inputFile = null;
let domButton;
const handleSubmit = async values => {
const { value } = values;
closeCurrentModal();
importSqlDump(inputFile, connection);
};
const electron = getElectron();
const handleUpload = file => {
inputLabel = `uploaded: ${file.shortName}`;
inputFile = file.filePath;
};
onMount(() => {
setUploadListener(handleUpload);
return () => {
setUploadListener(null);
};
});
const handleAddUrl = () => {
showModal(ChangeDownloadUrlModal, {
onConfirm: async url => {
await tick();
inputLabel = url;
inputFile = url;
},
});
};
const handleBrowse = async () => {
const electron = getElectron();
const files = await electron.showOpenDialog({
properties: ['openFile'],
filters: [
{ name: 'SQL Files', extensions: ['sql'] },
{ name: 'All Files', extensions: ['*'] },
],
});
if (files && files[0]) {
const path = window.require('path');
inputFile = files[0];
inputLabel = path.parse(inputFile).name;
}
};
async function handleFilesClick() {
const rect = domButton.getBoundingClientRect();
const left = rect.left;
const top = rect.bottom;
const files = await apiCall('files/list', { folder: 'sql' });
const menu = files.map(({ file }) => ({
label: file,
onClick: async () => {
inputFile = await apiCall('files/get-file-real-path', { folder: 'sql', file });
if (inputFile) {
inputLabel = file;
}
},
}));
currentDropDownMenu.set({ left, top, items: menu });
}
</script>
<FormProvider>
<ModalBase {...$$restProps}>
<svelte:fragment slot="header">Import database dump</svelte:fragment>
<div class="ml-3 mr-3 mt-3"><strong>Source:</strong> {inputLabel}</div>
<div class="flex ml-3 mr-3 mb-3">
{#if electron}
<FormStyledButton type="button" value="Browse" on:click={handleBrowse} />
{:else}
<UploadButton />
{/if}
<FormStyledButton value="Web URL" on:click={handleAddUrl} />
<FormStyledButton value="From files" on:click={handleFilesClick} bind:this={domButton} />
</div>
<div class="m-3">
<strong>Target:</strong>
{getConnectionLabel(connection)}
{#if connection.database}
({connection.database})
{/if}
</div>
<svelte:fragment slot="footer">
<FormSubmit
value="Run import"
on:click={e => handleSubmit(e.detail)}
disabled={!inputFile}
data-testid="ImportDatabaseDumpModal_runImport"
/>
<FormStyledButton
type="button"
value="Cancel"
on:click={closeCurrentModal}
data-testid="ImportDatabaseDumpModal_cancel"
/>
</svelte:fragment>
</ModalBase>
</FormProvider>

View File

@@ -160,6 +160,7 @@
filter={objectsFilter}
disableContextMenu
{checkedObjectsStore}
passProps={{ ingorePin: true }}
/>
</WidgetsInnerContainer>
</div>