From f64961446f71796521af1e81fc4917c939af31e2 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 19 Sep 2017 08:47:24 -0700 Subject: [PATCH] added channel select to publish tool --- helpers/publishHelpers.js | 30 ++++++++ passport/local-login.js | 2 +- passport/local-signup.js | 2 +- public/assets/css/componentStyle.css | 9 ++- public/assets/js/validationFunctions.js | 97 ++++++++++++++++++------ routes/api-routes.js | 18 ++++- routes/page-routes.js | 56 +++++++------- speech.js | 24 ++++-- views/allClaims.handlebars | 23 ------ views/login.handlebars | 23 +++++- views/partials/publishChannel.handlebars | 38 ++++++++++ views/partials/publishDetails.handlebars | 28 +++++++ views/partials/publishForm.handlebars | 47 ++++-------- views/partials/topBar.handlebars | 7 ++ views/profile.handlebars | 11 --- views/signup.handlebars | 20 ----- 16 files changed, 282 insertions(+), 153 deletions(-) delete mode 100644 views/allClaims.handlebars create mode 100644 views/partials/publishChannel.handlebars create mode 100644 views/partials/publishDetails.handlebars delete mode 100644 views/profile.handlebars delete mode 100644 views/signup.handlebars diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index ab0828c4..3462dfa2 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -130,4 +130,34 @@ module.exports = { }); }); }, + checkChannelAvailability (name) { + return new Promise((resolve, reject) => { + // find any records where the name is used + db.Certificate.findAll({ where: { name } }) + .then(result => { + if (result.length >= 1) { + // filter out any results that were not published from a spee.ch wallet address + getWalletList() + .then((walletList) => { + const filteredResult = result.filter((claim) => { + return walletList.includes(claim.address); + }); + if (filteredResult.length >= 1) { + resolve(false); + } else { + resolve(true); + } + }) + .catch((error) => { + reject(error); + }); + } else { + resolve(true); + } + }) + .catch(error => { + reject(error); + }); + }); + }, }; diff --git a/passport/local-login.js b/passport/local-login.js index 6fe29e2b..9420e1fa 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -20,7 +20,7 @@ module.exports = new PassportLocalStrategy( if (!user.validPassword(password, user.password)) { return done(null, false, {message: 'Incorrect username or password.'}); } - return done(null, user.dataValues); + return done(null, user); // user.dataValues? }) .catch(error => { return done(error); diff --git a/passport/local-signup.js b/passport/local-signup.js index 2da08450..0ae9ea94 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -26,7 +26,7 @@ module.exports = new PassportLocalStrategy( }) .then(user => { logger.debug('User record was created successfully'); - return done(null); + return done(null, user); // user.datavalues? }) .catch(error => { logger.debug(error); diff --git a/public/assets/css/componentStyle.css b/public/assets/css/componentStyle.css index 27ffd4b2..32696fc7 100644 --- a/public/assets/css/componentStyle.css +++ b/public/assets/css/componentStyle.css @@ -54,12 +54,15 @@ width: 90% } -#claim-name-input { +.input-text--primary { border: 0px; + background-color: #ffffff; + border-bottom: 1px solid grey; } -#claim-name-input:focus { - outline: none +.input-text--primary:focus { + outline: none; + border-bottom: 1px solid blue; } /* show routes */ diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index cd9ecb47..b73b16c3 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -28,29 +28,55 @@ function validateFile(file) { } } // validation function that checks to make sure the claim name is not already claimed -function isNameAvailable (name) { - return new Promise(function(resolve, reject) { - // make sure the claim name is still 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) { - resolve(); - } else { - reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); - } - } else { - reject("request to check claim name failed with status:" + this.status); - }; - } - }; - xhttp.send(); - }); +function isChannelAvailable (name) { + return new Promise(function(resolve, reject) { + // make sure the claim name is still available + var xhttp; + xhttp = new XMLHttpRequest(); + xhttp.open('GET', '/api/isChannelAvailable/' + name, true); + xhttp.responseType = 'json'; + xhttp.onreadystatechange = function() { + if (this.readyState == 4 ) { + if ( this.status == 200) { + if (this.response == true) { + resolve(); + } else { + reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); + } + } else { + reject("request to check claim name failed with status:" + this.status); + }; + } + }; + xhttp.send(); + }); } + +// validation function that checks to make sure the claim name is not already claimed +function isNameAvailable (name) { + return new Promise(function(resolve, reject) { + // make sure the claim name is still 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) { + resolve(); + } else { + reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); + } + } else { + reject("request to check claim name failed with status:" + this.status); + }; + } + }; + xhttp.send(); + }); +} + // validation function that checks to make sure the claim name is valid function validateClaimName (name) { // ensure a name was entered @@ -89,6 +115,31 @@ function checkClaimName(name){ document.getElementById('claim-name-available').hidden = true; } } + + +// validaiton function to check claim name as the input changes +function checkChannelName(event){ + console.log(event); + const name = event.target.value; + const target = document.getElementById(event.target.id); + const errorDisplay = target.parentNode.firstChild; + console.log('error display:', errorDisplay) + try { + // check to make sure it is available + isChannelAvailable(name) + .then(function() { + errorDisplay.hidden = false; + }) + .catch(function(error) { + errorDisplay.hidden = false; + showError(errorDisplay.getAttribute('id'), error.message); + }); + } catch (error) { + console.log(error.message); + document.getElementById(errorDisplay.getAttribute('id')).hidden = true; + } +} + // validation function which checks all aspects of the publish submission function validateSubmission(stagedFiles, name){ return new Promise(function (resolve, reject) { @@ -118,4 +169,4 @@ function validateSubmission(stagedFiles, name){ reject(error); }); }); -} \ No newline at end of file +} diff --git a/routes/api-routes.js b/routes/api-routes.js index ebd385a7..28a54daa 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -3,7 +3,7 @@ const multipart = require('connect-multiparty'); const multipartMiddleware = multipart(); const { publish } = require('../controllers/publishController.js'); const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); -const { createPublishParams, validateFile, checkNameAvailability } = require('../helpers/publishHelpers.js'); +const { createPublishParams, validateFile, checkNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); @@ -38,6 +38,22 @@ module.exports = (app, hostedContentPath) => { res.status(500).json(error); }); }); + // route to check whether spee.ch has published to a channel + app.get('/api/isChannelAvailable/:name', ({ ip, originalUrl, params }, res) => { + // send response + checkChannelAvailability(params.name) + .then(result => { + if (result === true) { + res.status(200).json(true); + } else { + logger.debug(`Rejecting publish request because ${params.name} has already been published via spee.ch`); + res.status(200).json(false); + } + }) + .catch(error => { + res.status(500).json(error); + }); + }); // route to run a resolve request on the daemon app.get('/api/resolve/:uri', ({ headers, ip, originalUrl, params }, res) => { // google analytics diff --git a/routes/page-routes.js b/routes/page-routes.js index 94d75adb..2aba1bd8 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -1,30 +1,35 @@ const errorHandlers = require('../helpers/errorHandlers.js'); -const db = require('../models'); const { postToStats, getStatsSummary, getTrendingClaims, getRecentClaims } = require('../controllers/statsController.js'); const passport = require('passport'); -// const { deAuthenticate } = require('../auth/authentication.js'); +const logger = require('winston'); module.exports = (app) => { // route for auth app.post('/signup', passport.authenticate('local-signup'), (req, res) => { - console.log('redirecting to user channel'); - // If this function gets called, authentication was successful. + logger.debug('redirecting to user channel'); + // If this function gets called, signup was successful. // `req.user` contains the authenticated user. res.redirect('/@' + req.user.channelName); }); app.post('/login', passport.authenticate('local-login'), (req, res) => { - console.log('redirecting to user channel'); - // If this function gets called, authentication was successful. + logger.debug('redirecting to user channel'); + // If this function gets called, login was successful. // `req.user` contains the authenticated user. res.redirect('/@' + req.user.channelName); }); + // route to log out + app.get('/logout', (req, res) => { + req.logout(); + res.redirect('/login'); + }); // route to display login page app.get('/login', (req, res) => { - res.status(200).render('login'); - }); - app.get('/signup', (req, res) => { - res.status(200).render('signup'); + if (req.user) { + res.status(200).redirect(`/@${req.user.channelName}`); + } else { + res.status(200).render('login'); + } }); // route to display login page // app.get('/users/:name', isAuthenticated, (req, res) => { @@ -50,7 +55,9 @@ module.exports = (app) => { getTrendingClaims(dateTime) .then(result => { // logger.debug(result); - res.status(200).render('trending', { trendingAssets: result }); + res.status(200).render('trending', { + trendingAssets: result, + }); }) .catch(error => { errorHandlers.handleRequestError(error, res); @@ -61,21 +68,26 @@ module.exports = (app) => { getRecentClaims() .then(result => { // logger.debug(result); - res.status(200).render('new', { newClaims: result }); + res.status(200).render('new', { + newClaims: result, + }); }) .catch(error => { errorHandlers.handleRequestError(error, res); }); }); // route to show statistics for spee.ch - app.get('/stats', ({ ip, originalUrl }, res) => { + app.get('/stats', ({ ip, originalUrl, user }, res) => { // get and render the content const startDate = new Date(); startDate.setDate(startDate.getDate() - 1); getStatsSummary(startDate) .then(result => { postToStats('show', originalUrl, ip, null, null, 'success'); - res.status(200).render('statistics', result); + res.status(200).render('statistics', { + user, + result, + }); }) .catch(error => { errorHandlers.handleRequestError(error, res); @@ -91,20 +103,8 @@ module.exports = (app) => { res.status(200).render('embed', { layout: 'embed', claimId, name }); }); // route to display all free public claims at a given name - app.get('/:name/all', ({ ip, originalUrl, params }, res) => { + app.get('/:name/all', (req, res) => { // get and render the content - db - .getAllFreeClaims(params.name) - .then(orderedFreeClaims => { - if (!orderedFreeClaims) { - res.status(307).render('noClaims'); - return; - } - postToStats('show', originalUrl, ip, null, null, 'success'); - res.status(200).render('allClaims', { claims: orderedFreeClaims }); - }) - .catch(error => { - errorHandlers.handleRequestError('show', originalUrl, ip, error, res); - }); + res.status(410).send('/:name/all is no longer supported'); }); }; diff --git a/speech.js b/speech.js index 2fbe3a90..fb7d9f9f 100644 --- a/speech.js +++ b/speech.js @@ -22,35 +22,35 @@ require('./config/slackLoggerConfig.js')(logger); // trust the proxy to get ip address for us app.enable('trust proxy'); + // add middleware app.use(helmet()); // set HTTP headers to protect against well-known web vulnerabilties app.use(express.static(`${__dirname}/public`)); // 'express.static' to serve static files from public directory app.use(bodyParser.json()); // 'body parser' for parsing application/json app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing application/x-www-form-urlencoded app.use(siofu.router); // 'socketio-file-upload' router for uploading with socket.io -app.use((req, res, next) => { // custom logging middleware to log all incomming http requests +app.use((req, res, next) => { // custom logging middleware to log all incoming http requests logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`); next(); }); + // initialize passport app.use(session({ secret: 'cats' })); app.use(passport.initialize()); app.use(passport.session()); -passport.serializeUser(function (user, done) { +passport.serializeUser((user, done) => { done(null, user.id); }); - -passport.deserializeUser(function (id, done) { +passport.deserializeUser((id, done) => { db.User.findOne({ where: { id } }) .then(user => { - done(null, user.dataValues); + done(null, user); // user.dataValues? }) .catch(error => { logger.error('sequelize error', error); + done(error, null); }); }); - -// Load passport strategies const localSignupStrategy = require('./passport/local-signup.js'); const localLoginStrategy = require('./passport/local-login.js'); passport.use('local-signup', localSignupStrategy); @@ -65,6 +65,16 @@ const hbs = expressHandlebars.create({ app.engine('handlebars', hbs.engine); app.set('view engine', 'handlebars'); +// middleware to pass user info back to client, if user is logged in +app.use((req, res, next) => { + if (req.user) { + res.locals.user = { + id : req.user.id, + channelName: req.user.channelName, + }; + } + next(); +}); // start the server db.sequelize .sync() // sync sequelize diff --git a/views/allClaims.handlebars b/views/allClaims.handlebars deleted file mode 100644 index 9531a577..00000000 --- a/views/allClaims.handlebars +++ /dev/null @@ -1,23 +0,0 @@ -
- {{> topBar}} -
-

All Claims

-

These are all the free, public assets at that claim. You can publish more at spee.ch.

- {{#each claims}} -
- -
-
    -
  • claim: {{this.name}}
  • -
  • claim_id: {{this.claim_id}}
  • -
  • link: spee.ch/{{this.name}}/{{this.claimId}}
  • -
  • author: {{this.value.stream.metadata.author}}
  • -
  • description: {{this.value.stream.metadata.description}}
  • -
  • license: {{this.value.stream.metadata.license}}
  • -
-
-
- {{/each}} -
- {{> footer}} -
\ No newline at end of file diff --git a/views/login.handlebars b/views/login.handlebars index 81fe1046..60f5df49 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -1,20 +1,39 @@
{{> topBar}}
+

Log In

+

Log in to an existing channel

- + @
- +
+ +

Create New

+

Create a brand new channel

+
+
+ + @ +
+
+ + +
+
+ +
+
+
{{> footer}}
diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars new file mode 100644 index 00000000..221ddaad --- /dev/null +++ b/views/partials/publishChannel.handlebars @@ -0,0 +1,38 @@ +
+ + + +
+ + \ No newline at end of file diff --git a/views/partials/publishDetails.handlebars b/views/partials/publishDetails.handlebars new file mode 100644 index 00000000..c04a003d --- /dev/null +++ b/views/partials/publishDetails.handlebars @@ -0,0 +1,28 @@ +

Details + [open] +

+ \ No newline at end of file diff --git a/views/partials/publishForm.handlebars b/views/partials/publishForm.handlebars index c329ad10..1e706417 100644 --- a/views/partials/publishForm.handlebars +++ b/views/partials/publishForm.handlebars @@ -12,41 +12,24 @@
-
- Spee.ch/ - -
- - - - - - - - - - - - - - - - - -
- -
+

+ Spee.ch/ + + +

+
+
+ {{> publishChannel}} +
+
+ {{> publishDetails}}

-

- - + + +

-

By clicking 'Publish' I attest that I have read and agree to the LBRY terms of service.

@@ -73,7 +56,5 @@ document.getElementById('input-error-claim-name').innerHTML = ''; document.getElementById('input-error-publish-submit').innerHTML = ''; document.getElementById('claim-name-available').hidden = true; - // remove nsfw check - } diff --git a/views/partials/topBar.handlebars b/views/partials/topBar.handlebars index 8a7f9384..f627f4df 100644 --- a/views/partials/topBar.handlebars +++ b/views/partials/topBar.handlebars @@ -4,5 +4,12 @@ popular source help + + {{#if user}} + @{{user.channelName}} + logout + {{else}} + login + {{/if}}
Open-source, decentralized image and video hosting.
\ No newline at end of file diff --git a/views/profile.handlebars b/views/profile.handlebars deleted file mode 100644 index 84b3515c..00000000 --- a/views/profile.handlebars +++ /dev/null @@ -1,11 +0,0 @@ -
- {{> topBar}} -
- {{#if isAuthenticated}} - {{> profile }} - {{else}} - {{> loginForm}} - {{/if}} -
- {{> footer}} -
\ No newline at end of file diff --git a/views/signup.handlebars b/views/signup.handlebars deleted file mode 100644 index 7a327202..00000000 --- a/views/signup.handlebars +++ /dev/null @@ -1,20 +0,0 @@ -
- {{> topBar}} -
-

Sign up

-
-
- - -
-
- - -
-
- -
-
-
- {{> footer}} -
\ No newline at end of file