diff --git a/.github/workflows/build-npm.yaml b/.github/workflows/build-npm.yaml index fba1d0c3e..6b108563b 100644 --- a/.github/workflows/build-npm.yaml +++ b/.github/workflows/build-npm.yaml @@ -6,7 +6,7 @@ on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' - # - 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+' # on: # push: diff --git a/app/src/electron.js b/app/src/electron.js index 0ef5cc6f0..d1c1cc790 100644 --- a/app/src/electron.js +++ b/app/src/electron.js @@ -48,6 +48,7 @@ function buildMenu() { submenu: [ commandItem('new.connection'), commandItem('file.open'), + commandItem('file.openArchive'), commandItem('group.save'), commandItem('group.saveAs'), commandItem('database.search'), diff --git a/packages/api/src/controllers/archive.js b/packages/api/src/controllers/archive.js index c1b38c3f2..9bf911f6a 100644 --- a/packages/api/src/controllers/archive.js +++ b/packages/api/src/controllers/archive.js @@ -3,7 +3,7 @@ const stream = require('stream'); const readline = require('readline'); const path = require('path'); const { formatWithOptions } = require('util'); -const { archivedir } = require('../utility/directories'); +const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('../utility/directories'); const socket = require('../utility/socket'); const JsonLinesDatastore = require('../utility/JsonLinesDatastore'); const { saveFreeTableData } = require('../utility/freeTableStorage'); @@ -33,9 +33,18 @@ module.exports = { return true; }, + createLink_meta: 'post', + async createLink({ linkedFolder }) { + const folder = await this.getNewArchiveFolder({ database: path.parse(linkedFolder).name + '.link' }); + fs.writeFile(path.join(archivedir(), folder), linkedFolder); + clearArchiveLinksCache(); + socket.emitChanged('archive-folders-changed'); + return true; + }, + files_meta: 'get', async files({ folder }) { - const dir = path.join(archivedir(), folder); + const dir = resolveArchiveFolder(folder); if (!(await fs.exists(dir))) return []; const files = await fs.readdir(dir); @@ -71,27 +80,31 @@ module.exports = { deleteFile_meta: 'post', async deleteFile({ folder, file }) { - await fs.unlink(path.join(archivedir(), folder, `${file}.jsonl`)); + await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.jsonl`)); socket.emitChanged(`archive-files-changed-${folder}`); }, deleteFolder_meta: 'post', async deleteFolder({ folder }) { if (!folder) throw new Error('Missing folder parameter'); - await fs.rmdir(path.join(archivedir(), folder), { recursive: true }); + if (folder.endsWith('.link')) { + await fs.unlink(path.join(archivedir(), folder)); + } else { + await fs.rmdir(path.join(archivedir(), folder), { recursive: true }); + } socket.emitChanged(`archive-folders-changed`); }, saveFreeTable_meta: 'post', async saveFreeTable({ folder, file, data }) { - saveFreeTableData(path.join(archivedir(), folder, `${file}.jsonl`), data); + saveFreeTableData(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), data); return true; }, loadFreeTable_meta: 'post', async loadFreeTable({ folder, file }) { return new Promise((resolve, reject) => { - const fileStream = fs.createReadStream(path.join(archivedir(), folder, `${file}.jsonl`)); + const fileStream = fs.createReadStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`)); const liner = readline.createInterface({ input: fileStream, }); diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 1662af13c..c82738b84 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -8,7 +8,7 @@ const { handleProcessCommunication } = require('../utility/processComm'); const config = require('./config'); const fs = require('fs-extra'); const exportDbModel = require('../utility/exportDbModel'); -const { archivedir } = require('../utility/directories'); +const { archivedir, resolveArchiveFolder } = require('../utility/directories'); const path = require('path'); const importDbModel = require('../utility/importDbModel'); const requireEngineDriver = require('../utility/requireEngineDriver'); @@ -262,7 +262,7 @@ module.exports = { return generateDeploySql({ connection, analysedStructure: await this.structure({ conid, database }), - modelFolder: path.join(archivedir(), archiveFolder), + modelFolder: resolveArchiveFolder(archiveFolder), }); // const deployedModel = generateDbPairingId(await importDbModel(path.join(archivedir(), archiveFolder))); // const currentModel = generateDbPairingId(await this.structure({ conid, database })); diff --git a/packages/api/src/controllers/files.js b/packages/api/src/controllers/files.js index c080c8445..f23875df8 100644 --- a/packages/api/src/controllers/files.js +++ b/packages/api/src/controllers/files.js @@ -1,6 +1,6 @@ const fs = require('fs-extra'); const path = require('path'); -const { filesdir, archivedir } = require('../utility/directories'); +const { filesdir, archivedir, resolveArchiveFolder } = require('../utility/directories'); const hasPermission = require('../utility/hasPermission'); const socket = require('../utility/socket'); const scheduler = require('./scheduler'); @@ -59,7 +59,7 @@ module.exports = { load_meta: 'post', async load({ folder, file, format }) { if (folder.startsWith('archive:')) { - const text = await fs.readFile(path.join(archivedir(), folder.substring('archive:'.length), file), { + const text = await fs.readFile(path.join(resolveArchiveFolder(folder.substring('archive:'.length)), file), { encoding: 'utf-8', }); return deserialize(format, text); @@ -73,7 +73,7 @@ module.exports = { save_meta: 'post', async save({ folder, file, data, format }) { if (folder.startsWith('archive:')) { - const dir = path.join(archivedir(), folder.substring('archive:'.length)); + const dir = resolveArchiveFolder(folder.substring('archive:'.length)); await fs.writeFile(path.join(dir, file), serialize(format, data)); socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`); } else { diff --git a/packages/api/src/shell/archiveReader.js b/packages/api/src/shell/archiveReader.js index 0c8eff5d6..3a6005664 100644 --- a/packages/api/src/shell/archiveReader.js +++ b/packages/api/src/shell/archiveReader.js @@ -1,9 +1,9 @@ const path = require('path'); -const { archivedir } = require('../utility/directories'); +const { archivedir, resolveArchiveFolder } = require('../utility/directories'); const jsonLinesReader = require('./jsonLinesReader'); function archiveReader({ folderName, fileName, ...other }) { - const jsonlFile = path.join(archivedir(), folderName, `${fileName}.jsonl`); + const jsonlFile = path.join(resolveArchiveFolder(folderName), `${fileName}.jsonl`); const res = jsonLinesReader({ fileName: jsonlFile, ...other }); return res; } diff --git a/packages/api/src/shell/archiveWriter.js b/packages/api/src/shell/archiveWriter.js index 20e1573d9..7f7911c7a 100644 --- a/packages/api/src/shell/archiveWriter.js +++ b/packages/api/src/shell/archiveWriter.js @@ -1,11 +1,11 @@ const path = require('path'); const fs = require('fs'); -const { archivedir } = require('../utility/directories'); +const { archivedir, resolveArchiveFolder } = require('../utility/directories'); // const socket = require('../utility/socket'); const jsonLinesWriter = require('./jsonLinesWriter'); function archiveWriter({ folderName, fileName }) { - const dir = path.join(archivedir(), folderName); + const dir = resolveArchiveFolder(folderName); if (!fs.existsSync(dir)) { console.log(`Creating directory ${dir}`); fs.mkdirSync(dir); diff --git a/packages/api/src/utility/directories.js b/packages/api/src/utility/directories.js index e318c1595..0dd2948a1 100644 --- a/packages/api/src/utility/directories.js +++ b/packages/api/src/utility/directories.js @@ -71,6 +71,22 @@ function getPluginBackendPath(packageName) { return path.join(pluginsdir(), packageName, 'dist', 'backend.js'); } +let archiveLinksCache = {}; + +function resolveArchiveFolder(folder) { + if (folder.endsWith('.link')) { + if (!archiveLinksCache[folder]) { + archiveLinksCache[folder] = fs.readFileSync(path.join(archivedir(), folder), 'utf-8'); + } + return archiveLinksCache[folder]; + } + return path.join(archivedir(), folder); +} + +function clearArchiveLinksCache() { + archiveLinksCache = {}; +} + module.exports = { datadir, jsldir, @@ -83,4 +99,6 @@ module.exports = { packagedPluginsDir, packagedPluginList, getPluginBackendPath, + resolveArchiveFolder, + clearArchiveLinksCache, }; diff --git a/packages/api/src/utility/getJslFileName.js b/packages/api/src/utility/getJslFileName.js index d74c1ffe6..8c8ac4c68 100644 --- a/packages/api/src/utility/getJslFileName.js +++ b/packages/api/src/utility/getJslFileName.js @@ -1,10 +1,10 @@ const path = require('path'); -const { jsldir, archivedir } = require('./directories'); +const { jsldir, archivedir, resolveArchiveFolder } = require('./directories'); function getJslFileName(jslid) { const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/); if (archiveMatch) { - return path.join(archivedir(), archiveMatch[1], `${archiveMatch[2]}.jsonl`); + return path.join(resolveArchiveFolder(archiveMatch[1]), `${archiveMatch[2]}.jsonl`); } return path.join(jsldir(), `${jslid}.jsonl`); } diff --git a/packages/api/src/utility/importDbModel.js b/packages/api/src/utility/importDbModel.js index c07eacb9e..162ebfd3a 100644 --- a/packages/api/src/utility/importDbModel.js +++ b/packages/api/src/utility/importDbModel.js @@ -3,14 +3,12 @@ const path = require('path'); const yaml = require('js-yaml'); const { databaseInfoFromYamlModel, DatabaseAnalyser } = require('dbgate-tools'); const { startsWith } = require('lodash'); -const { archivedir } = require('./directories'); +const { archivedir, resolveArchiveFolder } = require('./directories'); async function importDbModel(inputDir) { const files = []; - const dir = inputDir.startsWith('archive:') - ? path.join(archivedir(), inputDir.substring('archive:'.length)) - : inputDir; + const dir = inputDir.startsWith('archive:') ? resolveArchiveFolder(inputDir.substring('archive:'.length)) : inputDir; for (const name of await fs.readdir(dir)) { if (name.endsWith('.table.yaml') || name.endsWith('.sql')) { diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 3c3dcca89..dcc0ccf27 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -19,6 +19,7 @@ import './recentDatabaseSwitch'; import hasPermission from '../utility/hasPermission'; import _ from 'lodash'; import { findEngineDriver } from 'dbgate-tools'; +import { openArchiveFolder } from '../utility/openArchiveFolder'; const electron = getElectron(); @@ -213,6 +214,13 @@ if (electron) { keyText: 'Ctrl+O', onClick: openElectronFile, }); + + registerCommand({ + id: 'file.openArchive', + category: 'File', + name: 'Open DB Model/Archive', + onClick: openArchiveFolder, + }); } registerCommand({ diff --git a/packages/web/src/utility/openArchiveFolder.ts b/packages/web/src/utility/openArchiveFolder.ts new file mode 100644 index 000000000..7bc7cbe8a --- /dev/null +++ b/packages/web/src/utility/openArchiveFolder.ts @@ -0,0 +1,14 @@ +import { get } from 'svelte/store'; +import getElectron from './getElectron'; +import { extensions } from '../stores'; +import axiosInstance from '../utility/axiosInstance'; + +export function openArchiveFolder() { + const electron = getElectron(); + const ext = get(extensions); + const filePaths = electron.remote.dialog.showOpenDialogSync(electron.remote.getCurrentWindow(), { + properties: ['openDirectory'], + }); + const linkedFolder = filePaths && filePaths[0]; + axiosInstance.post('archive/create-link', { linkedFolder }); +}