diff --git a/packages/api/package.json b/packages/api/package.json index 41d6b6746..0ac1c406f 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -49,6 +49,7 @@ "jsonwebtoken": "^8.5.1", "line-reader": "^0.4.0", "lodash": "^4.17.21", + "mkdirp": "^3.0.1", "moment": "^2.24.0", "ncp": "^2.0.0", "node-cron": "^2.0.3", diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 9c470b030..888ed160a 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -32,6 +32,7 @@ const { MissingCredentialsError } = require('../utility/exceptions'); const pipeForkLogs = require('../utility/pipeForkLogs'); const crypto = require('crypto'); const loadModelTransform = require('../utility/loadModelTransform'); +const exportDbModelSql = require('../utility/exportDbModelSql'); const logger = getLogger('databaseConnections'); @@ -398,7 +399,7 @@ module.exports = { }, structure_meta: true, - async structure({ conid, database, modelTransFile }, req) { + async structure({ conid, database, modelTransFile = null }, req) { testConnectionPermission(conid, req); if (conid == '__model') { const model = await importDbModel(database); @@ -439,14 +440,33 @@ module.exports = { }, exportModel_meta: true, - async exportModel({ conid, database }, req) { + async exportModel({ conid, database, outputFolder }, req) { testConnectionPermission(conid, req); - const archiveFolder = await archive.getNewArchiveFolder({ database }); - await fs.mkdir(path.join(archivedir(), archiveFolder)); + + const realFolder = outputFolder.startsWith('archive:') + ? resolveArchiveFolder(outputFolder.substring('archive:'.length)) + : outputFolder; + const model = await this.structure({ conid, database }); - await exportDbModel(model, path.join(archivedir(), archiveFolder)); - socket.emitChanged(`archive-folders-changed`); - return { archiveFolder }; + await exportDbModel(model, realFolder); + + if (outputFolder.startsWith('archive:')) { + socket.emitChanged(`archive-files-changed`, { folder: outputFolder.substring('archive:'.length) }); + } + return { status: 'ok' }; + }, + + exportModelSql_meta: true, + async exportModelSql({ conid, database, outputFolder, outputFile }, req) { + testConnectionPermission(conid, req); + + const connection = await connections.getCore({ conid }); + const driver = requireEngineDriver(connection); + + const model = await this.structure({ conid, database }); + await exportDbModelSql(model, driver, outputFolder, outputFile); + + return { status: 'ok' }; }, generateDeploySql_meta: true, diff --git a/packages/web/src/forms/FormArchiveFolderSelect.svelte b/packages/web/src/forms/FormArchiveFolderSelect.svelte index 1ee8d958a..887862e84 100644 --- a/packages/web/src/forms/FormArchiveFolderSelect.svelte +++ b/packages/web/src/forms/FormArchiveFolderSelect.svelte @@ -22,6 +22,7 @@ label: folder.name, })), ...additionalFolders + .filter(x => x != '@create') .filter(x => !($folders || []).find(y => y.name == x)) .map(folder => ({ value: folder, diff --git a/packages/web/src/forms/FormElectronFileSelectorRaw.svelte b/packages/web/src/forms/FormElectronFileSelectorRaw.svelte index 2f859da4a..b524a85e7 100644 --- a/packages/web/src/forms/FormElectronFileSelectorRaw.svelte +++ b/packages/web/src/forms/FormElectronFileSelectorRaw.svelte @@ -9,20 +9,32 @@ export let name; export let disabled = false; export let defaultFileName = ''; - export let dialogProperties = ['showHiddenFiles', 'openFile']; + export let dialogProperties = undefined; + export let isSaveDialog = false; + export let dialogFilters = [{ name: 'All Files', extensions: ['*'] }]; const { values, setFieldValue } = getFormContext(); async function handleBrowse() { const electron = getElectron(); if (!electron) return; - const filePaths = await electron.showOpenDialog({ - defaultPath: values[name], - properties: dialogProperties, - filters: [{ name: 'All Files', extensions: ['*'] }], - }); - const filePath = filePaths && filePaths[0]; - if (filePath) setFieldValue(name, filePath); + + if (isSaveDialog) { + const filePath = await electron.showSaveDialog({ + defaultPath: values[name], + properties: dialogProperties ?? ['showHiddenFiles', 'showOverwriteConfirmation'], + filters: dialogFilters, + }); + if (filePath) setFieldValue(name, filePath); + } else { + const filePaths = await electron.showOpenDialog({ + defaultPath: values[name], + properties: dialogProperties ?? ['showHiddenFiles', 'openFile'], + filters: dialogFilters, + }); + const filePath = filePaths && filePaths[0]; + if (filePath) setFieldValue(name, filePath); + } } diff --git a/yarn.lock b/yarn.lock index 527c1ffa6..3c5b61754 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8390,6 +8390,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + moment@^2.24.0, moment@^2.29.2: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"