From 7884fa31b0b85e8073892c7ed2e0600c9269f545 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 1 Nov 2017 13:07:13 -0700 Subject: [PATCH 01/21] changed upload to use formdata and /post api --- config/default.json | 1 + config/development.json | 3 +- config/production.json | 3 +- helpers/publishHelpers.js | 8 +-- package.json | 2 - public/assets/js/publishFileFunctions.js | 37 ++++++++++- routes/api-routes.js | 7 +-- routes/sockets-routes.js | 79 ------------------------ speech.js | 3 +- views/index.handlebars | 27 +++----- 10 files changed, 58 insertions(+), 112 deletions(-) delete mode 100644 routes/sockets-routes.js diff --git a/config/default.json b/config/default.json index c5b65a20..5cbfe25a 100644 --- a/config/default.json +++ b/config/default.json @@ -1,6 +1,7 @@ { "WalletConfig": { "LbryClaimAddress": null, + "LbryChangeAddress": null, "DefaultChannel": null }, "AnalyticsConfig":{ diff --git a/config/development.json b/config/development.json index 4beca9ff..90310068 100644 --- a/config/development.json +++ b/config/development.json @@ -1,6 +1,7 @@ { "WalletConfig": { - "DefaultChannel": "@speechDev" + "DefaultChannel": "@speechDev", + "LbryChangeAddress": "bPsZ4wmrNUsn7gMP9cZ9G6RjM5RpuJAeTt" }, "AnalyticsConfig":{ "GoogleId": "UA-100747990-1" diff --git a/config/production.json b/config/production.json index a5bc5074..1c5055dd 100644 --- a/config/production.json +++ b/config/production.json @@ -1,6 +1,7 @@ { "WalletConfig": { - "DefaultChannel": "@speech" + "DefaultChannel": "@speech", + "LbryChangeAddress": null }, "AnalyticsConfig":{ "GoogleId": "UA-60403362-3" diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 1ba54ddc..7485d851 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -11,9 +11,6 @@ module.exports = { if (!body.name) { throw new Error('no name field found in request'); } - if (!body.nsfw) { - throw new Error('no nsfw field found in request'); - } if (!files) { throw new Error('no files found in request'); } @@ -94,7 +91,7 @@ module.exports = { case '0': return false; default: - return null; + return false; } }, cleanseChannelName (channelName) { @@ -138,7 +135,8 @@ module.exports = { license, nsfw, }, - claim_address: config.get('WalletConfig.LbryClaimAddress'), + claim_address : config.get('WalletConfig.LbryClaimAddress'), + change_address: config.get('WalletConfig.LbryChangeAddress'), }; // add thumbnail to channel if video if (thumbnail !== null) { diff --git a/package.json b/package.json index f9acff82..00b84557 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,6 @@ "sequelize": "^4.1.0", "sequelize-cli": "^3.0.0-3", "sleep": "^5.1.1", - "socket.io": "^2.0.1", - "socketio-file-upload": "^0.6.0", "universal-analytics": "^0.4.13", "winston": "^2.3.1", "winston-slack-webhook": "billbitt/winston-slack-webhook" diff --git a/public/assets/js/publishFileFunctions.js b/public/assets/js/publishFileFunctions.js index 0a84b8a5..3576a361 100644 --- a/public/assets/js/publishFileFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -81,7 +81,7 @@ function publishStagedFile(event) { // validate, submit, and handle response validateFilePublishSubmission(stagedFiles, claimName, channelName) .then(() => { - uploader.submitFiles(stagedFiles); + publishFile(stagedFiles[0], claimName); }) .catch(error => { if (error.name === 'FileError') { @@ -96,4 +96,37 @@ function publishStagedFile(event) { } return; }) -}; \ No newline at end of file +}; + +var publishFile = function (file, name) { + var uri = "/api/publish"; + var xhr = new XMLHttpRequest(); + var fd = new FormData(); + + console.log('publish file, file:', file); + console.log('publish file, name:', name); + fd.append('file', file); + fd.append('name', name); + + xhr.upload.addEventListener("progress", function(e) { + if (e.lengthComputable) { + var percentage = Math.round((e.loaded * 100) / e.total); + console.log('progress:', percentage); + } + }, false); + + xhr.upload.addEventListener("load", function(e){ + console.log('loaded 100%'); + }, false); + + xhr.open("POST", uri, true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + alert(xhr.responseText); // handle response. + } else { + console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status); + } + }; + // Initiate a multipart/form-data upload + xhr.send(fd); +} diff --git a/routes/api-routes.js b/routes/api-routes.js index 0c92879d..a5ca5c17 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -72,7 +72,7 @@ module.exports = (app) => { }); // route to run a publish request on the daemon app.post('/api/publish', multipartMiddleware, (req, res) => { - logger.debug('req:', req); + logger.debug('req:', req.body, req.files); // validate that mandatory parts of the request are present const body = req.body; const files = req.files; @@ -89,8 +89,7 @@ module.exports = (app) => { const filePath = file.path; const fileType = file.type; const name = body.name; - let nsfw = body.nsfw; - nsfw = cleanseNSFW(nsfw); // cleanse nsfw + const nsfw = cleanseNSFW(body.nsfw); // cleanse nsfw input try { validatePublishSubmission(file, name, nsfw); } catch (error) { @@ -107,7 +106,7 @@ module.exports = (app) => { let channelName = body.channelName || null; channelName = cleanseChannelName(channelName); const channelPassword = body.channelPassword || null; - logger.debug(`license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}"`); + logger.debug(`license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`); // check channel authorization authenticateChannelCredentials(channelName, channelPassword) .then(result => { diff --git a/routes/sockets-routes.js b/routes/sockets-routes.js deleted file mode 100644 index 88bffef2..00000000 --- a/routes/sockets-routes.js +++ /dev/null @@ -1,79 +0,0 @@ -const logger = require('winston'); -const { publish } = require('../controllers/publishController.js'); -const { createPublishParams } = require('../helpers/publishHelpers.js'); -const { useObjectPropertiesIfNoKeys } = require('../helpers/errorHandlers.js'); -const { postToStats } = require('../controllers/statsController.js'); - -module.exports = (app, siofu, hostedContentPath) => { - const http = require('http'); - const server = http.Server(app); - const io = require('socket.io')(server); - - io.on('connection', socket => { - logger.silly('a user connected via sockets'); - // attach upload listeners - const uploader = new siofu(); - uploader.dir = hostedContentPath; - uploader.listen(socket); - // listener for when file upload starts - uploader.on('start', ({ file }) => { - // log - logger.info('client started an upload:', file.name); - // server side test to make sure file is not a bad file type - if (/\.exe$/.test(file.name)) { - uploader.abort(file.id, socket); - } - }); - // listener for when file upload encounters an error - uploader.on('error', ({ error }) => { - logger.error('an error occured while uploading', error); - postToStats('PUBLISH', '/', null, null, null, error); - socket.emit('publish-status', error); - }); - // listener for when file has been uploaded - uploader.on('saved', ({ file }) => { - if (file.success) { - logger.debug(`Client successfully uploaded ${file.name}`); - socket.emit('publish-update', 'File upload successfully completed. Your image is being published to LBRY (this might take a second)...'); - // /* - // NOTE: need to validate that client has the credentials to the channel they chose - // otherwise they could circumvent security. - // */ - let thumbnail; - if (file.meta.thumbnail) { - thumbnail = file.meta.thumbnail; - } else { - thumbnail = null; - } - let channelName; - if (file.meta.channel) { - channelName = file.meta.channel; - } else { - channelName = null; - } - // prepare the publish parameters - const publishParams = createPublishParams(file.pathName, file.meta.name, file.meta.title, file.meta.description, file.meta.license, file.meta.nsfw, thumbnail, channelName); - logger.debug('publish parameters:', publishParams); - // publish the file - publish(publishParams, file.name, file.meta.type) - .then(result => { - socket.emit('publish-complete', { name: publishParams.name, result }); - }) - .catch(error => { - logger.error('Publish Error:', useObjectPropertiesIfNoKeys(error)); - socket.emit('publish-failure', error.message); - }); - } else { - socket.emit('publish-failure', 'File uploaded, but with errors'); - logger.error(`An error occurred in uploading the client's file`); - // to-do: remove the file, if not done automatically - } - }); - // handle disconnect - socket.on('disconnect', () => { - logger.silly('a user disconnected via sockets'); - }); - }); - - return server; -}; diff --git a/speech.js b/speech.js index b6e00a7b..2263e5d2 100644 --- a/speech.js +++ b/speech.js @@ -82,7 +82,8 @@ db.sequelize require('./routes/page-routes.js')(app); require('./routes/serve-routes.js')(app); require('./routes/home-routes.js')(app); - return require('./routes/sockets-routes.js')(app, siofu, hostedContentPath); + const http = require('http'); + return http.Server(app); }) .then(server => { // start the server server.listen(PORT, () => { diff --git a/views/index.handlebars b/views/index.handlebars index ba66b4ef..6fa807a5 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -51,15 +51,10 @@ - - - diff --git a/views/partials/publishForm-Submit.handlebars b/views/partials/publishForm-Submit.handlebars index 486e8841..494eebcb 100644 --- a/views/partials/publishForm-Submit.handlebars +++ b/views/partials/publishForm-Submit.handlebars @@ -1,6 +1,6 @@
- +
-- 2.45.2 From f9daf0f8bef1c7154cd8061e1fb0da989129dc5e Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 2 Nov 2017 11:50:05 -0700 Subject: [PATCH 03/21] updated error handling --- controllers/publishController.js | 2 +- helpers/errorHandlers.js | 59 ++++++++++++++++++++------------ routes/api-routes.js | 16 ++++----- routes/page-routes.js | 12 +++---- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 84c68750..d8ee0bf6 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -67,7 +67,7 @@ module.exports = { resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim; }) .catch(error => { - logger.error('publishController.publish, error', error); + logger.error('publishController.publish error', error); publishHelpers.deleteTemporaryFile(publishParams.file_path); // delete the local file reject(error); }); diff --git a/helpers/errorHandlers.js b/helpers/errorHandlers.js index 3778eeeb..5bc73edf 100644 --- a/helpers/errorHandlers.js +++ b/helpers/errorHandlers.js @@ -2,38 +2,53 @@ const logger = require('winston'); const { postToStats } = require('../controllers/statsController.js'); module.exports = { - handleRequestError (action, originalUrl, ip, error, res) { - logger.error(`Request Error: ${originalUrl}`, module.exports.useObjectPropertiesIfNoKeys(error)); - postToStats(action, originalUrl, ip, null, null, error); - if (error.response) { - res.status(error.response.status).render('requestError', {message: error.response.data.error.message, status: error.response.status}); - } else if (error.code === 'ECONNREFUSED') { - res.status(503).render('requestError', {message: 'Connection refused. The daemon may not be running.', status: 503}); - } else if (error.message) { - res.status(500).render('requestError', {message: error.message, status: 500}); - } else { - res.status(400).render('requestError', {message: error, status: 400}); - } - }, - handlePublishError (error) { - logger.error('Publish Error:', module.exports.useObjectPropertiesIfNoKeys(error)); + returnErrorMessageAndStatus: function (error) { + let status; + let message; if (error.code === 'ECONNREFUSED') { - return 'Connection refused. The daemon may not be running.'; + status = 503; + message = 'Connection refused. The daemon may not be running.'; } else if (error.response) { + status = error.response.status || 500; if (error.response.data) { if (error.response.data.message) { - return error.response.data.message; + message = error.response.data.message; } else if (error.response.data.error) { - return error.response.data.error.message; + message = error.response.data.error.message; + } else { + message = error.response.data; } - return error.response.data; + } else { + message = error.response; } - return error.response; } else { - return error; + message = error; } + return [status, message]; }, - useObjectPropertiesIfNoKeys (err) { + handleRequestError: function (action, originalUrl, ip, error, res) { + logger.error(`Request Error on ${originalUrl}`, module.exports.useObjectPropertiesIfNoKeys(error)); + postToStats(action, originalUrl, ip, null, null, error); + const errorStatusAndMessage = this.returnErrorMessageAndStatus(error); + res + .status(errorStatusAndMessage[0]) + .render('requestError', { + status : errorStatusAndMessage[0], + message: errorStatusAndMessage[1], + }); + }, + handleApiError: function (action, originalUrl, ip, error, res) { + logger.error(`Api ${action} Error on ${originalUrl}`, module.exports.useObjectPropertiesIfNoKeys(error)); + postToStats(action, originalUrl, ip, null, null, error); + const errorStatusAndMessage = this.returnErrorMessageAndStatus(error); + res + .status(errorStatusAndMessage[0]) + .json({ + success: false, + message: errorStatusAndMessage[1], + }); + }, + useObjectPropertiesIfNoKeys: function (err) { if (Object.keys(err).length === 0) { let newErrorObject = {}; Object.getOwnPropertyNames(err).forEach((key) => { diff --git a/routes/api-routes.js b/routes/api-routes.js index a5ca5c17..f020fc26 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -21,7 +21,7 @@ module.exports = (app) => { res.status(200).json(claimsList); }) .catch(error => { - errorHandlers.handleRequestError('publish', originalUrl, ip, error, res); + errorHandlers.handleApiError('claim_list', originalUrl, ip, error, res); }); }); // route to check whether spee.ch has published to a claim @@ -67,15 +67,12 @@ module.exports = (app) => { res.status(200).json(resolvedUri); }) .catch(error => { - errorHandlers.handleRequestError('publish', originalUrl, ip, error, res); + errorHandlers.handleApiError('resolve', originalUrl, ip, error, res); }); }); // route to run a publish request on the daemon - app.post('/api/publish', multipartMiddleware, (req, res) => { - logger.debug('req:', req.body, req.files); + app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl }, res) => { // validate that mandatory parts of the request are present - const body = req.body; - const files = req.files; try { validateApiPublishRequest(body, files); } catch (error) { @@ -138,8 +135,7 @@ module.exports = (app) => { }); }) .catch(error => { - logger.error('publish api error', error); - res.status(400).json({success: false, message: error.message}); + errorHandlers.handleApiError('publish', originalUrl, ip, error, res); }); }); @@ -157,7 +153,7 @@ module.exports = (app) => { }); }); // route to get a short channel id from long channel Id - app.get('/api/shortChannelId/:longId/:name', ({ params }, res) => { + app.get('/api/shortChannelId/:longId/:name', ({ ip, originalUrl, params }, res) => { // serve content db.Certificate.getShortChannelIdFromLongChannelId(params.longId, params.name) .then(shortId => { @@ -166,7 +162,7 @@ module.exports = (app) => { }) .catch(error => { logger.error('api error getting short channel id', error); - res.status(400).json(error.message); + errorHandlers.handleApiError('short channel id', originalUrl, ip, error, res); }); }); }; diff --git a/routes/page-routes.js b/routes/page-routes.js index 6b0368e9..ac56b889 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -24,7 +24,7 @@ module.exports = (app) => { app.get('/trending', (req, res) => { res.status(301).redirect('/popular'); }); - app.get('/popular', (req, res) => { + app.get('/popular', ({ ip, originalUrl }, res) => { const startDate = new Date(); startDate.setDate(startDate.getDate() - 1); const dateTime = startDate.toISOString().slice(0, 19).replace('T', ' '); @@ -36,20 +36,18 @@ module.exports = (app) => { }); }) .catch(error => { - errorHandlers.handleRequestError(null, null, null, error, res); + errorHandlers.handleRequestError('popular', originalUrl, ip, error, res); }); }); // route to display a list of the trending images - app.get('/new', (req, res) => { + app.get('/new', ({ ip, originalUrl }, res) => { getRecentClaims() .then(result => { // logger.debug(result); - res.status(200).render('new', { - newClaims: result, - }); + res.status(200).render('new', { newClaims: result }); }) .catch(error => { - errorHandlers.handleRequestError(null, null, null, error, res); + errorHandlers.handleRequestError('new', originalUrl, ip, error, res); }); }); // route to send embedable video player (for twitter) -- 2.45.2 From c658f67ec48d7e797bdec4dfb7867567aa0f45b3 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 2 Nov 2017 12:25:35 -0700 Subject: [PATCH 04/21] removed change_address --- config/default.json | 1 - config/development.json | 1 - config/production.json | 1 - controllers/publishController.js | 1 - helpers/publishHelpers.js | 3 +-- public/assets/js/publishFileFunctions.js | 25 ++++++++++++------------ views/fourOhFour.handlebars | 2 +- views/index.handlebars | 4 ---- 8 files changed, 14 insertions(+), 24 deletions(-) diff --git a/config/default.json b/config/default.json index 5cbfe25a..c5b65a20 100644 --- a/config/default.json +++ b/config/default.json @@ -1,7 +1,6 @@ { "WalletConfig": { "LbryClaimAddress": null, - "LbryChangeAddress": null, "DefaultChannel": null }, "AnalyticsConfig":{ diff --git a/config/development.json b/config/development.json index 90310068..1b9e9079 100644 --- a/config/development.json +++ b/config/development.json @@ -1,7 +1,6 @@ { "WalletConfig": { "DefaultChannel": "@speechDev", - "LbryChangeAddress": "bPsZ4wmrNUsn7gMP9cZ9G6RjM5RpuJAeTt" }, "AnalyticsConfig":{ "GoogleId": "UA-100747990-1" diff --git a/config/production.json b/config/production.json index 1c5055dd..754b71a2 100644 --- a/config/production.json +++ b/config/production.json @@ -1,7 +1,6 @@ { "WalletConfig": { "DefaultChannel": "@speech", - "LbryChangeAddress": null }, "AnalyticsConfig":{ "GoogleId": "UA-60403362-3" diff --git a/controllers/publishController.js b/controllers/publishController.js index d8ee0bf6..9af56217 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -67,7 +67,6 @@ module.exports = { resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim; }) .catch(error => { - logger.error('publishController.publish error', error); publishHelpers.deleteTemporaryFile(publishParams.file_path); // delete the local file reject(error); }); diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 7485d851..a9142274 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -135,8 +135,7 @@ module.exports = { license, nsfw, }, - claim_address : config.get('WalletConfig.LbryClaimAddress'), - change_address: config.get('WalletConfig.LbryChangeAddress'), + claim_address: config.get('WalletConfig.LbryClaimAddress'), }; // add thumbnail to channel if video if (thumbnail !== null) { diff --git a/public/assets/js/publishFileFunctions.js b/public/assets/js/publishFileFunctions.js index 53b60a7b..5f889e41 100644 --- a/public/assets/js/publishFileFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -123,29 +123,28 @@ var publishFileFunctions = { var uri = "/api/publish"; var xhr = new XMLHttpRequest(); var fd = this.appendDataToFormData(file, metadata); - console.log('fd2:', fd); xhr.upload.addEventListener("loadstart", function(e) { - uploadStarted(); + showUploadStartedMessage(); }) xhr.upload.addEventListener("progress", function(e) { if (e.lengthComputable) { var percentage = Math.round((e.loaded * 100) / e.total); console.log('progress:', percentage); - uploadProgress(percentage); + showUploadProgressMessage(percentage); } }, false); xhr.upload.addEventListener("load", function(e){ console.log('loaded 100%'); - filePublishUpdate("Your file has been loaded, and is now being published to the blockchain. Sit tight...") + showFilePublishUpdate("Your file has been loaded, and is now being published to the blockchain. Sit tight...") }, false); xhr.open("POST", uri, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText); // handle response. - filePublishComplete(xhr.responseText); + showFilePublishComplete(xhr.responseText); } else { - filePublishFailure(xhr.responseText); + showFilePublishFailure(xhr.responseText); } } else { console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status); @@ -184,7 +183,7 @@ var publishFileFunctions = { }, } -function uploadStarted (){ +function showUploadStartedMessage (){ console.log('starting upload'); // hide the publish tool hidePublishTools(); @@ -192,27 +191,27 @@ function uploadStarted (){ showPublishStatus(); showPublishProgressBar(); }; -function uploadProgress (percentage){ +function showUploadProgressMessage (percentage){ updatePublishStatus('

File is loading to server

') updateUploadPercent(`

${percentage}%

`) }; -function filePublishUpdate (msg) { +function showFilePublishUpdate (msg) { updatePublishStatus(`

${msg}

`); updateUploadPercent(`

Curious what magic is happening here? Learn more.

`); }; -function filePublishFailure (msg){ +function showFilePublishFailure (msg){ updatePublishStatus('

--(✖╭╮✖)→

' + JSON.stringify(msg) + '

For help, post the above error text in the #speech channel on the lbry discord'); hidePublishProgressBar(); hideUploadPercent(); }; -function filePublishComplete (msg) { - console.log('filePublishComplete message:', msg); +function showFilePublishComplete (msg) { + console.log('Publish complete! message:', msg); const showUrl = msg.result.claim_id + "/" + msg.name; // update status updatePublishStatus('

Your publish is complete! You are being redirected to it now.

'); updateUploadPercent('

If you do not get redirected, click here.

') // redirect the user - window.location.href = showUrl; + // window.location.href = showUrl; }; function hidePublishTools() { diff --git a/views/fourOhFour.handlebars b/views/fourOhFour.handlebars index eeb979ac..30d8ed8c 100644 --- a/views/fourOhFour.handlebars +++ b/views/fourOhFour.handlebars @@ -1,4 +1,4 @@ -
+

404: Not Found

That page does not exist. Return home.

diff --git a/views/index.handlebars b/views/index.handlebars index 4b80e119..a47522b6 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -50,7 +50,3 @@
- - -- 2.45.2 From 934c0fedd13f877fe5d3f2e1e7f1e2d1dcc78de7 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 2 Nov 2017 15:12:26 -0700 Subject: [PATCH 05/21] put all publish functions into an object --- config/development.json | 2 +- config/production.json | 2 +- public/assets/js/dropzoneFunctions.js | 2 +- public/assets/js/publishFileFunctions.js | 271 +++++++++---------- public/assets/js/validationFunctions.js | 2 +- routes/api-routes.js | 1 + views/index.handlebars | 6 +- views/partials/publishForm-Submit.handlebars | 2 +- 8 files changed, 141 insertions(+), 147 deletions(-) diff --git a/config/development.json b/config/development.json index 1b9e9079..4beca9ff 100644 --- a/config/development.json +++ b/config/development.json @@ -1,6 +1,6 @@ { "WalletConfig": { - "DefaultChannel": "@speechDev", + "DefaultChannel": "@speechDev" }, "AnalyticsConfig":{ "GoogleId": "UA-100747990-1" diff --git a/config/production.json b/config/production.json index 754b71a2..a5bc5074 100644 --- a/config/production.json +++ b/config/production.json @@ -1,6 +1,6 @@ { "WalletConfig": { - "DefaultChannel": "@speech", + "DefaultChannel": "@speech" }, "AnalyticsConfig":{ "GoogleId": "UA-60403362-3" diff --git a/public/assets/js/dropzoneFunctions.js b/public/assets/js/dropzoneFunctions.js index 7eae662f..b0ab1abb 100644 --- a/public/assets/js/dropzoneFunctions.js +++ b/public/assets/js/dropzoneFunctions.js @@ -6,7 +6,7 @@ function drop_handler(event) { if (dt.items) { if (dt.items[0].kind == 'file') { var droppedFile = dt.items[0].getAsFile(); - previewAndStageFile(droppedFile); + publishFileFunctions.previewAndStageFile(droppedFile); } } } diff --git a/public/assets/js/publishFileFunctions.js b/public/assets/js/publishFileFunctions.js index 5f889e41..279d5960 100644 --- a/public/assets/js/publishFileFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -1,77 +1,68 @@ -/* publish functions */ - -let stagedFiles = null; - -const previewReader = new FileReader(); - -function triggerFileChooser(fileInputId) { - document.getElementById(fileInputId).click(); -} - -function setImagePreview (selectedFile) { - const assetPreview = document.getElementById('asset-preview-target'); - const thumbnailInput = document.getElementById('claim-thumbnail-input'); - const thumbnailInputTool = document.getElementById('publish-thumbnail'); - if (selectedFile.type !== 'video/mp4') { - if (selectedFile.type === 'image/gif') { - assetPreview.innerHTML = `

loading preview...

` - } - previewReader.readAsDataURL(selectedFile); - previewReader.onloadend = function () { - assetPreview.innerHTML = 'image preview'; - }; - // clear & hide the thumbnail selection input - thumbnailInput.value = ''; - thumbnailInputTool.hidden = true; - } else { - assetPreview.innerHTML = ``; - // clear & show the thumbnail selection input - thumbnailInput.value = ''; - thumbnailInputTool.hidden = false; - } -} - -function hidePrimaryDropzone () { - const primaryDropzone = document.getElementById('primary-dropzone'); - const publishForm = document.getElementById('publish-form'); - primaryDropzone.setAttribute('class', 'hidden'); - publishForm.setAttribute('class', 'row') -} - -function updateClaimNameInputWithFileName (selectedFile) { - const nameInput = document.getElementById('claim-name-input'); - if (nameInput.value === "") { - var filename = selectedFile.name.substring(0, selectedFile.name.indexOf('.')) - nameInput.value = cleanseClaimName(filename); - checkClaimName(nameInput.value); - } -} - -function previewAndStageFile(selectedFile){ - const fileSelectionInputError = document.getElementById('input-error-file-selection'); - // When a file is selected for publish, validate that file and - // stage it so it will be ready when the publish button is clicked - try { - validateFile(selectedFile); // validate the file's name, type, and size - } catch (error) { - showError(fileSelectionInputError, error.message); - return; - } - // set image preview, if an image was provided - setImagePreview(selectedFile); - // hide the primary drop zone - hidePrimaryDropzone(); - // set the name input value to the image name if none is set yet - updateClaimNameInputWithFileName(selectedFile); - // store the selected file for upload - stagedFiles = [selectedFile]; -} - -function cancelPublish () { - window.location.href = '/'; -} +var stagedFiles = null; var publishFileFunctions = { + triggerFileChooser: function (fileInputId) { + document.getElementById(fileInputId).click(); + }, + cancelPublish: function () { + window.location.href = '/'; + }, + previewAndStageFile: function (selectedFile) { + const fileSelectionInputError = document.getElementById('input-error-file-selection'); + // When a file is selected for publish, validate that file and + // stage it so it will be ready when the publish button is clicked + try { + validateFile(selectedFile); // validate the file's name, type, and size + } catch (error) { + showError(fileSelectionInputError, error.message); + return; + } + // set image preview, if an image was provided + this.setImagePreview(selectedFile); + // hide the primary drop zone + this.hidePrimaryDropzone(); + // set the name input value to the image name if none is set yet + this.updateClaimNameInputWithFileName(selectedFile); + // store the selected file for upload + stagedFiles = [selectedFile]; + }, + hidePrimaryDropzone: function () { + const primaryDropzone = document.getElementById('primary-dropzone'); + const publishForm = document.getElementById('publish-form'); + primaryDropzone.setAttribute('class', 'hidden'); + publishForm.setAttribute('class', 'row') + }, + updateClaimNameInputWithFileName: function (selectedFile) { + const nameInput = document.getElementById('claim-name-input'); + if (nameInput.value === "") { + var filename = selectedFile.name.substring(0, selectedFile.name.indexOf('.')) + nameInput.value = cleanseClaimName(filename); + checkClaimName(nameInput.value); + } + }, + setImagePreview: function (selectedFile) { + const assetPreview = document.getElementById('asset-preview-target'); + const thumbnailInput = document.getElementById('claim-thumbnail-input'); + const thumbnailInputTool = document.getElementById('publish-thumbnail'); + if (selectedFile.type !== 'video/mp4') { + const previewReader = new FileReader(); + if (selectedFile.type === 'image/gif') { + assetPreview.innerHTML = `

loading preview...

` + } + previewReader.readAsDataURL(selectedFile); + previewReader.onloadend = function () { + assetPreview.innerHTML = 'image preview'; + }; + // clear & hide the thumbnail selection input + thumbnailInput.value = ''; + thumbnailInputTool.hidden = true; + } else { + assetPreview.innerHTML = ``; + // clear & show the thumbnail selection input + thumbnailInput.value = ''; + thumbnailInputTool.hidden = false; + } + }, returnNullOrChannel: function () { const channelInput = document.getElementById('channel-name-select'); const radios = document.getElementsByName('anonymous-or-channel'); @@ -123,28 +114,30 @@ var publishFileFunctions = { var uri = "/api/publish"; var xhr = new XMLHttpRequest(); var fd = this.appendDataToFormData(file, metadata); + var that = this; xhr.upload.addEventListener("loadstart", function(e) { - showUploadStartedMessage(); + that.showUploadStartedMessage(); }) xhr.upload.addEventListener("progress", function(e) { if (e.lengthComputable) { var percentage = Math.round((e.loaded * 100) / e.total); console.log('progress:', percentage); - showUploadProgressMessage(percentage); + that.showUploadProgressMessage(percentage); } }, false); xhr.upload.addEventListener("load", function(e){ console.log('loaded 100%'); - showFilePublishUpdate("Your file has been loaded, and is now being published to the blockchain. Sit tight...") + that.showFilePublishUpdate("Your file has been loaded, and is now being published to the blockchain. Sit tight...") }, false); xhr.open("POST", uri, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { - alert(xhr.responseText); // handle response. - showFilePublishComplete(xhr.responseText); + console.log('publish complete!'); + that.showFilePublishComplete(JSON.parse(xhr.response).message); } else { - showFilePublishFailure(xhr.responseText); + console.log(xhr.response); + that.showFilePublishFailure(JSON.parse(xhr.response).message); } } else { console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status); @@ -181,67 +174,67 @@ var publishFileFunctions = { return; }) }, + showUploadStartedMessage: function (){ + console.log('starting upload'); + // hide the publish tool + this.hidePublishTools(); + // show the progress status and animation + this.showPublishStatus(); + this.showPublishProgressBar(); + }, + showUploadProgressMessage: function (percentage){ + this.updatePublishStatus('

File is loading to server

'); + this.updateUploadPercent('

' + percentage + '

'); + }, + showFilePublishUpdate: function (msg) { + this.updatePublishStatus('

' + msg + '

'); + this.updateUploadPercent('

Curious what magic is happening here? Learn more.

'); + }, + showFilePublishFailure: function (msg){ + this.updatePublishStatus('

Something went wrong...

' + msg + '

For help, post the above error text in the #speech channel on the lbry discord'); + this.hidePublishProgressBar(); + this.hideUploadPercent(); + }, + showFilePublishComplete: function (msg) { + console.log('Publish complete!'); + const showUrl = msg.lbryTx.claim_id + "/" + msg.name; + // update status + this.updatePublishStatus('

Your publish is complete! You are being redirected to it now.

'); + this.updateUploadPercent('

If you do not get redirected, click here.

') + // redirect the user + window.location.href = showUrl; + }, + hidePublishTools: function () { + const publishFormWrapper = document.getElementById('publish-form'); + publishFormWrapper.setAttribute('class', 'hidden'); + }, + // publish status functions + showPublishStatus: function () { + const publishStatus = document.getElementById('publish-status'); + publishStatus.setAttribute('class', 'row row--tall flex-container--column flex-container--center-center'); + }, + updatePublishStatus: function (msg){ + const publishUpdate = document.getElementById('publish-update'); + publishUpdate.innerHTML = msg; + }, + // progress bar functions + showPublishProgressBar: function (){ + const publishProgressBar = document.getElementById('publish-progress-bar'); + createProgressBar(publishProgressBar, 12); + }, + hidePublishProgressBar: function (){ + const publishProgressBar = document.getElementById('publish-progress-bar'); + publishProgressBar.hidden = true; + }, + // upload percent functions + updateUploadPercent: function (msg){ + const uploadPercent = document.getElementById('upload-percent'); + uploadPercent.innerHTML = msg; + }, + hideUploadPercent: function (){ + const uploadPercent = document.getElementById('upload-percent'); + uploadPercent.hidden = true; + }, } -function showUploadStartedMessage (){ - console.log('starting upload'); - // hide the publish tool - hidePublishTools(); - // show the progress status and animation - showPublishStatus(); - showPublishProgressBar(); -}; -function showUploadProgressMessage (percentage){ - updatePublishStatus('

File is loading to server

') - updateUploadPercent(`

${percentage}%

`) -}; -function showFilePublishUpdate (msg) { - updatePublishStatus(`

${msg}

`); - updateUploadPercent(`

Curious what magic is happening here? Learn more.

`); -}; -function showFilePublishFailure (msg){ - updatePublishStatus('

--(✖╭╮✖)→

' + JSON.stringify(msg) + '

For help, post the above error text in the #speech channel on the lbry discord'); - hidePublishProgressBar(); - hideUploadPercent(); -}; -function showFilePublishComplete (msg) { - console.log('Publish complete! message:', msg); - const showUrl = msg.result.claim_id + "/" + msg.name; - // update status - updatePublishStatus('

Your publish is complete! You are being redirected to it now.

'); - updateUploadPercent('

If you do not get redirected, click here.

') - // redirect the user - // window.location.href = showUrl; -}; -function hidePublishTools() { - const publishFormWrapper = document.getElementById('publish-form'); - publishFormWrapper.setAttribute('class', 'hidden'); -} -// publish status functions -function showPublishStatus() { - const publishStatus = document.getElementById('publish-status'); - publishStatus.setAttribute('class', 'row row--tall flex-container--column flex-container--center-center'); -} -function updatePublishStatus(msg){ - const publishUpdate = document.getElementById('publish-update'); - publishUpdate.innerHTML = msg; -} -// progress bar functions -function showPublishProgressBar(){ - const publishProgressBar = document.getElementById('publish-progress-bar'); - createProgressBar(publishProgressBar, 12); -} -function hidePublishProgressBar(){ - const publishProgressBar = document.getElementById('publish-progress-bar'); - publishProgressBar.hidden = true; -} -// upload percent functions -function updateUploadPercent(msg){ - const uploadPercent = document.getElementById('upload-percent'); - uploadPercent.innerHTML = msg; -} -function hideUploadPercent(){ - const uploadPercent = document.getElementById('upload-percent'); - uploadPercent.hidden = true; -} \ No newline at end of file diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index 0f3e6f06..a1814203 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -38,7 +38,7 @@ function validateFile(file) { // validation function that checks to make sure the claim name is valid function validateClaimName (name) { - console.log('claim name:', name); + console.log('validating the claim name'); // ensure a name was entered if (name.length < 1) { throw new NameError("You must enter a name for your url"); diff --git a/routes/api-routes.js b/routes/api-routes.js index f020fc26..6ebca2f5 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -129,6 +129,7 @@ module.exports = (app) => { res.status(200).json({ success: true, message: { + name, url : `spee.ch/${result.claim_id}/${name}`, lbryTx: result, }, diff --git a/views/index.handlebars b/views/index.handlebars index a47522b6..09c01c0e 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -1,8 +1,8 @@
- +
-
+

Drag & drop image or video here to publish

@@ -22,7 +22,7 @@
-
+
- +

By clicking 'Upload', you affirm that you have the rights to publish this content to the LBRY network, and that you understand the properties of publishing it to a decentralized, user-controlled network. Read more.

-- 2.45.2 From d92f499269747f01e321ab096922b3b899fe0bd1 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 2 Nov 2017 15:47:55 -0700 Subject: [PATCH 06/21] combined validation functions into one object --- public/assets/js/createChannelFunctions.js | 2 +- public/assets/js/loginFunctions.js | 6 +- public/assets/js/publishFileFunctions.js | 19 +- public/assets/js/validationFunctions.js | 460 +++++++++--------- views/partials/channelCreationForm.handlebars | 2 +- views/partials/publishForm-Url.handlebars | 2 +- 6 files changed, 241 insertions(+), 250 deletions(-) diff --git a/public/assets/js/createChannelFunctions.js b/public/assets/js/createChannelFunctions.js index faef7189..379c9430 100644 --- a/public/assets/js/createChannelFunctions.js +++ b/public/assets/js/createChannelFunctions.js @@ -24,7 +24,7 @@ function publishNewChannel (event) { // prevent default so this script can handle submission event.preventDefault(); // validate submission - validateNewChannelSubmission(userName, password) + validationFunctions.validateNewChannelSubmission(userName, password) .then(() => { showChannelCreateInProgressDisplay(); return sendAuthRequest(userName, password, '/signup') // post the request diff --git a/public/assets/js/loginFunctions.js b/public/assets/js/loginFunctions.js index 4dfa3a20..5f3a13b0 100644 --- a/public/assets/js/loginFunctions.js +++ b/public/assets/js/loginFunctions.js @@ -47,7 +47,7 @@ function loginToChannel (event) { const password = document.getElementById('channel-login-password-input').value; // prevent default event.preventDefault() - validateNewChannelLogin(userName, password) + validationFunctions.validateNewChannelLogin(userName, password) .then(() => { // send request return sendAuthRequest(userName, password, '/login') @@ -71,9 +71,9 @@ function loginToChannel (event) { .catch(error => { const loginErrorDisplayElement = document.getElementById('login-error-display-element'); if (error.name){ - showError(loginErrorDisplayElement, error.message); + validationFunctions.showError(loginErrorDisplayElement, error.message); } else { - showError(loginErrorDisplayElement, 'There was an error logging into your channel'); + validationFunctions.showError(loginErrorDisplayElement, 'There was an error logging into your channel'); } }) } diff --git a/public/assets/js/publishFileFunctions.js b/public/assets/js/publishFileFunctions.js index 279d5960..6dfb8060 100644 --- a/public/assets/js/publishFileFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -12,9 +12,9 @@ var publishFileFunctions = { // When a file is selected for publish, validate that file and // stage it so it will be ready when the publish button is clicked try { - validateFile(selectedFile); // validate the file's name, type, and size + validationFunctions.validateFile(selectedFile); // validate the file's name, type, and size } catch (error) { - showError(fileSelectionInputError, error.message); + validationFunctions.showError(fileSelectionInputError, error.message); return; } // set image preview, if an image was provided @@ -36,8 +36,8 @@ var publishFileFunctions = { const nameInput = document.getElementById('claim-name-input'); if (nameInput.value === "") { var filename = selectedFile.name.substring(0, selectedFile.name.indexOf('.')) - nameInput.value = cleanseClaimName(filename); - checkClaimName(nameInput.value); + nameInput.value = validationFunctions.cleanseClaimName(filename); + validationFunctions.checkClaimName(nameInput.value); } }, setImagePreview: function (selectedFile) { @@ -99,7 +99,6 @@ var publishFileFunctions = { } }, appendDataToFormData: function (file, metadata) { - console.log(metadata); var fd = new FormData(); fd.append('file', file) for (var key in metadata) { @@ -156,20 +155,20 @@ var publishFileFunctions = { const channelSelectError = document.getElementById('input-error-channel-select'); const publishSubmitError = document.getElementById('input-error-publish-submit'); // validate, submit, and handle response - validateFilePublishSubmission(stagedFiles, metadata) + validationFunctions.validateFilePublishSubmission(stagedFiles, metadata) .then( function() { that.publishFile(stagedFiles[0], metadata); }) .catch(error => { if (error.name === 'FileError') { - showError(fileSelectionInputError, error.message); + validationFunctions.showError(fileSelectionInputError, error.message); } else if (error.name === 'NameError') { - showError(claimNameError, error.message); + validationFunctions.showError(claimNameError, error.message); } else if (error.name === 'ChannelNameError'){ console.log(error); - showError(channelSelectError, error.message); + validationFunctions.showError(channelSelectError, error.message); } else { - showError(publishSubmitError, error.message); + validationFunctions.showError(publishSubmitError, error.message); } return; }) diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index a1814203..60ecf341 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -1,239 +1,231 @@ // validation function which checks the proposed file's type, size, and name -function validateFile(file) { - if (!file) { - console.log('no file found'); - throw new Error('no file provided'); - } - if (/'/.test(file.name)) { - console.log('file name had apostrophe in it'); - throw new Error('apostrophes are not allowed in the file name'); - } - // validate size and type - switch (file.type) { - case 'image/jpeg': - case 'image/jpg': - case 'image/png': - if (file.size > 10000000){ - console.log('file was too big'); - throw new Error('Sorry, images are limited to 10 megabytes.'); - } - break; - case 'image/gif': - if (file.size > 50000000){ - console.log('file was too big'); - throw new Error('Sorry, .gifs are limited to 50 megabytes.'); - } - break; - case 'video/mp4': - if (file.size > 50000000){ - console.log('file was too big'); - throw new Error('Sorry, videos are limited to 50 megabytes.'); - } - break; - default: - console.log('file type is not supported'); - throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.') - } -} - -// validation function that checks to make sure the claim name is valid -function validateClaimName (name) { - console.log('validating the claim name'); - // ensure a name was entered - if (name.length < 1) { - throw new NameError("You must enter a name for your url"); - } - // validate the characters in the 'name' field - const invalidCharacters = /[^A-Za-z0-9,-]/g.exec(name); - if (invalidCharacters) { - throw new NameError('"' + invalidCharacters + '" characters are not allowed'); - } -} - -function validateChannelName (name) { - name = name.substring(name.indexOf('@') + 1); - // ensure a name was entered - if (name.length < 1) { - throw new ChannelNameError("You must enter a name for your channel"); - } - // validate the characters in the 'name' field - const invalidCharacters = /[^A-Za-z0-9,-,@]/g.exec(name); - if (invalidCharacters) { - throw new ChannelNameError('"' + invalidCharacters + '" characters are not allowed'); - } -} - -function validatePassword (password) { - if (password.length < 1) { - throw new ChannelPasswordError("You must enter a password for you channel"); - } -} - -function cleanseClaimName(name) { - name = name.replace(/\s+/g, '-'); // replace spaces with dashes - name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-' - return name; -} - -// validation functions to check claim & channel name eligibility as the inputs change - -function isNameAvailable (name, apiUrl) { - const url = apiUrl + name; - return getRequest(url) -} - -function showError(errorDisplay, errorMsg) { - errorDisplay.hidden = false; - errorDisplay.innerText = errorMsg; -} - -function hideError(errorDisplay) { - errorDisplay.hidden = true; - errorDisplay.innerText = ''; -} - -function showSuccess (successElement) { - successElement.hidden = false; - successElement.innerHTML = "✔"; -} - -function hideSuccess (successElement) { - successElement.hidden = true; - successElement.innerHTML = ""; -} - -function checkAvailability(name, successDisplayElement, errorDisplayElement, validateName, isNameAvailable, errorMessage, apiUrl) { - try { - // check to make sure the characters are valid - validateName(name); - // check to make sure it is available - isNameAvailable(name, apiUrl) - .then(result => { - if (result === true) { - hideError(errorDisplayElement); - showSuccess(successDisplayElement) - } else { - hideSuccess(successDisplayElement); - showError(errorDisplayElement, errorMessage); +var validationFunctions = { + validateFile: function (file) { + if (!file) { + console.log('no file found'); + throw new Error('no file provided'); + } + if (/'/.test(file.name)) { + console.log('file name had apostrophe in it'); + throw new Error('apostrophes are not allowed in the file name'); + } + // validate size and type + switch (file.type) { + case 'image/jpeg': + case 'image/jpg': + case 'image/png': + if (file.size > 10000000) { + console.log('file was too big'); + throw new Error('Sorry, images are limited to 10 megabytes.'); } - }) - .catch(error => { - hideSuccess(successDisplayElement); - showError(errorDisplayElement, error.message); - }); - } catch (error) { - hideSuccess(successDisplayElement); - showError(errorDisplayElement, error.message); - } -} - -function checkClaimName(name){ - const successDisplayElement = document.getElementById('input-success-claim-name'); - const errorDisplayElement = document.getElementById('input-error-claim-name'); - checkAvailability(name, successDisplayElement, errorDisplayElement, validateClaimName, isNameAvailable, 'Sorry, that ending is already taken', '/api/isClaimAvailable/'); -} - -function checkChannelName(name){ - const successDisplayElement = document.getElementById('input-success-channel-name'); - const errorDisplayElement = document.getElementById('input-error-channel-name'); - name = `@${name}`; - checkAvailability(name, successDisplayElement, errorDisplayElement, validateChannelName, isNameAvailable, 'Sorry, that name is already taken', '/api/isChannelAvailable/'); -} - -// validation function which checks all aspects of the publish submission -function validateFilePublishSubmission(stagedFiles, metadata){ - const channelName = metadata.channelName; - const claimName = metadata.name; - return new Promise(function (resolve, reject) { - // 1. make sure 1 file was staged - if (!stagedFiles) { - reject(new FileError("Please select a file")); - return; - } else if (stagedFiles.length > 1) { - reject(new FileError("Only one file is allowed at a time")); - return; - } - // 2. validate the file's name, type, and size - try { - validateFile(stagedFiles[0]); - } catch (error) { - reject(error); - return; - } - // 3. validate that a channel was chosen - if (channelName === 'new' || channelName === 'login') { - reject(new ChannelNameError("Please log in to a channel")); - return; - }; - // 4. validate the claim name - try { - validateClaimName(claimName); - } catch (error) { - reject(error); - return; - } - // if all validation passes, check availability of the name (note: do we need to re-validate channel name vs. credentials as well?) - return isNameAvailable(claimName, '/api/isClaimAvailable/') - .then(result => { - if (result) { - resolve(); - } else { - reject(new NameError('Sorry, that ending is already taken')); + break; + case 'image/gif': + if (file.size > 50000000) { + console.log('file was too big'); + throw new Error('Sorry, .gifs are limited to 50 megabytes.'); } - }) - .catch(error => { + break; + case 'video/mp4': + if (file.size > 50000000) { + console.log('file was too big'); + throw new Error('Sorry, videos are limited to 50 megabytes.'); + } + break; + default: + console.log('file type is not supported'); + throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.') + } + }, + // validation function that checks to make sure the claim name is valid + validateClaimName: function (name) { + console.log('validating the claim name'); + // ensure a name was entered + if (name.length < 1) { + throw new NameError("You must enter a name for your url"); + } + // validate the characters in the 'name' field + const invalidCharacters = /[^A-Za-z0-9,-]/g.exec(name); + if (invalidCharacters) { + throw new NameError('"' + invalidCharacters + '" characters are not allowed'); + } + }, + validateChannelName: function (name) { + name = name.substring(name.indexOf('@') + 1); + // ensure a name was entered + if (name.length < 1) { + throw new ChannelNameError("You must enter a name for your channel"); + } + // validate the characters in the 'name' field + const invalidCharacters = /[^A-Za-z0-9,-,@]/g.exec(name); + if (invalidCharacters) { + throw new ChannelNameError('"' + invalidCharacters + '" characters are not allowed'); + } + }, + validatePassword: function (password) { + if (password.length < 1) { + throw new ChannelPasswordError("You must enter a password for you channel"); + } + }, + cleanseClaimName: function (name) { + name = name.replace(/\s+/g, '-'); // replace spaces with dashes + name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-' + return name; + }, + // validation functions to check claim & channel name eligibility as the inputs change + isNameAvailable: function (name, apiUrl) { + const url = apiUrl + name; + return getRequest(url) + }, + showError: function (errorDisplay, errorMsg) { + errorDisplay.hidden = false; + errorDisplay.innerText = errorMsg; + }, + hideError: function (errorDisplay) { + errorDisplay.hidden = true; + errorDisplay.innerText = ''; + }, + showSuccess: function (successElement) { + successElement.hidden = false; + successElement.innerHTML = "✔"; + }, + hideSuccess: function (successElement) { + successElement.hidden = true; + successElement.innerHTML = ""; + }, + checkAvailability: function (name, successDisplayElement, errorDisplayElement, validateName, errorMessage, apiUrl) { + var that = this; + try { + // check to make sure the characters are valid + validateName(name); + // check to make sure it is available + that.isNameAvailable(name, apiUrl) + .then(function (result) { + if (result === true) { + that.hideError(errorDisplayElement); + that.showSuccess(successDisplayElement) + } else { + that.hideSuccess(successDisplayElement); + that.showError(errorDisplayElement, errorMessage); + } + }) + .catch(error => { + that.hideSuccess(successDisplayElement); + that.showError(errorDisplayElement, error.message); + }); + } catch (error) { + that.hideSuccess(successDisplayElement); + that.showError(errorDisplayElement, error.message); + } + }, + checkClaimName: function (name) { + const successDisplayElement = document.getElementById('input-success-claim-name'); + const errorDisplayElement = document.getElementById('input-error-claim-name'); + this.checkAvailability(name, successDisplayElement, errorDisplayElement, this.validateClaimName, 'Sorry, that ending is already taken', '/api/isClaimAvailable/'); + }, + checkChannelName: function (name) { + const successDisplayElement = document.getElementById('input-success-channel-name'); + const errorDisplayElement = document.getElementById('input-error-channel-name'); + name = `@${name}`; + this.checkAvailability(name, successDisplayElement, errorDisplayElement, this.validateChannelName, 'Sorry, that name is already taken', '/api/isChannelAvailable/'); + }, + // validation function which checks all aspects of the publish submission + validateFilePublishSubmission: function (stagedFiles, metadata) { + const channelName = metadata.channelName; + const claimName = metadata.name; + var that = this; + return new Promise(function (resolve, reject) { + // 1. make sure 1 file was staged + if (!stagedFiles) { + reject(new FileError("Please select a file")); + return; + } else if (stagedFiles.length > 1) { + reject(new FileError("Only one file is allowed at a time")); + return; + } + // 2. validate the file's name, type, and size + try { + that.validateFile(stagedFiles[0]); + } catch (error) { reject(error); - }); - }); -} - -// validation function which checks all aspects of a new channel submission -function validateNewChannelSubmission(userName, password){ - const channelName = `@${userName}`; - return new Promise(function (resolve, reject) { - // 1. validate name - try { - validateChannelName(channelName); - } catch (error) { - return reject(error); - } - // 2. validate password - try { - validatePassword(password); - } catch (error) { - return reject(error); - } - // 3. if all validation passes, check availability of the name - isNameAvailable(channelName, '/api/isChannelAvailable/') // validate the availability - .then(result => { - if (result) { - resolve(); - } else { - reject(new ChannelNameError('Sorry, that name is already taken')); - } - }) - .catch( error => { - console.log('error evaluating channel name availability', error); + return; + } + // 3. validate that a channel was chosen + if (channelName === 'new' || channelName === 'login') { + reject(new ChannelNameError("Please log in to a channel")); + return; + } + ; + // 4. validate the claim name + try { + that.validateClaimName(claimName); + } catch (error) { reject(error); - }); - }); -} -// validation function which checks all aspects of a new channel login -function validateNewChannelLogin(userName, password){ - const channelName = `@${userName}`; - return new Promise(function (resolve, reject) { - // 1. validate name - try { - validateChannelName(channelName); - } catch (error) { - return reject(error); - } - // 2. validate password - try { - validatePassword(password); - } catch (error) { - return reject(error); - } - resolve(); - }); -} \ No newline at end of file + return; + } + // if all validation passes, check availability of the name (note: do we need to re-validate channel name vs. credentials as well?) + return that.isNameAvailable(claimName, '/api/isClaimAvailable/') + .then(result => { + if (result) { + resolve(); + } else { + reject(new NameError('Sorry, that ending is already taken')); + } + }) + .catch(error => { + reject(error); + }); + }); + }, + // validation function which checks all aspects of a new channel submission + validateNewChannelSubmission: function (userName, password) { + const channelName = `@${userName}`; + var that = this; + return new Promise(function (resolve, reject) { + // 1. validate name + try { + that.validateChannelName(channelName); + } catch (error) { + return reject(error); + } + // 2. validate password + try { + that.validatePassword(password); + } catch (error) { + return reject(error); + } + // 3. if all validation passes, check availability of the name + that.isNameAvailable(channelName, '/api/isChannelAvailable/') // validate the availability + .then(function(result) { + if (result) { + resolve(); + } else { + reject(new ChannelNameError('Sorry, that name is already taken')); + } + }) + .catch(function(error) { + console.log('error evaluating channel name availability', error); + reject(error); + }); + }); + }, + // validation function which checks all aspects of a new channel login + validateNewChannelLogin: function (userName, password) { + const channelName = `@${userName}`; + var that = this; + return new Promise(function (resolve, reject) { + // 1. validate name + try { + that.validateChannelName(channelName); + } catch (error) { + return reject(error); + } + // 2. validate password + try { + that.validatePassword(password); + } catch (error) { + return reject(error); + } + resolve(); + }); + } +}; \ No newline at end of file diff --git a/views/partials/channelCreationForm.handlebars b/views/partials/channelCreationForm.handlebars index 685edad9..911deae5 100644 --- a/views/partials/channelCreationForm.handlebars +++ b/views/partials/channelCreationForm.handlebars @@ -6,7 +6,7 @@
@ - +
diff --git a/views/partials/publishForm-Url.handlebars b/views/partials/publishForm-Url.handlebars index 5e88224b..d5200ae4 100644 --- a/views/partials/publishForm-Url.handlebars +++ b/views/partials/publishForm-Url.handlebars @@ -9,7 +9,7 @@ {{user.channelName}}:{{user.shortChannelId}} xyzThis will be a random id / - +
-- 2.45.2 From e34e78c169fd96ec70047312590f6631737937bf Mon Sep 17 00:00:00 2001 From: bill bittner Date: Fri, 3 Nov 2017 13:37:23 -0700 Subject: [PATCH 07/21] fixed the anonymous discrepency --- README.md | 20 +++---- auth/authentication.js | 32 ++++++----- controllers/publishController.js | 19 ++++--- helpers/errorHandlers.js | 11 +++- helpers/publishHelpers.js | 27 +--------- public/assets/js/publishFileFunctions.js | 22 +++----- routes/api-routes.js | 54 +++++++++++++------ views/partials/publishForm-Channel.handlebars | 8 +-- 8 files changed, 97 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index c6a7f6d2..03d3b0dc 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,17 @@ spee.ch is a single-serving site that reads and publishes images and videos to a #### POST * /api/publish - * example: `curl -X POST -F 'name=MyPictureName' -F 'nsfw=false' -F 'file=@/path/to/my/picture.jpeg' https://spee.ch/api/publish` + * example: `curl -X POST -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/publish` * Parameters: - * name (string) - * nsfw (boolean) - * file (.mp4, .jpeg, .jpg, .gif, or .png) - * license (string, optional) - * title (string, optional) - * description (string, optional) - * thumbnail (string, optional) (for .mp4 uploads only) - * channelName(string, optional) - * channelPassword (string, optional) + * `name` + * `file` (.mp4, .jpeg, .jpg, .gif, or .png) + * `nsfw` (optional) + * `license` (optional) + * `title` (optional) + * `description` (optional) + * `thumbnail` url to thumbnail image, for .mp4 uploads only (optional) + * `channelName`(optional) + * `channelPassword` (optional,; required if `channelName` is provided) ## bugs If you find a bug or experience a problem, please report your issue here on github and find us in the lbry slack! diff --git a/auth/authentication.js b/auth/authentication.js index 631aee66..8c2045c0 100644 --- a/auth/authentication.js +++ b/auth/authentication.js @@ -2,12 +2,14 @@ const db = require('../models'); const logger = require('winston'); module.exports = { - authenticateChannelCredentials (channelName, userPassword) { + authenticateChannelCredentials (skipAuth, channelName, userPassword) { return new Promise((resolve, reject) => { - if (!channelName) { - resolve(true); + // skip authentication if not needed + if (skipAuth) { + resolve(skipAuth); return; } + // authentication const userName = channelName.substring(1); logger.debug(`authenticateChannelCredentials > channelName: ${channelName} username: ${userName} pass: ${userPassword}`); db.User @@ -18,17 +20,23 @@ module.exports = { resolve(false); return; } - if (!user.validPassword(userPassword, user.password)) { - logger.debug('incorrect password'); - resolve(false); - return; - } - logger.debug('user found:', user.dataValues); - resolve(true); + return user.comparePassword(userPassword, (passwordErr, isMatch) => { + if (passwordErr) { + logger.error('comparePassword error:', passwordErr); + resolve(false); + return; + } + if (!isMatch) { + logger.debug('incorrect password'); + resolve(false); + return; + } + logger.debug('...password was a match...'); + resolve(true); + }); }) .catch(error => { - logger.error(error); - reject(); + reject(error); }); }); }, diff --git a/controllers/publishController.js b/controllers/publishController.js index 9af56217..f57d3eae 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -13,17 +13,16 @@ module.exports = { .then(tx => { logger.info(`Successfully published ${fileName}`, tx); publishResults = tx; - return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); // note: should this be db.User ?? - }) - .then(channel => { - let certificateId; - if (channel) { - certificateId = channel.channelClaimId; - logger.debug('successfully found channel in Channel table'); + if (publishParams.channel_name) { + logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); + return db.Channel.findOne({where: {channelName: publishParams.channel_name}}).then(channel => { return channel.channelClaimId }); } else { - certificateId = null; - logger.debug('channel for publish not found in Channel table'); - }; + logger.debug('this claim was published in channel: n/a'); + return null; + } + }) + .then(certificateId => { + logger.debug(`certificateId: ${certificateId}`); const fileRecord = { name : publishParams.name, claimId : publishResults.claim_id, diff --git a/helpers/errorHandlers.js b/helpers/errorHandlers.js index 5bc73edf..9aafc4b7 100644 --- a/helpers/errorHandlers.js +++ b/helpers/errorHandlers.js @@ -3,11 +3,12 @@ const { postToStats } = require('../controllers/statsController.js'); module.exports = { returnErrorMessageAndStatus: function (error) { - let status; - let message; + let status, message; + // check for daemon being turned off if (error.code === 'ECONNREFUSED') { status = 503; message = 'Connection refused. The daemon may not be running.'; + // check for errors from the deamon } else if (error.response) { status = error.response.status || 500; if (error.response.data) { @@ -21,7 +22,13 @@ module.exports = { } else { message = error.response; } + // check for spee.ch thrown errors + } else if (error.message) { + status = 400; + message = error.message; + // fallback for everything else } else { + status = 400; message = error; } return [status, message]; diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index a9142274..4489f290 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -18,11 +18,10 @@ module.exports = { throw new Error('no file with key of [file] found in request'); } }, - validatePublishSubmission (file, claimName, nsfw) { + validatePublishSubmission (file, claimName) { try { module.exports.validateFile(file); module.exports.validateClaimName(claimName); - module.exports.validateNSFW(nsfw); } catch (error) { throw error; } @@ -76,24 +75,6 @@ module.exports = { throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing through spee.ch'); } }, - cleanseNSFW (nsfw) { - switch (nsfw) { - case true: - case 'on': - case 'true': - case 1: - case '1': - return true; - case false: - case 'false': - case 'off': - case 0: - case '0': - return false; - default: - return false; - } - }, cleanseChannelName (channelName) { if (channelName) { if (channelName.indexOf('@') !== 0) { @@ -102,12 +83,6 @@ module.exports = { } return channelName; }, - validateNSFW (nsfw) { - if (nsfw === true || nsfw === false) { - return; - } - throw new Error('NSFW must be set to either true or false'); - }, createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) { logger.debug(`Creating Publish Parameters`); // provide defaults for title diff --git a/public/assets/js/publishFileFunctions.js b/public/assets/js/publishFileFunctions.js index 6dfb8060..4cdc1ebc 100644 --- a/public/assets/js/publishFileFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -64,20 +64,12 @@ var publishFileFunctions = { } }, returnNullOrChannel: function () { - const channelInput = document.getElementById('channel-name-select'); - const radios = document.getElementsByName('anonymous-or-channel'); - let anonymousOrInChannel; - // replace channelName with 'anonymous' if appropriate - for (let i = 0; i < radios.length; i++) { - if (radios[i].checked) { - anonymousOrInChannel = radios[i].value; // do whatever you want with the checked radio - break; // only one radio can be logically checked, don't check the rest - } + const channelRadio = document.getElementById('channel-radio'); + if (channelRadio.checked) { + const channelInput = document.getElementById('channel-name-select'); + return channelInput.value.trim(); } - if (anonymousOrInChannel === 'anonymous') { - return null; - }; - return channelInput.value.trim(); + return null; }, createMetadata: function() { const nameInput = document.getElementById('claim-name-input'); @@ -89,13 +81,13 @@ var publishFileFunctions = { return { name: nameInput.value.trim(), - channel: this.returnNullOrChannel(), + channelName: this.returnNullOrChannel(), title: titleInput.value.trim(), description: descriptionInput.value.trim(), license: licenseInput.value.trim(), nsfw: nsfwInput.checked, type: stagedFiles[0].type, - thumbnail: thumbnailInput.value.trim() + thumbnail: thumbnailInput.value.trim(), } }, appendDataToFormData: function (file, metadata) { diff --git a/routes/api-routes.js b/routes/api-routes.js index 6ebca2f5..419ced91 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -4,7 +4,7 @@ const multipartMiddleware = multipart(); const db = require('../models'); const { publish } = require('../controllers/publishController.js'); const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); -const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseNSFW, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); +const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); const { authenticateChannelCredentials } = require('../auth/authentication.js'); @@ -71,7 +71,9 @@ module.exports = (app) => { }); }); // route to run a publish request on the daemon - app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl }, res) => { + app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => { + logger.debug('api/publish body:', body); + let file, fileName, filePath, fileType, name, nsfw, license, title, description, thumbnail, anonymous, skipAuth, channelName, channelPassword; // validate that mandatory parts of the request are present try { validateApiPublishRequest(body, files); @@ -81,12 +83,12 @@ module.exports = (app) => { return; } // validate file, name, license, and nsfw - const file = files.file; - const fileName = file.name; - const filePath = file.path; - const fileType = file.type; - const name = body.name; - const nsfw = cleanseNSFW(body.nsfw); // cleanse nsfw input + file = files.file; + fileName = file.name; + filePath = file.path; + fileType = file.type; + name = body.name; + nsfw = (body.nsfw === 'true'); try { validatePublishSubmission(file, name, nsfw); } catch (error) { @@ -94,18 +96,36 @@ module.exports = (app) => { res.status(400).json({success: false, message: error.message}); return; } - logger.debug(`name: ${name}, nsfw: ${nsfw}`); // optional inputs - const license = body.license || null; - const title = body.title || null; - const description = body.description || null; - const thumbnail = body.thumbnail || null; - let channelName = body.channelName || null; + license = body.license || null; + title = body.title || null; + description = body.description || null; + thumbnail = body.thumbnail || null; + anonymous = (body.channelName === 'null') || (body.channelName === undefined); + if (user) { + channelName = user.channelName || null; + } else { + channelName = body.channelName || null; + } + channelPassword = body.channelPassword || null; + skipAuth = false; + // case 1: publish from spee.ch, client logged in + if (user) { + skipAuth = true; + if (anonymous) { + channelName = null; + } + // case 2: publish from api or spee.ch, client not logged in + } else { + if (anonymous) { + skipAuth = true; + channelName = null; + } + } channelName = cleanseChannelName(channelName); - const channelPassword = body.channelPassword || null; - logger.debug(`license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`); + logger.debug(`name: ${name}, license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`); // check channel authorization - authenticateChannelCredentials(channelName, channelPassword) + authenticateChannelCredentials(skipAuth, channelName, channelPassword) .then(result => { if (!result) { throw new Error('Authentication failed, you do not have access to that channel'); diff --git a/views/partials/publishForm-Channel.handlebars b/views/partials/publishForm-Channel.handlebars index 222726b6..43fb5d70 100644 --- a/views/partials/publishForm-Channel.handlebars +++ b/views/partials/publishForm-Channel.handlebars @@ -3,11 +3,11 @@
- - + +
- - + +
-- 2.45.2 From fc5d8510ad0d167e62348adcd3638780e899c5e4 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Fri, 3 Nov 2017 14:23:54 -0700 Subject: [PATCH 08/21] updated Claim record save after publish to include channelName --- controllers/publishController.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index f57d3eae..19766c05 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -15,13 +15,19 @@ module.exports = { publishResults = tx; if (publishParams.channel_name) { logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); - return db.Channel.findOne({where: {channelName: publishParams.channel_name}}).then(channel => { return channel.channelClaimId }); + return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); } else { logger.debug('this claim was published in channel: n/a'); return null; } }) - .then(certificateId => { + .then(channel => { + let certificateId = null; + let channelName = null; + if (channel) { + certificateId = channel.channelClaimId; + channelName = channel.channelName; + } logger.debug(`certificateId: ${certificateId}`); const fileRecord = { name : publishParams.name, @@ -47,8 +53,9 @@ module.exports = { height : 0, contentType: fileType, nsfw : publishParams.metadata.nsfw, - certificateId, amount : publishParams.bid, + certificateId, + channelName, }; const upsertCriteria = { name : publishParams.name, -- 2.45.2 From 0517326c43b8402ea1d5d6ce0593f2a66b7ffa75 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Fri, 3 Nov 2017 16:18:00 -0700 Subject: [PATCH 09/21] removed the record console log --- routes/serve-routes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/routes/serve-routes.js b/routes/serve-routes.js index 2e2ea652..4f080004 100644 --- a/routes/serve-routes.js +++ b/routes/serve-routes.js @@ -49,7 +49,6 @@ function extractPageFromClaims (claims, pageNumber) { const claimStartIndex = (pageNumber - 1) * CLAIMS_PER_PAGE; const claimEndIndex = claimStartIndex + 10; const pageOfClaims = claims.slice(claimStartIndex, claimEndIndex); - logger.debug('page of claims:', pageOfClaims); return pageOfClaims; } -- 2.45.2 From 63458aff19a1c7a0c970a8406ddd7397b733a210 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 6 Nov 2017 14:15:47 -0800 Subject: [PATCH 10/21] changed the multipart download directory --- controllers/publishController.js | 15 ++++++++++----- models/index.js | 2 +- package.json | 1 - routes/api-routes.js | 5 +++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 19766c05..5dd06482 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -6,13 +6,13 @@ const publishHelpers = require('../helpers/publishHelpers.js'); module.exports = { publish (publishParams, fileName, fileType) { return new Promise((resolve, reject) => { - let publishResults = {}; - // 1. publish the file + let publishResults, certificateId, channelName; + // publish the file return lbryApi.publishClaim(publishParams) - // 2. upsert File record (update is in case the claim has been published before by this daemon) .then(tx => { logger.info(`Successfully published ${fileName}`, tx); publishResults = tx; + // get the channel information if (publishParams.channel_name) { logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); @@ -22,13 +22,16 @@ module.exports = { } }) .then(channel => { - let certificateId = null; - let channelName = null; + certificateId = null; + channelName = null; if (channel) { certificateId = channel.channelClaimId; channelName = channel.channelName; } logger.debug(`certificateId: ${certificateId}`); + }) + .then(() => { + // create the File record const fileRecord = { name : publishParams.name, claimId : publishResults.claim_id, @@ -42,6 +45,7 @@ module.exports = { fileType, nsfw : publishParams.metadata.nsfw, }; + // create the Claim record const claimRecord = { name : publishParams.name, claimId : publishResults.claim_id, @@ -57,6 +61,7 @@ module.exports = { certificateId, channelName, }; + // upsert File record (update is in case the claim has been published before by this daemon) const upsertCriteria = { name : publishParams.name, claimId: publishResults.claim_id, diff --git a/models/index.js b/models/index.js index f8293431..62e4b23d 100644 --- a/models/index.js +++ b/models/index.js @@ -66,7 +66,7 @@ db.upsert = (Model, values, condition, tableName) => { } }) .catch(function (error) { - logger.error('Sequelize findOne error', error); + logger.error(`${tableName}.upsert error`, error); }); }; diff --git a/package.json b/package.json index 00b84557..9bf792f2 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "cookie-session": "^2.0.0-beta.3", "express": "^4.15.2", "express-handlebars": "^3.0.0", - "express-session": "^1.15.5", "form-data": "^2.3.1", "helmet": "^3.8.1", "mysql2": "^1.3.5", diff --git a/routes/api-routes.js b/routes/api-routes.js index 419ced91..edcc6cb7 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -1,6 +1,6 @@ const logger = require('winston'); const multipart = require('connect-multiparty'); -const multipartMiddleware = multipart(); +const multipartMiddleware = multipart({uploadDir: '/home/lbry/test/'}); const db = require('../models'); const { publish } = require('../controllers/publishController.js'); const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); @@ -73,6 +73,7 @@ module.exports = (app) => { // route to run a publish request on the daemon app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => { logger.debug('api/publish body:', body); + logger.debug('api/publish body:', files); let file, fileName, filePath, fileType, name, nsfw, license, title, description, thumbnail, anonymous, skipAuth, channelName, channelPassword; // validate that mandatory parts of the request are present try { @@ -84,7 +85,7 @@ module.exports = (app) => { } // validate file, name, license, and nsfw file = files.file; - fileName = file.name; + fileName = file.path.substring(file.path.lastIndexOf('/') + 1); filePath = file.path; fileType = file.type; name = body.name; -- 2.45.2 From 1f37c33206146cfd0f7613817dd17d9fde9f66f8 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 6 Nov 2017 14:22:00 -0800 Subject: [PATCH 11/21] updated comments --- controllers/publishController.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 5dd06482..d832c814 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -22,6 +22,7 @@ module.exports = { } }) .then(channel => { + // set channel information certificateId = null; channelName = null; if (channel) { @@ -61,12 +62,12 @@ module.exports = { certificateId, channelName, }; - // upsert File record (update is in case the claim has been published before by this daemon) + // upsert criteria const upsertCriteria = { name : publishParams.name, claimId: publishResults.claim_id, }; - // create the records + // upsert the records return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim')]); }) .then(([file, claim]) => { -- 2.45.2 From 40d40c22786d13bc338139ee51cadb72024591f4 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 6 Nov 2017 15:18:45 -0800 Subject: [PATCH 12/21] consolidated config to one config file --- .gitignore | 3 ++- config/custom-environment-variables.json | 15 ------------ config/default-0.json | 1 - config/default.json | 23 ------------------- config/development.json | 13 ----------- config/production.json | 13 ----------- .../{slackLoggerConfig.js => slackConfig.js} | 19 +++++++-------- config/speechConfig_example.js | 23 +++++++++++++++++++ controllers/statsController.js | 4 ++-- helpers/configVarCheck.js | 10 ++++---- helpers/handlebarsHelpers.js | 4 ++-- helpers/publishHelpers.js | 6 ++--- models/index.js | 8 +++---- public/assets/js/publishFileFunctions.js | 2 +- speech.js | 8 +++---- 15 files changed, 53 insertions(+), 99 deletions(-) delete mode 100644 config/custom-environment-variables.json delete mode 100644 config/default-0.json delete mode 100644 config/default.json delete mode 100644 config/development.json delete mode 100644 config/production.json rename config/{slackLoggerConfig.js => slackConfig.js} (59%) create mode 100644 config/speechConfig_example.js diff --git a/.gitignore b/.gitignore index 761ac590..bc72ae59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .idea -config/config.json \ No newline at end of file +config/config.json +config/speechConfig.js \ No newline at end of file diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json deleted file mode 100644 index 78faebbf..00000000 --- a/config/custom-environment-variables.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "WalletConfig": { - "LbryClaimAddress": "LBRY_CLAIM_ADDRESS" - }, - "Database": { - "Username": "MYSQL_USERNAME", - "Password": "MYSQL_PASSWORD" - }, - "Logging": { - "SlackWebHook": "SLACK_WEB_HOOK" - }, - "Session": { - "SessionKey": "SESSION_KEY" - } -} \ No newline at end of file diff --git a/config/default-0.json b/config/default-0.json deleted file mode 100644 index 9e26dfee..00000000 --- a/config/default-0.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/config/default.json b/config/default.json deleted file mode 100644 index c5b65a20..00000000 --- a/config/default.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "WalletConfig": { - "LbryClaimAddress": null, - "DefaultChannel": null - }, - "AnalyticsConfig":{ - "GoogleId": null - }, - "Database": { - "Database": "lbry", - "Username": null, - "Password": null - }, - "Logging": { - "LogLevel": null, - "SlackWebHook": null, - "SlackErrorChannel": null, - "SlackInfoChannel": null - }, - "Session": { - "SessionKey": null - } -} \ No newline at end of file diff --git a/config/development.json b/config/development.json deleted file mode 100644 index 4beca9ff..00000000 --- a/config/development.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "WalletConfig": { - "DefaultChannel": "@speechDev" - }, - "AnalyticsConfig":{ - "GoogleId": "UA-100747990-1" - }, - "Logging": { - "LogLevel": "silly", - "SlackErrorChannel": "#staging_speech-errors", - "SlackInfoChannel": "none" - } -} \ No newline at end of file diff --git a/config/production.json b/config/production.json deleted file mode 100644 index a5bc5074..00000000 --- a/config/production.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "WalletConfig": { - "DefaultChannel": "@speech" - }, - "AnalyticsConfig":{ - "GoogleId": "UA-60403362-3" - }, - "Logging": { - "LogLevel": "verbose", - "SlackErrorChannel": "#speech-errors", - "SlackInfoChannel": "#speech-logs" - } -} diff --git a/config/slackLoggerConfig.js b/config/slackConfig.js similarity index 59% rename from config/slackLoggerConfig.js rename to config/slackConfig.js index 4bd88200..74227bbd 100644 --- a/config/slackLoggerConfig.js +++ b/config/slackConfig.js @@ -1,29 +1,26 @@ -const config = require('config'); -const SLACK_WEB_HOOK = config.get('Logging.SlackWebHook'); -const SLACK_ERROR_CHANNEL = config.get('Logging.SlackErrorChannel'); -const SLACK_INFO_CHANNEL = config.get('Logging.SlackInfoChannel'); +const config = require('./speechConfig.js'); const winstonSlackWebHook = require('winston-slack-webhook').SlackWebHook; module.exports = (winston) => { - if (SLACK_WEB_HOOK) { - // add a transport for errors to slack + if (config.logging.slackWebHook) { + // add a transport for errors to slack winston.add(winstonSlackWebHook, { name : 'slack-errors-transport', level : 'error', - webhookUrl: SLACK_WEB_HOOK, - channel : SLACK_ERROR_CHANNEL, + webhookUrl: config.logging.slackWebHook, + channel : config.logging.slackErrorChannel, username : 'spee.ch', iconEmoji : ':face_with_head_bandage:', }); winston.add(winstonSlackWebHook, { name : 'slack-info-transport', level : 'info', - webhookUrl: SLACK_WEB_HOOK, - channel : SLACK_INFO_CHANNEL, + webhookUrl: config.logging.slackWebHook, + channel : config.logging.slackInfoChannel, username : 'spee.ch', iconEmoji : ':nerd_face:', }); - // send test message + // send test message winston.error('Slack error logging is online.'); winston.info('Slack info logging is online.'); } else { diff --git a/config/speechConfig_example.js b/config/speechConfig_example.js new file mode 100644 index 00000000..d139c56c --- /dev/null +++ b/config/speechConfig_example.js @@ -0,0 +1,23 @@ +module.exports = { + wallet: { + lbryClaimAddress: null, + defaultChannel : null, + }, + analytics: { + googleId: null, + }, + sql: { + database: 'lbry', + username: null, + password: null, + }, + logging: { + logLevel : null, + slackWebHook : null, + slackErrorChannel: null, + slackInfoChannel : null, + }, + session: { + sessionKey: null, + }, +}; diff --git a/controllers/statsController.js b/controllers/statsController.js index 50dcc36d..6f3d4475 100644 --- a/controllers/statsController.js +++ b/controllers/statsController.js @@ -1,8 +1,8 @@ const logger = require('winston'); const ua = require('universal-analytics'); -const config = require('config'); +const config = require('../config/speechConfig.js'); const db = require('../models'); -const googleApiKey = config.get('AnalyticsConfig.GoogleId'); +const googleApiKey = config.analytics.googleId; module.exports = { postToStats (action, url, ipAddress, name, claimId, result) { diff --git a/helpers/configVarCheck.js b/helpers/configVarCheck.js index b4b40e51..b849245f 100644 --- a/helpers/configVarCheck.js +++ b/helpers/configVarCheck.js @@ -1,15 +1,13 @@ -const config = require('config'); +const config = require('../config/speechConfig.js'); const logger = require('winston'); -const fs = require('fs'); module.exports = function () { // get the config file - const defaultConfigFile = JSON.parse(fs.readFileSync('./config/default.json')); - for (let configCategoryKey in defaultConfigFile) { - if (defaultConfigFile.hasOwnProperty(configCategoryKey)) { + for (let configCategoryKey in config) { + if (config.hasOwnProperty(configCategoryKey)) { // get the final variables for each config category - const configVariables = config.get(configCategoryKey); + const configVariables = config[configCategoryKey]; for (let configVarKey in configVariables) { if (configVariables.hasOwnProperty(configVarKey)) { // print each variable diff --git a/helpers/handlebarsHelpers.js b/helpers/handlebarsHelpers.js index b4fb3c42..33ccb49d 100644 --- a/helpers/handlebarsHelpers.js +++ b/helpers/handlebarsHelpers.js @@ -1,10 +1,10 @@ const Handlebars = require('handlebars'); -const config = require('config'); +const config = require('../config/speechConfig.js'); module.exports = { // define any extra helpers you may need googleAnalytics () { - const googleApiKey = config.get('AnalyticsConfig.GoogleId'); + const googleApiKey = config.analytics.googleId; return new Handlebars.SafeString( `