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..7ce356d37 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(extendDatabaseInfo(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(extendDatabaseInfo(model), driver, outputFolder, outputFile);
+
+ return { status: 'ok' };
},
generateDeploySql_meta: true,
diff --git a/packages/web/src/appobj/DatabaseAppObject.svelte b/packages/web/src/appobj/DatabaseAppObject.svelte
index 15f59ef9e..f2f4b3be3 100644
--- a/packages/web/src/appobj/DatabaseAppObject.svelte
+++ b/packages/web/src/appobj/DatabaseAppObject.svelte
@@ -170,14 +170,18 @@
};
const handleExportModel = async () => {
- const resp = await apiCall('database-connections/export-model', {
+ showModal(ExportDbModelModal, {
conid: connection._id,
database: name,
});
- currentArchive.set(resp.archiveFolder);
- selectedWidget.set('archive');
- visibleWidgetSideBar.set(true);
- showSnackbarSuccess(`Saved to archive ${resp.archiveFolder}`);
+ // const resp = await apiCall('database-connections/export-model', {
+ // conid: connection._id,
+ // database: name,
+ // });
+ // currentArchive.set(resp.archiveFolder);
+ // selectedWidget.set('archive');
+ // visibleWidgetSideBar.set(true);
+ // showSnackbarSuccess(`Saved to archive ${resp.archiveFolder}`);
};
const handleCompareWithCurrentDb = () => {
@@ -198,13 +202,13 @@
);
};
- const handleOpenJsonModel = async () => {
- const db = await getDatabaseInfo({
- conid: connection._id,
- database: name,
- });
- openJsonDocument(db, name);
- };
+ // const handleOpenJsonModel = async () => {
+ // const db = await getDatabaseInfo({
+ // conid: connection._id,
+ // database: name,
+ // });
+ // openJsonDocument(db, name);
+ // };
const handleGenerateScript = async () => {
const data = await apiCall('database-connections/export-keys', {
@@ -325,11 +329,12 @@
hasPermission(`dbops/sql-generator`) && { onClick: handleSqlGenerator, text: 'SQL Generator' },
driver?.supportsDatabaseProfiler &&
hasPermission(`dbops/profiler`) && { onClick: handleDatabaseProfiler, text: 'Database profiler' },
+ // isSqlOrDoc &&
+ // isSqlOrDoc &&
+ // hasPermission(`dbops/model/view`) && { onClick: handleOpenJsonModel, text: 'Open model as JSON' },
isSqlOrDoc &&
- isSqlOrDoc &&
- hasPermission(`dbops/model/view`) && { onClick: handleOpenJsonModel, text: 'Open model as JSON' },
- isSqlOrDoc &&
- hasPermission(`dbops/model/view`) && { onClick: handleExportModel, text: 'Export DB model - experimental' },
+ isProApp() &&
+ hasPermission(`dbops/model/view`) && { onClick: handleExportModel, text: 'Export DB model' },
isSqlOrDoc &&
_.get($currentDatabase, 'connection._id') &&
hasPermission('dbops/model/compare') &&
@@ -408,6 +413,7 @@
import newTable from '../tableeditor/newTable';
import { loadSchemaList, switchCurrentDatabase } from '../utility/common';
import { isProApp } from '../utility/proTools';
+ import ExportDbModelModal from '../modals/ExportDbModelModal.svelte';
export let data;
export let passProps;
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 e59fb65f8..b524a85e7 100644
--- a/packages/web/src/forms/FormElectronFileSelectorRaw.svelte
+++ b/packages/web/src/forms/FormElectronFileSelectorRaw.svelte
@@ -9,19 +9,32 @@
export let name;
export let disabled = false;
export let defaultFileName = '';
+ 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: ['showHiddenFiles', 'openFile'],
- 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/packages/web/src/modals/ExportDbModelModal.svelte b/packages/web/src/modals/ExportDbModelModal.svelte
new file mode 100644
index 000000000..d79cee84b
--- /dev/null
+++ b/packages/web/src/modals/ExportDbModelModal.svelte
@@ -0,0 +1,5 @@
+
+
+
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"