From 0aa30950a502a146cd0f779b24bd9337686e831e Mon Sep 17 00:00:00 2001 From: mario Date: Tue, 23 Jan 2018 18:31:54 +0200 Subject: [PATCH 01/10] adds a loading spinner for when file is being uploaded rel #86 --- public/style.css | 63 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/public/style.css b/public/style.css index 3b3119f..63c0d58 100644 --- a/public/style.css +++ b/public/style.css @@ -1,27 +1,56 @@ .bg-turquoise { - background-color: #409A93; + background-color: #409a93; } .bg-main { - background-color: #F2F2F2; + background-color: #f2f2f2; } - .light-text { - color: #95989A; - } +.light-text { + color: #95989a; +} + +.lato { + font-family: 'lato', sans-serif; +} - .lato { - font-family: 'lato', sans-serif; - } +.dark-text { + color: #5c5c5c; +} - .dark-text { - color: #5C5C5C; - } +.b--turquoise { + border-color: #409a93; +} - .b--turquoise { - border-color: #409A93; - } +.text-turquoise { + color: #409a93; +} - .text-turquoise { - color: #409A93; - } +#spinner { + border: 0.2rem solid #f3f3f3; + border-radius: 75%; + border-top: 0.2rem solid #3498db; + width: 2rem; + height: 2rem; + -webkit-animation: spin 2s linear infinite; /* Safari */ + animation: spin 2s linear infinite; +} + +/* Safari */ +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} From c7bc7faaeaa50b90c87891dbbdd194156b7c049a Mon Sep 17 00:00:00 2001 From: mario Date: Tue, 23 Jan 2018 18:32:33 +0200 Subject: [PATCH 02/10] setup image hosting with AWS S3 for the event form rel #86 --- package-lock.json | 97 ++++++++++++++++++++++++++++++++++- package.json | 1 + public/event-image-upload.js | 45 ++++++++++++++++ public/validate-event-form.js | 5 +- src/controllers/router.js | 30 +++++++++-- src/controllers/sign_s3.js | 32 ++++++++++++ src/middleware/langError.js | 4 +- src/text.js | 19 ++++--- views/event-form.hbs | 27 ++++++++-- 9 files changed, 239 insertions(+), 21 deletions(-) create mode 100644 public/event-image-upload.js create mode 100644 src/controllers/sign_s3.js diff --git a/package-lock.json b/package-lock.json index 5e7ecc5..43f02ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -164,6 +164,22 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "aws-sdk": { + "version": "2.186.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.186.0.tgz", + "integrity": "sha1-ZOzOpb8ESYEDI8MT2ctBwqSihQ0=", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.1.0", + "xml2js": "0.4.17", + "xmlbuilder": "4.2.1" + } + }, "aws-sign2": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", @@ -179,6 +195,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" + }, "base64url": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", @@ -327,6 +348,16 @@ "repeat-element": "1.1.2" } }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -802,6 +833,11 @@ } } }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -2099,6 +2135,11 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -2302,8 +2343,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -2325,6 +2365,11 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -2433,6 +2478,11 @@ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "optional": true }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, "lodash._baseassign": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", @@ -2992,6 +3042,11 @@ "strict-uri-encode": "1.1.0" } }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -3215,6 +3270,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -3740,6 +3800,22 @@ "xdg-basedir": "3.0.0" } }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -3838,6 +3914,23 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "xml2js": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", + "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "requires": { + "sax": "1.2.1", + "xmlbuilder": "4.2.1" + } + }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "requires": { + "lodash": "4.17.4" + } + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index c49daa1..39692b8 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ }, "homepage": "https://github.com/foundersandcoders/OTP-Data-Entry#readme", "dependencies": { + "aws-sdk": "^2.186.0", "body-parser": "^1.18.0", "compression": "^1.7.1", "cookie-parser": "^1.4.3", diff --git a/public/event-image-upload.js b/public/event-image-upload.js new file mode 100644 index 0000000..466cac0 --- /dev/null +++ b/public/event-image-upload.js @@ -0,0 +1,45 @@ +(function() { + var fileInput = document.getElementById('eventFileInput'); + var fileErrorMessage = document.getElementById('fileErrorMessage'); + var hiddenFileInput = document.getElementById('hiddenFileInput'); + var spinner = document.getElementById('spinner'); + var imagePreview = document.getElementById('imagePreview'); + + fileInput.onchange = function() { + var fileInputFiles = fileInput.files; + var file = fileInputFiles[0]; + imagePreview.src && (imagePreview.src = ''); + getSignedRequest(file); + spinner.classList.toggle('dn'); + imagePreview.classList.add('dn'); + }; + + function getSignedRequest(file) { + file && + axios + .get('/sign-s3?file-name=' + file.name + '&file-type=' + file.type) + .then(function(res) { + uploadFile(file, res.data.signedRequest, res.data.url); + }) + .catch(function(err) { + console.log('getSignedRequest', err); + fileErrorMessage.textContent = 'Could not upload file'; + }); + } + + function uploadFile(file, signedRequest, url) { + axios + .put(signedRequest, file) + .then(function(res) { + console.log(url); + spinner.classList.toggle('dn'); + imagePreview.classList.remove('dn'); + imagePreview.src = url; + hiddenFileInput.value = url; + }) + .catch(function(err) { + console.log(err); + fileErrorMessage.textContent = 'Could not upload file'; + }); + } +})(); diff --git a/public/validate-event-form.js b/public/validate-event-form.js index 061525a..9611d3e 100644 --- a/public/validate-event-form.js +++ b/public/validate-event-form.js @@ -21,10 +21,13 @@ description_en: elements.description_en.value, description_ar: elements.description_ar.value, eventPlace: elements.eventPlace.value, - imageUrl: elements.imageUrl.value, cost: elements.cost.value, }; + data.imageUrl = elements.s3Url + ? elements.s3Url.value + : elements.imageUrl.value; + // set time in the data object data.startTime = new Date( new Date( diff --git a/src/controllers/router.js b/src/controllers/router.js index e229be1..269cb36 100644 --- a/src/controllers/router.js +++ b/src/controllers/router.js @@ -6,7 +6,12 @@ const oauthCode = require('./OAuth/code.js'); const oauthToken = require('./OAuth/token.js'); const checkLoggedIn = require('../middleware/checkLoggedIn.js'); +router.get('*', (req, res, next) => { + console.log(req.url); + next(); +}); router.get('/', require('./home.js')); +router.get('/sign-s3', require('./sign_s3.js')); router.get('/:lang/content', require('../content.js')); // places @@ -14,17 +19,34 @@ router.get('/:lang/places', placeController.getAll); router.get('/:lang/place/:id', placeController.getSpecific); router.get('/:lang/add-place', placeController.renderForm); router.post('/:lang/add-place', placeController.addPlace); -router.get('/:lang/edit-place/:id', checkLoggedIn, placeController.renderEditForm); +router.get( + '/:lang/edit-place/:id', + checkLoggedIn, + placeController.renderEditForm, +); router.post('/:lang/edit-place/:id', checkLoggedIn, placeController.addPlace); -router.get('/:lang/delete-place/:id', checkLoggedIn, placeController.deletePlace); +router.get( + '/:lang/delete-place/:id', + checkLoggedIn, + placeController.deletePlace, +); // events router.get('/:lang/events', eventsController.getAll); router.get('/:lang/event/:id', eventsController.getSpecific); -router.get('/:lang/delete-event/:id', checkLoggedIn, eventsController.deleteEvent); +router.get( + '/:lang/delete-event/:id', + checkLoggedIn, + eventsController.deleteEvent, +); router.get('/:lang/add-event', placesList, eventsController.renderForm); router.post('/:lang/add-event', eventsController.addEvent); -router.get('/:lang/edit-event/:id', checkLoggedIn, placesList, eventsController.renderEditForm); +router.get( + '/:lang/edit-event/:id', + checkLoggedIn, + placesList, + eventsController.renderEditForm, +); router.post('/:lang/edit-event/:id', checkLoggedIn, eventsController.addEvent); router.get('/:lang/login', oauthCode); diff --git a/src/controllers/sign_s3.js b/src/controllers/sign_s3.js new file mode 100644 index 0000000..abbce25 --- /dev/null +++ b/src/controllers/sign_s3.js @@ -0,0 +1,32 @@ +const aws = require('aws-sdk'); +require('env2')('./config.env'); +const S3_BUCKET = process.env.S3_BUCKET; + +// set the region of the S3 bucket +aws.config.region = 'eu-west-2'; + +module.exports = (req, res) => { + const s3 = new aws.S3(); + const fileName = req.query['file-name']; + const fileType = req.query['file-type']; + const s3Params = { + Bucket: S3_BUCKET, + Key: fileName, + Expires: 60, + ContentType: fileType, + ACL: 'public-read', + }; + + s3.getSignedUrl('putObject', s3Params, (err, data) => { + if (err) { + res.status(500).end(); + } + + res.status(200).end( + JSON.stringify({ + signedRequest: data, + url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`, + }), + ); + }); +}; diff --git a/src/middleware/langError.js b/src/middleware/langError.js index d726a16..7a26192 100644 --- a/src/middleware/langError.js +++ b/src/middleware/langError.js @@ -3,12 +3,12 @@ module.exports = (req, res, next) => { const lang = path.split('/')[1]; if (lang !== 'en' && lang !== 'ar') { - if ((path === '/') || (path === '/oauth/token')) { + if (path === '/' || path === '/oauth/token' || path === '/sign-s3') { return next(); } else { return res.render('error', { statusCode: 404, - errorMessage: 'Page not Found' + errorMessage: 'Page not Found', }); } } diff --git a/src/text.js b/src/text.js index 3fb6460..0b15287 100644 --- a/src/text.js +++ b/src/text.js @@ -19,7 +19,8 @@ module.exports = { noLocation: 'No Location available', accessibilityOptions: 'Accessibility Options', categories: 'Categories', - formInfo: '*indicates a required field in at least one of English or Arabic', + formInfo: + '*indicates a required field in at least one of English or Arabic', required: 'Required field', selectPlace: 'Select place', // places @@ -42,7 +43,8 @@ module.exports = { eventStart: 'Start time', eventEnd: 'End time', formOwnerId: 'Owner ID', - formImageUrl: 'Image URL', + formImage: 'Image', + formImageUrl: 'Image-URL', formSubmit: 'Submit', // Accessibility options audioRecordings: 'Audio recordings', @@ -77,7 +79,7 @@ module.exports = { miscellaneous: 'Miscellaneous', party: 'Party', sportEvent: 'Sport', - wedding: 'Wedding' + wedding: 'Wedding', }, ar: { pageTitle: 'إدخال البيانات', @@ -99,7 +101,8 @@ module.exports = { noLocation: 'لا يوجد مكان', accessibilityOptions: 'امكانيات سهولة الوصول', categories: 'فئات', - formInfo: '* مطلوب التعبئة على الاقل في واحدة من اللغتين العربية او الانجليزية', + formInfo: + '* مطلوب التعبئة على الاقل في واحدة من اللغتين العربية او الانجليزية', required: ' مطلوب التعبئة', selectPlace: 'اختر مكان الحدث', // places @@ -130,7 +133,8 @@ module.exports = { eventStart: 'وقت البدء', eventEnd: 'وقت النهاية', formOwnerId: 'هوية صاحب المكان', - formImageUrl: 'صورة', + formImage: 'صورة', + formImageUrl: 'رابط الصورة', formSubmit: 'إعرض', // Accessibility options audioRecordings: 'خدمات صوتية', @@ -165,7 +169,6 @@ module.exports = { miscellaneous: 'متنوع', party: 'حفل', sportEvent: 'رياضة', - wedding: 'عرس' - - } + wedding: 'عرس', + }, }; diff --git a/views/event-form.hbs b/views/event-form.hbs index 554a789..36d954e 100644 --- a/views/event-form.hbs +++ b/views/event-form.hbs @@ -139,7 +139,7 @@ - {{localText.eventsCost}} + {{localText.eventsCost}} {{#if event.cost}} @@ -149,15 +149,33 @@ - {{localText.formImageUrl}} + {{localText.formImageUrl}} {{#if event.imageUrl}} - + {{else}} - + {{/if}} + + + + OR + + + + {{localText.formImage}} + + +
+ +
+
+ Your uploaded image + + + {{localText.accessibilityOptions}} @@ -263,3 +281,4 @@ + From 04381a9d24ed407c9635855205dfa6eea6ae1a2e Mon Sep 17 00:00:00 2001 From: mario Date: Tue, 23 Jan 2018 19:46:13 +0200 Subject: [PATCH 03/10] add the upload file option to the place form and make it functional rel #86 --- public/event-image-upload.js | 3 --- public/place-image-upload.js | 42 +++++++++++++++++++++++++++++++++++ public/validate-place-form.js | 5 ++++- views/place-form.hbs | 26 ++++++++++++++++++---- views/place.hbs | 3 --- 5 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 public/place-image-upload.js diff --git a/public/event-image-upload.js b/public/event-image-upload.js index 466cac0..f978e59 100644 --- a/public/event-image-upload.js +++ b/public/event-image-upload.js @@ -22,7 +22,6 @@ uploadFile(file, res.data.signedRequest, res.data.url); }) .catch(function(err) { - console.log('getSignedRequest', err); fileErrorMessage.textContent = 'Could not upload file'; }); } @@ -31,14 +30,12 @@ axios .put(signedRequest, file) .then(function(res) { - console.log(url); spinner.classList.toggle('dn'); imagePreview.classList.remove('dn'); imagePreview.src = url; hiddenFileInput.value = url; }) .catch(function(err) { - console.log(err); fileErrorMessage.textContent = 'Could not upload file'; }); } diff --git a/public/place-image-upload.js b/public/place-image-upload.js new file mode 100644 index 0000000..87be092 --- /dev/null +++ b/public/place-image-upload.js @@ -0,0 +1,42 @@ +(function() { + var fileInput = document.getElementById('placeImageInput'); + var hiddenFileInput = document.getElementById('hiddenFileInput'); + var fileErrorMessage = document.getElementById('fileErrorMessage'); + var spinner = document.getElementById('spinner'); + var imagePreview = document.getElementById('imagePreview'); + + fileInput.onchange = function() { + var fileInputFiles = fileInput.files; + var file = fileInputFiles[0]; + imagePreview.src && (imagePreview.src = ''); + getSignedRequest(file); + spinner.classList.toggle('dn'); + imagePreview.classList.add('dn'); + }; + + function getSignedRequest(file) { + file && + axios + .get('/sign-s3?file-name=' + file.name + '&file-type=' + file.type) + .then(function(res) { + uploadFile(file, res.data.signedRequest, res.data.url); + }) + .catch(function(err) { + fileErrorMessage.textContent = 'Could not upload file'; + }); + } + + function uploadFile(file, signedRequest, url) { + axios + .put(signedRequest, file) + .then(function(res) { + spinner.classList.toggle('dn'); + imagePreview.classList.remove('dn'); + imagePreview.src = url; + hiddenFileInput.value = url; + }) + .catch(function(err) { + fileErrorMessage.textContent = 'Could not upload file'; + }); + } +})(); diff --git a/public/validate-place-form.js b/public/validate-place-form.js index e1f0e63..75d000f 100644 --- a/public/validate-place-form.js +++ b/public/validate-place-form.js @@ -26,9 +26,12 @@ website: elements.website.value, phone: elements.phone.value, email: elements.email.value, - imageUrl: elements.imageUrl.value, }; + data.imageUrl = elements.s3Url + ? elements.s3Url.value + : elements.imageUrl.value; + // Check if place name input were filled if (!data.name_en && !data.name_ar) { return notValid('*Please input a name'); diff --git a/views/place-form.hbs b/views/place-form.hbs index b1b1af3..0ba0031 100644 --- a/views/place-form.hbs +++ b/views/place-form.hbs @@ -129,15 +129,33 @@ - {{localText.formImageUrl}} + {{localText.formImageUrl}} {{#if place.imageUrl}} - + {{else}} - + {{/if}} + + + + OR + + + + {{localText.formImage}} + + +
+ +
+
+ Your uploaded image + + + {{localText.accessibilityOptions}} @@ -232,5 +250,5 @@ - + diff --git a/views/place.hbs b/views/place.hbs index 8f26592..a2e506f 100644 --- a/views/place.hbs +++ b/views/place.hbs @@ -64,6 +64,3 @@ - - - From 3be10091e412a443dad48d08f87c8700696ce4b2 Mon Sep 17 00:00:00 2001 From: mario Date: Mon, 29 Jan 2018 15:57:41 +0200 Subject: [PATCH 04/10] prevent the user from submitting the form if a photo is uploading rel #86 --- public/validate-event-form.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/validate-event-form.js b/public/validate-event-form.js index 9611d3e..8fd4547 100644 --- a/public/validate-event-form.js +++ b/public/validate-event-form.js @@ -24,6 +24,13 @@ cost: elements.cost.value, }; + // checks if a file is being uploaded + if ( + document.getElementById('spinner').classList.value.indexOf('dn') === -1 + ) { + return notValid('*Image did not finish upload'); + } + data.imageUrl = elements.s3Url ? elements.s3Url.value : elements.imageUrl.value; From fa34bfb826b9884d3c2bb1b51026a9b0b3de38cb Mon Sep 17 00:00:00 2001 From: mario Date: Mon, 29 Jan 2018 15:58:50 +0200 Subject: [PATCH 05/10] prevent the user from submitting the place form if a photo is uploading rel #86 --- public/validate-place-form.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/validate-place-form.js b/public/validate-place-form.js index 75d000f..f879ad1 100644 --- a/public/validate-place-form.js +++ b/public/validate-place-form.js @@ -28,6 +28,13 @@ email: elements.email.value, }; + // checks if a file is being uploaded + if ( + document.getElementById('spinner').classList.value.indexOf('dn') === -1 + ) { + return notValid('*Image did not finish upload'); + } + data.imageUrl = elements.s3Url ? elements.s3Url.value : elements.imageUrl.value; From 857ac6f83fc98e31413d1b5a0f70bb9eef3f216f Mon Sep 17 00:00:00 2001 From: mario Date: Mon, 29 Jan 2018 16:51:23 +0200 Subject: [PATCH 06/10] use composition for calling functions rel #86 --- public/event-image-upload.js | 40 +++++++++++++++++------------------- public/place-image-upload.js | 40 +++++++++++++++++------------------- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/public/event-image-upload.js b/public/event-image-upload.js index f978e59..4968bfa 100644 --- a/public/event-image-upload.js +++ b/public/event-image-upload.js @@ -9,34 +9,32 @@ var fileInputFiles = fileInput.files; var file = fileInputFiles[0]; imagePreview.src && (imagePreview.src = ''); - getSignedRequest(file); spinner.classList.toggle('dn'); imagePreview.classList.add('dn'); - }; - - function getSignedRequest(file) { - file && - axios - .get('/sign-s3?file-name=' + file.name + '&file-type=' + file.type) - .then(function(res) { - uploadFile(file, res.data.signedRequest, res.data.url); - }) - .catch(function(err) { - fileErrorMessage.textContent = 'Could not upload file'; - }); - } - - function uploadFile(file, signedRequest, url) { - axios - .put(signedRequest, file) + getSignedRequest(file) .then(function(res) { + imagePreview.src = res.data.url; + hiddenFileInput.value = res.data.url; + return uploadFile(res.data.signedRequest, file); + }) + .then(function() { spinner.classList.toggle('dn'); imagePreview.classList.remove('dn'); - imagePreview.src = url; - hiddenFileInput.value = url; }) .catch(function(err) { + imagePreview.src = ''; + hiddenFileInput.value = ''; fileErrorMessage.textContent = 'Could not upload file'; }); - } + + function getSignedRequest(file) { + return axios.get( + '/sign-s3?file-name=' + file.name + '&file-type=' + file.type, + ); + } + + function uploadFile(signedRequest, file) { + return axios.put(signedRequest, file); + } + }; })(); diff --git a/public/place-image-upload.js b/public/place-image-upload.js index 87be092..16412d0 100644 --- a/public/place-image-upload.js +++ b/public/place-image-upload.js @@ -9,34 +9,32 @@ var fileInputFiles = fileInput.files; var file = fileInputFiles[0]; imagePreview.src && (imagePreview.src = ''); - getSignedRequest(file); spinner.classList.toggle('dn'); imagePreview.classList.add('dn'); - }; - - function getSignedRequest(file) { - file && - axios - .get('/sign-s3?file-name=' + file.name + '&file-type=' + file.type) - .then(function(res) { - uploadFile(file, res.data.signedRequest, res.data.url); - }) - .catch(function(err) { - fileErrorMessage.textContent = 'Could not upload file'; - }); - } - - function uploadFile(file, signedRequest, url) { - axios - .put(signedRequest, file) + getSignedRequest(file) .then(function(res) { + imagePreview.src = res.data.url; + hiddenFileInput.value = res.data.url; + return uploadFile(res.data.signedRequest, file); + }) + .then(function() { spinner.classList.toggle('dn'); imagePreview.classList.remove('dn'); - imagePreview.src = url; - hiddenFileInput.value = url; }) .catch(function(err) { + imagePreview.src = ''; + hiddenFileInput.value = ''; fileErrorMessage.textContent = 'Could not upload file'; }); - } + + function getSignedRequest(file) { + return axios.get( + '/sign-s3?file-name=' + file.name + '&file-type=' + file.type, + ); + } + + function uploadFile(signedRequest, file) { + return axios.put(signedRequest, file); + } + }; })(); From 93d3c7aa2dcf17b79f6d7f56aa340e68896b9ce6 Mon Sep 17 00:00:00 2001 From: mario Date: Mon, 29 Jan 2018 17:08:13 +0200 Subject: [PATCH 07/10] setup morgan --- package-lock.json | 33 +++++++++++++++++++++++++++++++++ package.json | 1 + src/app.js | 6 ++++++ src/controllers/router.js | 4 ---- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 43f02ab..c2013bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -205,6 +205,15 @@ "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" }, + "basic-auth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", + "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", @@ -2716,6 +2725,30 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" }, + "morgan": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", + "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", + "dev": true, + "requires": { + "basic-auth": "2.0.0", + "debug": "2.6.9", + "depd": "1.1.1", + "on-finished": "2.3.0", + "on-headers": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/package.json b/package.json index 39692b8..eb82157 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "request": "^2.81.0" }, "devDependencies": { + "morgan": "^1.9.0", "nodemon": "^1.12.0", "pre-commit": "^1.2.2", "snazzy": "^7.0.0", diff --git a/src/app.js b/src/app.js index 2673d5e..8ab0770 100644 --- a/src/app.js +++ b/src/app.js @@ -11,6 +11,7 @@ const checkedDropDown = require('./helpers/check_dropdown_option.js'); const iterate = require('./helpers/iterate.js'); const cookieParser = require('cookie-parser'); const compression = require('compression'); +const morgan = require('morgan'); const app = express(); @@ -20,6 +21,11 @@ app.use(cookieParser()); app.use(express.static('public')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); +app.use( + morgan('dev', { + skip: (req, res) => res.statusCode < 400, + }), +); // Set up local languages app.locals.en = languages.en; diff --git a/src/controllers/router.js b/src/controllers/router.js index 269cb36..707c480 100644 --- a/src/controllers/router.js +++ b/src/controllers/router.js @@ -6,10 +6,6 @@ const oauthCode = require('./OAuth/code.js'); const oauthToken = require('./OAuth/token.js'); const checkLoggedIn = require('../middleware/checkLoggedIn.js'); -router.get('*', (req, res, next) => { - console.log(req.url); - next(); -}); router.get('/', require('./home.js')); router.get('/sign-s3', require('./sign_s3.js')); router.get('/:lang/content', require('../content.js')); From 7b7670a031020b699890a0c3c8fdd55985be8187 Mon Sep 17 00:00:00 2001 From: mario Date: Mon, 29 Jan 2018 17:14:01 +0200 Subject: [PATCH 08/10] add uuid to the image domain's name rel #86 --- package-lock.json | 15 +++++++++++---- package.json | 3 ++- src/controllers/sign_s3.js | 3 ++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2013bc..bfdda77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -178,6 +178,13 @@ "uuid": "3.1.0", "xml2js": "0.4.17", "xmlbuilder": "4.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + } } }, "aws-sign2": { @@ -3261,7 +3268,7 @@ "stringstream": "0.0.5", "tough-cookie": "2.3.2", "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "uuid": "3.2.1" }, "dependencies": { "qs": { @@ -3870,9 +3877,9 @@ "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" }, "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" }, "vary": { "version": "1.1.1", diff --git a/package.json b/package.json index eb82157..e76bf2c 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "express-handlebars": "^3.0.0", "jsonwebtoken": "^8.1.0", "query-string": "^5.0.0", - "request": "^2.81.0" + "request": "^2.81.0", + "uuid": "^3.2.1" }, "devDependencies": { "morgan": "^1.9.0", diff --git a/src/controllers/sign_s3.js b/src/controllers/sign_s3.js index abbce25..ae92c80 100644 --- a/src/controllers/sign_s3.js +++ b/src/controllers/sign_s3.js @@ -1,5 +1,6 @@ const aws = require('aws-sdk'); require('env2')('./config.env'); +const uuidv4 = require('uuid/v4'); const S3_BUCKET = process.env.S3_BUCKET; // set the region of the S3 bucket @@ -11,7 +12,7 @@ module.exports = (req, res) => { const fileType = req.query['file-type']; const s3Params = { Bucket: S3_BUCKET, - Key: fileName, + Key: `${fileName}:${uuidv4()}`, Expires: 60, ContentType: fileType, ACL: 'public-read', From 33edbdf1914c7f0393d996273977094082c392e0 Mon Sep 17 00:00:00 2001 From: mario Date: Wed, 31 Jan 2018 15:07:59 +0200 Subject: [PATCH 09/10] have all upload image code in 1 file rel #86 --- ...{event-image-upload.js => image-upload.js} | 8 ++-- public/place-image-upload.js | 40 ------------------- views/event-form.hbs | 2 +- views/place-form.hbs | 2 +- 4 files changed, 7 insertions(+), 45 deletions(-) rename public/{event-image-upload.js => image-upload.js} (88%) delete mode 100644 public/place-image-upload.js diff --git a/public/event-image-upload.js b/public/image-upload.js similarity index 88% rename from public/event-image-upload.js rename to public/image-upload.js index 4968bfa..7bb99ad 100644 --- a/public/event-image-upload.js +++ b/public/image-upload.js @@ -1,14 +1,16 @@ (function() { - var fileInput = document.getElementById('eventFileInput'); - var fileErrorMessage = document.getElementById('fileErrorMessage'); + var fileInput = + document.getElementById('eventFileInput') || + document.getElementById('placeImageInput'); var hiddenFileInput = document.getElementById('hiddenFileInput'); + var fileErrorMessage = document.getElementById('fileErrorMessage'); var spinner = document.getElementById('spinner'); var imagePreview = document.getElementById('imagePreview'); fileInput.onchange = function() { var fileInputFiles = fileInput.files; var file = fileInputFiles[0]; - imagePreview.src && (imagePreview.src = ''); + imagePreview.src = imagePreview.src && ''; spinner.classList.toggle('dn'); imagePreview.classList.add('dn'); getSignedRequest(file) diff --git a/public/place-image-upload.js b/public/place-image-upload.js deleted file mode 100644 index 16412d0..0000000 --- a/public/place-image-upload.js +++ /dev/null @@ -1,40 +0,0 @@ -(function() { - var fileInput = document.getElementById('placeImageInput'); - var hiddenFileInput = document.getElementById('hiddenFileInput'); - var fileErrorMessage = document.getElementById('fileErrorMessage'); - var spinner = document.getElementById('spinner'); - var imagePreview = document.getElementById('imagePreview'); - - fileInput.onchange = function() { - var fileInputFiles = fileInput.files; - var file = fileInputFiles[0]; - imagePreview.src && (imagePreview.src = ''); - spinner.classList.toggle('dn'); - imagePreview.classList.add('dn'); - getSignedRequest(file) - .then(function(res) { - imagePreview.src = res.data.url; - hiddenFileInput.value = res.data.url; - return uploadFile(res.data.signedRequest, file); - }) - .then(function() { - spinner.classList.toggle('dn'); - imagePreview.classList.remove('dn'); - }) - .catch(function(err) { - imagePreview.src = ''; - hiddenFileInput.value = ''; - fileErrorMessage.textContent = 'Could not upload file'; - }); - - function getSignedRequest(file) { - return axios.get( - '/sign-s3?file-name=' + file.name + '&file-type=' + file.type, - ); - } - - function uploadFile(signedRequest, file) { - return axios.put(signedRequest, file); - } - }; -})(); diff --git a/views/event-form.hbs b/views/event-form.hbs index 36d954e..f918ee4 100644 --- a/views/event-form.hbs +++ b/views/event-form.hbs @@ -281,4 +281,4 @@ - + diff --git a/views/place-form.hbs b/views/place-form.hbs index 0ba0031..c8eb918 100644 --- a/views/place-form.hbs +++ b/views/place-form.hbs @@ -250,5 +250,5 @@ - + From 4790bd513b9b891b446aeb27b4996d174d0afe66 Mon Sep 17 00:00:00 2001 From: mario Date: Wed, 7 Feb 2018 15:18:04 +0200 Subject: [PATCH 10/10] move the morgan moduleto dep instead of dev dep rel #102 --- package-lock.json | 3 --- package.json | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index bfdda77..d2e6f0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -216,7 +216,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", - "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -2736,7 +2735,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", - "dev": true, "requires": { "basic-auth": "2.0.0", "debug": "2.6.9", @@ -2749,7 +2747,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } diff --git a/package.json b/package.json index e76bf2c..9e62294 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,12 @@ "express": "^4.15.3", "express-handlebars": "^3.0.0", "jsonwebtoken": "^8.1.0", + "morgan": "^1.9.0", "query-string": "^5.0.0", "request": "^2.81.0", "uuid": "^3.2.1" }, "devDependencies": { - "morgan": "^1.9.0", "nodemon": "^1.12.0", "pre-commit": "^1.2.2", "snazzy": "^7.0.0",