diff --git a/packages/api/package.json b/packages/api/package.json index fd66f5cca..c50faa68e 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -18,6 +18,7 @@ "exceljs": "^4.0.1", "express": "^4.17.1", "express-basic-auth": "^1.2.0", + "express-fileupload": "^1.2.0", "find-free-port": "^2.0.0", "fs-extra": "^8.1.0", "http": "^0.0.0", diff --git a/packages/api/src/controllers/uploads.js b/packages/api/src/controllers/uploads.js new file mode 100644 index 000000000..d593bf104 --- /dev/null +++ b/packages/api/src/controllers/uploads.js @@ -0,0 +1,26 @@ +const path = require('path'); +const { uploadsdir } = require('../utility/directories'); +const uuidv1 = require('uuid/v1'); + +module.exports = { + upload_meta: { + method: 'post', + raw: true, + }, + upload(req, res) { + const { data } = req.files || {}; + if (!data) { + res.json(null); + return; + } + const uploadName = uuidv1(); + const filePath = path.join(uploadsdir(), uploadName); + console.log(`Uploading file ${data.name}, size=${data.size}`); + data.mv(filePath, () => { + res.json({ + originalName: data.name, + uploadName, + }); + }); + }, +}; diff --git a/packages/api/src/main.js b/packages/api/src/main.js index de0ece6bf..bafaf0c59 100644 --- a/packages/api/src/main.js +++ b/packages/api/src/main.js @@ -1,6 +1,7 @@ const express = require('express'); const basicAuth = require('express-basic-auth'); const bodyParser = require('body-parser'); +const fileUpload = require('express-fileupload'); const http = require('http'); const cors = require('cors'); const io = require('socket.io'); @@ -21,6 +22,7 @@ const jsldata = require('./controllers/jsldata'); const config = require('./controllers/config'); const files = require('./controllers/files'); const archive = require('./controllers/archive'); +const uploads = require('./controllers/uploads'); const { rundir } = require('./utility/directories'); @@ -47,6 +49,13 @@ function start(argument = null) { app.use(cors()); app.use(bodyParser.json({ limit: '50mb' })); + app.use( + '/uploads', + fileUpload({ + limits: { fileSize: 4 * 1024 * 1024 }, + }) + ); + useController(app, '/connections', connections); useController(app, '/server-connections', serverConnections); useController(app, '/database-connections', databaseConnections); @@ -57,6 +66,7 @@ function start(argument = null) { useController(app, '/config', config); useController(app, '/files', files); useController(app, '/archive', archive); + useController(app, '/uploads', uploads); if (process.env.PAGES_DIRECTORY) { app.use('/pages', express.static(process.env.PAGES_DIRECTORY)); diff --git a/packages/web/package.json b/packages/web/package.json index 891979e11..7802975bc 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -19,6 +19,7 @@ "react": "^16.12.0", "react-ace": "^8.0.0", "react-dom": "^16.12.0", + "react-dropzone": "^11.2.3", "react-json-view": "^1.19.1", "react-modal": "^3.11.1", "react-scripts": "3.3.0", diff --git a/packages/web/src/Screen.js b/packages/web/src/Screen.js index 86149a3f3..a2ce0db8f 100644 --- a/packages/web/src/Screen.js +++ b/packages/web/src/Screen.js @@ -3,6 +3,7 @@ import React from 'react'; import theme from './theme'; import styled from 'styled-components'; +import { useDropzone } from 'react-dropzone'; import TabsPanel from './TabsPanel'; import TabContent from './TabContent'; import WidgetIconPanel from './widgets/WidgetIconPanel'; @@ -12,6 +13,7 @@ import ToolBar from './widgets/Toolbar'; import StatusBar from './widgets/StatusBar'; import { useSplitterDrag, HorizontalSplitHandle } from './widgets/Splitter'; import { ModalLayer } from './modals/showModal'; +import resolveApi from './utility/resolveApi'; const BodyDiv = styled.div` position: fixed; @@ -96,6 +98,15 @@ const ScreenHorizontalSplitHandle = styled(HorizontalSplitHandle)` bottom: ${theme.statusBar.height}px; `; +const DragAndDropTarget = styled.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: red; +`; + export default function Screen() { const currentWidget = useCurrentWidget(); const leftPanelWidth = useLeftPanelWidth(); @@ -105,8 +116,46 @@ export default function Screen() { : theme.widgetMenu.iconSize; const toolbarPortalRef = React.useRef(); const onSplitDown = useSplitterDrag('clientX', (diff) => setLeftPanelWidth((v) => v + diff)); + + const onDrop = React.useCallback((files) => { + // Do something with the files + console.log('FILES', files); + files.forEach(async (file) => { + if (parseInt(file.size, 10) >= 4 * 1024 * 1024) { + // to big file + return; + } + + const formData = new FormData(); + formData.append('data', file); + + const fetchOptions = { + method: 'POST', + body: formData, + }; + + const apiBase = resolveApi(); + const resp = await fetch(`${apiBase}/uploads/upload`, fetchOptions); + const event = await resp.json(); + + return event; + + // const reader = new FileReader(); + + // reader.onabort = () => console.log('file reading was aborted'); + // reader.onerror = () => console.log('file reading has failed'); + // reader.onload = () => { + // // Do whatever you want with the file contents + // const binaryStr = reader.result; + // console.log(binaryStr); + // }; + // reader.readAsArrayBuffer(file); + }); + }, []); + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); + return ( - <> +
@@ -134,6 +183,13 @@ export default function Screen() { - + + {!!isDragActive && ( + + Drop the files here ... + {' '} + + )} +
); } diff --git a/yarn.lock b/yarn.lock index f9e9c815a..e8d6a0e07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2297,6 +2297,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + autoprefixer@^9.6.1: version "9.7.4" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378" @@ -2910,6 +2915,13 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" + byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -4216,6 +4228,13 @@ detect-port-alt@1.1.6: address "^1.0.1" debug "^2.6.0" +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + diff-match-patch@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1" @@ -4986,6 +5005,13 @@ express-basic-auth@^1.2.0: dependencies: basic-auth "^2.0.1" +express-fileupload@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.2.0.tgz#356c4dfd645be71ab9fb2f4e6d84eeb00d247979" + integrity sha512-oe4WpKcSppXnl5peornawWUa6tKmIc1/kJxMNRGJR1A0v4zyLL6VsFR6wZ8P2a4Iq3aGx8xae3Vlr+MOMQhFPw== + dependencies: + busboy "^0.3.1" + express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -5186,6 +5212,13 @@ file-loader@4.3.0: loader-utils "^1.2.3" schema-utils "^2.5.0" +file-selector@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.3.tgz#e2958cdd4366f95e59dc618b95c700abe72ed7a6" + integrity sha512-d+hc9ctodLSVG55V2V5I4/eJBEr2p2na/kDN46Ty7PBhdp/Q5NmeQTXKa1Hx3AcIL1lgSFKZI0ve/v5ZXGCDkQ== + dependencies: + tslib "^2.0.3" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -10039,6 +10072,15 @@ react-dom@^16.12.0: prop-types "^15.6.2" scheduler "^0.18.0" +react-dropzone@^11.2.3: + version "11.2.3" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.2.3.tgz#8a49e9fc7ab75eaccf748c382a790092c035cef1" + integrity sha512-D99BhHm7H1h7ztUH/FLDo4wDy7VzXMbHoS/tUZHtoaY37Y55uadq0kUKqoaJ8PXl+niqdb5t6GankuEaAL6Vwg== + dependencies: + attr-accept "^2.2.1" + file-selector "^0.2.2" + prop-types "^15.7.2" + react-error-overlay@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.5.tgz#55d59c2a3810e8b41922e0b4e5f85dcf239bd533" @@ -11347,6 +11389,11 @@ stream-transform@^2.0.1: dependencies: mixme "^0.3.1" +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -11913,6 +11960,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"