diff --git a/frontend/src/download.js b/frontend/src/download.js index c7a4937b..b3457c95 100644 --- a/frontend/src/download.js +++ b/frontend/src/download.js @@ -1,53 +1,67 @@ const FileReceiver = require('./fileReceiver'); -let download = () => { - const fileReceiver = new FileReceiver(); +$(document).ready(function() { + $('#send-file').click(() => { + window.location.replace(`${window.location.origin}`); + }); + let download = () => { + const fileReceiver = new FileReceiver(); - let li = document.createElement('li'); - let name = document.createElement('p'); - li.appendChild(name); - let progress = document.createElement('p'); - li.appendChild(progress); + let li = document.createElement('li'); + let name = document.createElement('p'); + let progress = document.createElement('p'); + let btn = $('#download-btn'); - document.getElementById('downloaded_files').appendChild(li); + // li.appendChild(name); + // li.appendChild(progress); - fileReceiver.on('progress', percentComplete => { - progress.innerText = `Progress: ${percentComplete}%`; + //document.getElementById('downloaded_files').appendChild(li); - if (percentComplete === 100) { - fileReceiver.removeAllListeners('progress'); + fileReceiver.on('progress', percentComplete => { + progress.innerText = `Progress: ${percentComplete}%`; - let finished = document.createElement('p'); - finished.innerText = 'Your download has finished.'; - li.appendChild(finished); + if (percentComplete === 100) { + fileReceiver.removeAllListeners('progress'); + btn.text('Download complete!'); + btn.attr('disabled', 'true'); + // let finished = document.createElement('p'); + // finished.innerText = 'Your download has finished.'; + // li.appendChild(finished); - let close = document.createElement('button'); - close.innerText = 'Ok'; - close.addEventListener('click', () => { - document.getElementById('downloaded_files').removeChild(li); + // let close = document.createElement('button'); + // close.innerText = 'Ok'; + // close.addEventListener('click', () => { + // document.getElementById('downloaded_files').removeChild(li); + // }); + // li.appendChild(close); + } + }); + + fileReceiver + .download() + .catch(err => { + $('.title').text( + 'This link has expired or never existed in the first place.' + ); + $('#download-btn').hide(); + $('#expired-img').show(); + console.log('The file has expired, or has already been deleted.'); + // document.getElementById('downloaded_files').removeChild(li); + return; + }) + .then(([decrypted, fname]) => { + name.innerText = fname; + let dataView = new DataView(decrypted); + let blob = new Blob([dataView]); + let downloadUrl = URL.createObjectURL(blob); + + let a = document.createElement('a'); + a.href = downloadUrl; + a.download = fname; + document.body.appendChild(a); + a.click(); }); - li.appendChild(close); - } - }); + }; - fileReceiver.download() - .catch((err) => { - console.log('The file has expired, or has already been deleted.'); - document.getElementById('downloaded_files').removeChild(li); - return; - }) - .then(([decrypted, fname]) => { - name.innerText = fname; - let dataView = new DataView(decrypted); - let blob = new Blob([dataView]); - let downloadUrl = URL.createObjectURL(blob); - - let a = document.createElement('a'); - a.href = downloadUrl; - a.download = fname; - document.body.appendChild(a); - a.click(); - }); -}; - -window.download = download; + window.download = download; +}); diff --git a/frontend/src/fileReceiver.js b/frontend/src/fileReceiver.js index 25ddde73..c030fb71 100644 --- a/frontend/src/fileReceiver.js +++ b/frontend/src/fileReceiver.js @@ -20,9 +20,10 @@ class FileReceiver extends EventEmitter { }; xhr.onload = function(e) { - if (xhr.status === 404) { - reject(new Error('The file has expired, or has already been deleted.')); + reject( + new Error('The file has expired, or has already been deleted.') + ); return; } @@ -58,8 +59,7 @@ class FileReceiver extends EventEmitter { true, ['encrypt', 'decrypt'] ) - ]) - .then(([fdata, key]) => { + ]).then(([fdata, key]) => { let salt = this.salt; return Promise.all([ window.crypto.subtle.decrypt( diff --git a/frontend/src/upload.js b/frontend/src/upload.js index b27e1a0a..2c478610 100644 --- a/frontend/src/upload.js +++ b/frontend/src/upload.js @@ -1,50 +1,87 @@ const FileSender = require('./fileSender'); -let onChange = event => { - const file = event.target.files[0]; +$(document).ready(function() { + let copyBtn = $('#copy-btn'); + copyBtn.attr('disabled', false); + copyBtn.html('Copy'); + $('#page-one').show(); + $('#file-list').hide(); + $('#upload-progress').hide(); + $('#share-link').hide(); - let fileList = document.getElementById('uploaded-files'); - let row = document.createElement('tr'); - let name = document.createElement('td'); - let link = document.createElement('td'); - let expiry = document.createElement('td'); - - let cellText = document.createTextNode(file.name); - - name.appendChild(cellText); - - let progress = document.createElement('p'); - - row.appendChild(name); - row.appendChild(link); - row.appendChild(expiry); - fileList.appendChild(row); - - const fileSender = new FileSender(file); - fileSender.on('progress', percentComplete => { - progress.innerText = `Progress: ${percentComplete}%`; + copyBtn.click(() => { + console.log('copied'); + var aux = document.createElement('input'); + aux.setAttribute('value', $('#link').attr('value')); + document.body.appendChild(aux); + aux.select(); + document.execCommand('copy'); + document.body.removeChild(aux); + copyBtn.attr('disabled', true); + copyBtn.html('Copied!'); }); - fileSender.upload().then(info => { - const url = `${window.location - .origin}/download/${info.fileId}/#${info.secretKey}`; - link.innerHTML = url; - localStorage.setItem(info.fileId, info.deleteToken); - let del = document.createElement('td'); - let btn = document.createElement('button'); - btn.innerHTML = 'x'; - btn.classList.add('delete-btn'); - btn.addEventListener('click', () => { - FileSender.delete( - info.fileId, - localStorage.getItem(info.fileId) - ).then(() => { - fileList.removeChild(row); - localStorage.removeItem(info.fileId); - }); + $('.send-new').click(() => { + $('#page-one').show(); + $('#file-list').show(); + $('#upload-progress').hide(); + $('#share-link').hide(); + copyBtn.attr('disabled', false); + copyBtn.html('Copy'); + }); + + let onChange = event => { + const file = event.target.files[0]; + + let fileList = $('#uploaded-files'); + let row = document.createElement('tr'); + let name = document.createElement('td'); + let link = document.createElement('td'); + let expiry = document.createElement('td'); + + let cellText = document.createTextNode(file.name); + + name.appendChild(cellText); + + let progress = document.createElement('p'); + + row.appendChild(name); + row.appendChild(link); + row.appendChild(expiry); + fileList.append(row); + + const fileSender = new FileSender(file); + fileSender.on('progress', percentComplete => { + $('#page-one').hide(); + $('#file-list').hide(); + $('#upload-progress').show(); + $('#upload-filename').innerHTML += file.name; + progress.innerText = `Progress: ${percentComplete}%`; }); - del.appendChild(btn); - row.appendChild(del); - }); -}; + fileSender.upload().then(info => { + const url = `${window.location + .origin}/download/${info.fileId}/#${info.secretKey}`; + $('#link').attr('value', url); + link.innerHTML = url; + localStorage.setItem(info.fileId, info.deleteToken); + let del = document.createElement('td'); + let btn = document.createElement('button'); + btn.innerHTML = 'x'; + btn.classList.add('delete-btn'); + btn.addEventListener('click', e => { + FileSender.delete( + info.fileId, + localStorage.getItem(info.fileId) + ).then(() => { + e.target.parentNode.parentNode.remove(); + localStorage.removeItem(info.fileId); + }); + }); + del.appendChild(btn); + row.appendChild(del); + $('#upload-progress').hide(); + $('#share-link').show(); + }); + }; -window.onChange = onChange; + window.onChange = onChange; +}); diff --git a/public/download.html b/public/download.html index 3a0e6ac5..513d3c96 100644 --- a/public/download.html +++ b/public/download.html @@ -1,15 +1,31 @@ -Download your file - + Download your file + + + + - +
+
+
+ Your friend is sending you a file: +
+ +
+ Send your own files +
+
+
- + - + diff --git a/public/index.html b/public/index.html index ace23482..128d3a95 100644 --- a/public/index.html +++ b/public/index.html @@ -2,9 +2,10 @@ Firefox Fileshare - + + - + @@ -14,7 +15,7 @@ Share your files quickly, privately and securely.
-
+
DRAG & DROP
@@ -24,7 +25,7 @@
- +
@@ -37,12 +38,47 @@ File Copy URL - Expires in - Delete + Expires in + Delete +
+
+
+ Uploading +
+
+
+
+ +
+
+
+ + + + diff --git a/public/main.css b/public/main.css index afffea01..2329feb7 100644 --- a/public/main.css +++ b/public/main.css @@ -1,32 +1,35 @@ /*** index.html ***/ - -/** page-one **/ -body { +html { background: url('resources/background.png'); font-family: 'Fira Sans'; font-weight: 300; font-style: normal; - background-size: cover; + background-size: contain; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + align-content: center; + flex-direction: column; +} +input, select, textarea, button { + font-family:inherit; } +/** page-one **/ .main-window { border: 1px solid; width: 606px; - height: 447px; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; + min-height: 447px; background-color: white; border-radius: 5px; } .title { font-size: 14px; - width: 50%; - margin: 50px auto ; + width: 80%; + margin: 50px auto; + text-align: center; } .upload-window { @@ -44,7 +47,7 @@ body { #browse { float: right; - color: blue; + color: #2D7EFF; } #browse-text { @@ -96,12 +99,6 @@ td { width: 472px; margin: 10px auto ; table-layout: fixed; - overflow-y: scroll; -} - -#file-list { - overflow-y: scroll; - height: 90px; } .delete-btn { @@ -110,3 +107,93 @@ td { background: none; cursor: pointer; } + +/** upload-progress **/ + + +/** share-link **/ +.share-window { + width: 50%; + margin: 0 auto; + width: 470px; + height: 250px; + display: flex; + justify-content: center; + align-items: center; +} + +#share-window-r { + width: 50%; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +#share-window-r>div { + font-size: 12px; + padding-bottom: 10px; +} + +#copy { + display: flex; + flex-wrap: nowrap; +} + +#link { + width: 216px; + height: 41px; + border: 1px solid #979797; +} + +#copy-btn { + width: 60px; + height: 45px; + background: #337FEB; + border: 1px solid #979797; + color: white; + cursor: pointer; +} + +#copy-btn:disabled { + background: #47B04B; + cursor: auto; +} + +.send-new { + font-size: 14px; + margin: auto; + width: 80%; + text-align: center; + color: #2D7EFF; + cursor: pointer; +} + +/** download.html **/ +#download-btn { + font-size: 18px; + color: white; + width: 214px; + height: 87px; + margin: 50px auto; + text-align: center; + background: #337FEB; + border: 1px solid #3EA050; + border-radius: 6px; + font-weight: 300; + cursor: pointer; +} + +#download-btn:disabled { + background: #47B04B; + cursor: auto; +} + +#download { + text-align: center; +} + +#expired-img { + display: none; +} diff --git a/public/resources/link_expired.png b/public/resources/link_expired.png new file mode 100644 index 00000000..b4872db6 Binary files /dev/null and b/public/resources/link_expired.png differ diff --git a/public/resources/share.png b/public/resources/share.png new file mode 100644 index 00000000..0628d7ce Binary files /dev/null and b/public/resources/share.png differ diff --git a/server/portal_server.js b/server/portal_server.js index df726ae4..3d518ec4 100644 --- a/server/portal_server.js +++ b/server/portal_server.js @@ -1,115 +1,112 @@ -const express = require("express") -const busboy = require("connect-busboy"); -const path = require("path"); -const fs = require("fs-extra"); -const bodyParser = require("body-parser"); -const crypto = require("crypto"); +const express = require('express'); +const busboy = require('connect-busboy'); +const path = require('path'); +const fs = require('fs-extra'); +const bodyParser = require('body-parser'); +const crypto = require('crypto'); -const app = express() -const redis = require("redis"), - client = redis.createClient(); +const app = express(); +const redis = require('redis'), + client = redis.createClient(); -client.on("error", (err) => { +client.on('error', err => { console.log(err); -}) +}); app.use(busboy()); app.use(bodyParser.json()); -app.use(express.static(path.join(__dirname, "../public"))); +app.use(express.static(path.join(__dirname, '../public'))); -app.get("/download/:id", (req, res) => { - res.sendFile(path.join(__dirname + "/../public/download.html")); +app.get('/download/:id', (req, res) => { + res.sendFile(path.join(__dirname + '/../public/download.html')); }); -app.get("/assets/download/:id", (req, res) => { - +app.get('/assets/download/:id', (req, res) => { let id = req.params.id; - if (!validateID(id)){ + if (!validateID(id)) { res.send(404); return; } - - client.hget(id, "filename", (err, reply) => { // maybe some expiration logic too + client.hget(id, 'filename', (err, reply) => { + // maybe some expiration logic too if (!reply) { res.sendStatus(404); } else { - res.setHeader("Content-Disposition", "attachment; filename=" + reply); - res.setHeader("Content-Type", "application/octet-stream"); - - res.download(__dirname + "/../static/" + id, reply, (err) => { + res.setHeader('Content-Disposition', 'attachment; filename=' + reply); + res.setHeader('Content-Type', 'application/octet-stream'); + + res.download(__dirname + '/../static/' + id, reply, err => { if (!err) { client.del(id); - fs.unlinkSync(__dirname + "/../static/" + id); + fs.unlinkSync(__dirname + '/../static/' + id); } }); } - }) - + }); }); -app.post("/delete/:id", (req, res) => { +app.post('/delete/:id', (req, res) => { let id = req.params.id; - if (!validateID(id)){ + if (!validateID(id)) { res.send(404); return; } - + let delete_token = req.body.delete_token; - - if (!delete_token){ + + if (!delete_token) { res.sendStatus(404); } - client.hget(id, "delete", (err, reply) => { + client.hget(id, 'delete', (err, reply) => { if (!reply) { res.sendStatus(404); } else { client.del(id); - fs.unlinkSync(__dirname + "/../static/" + id); + fs.unlinkSync(__dirname + '/../static/' + id); res.sendStatus(200); } - }) + }); }); -app.post("/upload/:id", (req, res, next) => { - - if (!validateID(req.params.id)){ - res.send(404); - return; - } +app.post('/upload/:id', (req, res, next) => { + if (!validateID(req.params.id)) { + res.send(404); + return; + } - let fstream; - req.pipe(req.busboy); - req.busboy.on("file", (fieldname, file, filename) => { - console.log("Uploading: " + filename); + let fstream; + req.pipe(req.busboy); + req.busboy.on('file', (fieldname, file, filename) => { + console.log('Uploading: ' + filename); - //Path where image will be uploaded - fstream = fs.createWriteStream(__dirname + "/../static/" + req.params.id); - file.pipe(fstream); - fstream.on("close", () => { - let id = req.params.id; - let uuid = crypto.randomBytes(10).toString('hex'); + //Path where image will be uploaded + fstream = fs.createWriteStream(__dirname + '/../static/' + req.params.id); + file.pipe(fstream); + fstream.on('close', () => { + let id = req.params.id; + let uuid = crypto.randomBytes(10).toString('hex'); - client.hmset([id, "filename", filename, "delete", uuid]); + client.hmset([id, 'filename', filename, 'delete', uuid]); - // delete the file off the server in 24 hours - // setTimeout(() => { - // fs.unlinkSync(__dirname + "/static/" + id); - // }, 86400000); + // delete the file off the server in 24 hours + // setTimeout(() => { + // fs.unlinkSync(__dirname + "/static/" + id); + // }, 86400000); - client.expire(id, 86400000); - console.log("Upload Finished of " + filename); - res.send(uuid); - }); + client.expire(id, 86400000); + console.log('Upload Finished of ' + filename); + res.send(uuid); }); + }); }); app.listen(3000, () => { - console.log("Portal app listening on port 3000!") -}) + console.log('Portal app listening on port 3000!'); +}); -let validateID = (route_id) => { +let validateID = route_id => { return route_id.match(/^[0-9a-fA-F]{32}$/) !== null; -} \ No newline at end of file +};