mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-26 17:55:59 +00:00
export sql dump - can export to files
This commit is contained in:
@@ -62,6 +62,15 @@ module.exports = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
refresh_meta: true,
|
||||||
|
async refresh({ folders }, req) {
|
||||||
|
for (const folder of folders) {
|
||||||
|
socket.emitChanged(`files-changed-${folder}`);
|
||||||
|
socket.emitChanged(`all-files-changed`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
copy_meta: true,
|
copy_meta: true,
|
||||||
async copy({ folder, file, newFile }, req) {
|
async copy({ folder, file, newFile }, req) {
|
||||||
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||||
@@ -177,4 +186,24 @@ module.exports = {
|
|||||||
await fs.writeFile(filePath, getDiagramExport(html, css, themeType, themeClassName));
|
await fs.writeFile(filePath, getDiagramExport(html, css, themeType, themeClassName));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFileRealPath_meta: true,
|
||||||
|
async getFileRealPath({ folder, file }, req) {
|
||||||
|
if (folder.startsWith('archive:')) {
|
||||||
|
if (!hasPermission(`archive/write`, req)) return false;
|
||||||
|
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
|
||||||
|
return path.join(dir, file);
|
||||||
|
} else if (folder.startsWith('app:')) {
|
||||||
|
if (!hasPermission(`apps/write`, req)) return false;
|
||||||
|
const app = folder.substring('app:'.length);
|
||||||
|
return path.join(appdir(), app, file);
|
||||||
|
} else {
|
||||||
|
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||||
|
const dir = path.join(filesdir(), folder);
|
||||||
|
if (!(await fs.exists(dir))) {
|
||||||
|
await fs.mkdir(dir);
|
||||||
|
}
|
||||||
|
return path.join(dir, file);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chartjs-plugin-zoom": "^1.2.0",
|
"chartjs-plugin-zoom": "^1.2.0",
|
||||||
|
"date-fns": "^2.28.0",
|
||||||
"interval-operations": "^1.0.7"
|
"interval-operations": "^1.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSqlDump = () => {
|
const handleSqlDump = () => {
|
||||||
exportSqlDump(connection, name);
|
showModal(ExportDatabaseDumpModal, {
|
||||||
|
connection: { ...connection, database: name },
|
||||||
|
});
|
||||||
|
// exportSqlDump(connection, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSqlRestore = () => {
|
const handleSqlRestore = () => {
|
||||||
@@ -212,8 +215,8 @@
|
|||||||
{ divider: true },
|
{ divider: true },
|
||||||
isSqlOrDoc && !connection.isReadOnly && { onClick: handleImport, text: 'Import wizard' },
|
isSqlOrDoc && !connection.isReadOnly && { onClick: handleImport, text: 'Import wizard' },
|
||||||
isSqlOrDoc && { onClick: handleExport, text: 'Export wizard' },
|
isSqlOrDoc && { onClick: handleExport, text: 'Export wizard' },
|
||||||
driver?.supportsDatabaseDump && { onClick: handleSqlDump, text: 'Backup/export SQL dump' },
|
|
||||||
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleSqlRestore, text: 'Restore/import SQL dump' },
|
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleSqlRestore, text: 'Restore/import SQL dump' },
|
||||||
|
driver?.supportsDatabaseDump && { onClick: handleSqlDump, text: 'Backup/export SQL dump' },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
isSqlOrDoc && { onClick: handleShowDiagram, text: 'Show diagram' },
|
isSqlOrDoc && { onClick: handleShowDiagram, text: 'Show diagram' },
|
||||||
isSqlOrDoc && { onClick: handleSqlGenerator, text: 'SQL Generator' },
|
isSqlOrDoc && { onClick: handleSqlGenerator, text: 'SQL Generator' },
|
||||||
@@ -282,6 +285,7 @@
|
|||||||
import newQuery from '../query/newQuery';
|
import newQuery from '../query/newQuery';
|
||||||
import { exportSqlDump } from '../utility/exportFileTools';
|
import { exportSqlDump } from '../utility/exportFileTools';
|
||||||
import ImportDatabaseDumpModal from '../modals/ImportDatabaseDumpModal.svelte';
|
import ImportDatabaseDumpModal from '../modals/ImportDatabaseDumpModal.svelte';
|
||||||
|
import ExportDatabaseDumpModal from '../modals/ExportDatabaseDumpModal.svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
export let passProps;
|
export let passProps;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
|
import { filterName } from 'dbgate-tools';
|
||||||
|
|
||||||
interface FileTypeHandler {
|
interface FileTypeHandler {
|
||||||
icon: string;
|
icon: string;
|
||||||
format: string;
|
format: string;
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const extractKey = data => data.file;
|
export const extractKey = data => data.file;
|
||||||
|
export const createMatcher = ({ file }) => filter => filterName(filter, file);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -9,12 +9,14 @@
|
|||||||
<style>
|
<style>
|
||||||
label {
|
label {
|
||||||
border: 1px solid var(--theme-bg-button-inv-2);
|
border: 1px solid var(--theme-bg-button-inv-2);
|
||||||
padding: 5px;
|
padding: 4px;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
background-color: var(--theme-bg-button-inv);
|
background-color: var(--theme-bg-button-inv);
|
||||||
color: var(--theme-font-inv-1);
|
color: var(--theme-font-inv-1);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
label:hover:not(.disabled) {
|
label:hover:not(.disabled) {
|
||||||
|
|||||||
108
packages/web/src/modals/ExportDatabaseDumpModal.svelte
Normal file
108
packages/web/src/modals/ExportDatabaseDumpModal.svelte
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount, tick } from 'svelte';
|
||||||
|
import { format } 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';
|
||||||
|
|
||||||
|
export let connection;
|
||||||
|
|
||||||
|
let outputLabel;
|
||||||
|
let outputFile;
|
||||||
|
let pureFileName = null;
|
||||||
|
|
||||||
|
function getDefaultFileName() {
|
||||||
|
return `${connection.database}-${format(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 files = await electron.showSaveDialog({
|
||||||
|
properties: ['showOverwriteConfirmation'],
|
||||||
|
filters: [
|
||||||
|
{ name: 'SQL Files', extensions: ['*.sql'] },
|
||||||
|
{ name: 'All Files', extensions: ['*'] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (files && files[0]) {
|
||||||
|
const path = window.require('path');
|
||||||
|
outputFile = files[0];
|
||||||
|
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">Target: {outputLabel}</div>
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
{#if electron}
|
||||||
|
<FormStyledButton type="button" value="Browse" on:click={handleBrowse} />
|
||||||
|
{:else}
|
||||||
|
<FormStyledButton type="button" value="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>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<UploadButton />
|
<UploadButton />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<FormStyledButton value="Add web URL" on:click={handleAddUrl} />
|
<FormStyledButton value="Import from web URL" on:click={handleAddUrl} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
|
|||||||
@@ -12,7 +12,10 @@
|
|||||||
export let script;
|
export let script;
|
||||||
export let header;
|
export let header;
|
||||||
|
|
||||||
let isRunning;
|
export let openResultLabel;
|
||||||
|
export let onOpenResult;
|
||||||
|
|
||||||
|
let isRunning = true;
|
||||||
let runid;
|
let runid;
|
||||||
let isCanceled;
|
let isCanceled;
|
||||||
|
|
||||||
@@ -68,6 +71,16 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<FormStyledButton value="Close" on:click={handleClose} />
|
<FormStyledButton value="Close" on:click={handleClose} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if onOpenResult && !isRunning}
|
||||||
|
<FormStyledButton
|
||||||
|
value={openResultLabel || 'Open result'}
|
||||||
|
on:click={() => {
|
||||||
|
closeCurrentModal();
|
||||||
|
onOpenResult();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</ModalBase>
|
</ModalBase>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
|||||||
@@ -26,6 +26,28 @@ export async function importSqlDump(inputFile, connection) {
|
|||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function exportSqlDump(outputFile, connection, databaseName, pureFileName) {
|
||||||
|
const script = getCurrentConfig().allowShellScripting ? new ScriptWriter() : new ScriptWriterJson();
|
||||||
|
|
||||||
|
script.dumpDatabase({
|
||||||
|
connection,
|
||||||
|
databaseName,
|
||||||
|
outputFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
showModal(RunScriptModal, {
|
||||||
|
script: script.getScript(),
|
||||||
|
header: 'Exporting database',
|
||||||
|
onOpenResult:
|
||||||
|
pureFileName && !getElectron()
|
||||||
|
? () => {
|
||||||
|
window.open(`${resolveApi()}/uploads/get?file=${pureFileName}`, '_blank');
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
openResultLabel: 'Download SQL file',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function runImportExportScript({ script, runningMessage, canceledMessage, finishedMessage, afterFinish = null }) {
|
async function runImportExportScript({ script, runningMessage, canceledMessage, finishedMessage, afterFinish = null }) {
|
||||||
const electron = getElectron();
|
const electron = getElectron();
|
||||||
|
|
||||||
@@ -126,25 +148,25 @@ export async function exportQuickExportFile(dataName, reader, format, columnMap
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function exportSqlDump(connection, databaseName) {
|
// export async function exportSqlDump(connection, databaseName) {
|
||||||
await saveExportedFile(
|
// await saveExportedFile(
|
||||||
[{ name: 'SQL files', extensions: ['sql'] }],
|
// [{ name: 'SQL files', extensions: ['sql'] }],
|
||||||
`${databaseName}.sql`,
|
// `${databaseName}.sql`,
|
||||||
'sql',
|
// 'sql',
|
||||||
`${databaseName}-dump`,
|
// `${databaseName}-dump`,
|
||||||
filePath => {
|
// filePath => {
|
||||||
const script = getCurrentConfig().allowShellScripting ? new ScriptWriter() : new ScriptWriterJson();
|
// const script = getCurrentConfig().allowShellScripting ? new ScriptWriter() : new ScriptWriterJson();
|
||||||
|
|
||||||
script.dumpDatabase({
|
// script.dumpDatabase({
|
||||||
connection,
|
// connection,
|
||||||
databaseName,
|
// databaseName,
|
||||||
outputFile: filePath,
|
// outputFile: filePath,
|
||||||
});
|
// });
|
||||||
|
|
||||||
return script.getScript();
|
// return script.getScript();
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
export async function saveFileToDisk(
|
export async function saveFileToDisk(
|
||||||
filePathFunc,
|
filePathFunc,
|
||||||
|
|||||||
@@ -2,9 +2,17 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import AppObjectList from '../appobj/AppObjectList.svelte';
|
import AppObjectList from '../appobj/AppObjectList.svelte';
|
||||||
import * as savedFileAppObject from '../appobj/SavedFileAppObject.svelte';
|
import * as savedFileAppObject from '../appobj/SavedFileAppObject.svelte';
|
||||||
|
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||||
|
import InlineButton from '../buttons/InlineButton.svelte';
|
||||||
|
import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte';
|
||||||
|
import SearchInput from '../elements/SearchInput.svelte';
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import { apiCall } from '../utility/api';
|
||||||
import { useFiles } from '../utility/metadataLoaders';
|
import { useFiles } from '../utility/metadataLoaders';
|
||||||
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
||||||
|
|
||||||
|
let filter = '';
|
||||||
|
|
||||||
const sqlFiles = useFiles({ folder: 'sql' });
|
const sqlFiles = useFiles({ folder: 'sql' });
|
||||||
const shellFiles = useFiles({ folder: 'shell' });
|
const shellFiles = useFiles({ folder: 'shell' });
|
||||||
const markdownFiles = useFiles({ folder: 'markdown' });
|
const markdownFiles = useFiles({ folder: 'markdown' });
|
||||||
@@ -22,8 +30,20 @@
|
|||||||
...($sqliteFiles || []),
|
...($sqliteFiles || []),
|
||||||
...($diagramFiles || []),
|
...($diagramFiles || []),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function handleRefreshFiles() {
|
||||||
|
apiCall('files/refresh', { folders: ['sql', 'shell', 'markdown', 'charts', 'query', 'sqlite', 'diagrams'] });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<WidgetsInnerContainer>
|
<WidgetsInnerContainer>
|
||||||
<AppObjectList list={files} module={savedFileAppObject} groupFunc={data => _.startCase(data.folder)} />
|
<SearchBoxWrapper>
|
||||||
|
<SearchInput placeholder="Search saved files" bind:value={filter} />
|
||||||
|
<CloseSearchButton bind:filter />
|
||||||
|
<InlineButton on:click={handleRefreshFiles} title="Refresh files">
|
||||||
|
<FontIcon icon="icon refresh" />
|
||||||
|
</InlineButton>
|
||||||
|
</SearchBoxWrapper>
|
||||||
|
|
||||||
|
<AppObjectList list={files} module={savedFileAppObject} groupFunc={data => _.startCase(data.folder)} {filter} />
|
||||||
</WidgetsInnerContainer>
|
</WidgetsInnerContainer>
|
||||||
|
|||||||
@@ -3198,6 +3198,11 @@ date-fns@^2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.9.0.tgz#d0b175a5c37ed5f17b97e2272bbc1fa5aec677d2"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.9.0.tgz#d0b175a5c37ed5f17b97e2272bbc1fa5aec677d2"
|
||||||
integrity sha512-khbFLu/MlzLjEzy9Gh8oY1hNt/Dvxw3J6Rbc28cVoYWQaC1S3YI4xwkF9ZWcjDLscbZlY9hISMr66RFzZagLsA==
|
integrity sha512-khbFLu/MlzLjEzy9Gh8oY1hNt/Dvxw3J6Rbc28cVoYWQaC1S3YI4xwkF9ZWcjDLscbZlY9hISMr66RFzZagLsA==
|
||||||
|
|
||||||
|
date-fns@^2.28.0:
|
||||||
|
version "2.28.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
|
||||||
|
integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
|
||||||
|
|
||||||
date-utils@*:
|
date-utils@*:
|
||||||
version "1.2.21"
|
version "1.2.21"
|
||||||
resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64"
|
resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64"
|
||||||
|
|||||||
Reference in New Issue
Block a user