diff --git a/helpers/libraries/publishHelpers.js b/helpers/libraries/publishHelpers.js index c2e10091..0977d01a 100644 --- a/helpers/libraries/publishHelpers.js +++ b/helpers/libraries/publishHelpers.js @@ -3,6 +3,53 @@ const config = require('config'); const fs = require('fs'); module.exports = { + validateFile (file, name, license, nsfw) { + if (!file) { + throw new Error('No file was submitted or the key used was incorrect. Files posted through this route must use a key of "speech" or null'); + } + // check file type and size + switch (file.type) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + if (file.size > 5000000) { + throw new Error('Your image exceeds the 5 megabyte limit.'); + } + break; + case 'video/mp4': + if (file.size > 50000000) { + throw new Error('Your video exceeds the 50 megabyte limit.'); + } + break; + default: + throw new Error('The ' + file.Type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.'); + } + + // validate name + const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); + if (invalidCharacters) { + throw new Error('The name you provided is not allowed. Please use A-Z, a-z, 0-9, and "-" only.'); + } + // validate license + if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { + throw new Error('Only posts with a license of "Public Domain" or "Creative Commons" are eligible for publishing through spee.ch'); + } + switch (nsfw) { + case true: + case false: + case 'true': + case 'false': + case 'on': + case 'off': + case 0: + case '0': + case 1: + case '1': + break; + default: + throw new Error('NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"'); + } + }, createPublishParams (name, filePath, license, nsfw) { logger.debug(`Creating Publish Parameters for "${name}"`); // const payAddress = config.get('WalletConfig.LbryPayAddress'); diff --git a/public/assets/css/componentStyle.css b/public/assets/css/componentStyle.css index 0937c2ce..a837dabd 100644 --- a/public/assets/css/componentStyle.css +++ b/public/assets/css/componentStyle.css @@ -122,11 +122,22 @@ button.copy-button { height: 6em; } -#image-preview { - display: none; +#asset-preview-holder { + width: 100%; margin-bottom: 1em; } +.snapshot-generator { + display: block; + height: 1px; + left: 0; + object-fit: contain; + position: fixed; + top: 0; + width: 1px; + z-index: -1; +} + /* meme */ canvas { background-color: white; diff --git a/public/assets/js/claimPublish.js b/public/assets/js/index.js similarity index 57% rename from public/assets/js/claimPublish.js rename to public/assets/js/index.js index ef657746..81b1d9ee 100644 --- a/public/assets/js/claimPublish.js +++ b/public/assets/js/index.js @@ -4,65 +4,27 @@ var uploader = new SocketIOFileUpload(socket); var stagedFiles = null; /* configure the submit button */ -document.getElementById('publish-submit').addEventListener('click', function(event){ +function publishSelectedImage(event) { event.preventDefault(); + // validate inputs var name = document.getElementById('publish-name').value; - var invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); - // validate 'name' field - if (invalidCharacters) { - alert(invalidCharacters + ' is not allowed. A-Z, a-z, 0-9, and "-" only.'); - return; - } else if (name.length < 1) { - alert("You must enter a name for your claim"); + try { + validateSubmission(stagedFiles, name); + } catch (error) { + alert(error.message); return; } - // make sure only 1 file was selected - if (!stagedFiles) { - alert("Please select a file"); - return; - } else if (stagedFiles.length > 1) { - alert("Only one file is allowed at a time"); - return; - } - // make sure the content type is acceptable - switch (stagedFiles[0].type) { - case "image/png": - case "image/jpeg": - case "image/gif": - case "video/mp4": - break; - default: - alert("Only .png, .jpeg, .gif, and .mp4 files are currently supported"); - return; - } - // make sure the name is available - var xhttp; - xhttp = new XMLHttpRequest(); - xhttp.open('GET', '/api/isClaimAvailable/' + name, true); - xhttp.responseType = 'json'; - xhttp.onreadystatechange = function() { - if (this.readyState == 4 ) { - if ( this.status == 200) { - if (this.response == true) { - uploader.submitFiles(stagedFiles); - } else { - alert("That name has already been claimed by spee.ch. Please choose a different name."); - } - } else { - console.log("request to check claim name failed with status:", this.status); - }; - } - }; - xhttp.send(); -}) + // make sure the name is available then start the upload + validateClaimName(name) + .then(function() { + uploader.submitFiles(stagedFiles); //note: must pass the file as part of an array. + }) + .catch(function(error) { + alert(error); + }) +}; /* socketio-file-upload listeners */ -uploader.maxFileSize = 5000000; -uploader.addEventListener("error", function(data){ - if (data.code === 1) { - alert("Sorry, uploading is limitted to 5 megabytes."); - } -}); uploader.addEventListener('start', function(event){ var name = document.getElementById('publish-name').value; var license = document.getElementById('publish-license').value; diff --git a/public/assets/js/memeDraw.js b/public/assets/js/memeFodder-draw.js similarity index 100% rename from public/assets/js/memeDraw.js rename to public/assets/js/memeFodder-draw.js diff --git a/public/assets/js/memePublish.js b/public/assets/js/memeFodder-publish.js similarity index 100% rename from public/assets/js/memePublish.js rename to public/assets/js/memeFodder-publish.js diff --git a/public/assets/js/publishFunctions.js b/public/assets/js/publishFunctions.js index 1c02be73..67fcbf09 100644 --- a/public/assets/js/publishFunctions.js +++ b/public/assets/js/publishFunctions.js @@ -3,33 +3,104 @@ function updatePublishStatus(msg){ document.getElementById('publish-status').innerHTML = msg; } -/* regular publish helper functions */ - -function previewAndStageFile(selectedFile){ - var preview = document.getElementById('image-preview'); - var dropzone = document.getElementById('drop-zone'); - var previewReader = new FileReader(); - var nameInput = document.getElementById('publish-name'); - - preview.style.display = 'block'; - dropzone.style.display = 'none'; - - previewReader.onloadend = function () { - preview.src = previewReader.result; - }; - - if (selectedFile) { - previewReader.readAsDataURL(selectedFile); // reads the data and sets the img src - if (nameInput.value === "") { - nameInput.value = selectedFile.name.substring(0, selectedFile.name.indexOf('.')); - } - stagedFiles = [selectedFile]; // stores the selected file for upload - } else { - preview.src = ''; +function validateFile(file) { + if (!file) { + throw new Error('no file provided'); + } + // validate size and type + switch (file.type) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + if (file.size > 5000000){ + throw new Error('Sorry, images are limitted to 5 megabytes.'); + } + break; + case 'video/mp4': + if (file.size > 50000000){ + throw new Error('Sorry, videos are limitted to 50 megabytes.'); + } + break; + default: + throw new Error('The ' + file.Type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.') } } -/* drop zone function s*/ +function validateSubmission(stagedFiles, name){ + // make sure only 1 file was selected + if (!stagedFiles) { + throw new Error("Please select a file"); + } else if (stagedFiles.length > 1) { + throw new Error("Only one file is allowed at a time"); + } + // validate 'name' field + var invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); + if (invalidCharacters) { + throw new Error(invalidCharacters + ' is not allowed. A-Z, a-z, 0-9, and "-" only.'); + } else if (name.length < 1) { + throw new Error("You must enter a name for your claim"); + } +} + +function validateClaimName (name) { + var deferred = new Promise(function(resolve, reject) { + var xhttp; + xhttp = new XMLHttpRequest(); + xhttp.open('GET', '/api/isClaimAvailable/' + name, true); + xhttp.responseType = 'json'; + xhttp.onreadystatechange = function() { + if (this.readyState == 4 ) { + if ( this.status == 200) { + if (this.response == true) { + resolve(); + } else { + reject("That name has already been claimed by spee.ch. Please choose a different name."); + } + } else { + reject("request to check claim name failed with status:" + this.status); + }; + } + }; + xhttp.send(); + }); + return deferred; +} + +/* regular publish helper functions */ + +function previewAndStageFile(selectedFile){ + var previewHolder = document.getElementById('asset-preview-holder'); + var dropzone = document.getElementById('drop-zone'); + var previewReader = new FileReader(); + var nameInput = document.getElementById('publish-name'); + // validate the file + try { + validateFile(selectedFile); + } catch (error) { + alert(error.message); + return; + } + // set the preview + if (selectedFile.type === 'video/mp4') { + + } else { + previewReader.readAsDataURL(selectedFile); + previewReader.onloadend = function () { + dropzone.style.display = 'none'; + previewHolder.style.display = 'block'; + previewHolder.innerHTML = ''; + + }; + } + // set the name input value to the image name if none is set yet + if (nameInput.value === "") { + nameInput.value = selectedFile.name.substring(0, selectedFile.name.indexOf('.')); + } + // store the selected file for upload + stagedFiles = [selectedFile]; +} + +/* drop zone functions */ function drop_handler(ev) { ev.preventDefault(); @@ -64,7 +135,7 @@ function startPublish() { //download the image var dataUrl = canvas.toDataURL('image/jpeg'); // canvas defined in memeDraw.js var blob = dataURItoBlob(dataUrl) - var fileName = nameInput.value + ".jpg"; //note: need to dynamically grab type + var fileName = nameInput.value + ".jpeg"; //note: need to dynamically grab type var file = new File([blob], fileName, {type: 'image/jpeg', lastModified: Date.now()}); stageAndPublish(file); }; diff --git a/routes/api-routes.js b/routes/api-routes.js index a0065a58..f3e06263 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -3,7 +3,7 @@ const multipart = require('connect-multiparty'); const multipartMiddleware = multipart(); const publishController = require('../controllers/publishController.js'); const lbryApi = require('../helpers/libraries/lbryApi.js'); -const publishHelpers = require('../helpers/libraries/publishHelpers.js'); +const { createPublishParams, validateFile } = require('../helpers/libraries/publishHelpers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); @@ -55,64 +55,30 @@ module.exports = app => { errorHandlers.handleRequestError('publish', originalUrl, ip, error, res); }); }); + // route to run a publish request on the daemon app.post('/api/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl }, res) => { // google analytics sendGoogleAnalytics('publish', headers, ip, originalUrl); // validate that a file was provided const file = files.speech || files.null; - logger.debug(file); - if (!file) { - postToStats('publish', originalUrl, ip, 'Error: file'); - res.status(400).send('Error: No file was submitted or the key used was incorrect. Files posted through this route must use a key of "speech" or null'); - return; - } - // check if the size is 5 mb or less - if (file.size > 5000000) { - res.status(400).send('Error: only files of 5 megabytes or less are allowed'); - return; - } - // validate name const name = body.name || file.name.substring(0, file.name.indexOf('.')); - const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); - if (invalidCharacters) { - postToStats('publish', originalUrl, ip, 'Error: name'); - res.status(400).send('Error: The name you provided is not allowed. Please use A-Z, a-z, 0-9, "_" and "-" only.'); - return; - } - // validate license const license = body.license || 'No License Provided'; - if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { - postToStats('puplish', originalUrl, ip, 'Error: license'); - res.status(400).send('Error: Only posts with a license of "Public Domain" or "Creative Commons" are eligible for publishing through spee.ch'); + const nsfw = body.nsfw || true; + try { + validateFile(file, name, license, nsfw); + } catch (error) { + postToStats('publish', originalUrl, ip, error.message); + logger.debug('rejected >>', error.message); + res.status(400).send(error.message); return; } - const nsfw = body.nsfw || true; - switch (nsfw) { - case true: - case false: - case 'true': - case 'false': - case 'on': - case 'off': - case 0: - case '0': - case 1: - case '1': - break; - default: - postToStats('publish', originalUrl, ip, 'Error: nsfw'); - res.status(400).send('Error: NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"'); - return; - } + + // prepare the publish parameters const fileName = file.name; const filePath = file.path; const fileType = file.type; - /* - note: make sure it's not a harmful file type - */ - // prepare the publish parameters - const publishParams = publishHelpers.createPublishParams(name, filePath, license, nsfw); + const publishParams = createPublishParams(name, filePath, license, nsfw); // publish the file publishController .publish(publishParams, fileName, fileType) diff --git a/views/index.handlebars b/views/index.handlebars index 0e1302f9..a698756f 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -10,4 +10,4 @@ - + diff --git a/views/memeFodder.handlebars b/views/memeFodder.handlebars index 214bde12..12a1dd50 100644 --- a/views/memeFodder.handlebars +++ b/views/memeFodder.handlebars @@ -10,5 +10,5 @@ - - \ No newline at end of file + + \ No newline at end of file diff --git a/views/partials/publish.handlebars b/views/partials/publish.handlebars index 666b4519..d2b49224 100644 --- a/views/partials/publish.handlebars +++ b/views/partials/publish.handlebars @@ -5,8 +5,8 @@
Drag and drop your file here, or choose your file below.
-By clicking 'Publish' I attest that I have read and agree to the LBRY terms of service.