diff --git a/packages/api/src/controllers/cloud.js b/packages/api/src/controllers/cloud.js index 37fd9856f..c2cc1af2d 100644 --- a/packages/api/src/controllers/cloud.js +++ b/packages/api/src/controllers/cloud.js @@ -15,6 +15,7 @@ const { recryptConnection, getInternalEncryptor, encryptConnection } = require(' const { getConnectionLabel, getLogger, extractErrorLogData } = require('dbgate-tools'); const logger = getLogger('cloud'); const _ = require('lodash'); +const fs = require('fs-extra'); module.exports = { publicFiles_meta: true, @@ -205,6 +206,22 @@ module.exports = { return resp; }, + deleteContent_meta: true, + async deleteContent({ folid, cntid }) { + const resp = await callCloudApiPost(`content/delete/${folid}/${cntid}`); + socket.emitChanged('cloud-content-changed'); + socket.emit('cloud-content-updated'); + return resp; + }, + + renameContent_meta: true, + async renameContent({ folid, cntid, name }) { + const resp = await callCloudApiPost(`content/rename/${folid}/${cntid}`, { name }); + socket.emitChanged('cloud-content-changed'); + socket.emit('cloud-content-updated'); + return resp; + }, + saveFile_meta: true, async saveFile({ folid, cntid, fileName, data, contentFolder, format }) { const resp = await putCloudContent(folid, cntid, data, fileName, 'file', contentFolder, format); @@ -212,4 +229,22 @@ module.exports = { socket.emit('cloud-content-updated'); return resp; }, + + copyFile_meta: true, + async copyFile({ folid, cntid, name }) { + const resp = await callCloudApiPost(`content/duplicate/${folid}/${cntid}`, { name }); + socket.emitChanged('cloud-content-changed'); + socket.emit('cloud-content-updated'); + return resp; + }, + + exportFile_meta: true, + async exportFile({ folid, cntid, filePath }, req) { + const { content } = await getCloudContent(folid, cntid); + if (!content) { + throw new Error('File not found'); + } + await fs.writeFile(filePath, content); + return true; + }, }; diff --git a/packages/web/src/appobj/ConnectionAppObject.svelte b/packages/web/src/appobj/ConnectionAppObject.svelte index 531bb0788..7ed4d83ff 100644 --- a/packages/web/src/appobj/ConnectionAppObject.svelte +++ b/packages/web/src/appobj/ConnectionAppObject.svelte @@ -262,11 +262,15 @@ }); }; const handleDuplicate = () => { - apiCall('connections/save', { - ...data, - _id: undefined, - displayName: `${getConnectionLabel(data)} - copy`, - }); + if (data._id.startsWith('cloud://')) { + apiCall('cloud/duplicate-connection', { conid: data._id }); + } else { + apiCall('connections/save', { + ...data, + _id: undefined, + displayName: `${getConnectionLabel(data)} - copy`, + }); + } }; const handleCreateDatabase = () => { showModal(InputTextModal, { diff --git a/packages/web/src/appobj/SavedFileAppObject.svelte b/packages/web/src/appobj/SavedFileAppObject.svelte index 89f2ce970..2cd5e70bc 100644 --- a/packages/web/src/appobj/SavedFileAppObject.svelte +++ b/packages/web/src/appobj/SavedFileAppObject.svelte @@ -206,7 +206,14 @@ showModal(ConfirmModal, { message: `Really delete file ${data.file}?`, onConfirm: () => { - apiCall('files/delete', data); + if (data.folid && data.cntid) { + apiCall('cloud/delete-content', { + folid: data.folid, + cntid: data.cntid, + }); + } else { + apiCall('files/delete', data); + } }, }); }; @@ -217,7 +224,15 @@ label: 'New file name', header: 'Rename file', onConfirm: newFile => { - apiCall('files/rename', { ...data, newFile }); + if (data.folid && data.cntid) { + apiCall('cloud/rename-content', { + folid: data.folid, + cntid: data.cntid, + name: newFile, + }); + } else { + apiCall('files/rename', { ...data, newFile }); + } }, }); }; @@ -226,9 +241,17 @@ showModal(InputTextModal, { value: data.file, label: 'New file name', - header: 'Rename file', + header: 'Copy file', onConfirm: newFile => { - apiCall('files/copy', { ...data, newFile }); + if (data.folid && data.cntid) { + apiCall('cloud/copy-file', { + folid: data.folid, + cntid: data.cntid, + name: newFile, + }); + } else { + apiCall('files/copy', { ...data, newFile }); + } }, }); }; @@ -236,11 +259,19 @@ const handleDownload = () => { saveFileToDisk( async filePath => { - await apiCall('files/export-file', { - folder, - file: data.file, - filePath, - }); + if (data.folid && data.cntid) { + await apiCall('cloud/export-file', { + folid: data.folid, + cntid: data.cntid, + filePath, + }); + } else { + await apiCall('files/export-file', { + folder, + file: data.file, + filePath, + }); + } }, { formatLabel: handler.label, formatExtension: handler.format, defaultFileName: data.file } ); diff --git a/packages/web/src/modals/SaveFileModal.svelte b/packages/web/src/modals/SaveFileModal.svelte index 4665880fa..15ebd67e1 100644 --- a/packages/web/src/modals/SaveFileModal.svelte +++ b/packages/web/src/modals/SaveFileModal.svelte @@ -10,7 +10,6 @@ import { writable } from 'svelte/store'; import getElectron from '../utility/getElectron'; - import ChooseCloudFolderModal from './ChooseCloudFolderModal.svelte'; import ModalBase from './ModalBase.svelte'; import { closeCurrentModal, showModal } from './modalTools'; import FormCloudFolderSelect from '../forms/FormCloudFolderSelect.svelte'; @@ -22,8 +21,10 @@ export let fileExtension; export let filePath; export let onSave = undefined; + export let folid; + // export let cntid; - const values = writable({ name, cloudFolder: '__local' }); + const values = writable({ name, cloudFolder: folid ?? '__local' }); const electron = getElectron(); @@ -37,6 +38,8 @@ savedFile: name, savedFolder: folder, savedFilePath: null, + savedCloudFolderId: null, + savedCloudContentId: null, }); } } else { @@ -46,6 +49,7 @@ data, contentFolder: folder, format, + // cntid, }); if (resp.cntid) { closeCurrentModal(); @@ -55,7 +59,7 @@ savedFolder: folder, savedFilePath: null, savedCloudFolderId: cloudFolder, - savedCloudContentId: resp.cntid, + // savedCloudContentId: resp.cntid, }); } } @@ -75,6 +79,8 @@ savedFile: null, savedFolder: null, savedFilePath: filePath, + savedCloudFolderId: null, + savedCloudContentId: null, }); } }; diff --git a/packages/web/src/utility/cloudListeners.ts b/packages/web/src/utility/cloudListeners.ts index 7eef1cd46..bdb3b1bc6 100644 --- a/packages/web/src/utility/cloudListeners.ts +++ b/packages/web/src/utility/cloudListeners.ts @@ -47,5 +47,11 @@ export function installCloudListeners() { ensureCloudConnectionsLoaded(...conids); }); - apiOn('cloud-content-updated', () => cloudConnectionsStore.set({})); + apiOn('cloud-content-updated', () => { + const conids = Object.keys(getCloudConnectionsStore()); + cloudConnectionsStore.set({}); + for (const conn of conids) { + loadCloudConnection(conn); + } + }); } diff --git a/packages/web/src/utility/openElectronFile.ts b/packages/web/src/utility/openElectronFile.ts index f771cdcdf..51c265f8e 100644 --- a/packages/web/src/utility/openElectronFile.ts +++ b/packages/web/src/utility/openElectronFile.ts @@ -111,6 +111,8 @@ async function openSavedElectronFile(filePath, parsed, folder) { props: { savedFile: null, savedFolder: null, + savedCloudFolderId: null, + savedCloudContentId: null, savedFilePath: filePath, savedFormat: handler.format, ...connProps, diff --git a/packages/web/src/utility/openNewTab.ts b/packages/web/src/utility/openNewTab.ts index 59fab9db4..6b4070ded 100644 --- a/packages/web/src/utility/openNewTab.ts +++ b/packages/web/src/utility/openNewTab.ts @@ -30,7 +30,8 @@ export default async function openNewTab(newTab, initialData: any = undefined, o }; } - const { savedFile, savedFolder, savedFilePath, conid, database } = newTab.props || {}; + const { savedFile, savedFolder, savedFilePath, savedCloudFolderId, savedCloudContentId, conid, database } = + newTab.props || {}; if (conid && database) { const connection = await getConnectionInfo({ conid }); @@ -49,7 +50,9 @@ export default async function openNewTab(newTab, initialData: any = undefined, o x.closedTime == null && x.props.savedFile == savedFile && x.props.savedFolder == savedFolder && - x.props.savedFilePath == savedFilePath + x.props.savedFilePath == savedFilePath && + x.props.savedCloudFolderId == savedCloudFolderId && + x.props.savedCloudContentId == savedCloudContentId ); } diff --git a/packages/web/src/utility/saveTabFile.ts b/packages/web/src/utility/saveTabFile.ts index 60351ecdb..b4519fc9d 100644 --- a/packages/web/src/utility/saveTabFile.ts +++ b/packages/web/src/utility/saveTabFile.ts @@ -15,16 +15,31 @@ export default async function saveTabFile(editor, saveMode, folder, format, file const tabs = get(openedTabs); const tabid = editor.activator.tabid; const data = editor.getData(); - const { savedFile, savedFilePath, savedFolder } = tabs.find(x => x.tabid == tabid).props || {}; + const { savedFile, savedFilePath, savedFolder, savedCloudFolderId, savedCloudContentId } = + tabs.find(x => x.tabid == tabid).props || {}; const handleSave = async () => { - if (savedFile) { - await apiCall('files/save', { folder: savedFolder || folder, file: savedFile, data, format }); + if (savedCloudFolderId && savedCloudContentId) { + const resp = await apiCall('cloud/save-file', { + folid: savedCloudFolderId, + fileName: savedFile, + data, + contentFolder: folder, + format, + cntid: savedCloudContentId, + }); + if (resp.cntid) { + markTabSaved(tabid); + } + } else { + if (savedFile) { + await apiCall('files/save', { folder: savedFolder || folder, file: savedFile, data, format }); + } + if (savedFilePath) { + await apiCall('files/save-as', { filePath: savedFilePath, data, format }); + } + markTabSaved(tabid); } - if (savedFilePath) { - await apiCall('files/save-as', { filePath: savedFilePath, data, format }); - } - markTabSaved(tabid); }; const onSave = (title, newProps) => { @@ -60,6 +75,8 @@ export default async function saveTabFile(editor, saveMode, folder, format, file savedFile: null, savedFolder: null, savedFilePath: file, + savedCloudFolderId: null, + savedCloudContentId: null, }); } } else if ((savedFile || savedFilePath) && saveMode == 'save') { @@ -73,6 +90,8 @@ export default async function saveTabFile(editor, saveMode, folder, format, file name: savedFile || 'newFile', filePath: savedFilePath, onSave, + folid: savedCloudFolderId, + // cntid: savedCloudContentId, }); } } diff --git a/packages/web/src/widgets/PrivateCloudWidget.svelte b/packages/web/src/widgets/PrivateCloudWidget.svelte index 8fdb35f5d..1a53663d5 100644 --- a/packages/web/src/widgets/PrivateCloudWidget.svelte +++ b/packages/web/src/widgets/PrivateCloudWidget.svelte @@ -41,22 +41,25 @@ const serverStatus = useServerStatus(); $: emptyCloudContent = ($cloudContentList || []).filter(x => !x.items?.length).map(x => x.folid); - $: cloudContentFlat = ($cloudContentList || []) - .flatMap(fld => fld.items ?? []) - .map(data => { - if (data.type == 'connection') { - const conid = `cloud://${data.folid}/${data.cntid}`; - const status = $serverStatus ? $serverStatus[$volatileConnectionMapStore[conid] || conid] : undefined; + $: cloudContentFlat = _.sortBy( + ($cloudContentList || []) + .flatMap(fld => fld.items ?? []) + .map(data => { + if (data.type == 'connection') { + const conid = `cloud://${data.folid}/${data.cntid}`; + const status = $serverStatus ? $serverStatus[$volatileConnectionMapStore[conid] || conid] : undefined; - return { - ...data, - conid, - status, - }; - } + return { + ...data, + conid, + status, + }; + } - return data; - }); + return data; + }), + 'name' + ); $: contentGroupMap = _.keyBy($cloudContentList || [], x => x.folid); // $: console.log('cloudContentFlat', cloudContentFlat);