From 866bfda2931648a9ada8681b70509539fd38c38a Mon Sep 17 00:00:00 2001 From: bill bittner Date: Fri, 15 Sep 2017 11:09:21 -0700 Subject: [PATCH 01/43] added Helmet middleware for security --- package.json | 1 + speech.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 22f9872b..ed745433 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "connect-multiparty": "^2.0.0", "express": "^4.15.2", "express-handlebars": "^3.0.0", + "helmet": "^3.8.1", "mysql2": "^1.3.5", "nodemon": "^1.11.0", "sequelize": "^4.1.0", diff --git a/speech.js b/speech.js index 21dff58c..e95b6cf6 100644 --- a/speech.js +++ b/speech.js @@ -7,7 +7,7 @@ const Handlebars = require('handlebars'); const config = require('config'); const logger = require('winston'); const { getDownloadDirectory } = require('./helpers/lbryApi'); - +const helmet = require('helmet'); const PORT = 3000; // set port const app = express(); // create an Express application const db = require('./models'); // require our models for syncing @@ -20,7 +20,7 @@ require('./config/slackLoggerConfig.js')(logger); // trust the proxy to get ip address for us app.enable('trust proxy'); // add middleware -app.use(express.static(`${__dirname}/public`)); // 'express.static' to serve static files from public directory +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 From 242248c4f630712b7d35015fdf14a11db56dee53 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Fri, 15 Sep 2017 14:41:47 -0700 Subject: [PATCH 02/43] updated models --- auth/authentication.js | 5 +++++ models/certificate.js | 10 ++++++++++ models/claim.js | 9 +++++++++ models/file.js | 7 +++++++ models/user.js | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 auth/authentication.js create mode 100644 models/user.js diff --git a/auth/authentication.js b/auth/authentication.js new file mode 100644 index 00000000..294e273e --- /dev/null +++ b/auth/authentication.js @@ -0,0 +1,5 @@ +module.exports = { + isAuthenticated (req, res, next) { + + }, +}; diff --git a/models/certificate.js b/models/certificate.js index 702e8783..f8e8784d 100644 --- a/models/certificate.js +++ b/models/certificate.js @@ -87,5 +87,15 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D freezeTableName: true, } ); + + Certificate.associate = db => { + Certificate.belongsTo(db.User, { + onDelete : 'cascade', + foreignKey: { + allowNull: true, + }, + }); + }; + return Certificate; }; diff --git a/models/claim.js b/models/claim.js index b6a72a92..ab35576b 100644 --- a/models/claim.js +++ b/models/claim.js @@ -140,5 +140,14 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D } ); + Claim.associate = db => { + Claim.belongsTo(db.File, { + onDelete : 'cascade', + foreignKey: { + allowNull: true, + }, + }); + }; + return Claim; }; diff --git a/models/file.js b/models/file.js index faabf142..dbe74cf4 100644 --- a/models/file.js +++ b/models/file.js @@ -52,6 +52,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => { File.associate = db => { File.hasMany(db.Request); + File.belongsTo(db.User, { + onDelete : 'cascade', + foreignKey: { + allowNull: true, + }, + }); + File.hasOne(db.Claim); }; return File; diff --git a/models/user.js b/models/user.js new file mode 100644 index 00000000..d9acb6ee --- /dev/null +++ b/models/user.js @@ -0,0 +1,33 @@ +module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => { + const User = sequelize.define( + 'User', + { + channelName: { + type : STRING, + allowNull: false, + }, + channelClaimId: { + type : STRING, + allowNull: false, + }, + password: { + type : STRING, + allowNull: false, + }, + email: { + type : STRING, + allowNull: false, + }, + }, + { + freezeTableName: true, + } + ); + + User.associate = db => { + User.hasMany(db.File); + User.hasOne(db.Certificate); + }; + + return User; +}; From 3325faf063e285a44109f5651dd69fac0e4355ac Mon Sep 17 00:00:00 2001 From: bill bittner Date: Sat, 16 Sep 2017 17:50:22 -0700 Subject: [PATCH 03/43] basic passport flow --- helpers/handlebarsHelpers.js | 96 ++++++++++++++++ helpers/lbryApi.js | 11 ++ models/user.js | 9 +- package.json | 2 + passport/local-login.js | 27 +++++ passport/local-signup.js | 32 ++++++ routes/page-routes.js | 22 ++++ speech.js | 104 ++---------------- views/index.handlebars | 2 +- views/login.handlebars | 11 ++ ...lish.handlebars => publishForm.handlebars} | 0 views/profile.handlebars | 11 ++ views/setup.handlebars | 19 ++++ 13 files changed, 250 insertions(+), 96 deletions(-) create mode 100644 helpers/handlebarsHelpers.js create mode 100644 passport/local-login.js create mode 100644 passport/local-signup.js create mode 100644 views/login.handlebars rename views/partials/{publish.handlebars => publishForm.handlebars} (100%) create mode 100644 views/profile.handlebars create mode 100644 views/setup.handlebars diff --git a/helpers/handlebarsHelpers.js b/helpers/handlebarsHelpers.js new file mode 100644 index 00000000..771cdfae --- /dev/null +++ b/helpers/handlebarsHelpers.js @@ -0,0 +1,96 @@ +const Handlebars = require('handlebars'); +const config = require('config'); + +module.exports = { + // define any extra helpers you may need + googleAnalytics () { + const googleApiKey = config.get('AnalyticsConfig.GoogleId'); + return new Handlebars.SafeString( + `` + ); + }, + addOpenGraph (title, mimeType, showUrl, source, description, thumbnail) { + let basicTags = ` + + + `; + if (mimeType === 'video/mp4') { + return new Handlebars.SafeString( + `${basicTags} + + + + + + + ` + ); + } else if (mimeType === 'image/gif') { + return new Handlebars.SafeString( + `${basicTags} + + + + ` + ); + } else { + return new Handlebars.SafeString( + `${basicTags} + + + + ` + ); + } + }, + addTwitterCard (mimeType, source, embedUrl, directFileUrl) { + let basicTwitterTags = ``; + if (mimeType === 'video/mp4') { + return new Handlebars.SafeString( + `${basicTwitterTags} + + + + + + ` + ); + } else { + return new Handlebars.SafeString( + `${basicTwitterTags} ` + ); + } + }, + ifConditional (varOne, operator, varTwo, options) { + switch (operator) { + case '===': + return (varOne === varTwo) ? options.fn(this) : options.inverse(this); + case '!==': + return (varOne !== varTwo) ? options.fn(this) : options.inverse(this); + case '<': + return (varOne < varTwo) ? options.fn(this) : options.inverse(this); + case '<=': + return (varOne <= varTwo) ? options.fn(this) : options.inverse(this); + case '>': + return (varOne > varTwo) ? options.fn(this) : options.inverse(this); + case '>=': + return (varOne >= varTwo) ? options.fn(this) : options.inverse(this); + case '&&': + return (varOne && varTwo) ? options.fn(this) : options.inverse(this); + case '||': + return (varOne || varTwo) ? options.fn(this) : options.inverse(this); + case 'mod3': + return ((parseInt(varOne) % 3) === 0) ? options.fn(this) : options.inverse(this); + default: + return options.inverse(this); + } + }, +}; diff --git a/helpers/lbryApi.js b/helpers/lbryApi.js index 3777c593..d04b9f9c 100644 --- a/helpers/lbryApi.js +++ b/helpers/lbryApi.js @@ -120,4 +120,15 @@ module.exports = { }); }); }, + createChannel (channelName) { + return new Promise((resolve, reject) => { + resolve({ + tx : 'test', + txid : 'test', + nout : 'test', + fee : 'test', + claim_id: 'xxxxxxxxxxxxxxxxxx', + }); + }); + }, }; diff --git a/models/user.js b/models/user.js index d9acb6ee..76043b11 100644 --- a/models/user.js +++ b/models/user.js @@ -1,6 +1,6 @@ -module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => { +module.exports = (sequelize, { STRING }) => { const User = sequelize.define( - 'User', + 'User', { channelName: { type : STRING, @@ -21,6 +21,11 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => { }, { freezeTableName: true, + instanceMethods: { + validPassword: function (password) { + return (password === this.password); + }, + }, } ); diff --git a/package.json b/package.json index ed745433..97d90dd1 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "helmet": "^3.8.1", "mysql2": "^1.3.5", "nodemon": "^1.11.0", + "passport": "^0.4.0", + "passport-local": "^1.0.0", "sequelize": "^4.1.0", "sleep": "^5.1.1", "socket.io": "^2.0.1", diff --git a/passport/local-login.js b/passport/local-login.js new file mode 100644 index 00000000..4dd50517 --- /dev/null +++ b/passport/local-login.js @@ -0,0 +1,27 @@ +const PassportLocalStrategy = require('passport-local').Strategy; +const db = require('./models'); + +module.exports = new PassportLocalStrategy( + { + usernameField : 'username', // username key in the request body + passwordField : 'password', // password key in the request body + session : false, + passReqToCallback: true, + }, + (username, password, done) => { + return db.User + .findOne({where: {channelName: username}}) + .then(user => { + if (!user) { + return done(null, false, {message: 'Incorrect username or password.'}); + } + if (!user.validPassword(password)) { + return done(null, false, {message: 'Incorrect username or password.'}); + } + return done(null, user); + }) + .catch(error => { + return done(error); + }); + }, +); diff --git a/passport/local-signup.js b/passport/local-signup.js new file mode 100644 index 00000000..ea3fa9b6 --- /dev/null +++ b/passport/local-signup.js @@ -0,0 +1,32 @@ +const db = require('./models'); +const PassportLocalStrategy = require('passport-local').Strategy; +const lbryApi = require('../helpers/lbryApi.js'); + +module.exports = new PassportLocalStrategy( + { + usernameField : 'email', // sets the custom name of parameters in the POST body message + passwordField : 'password', // sets the custom name of parameters in the POST body message + session : false, // set to false because we will use token approach to auth + passReqToCallback: true, // we want to be able to read the post body message parameters in the callback + }, + (req, username, password, done) => { + // create the channel and retrieve the metadata + lbryApi.createChannel(username) + .then(channelInfo => { + // define an object that contains all the user data + const userData = { + channelName: username, + channelId : channelInfo.claim_Id, + password : password, + email : req.body.email.trim(), + }; + return db.User.create(userData); + }) + .then(user => { + return done(null); + }) + .catch(error => { + return done(error); + }); + }, +); diff --git a/routes/page-routes.js b/routes/page-routes.js index 36fd1517..0aace52f 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -1,8 +1,30 @@ 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'); module.exports = (app) => { + // 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'); + }); + // route for auth + app.post('/login', passport.authenticate('local-login'), (req, res) => { + // If this function gets called, authentication was successful. + // `req.user` contains the authenticated user. + res.redirect('/@' + req.user.username); + }); + // route to display login page + // app.get('/users/:name', isAuthenticated, (req, res) => { + // res.status(200).render('profile'); + // }); + app.get('/logout', deAuthenticate, (req, res) => { + res.status(200).render('/'); + }); // route to show 'about' page for spee.ch app.get('/about', (req, res) => { // get and render the content diff --git a/speech.js b/speech.js index e95b6cf6..4c18b8e7 100644 --- a/speech.js +++ b/speech.js @@ -4,6 +4,7 @@ const bodyParser = require('body-parser'); const siofu = require('socketio-file-upload'); const expressHandlebars = require('express-handlebars'); const Handlebars = require('handlebars'); +const handlebarsHelpers = require('./helpers/handlebarsHelpers.js'); const config = require('config'); const logger = require('winston'); const { getDownloadDirectory } = require('./helpers/lbryApi'); @@ -11,6 +12,7 @@ const helmet = require('helmet'); const PORT = 3000; // set port const app = express(); // create an Express application const db = require('./models'); // require our models for syncing +const passport = require('passport'); // configure logging const logLevel = config.get('Logging.LogLevel'); @@ -30,103 +32,19 @@ app.use((req, res, next) => { // custom logging middleware to log all incomming next(); }); +// initialize passport +app.use(passport.initialize()); +// Load passport strategies +const localSignupStrategy = require('./passport/local-signup.js'); +const localLoginStrategy = require('./passport/local-login.js'); +passport.use('local-signup', localSignupStrategy); +passport.use('local-login', localLoginStrategy); + // configure handlebars & register it with express app const hbs = expressHandlebars.create({ defaultLayout: 'main', // sets the default layout handlebars : Handlebars, // includes basic handlebars for access to that library - helpers : { - // define any extra helpers you may need - googleAnalytics () { - const googleApiKey = config.get('AnalyticsConfig.GoogleId'); - return new Handlebars.SafeString( - `` - ); - }, - addOpenGraph (title, mimeType, showUrl, source, description, thumbnail) { - let basicTags = ` - - - `; - if (mimeType === 'video/mp4') { - return new Handlebars.SafeString( - `${basicTags} - - - - - - - ` - ); - } else if (mimeType === 'image/gif') { - return new Handlebars.SafeString( - `${basicTags} - - - - ` - ); - } else { - return new Handlebars.SafeString( - `${basicTags} - - - - ` - ); - } - }, - addTwitterCard (mimeType, source, embedUrl, directFileUrl) { - let basicTwitterTags = ``; - if (mimeType === 'video/mp4') { - return new Handlebars.SafeString( - `${basicTwitterTags} - - - - - - ` - ); - } else { - return new Handlebars.SafeString( - `${basicTwitterTags} ` - ); - } - }, - ifConditional (varOne, operator, varTwo, options) { - switch (operator) { - case '===': - return (varOne === varTwo) ? options.fn(this) : options.inverse(this); - case '!==': - return (varOne !== varTwo) ? options.fn(this) : options.inverse(this); - case '<': - return (varOne < varTwo) ? options.fn(this) : options.inverse(this); - case '<=': - return (varOne <= varTwo) ? options.fn(this) : options.inverse(this); - case '>': - return (varOne > varTwo) ? options.fn(this) : options.inverse(this); - case '>=': - return (varOne >= varTwo) ? options.fn(this) : options.inverse(this); - case '&&': - return (varOne && varTwo) ? options.fn(this) : options.inverse(this); - case '||': - return (varOne || varTwo) ? options.fn(this) : options.inverse(this); - case 'mod3': - return ((parseInt(varOne) % 3) === 0) ? options.fn(this) : options.inverse(this); - default: - return options.inverse(this); - } - }, - }, + helpers : handlebarsHelpers, // custom defined helpers }); app.engine('handlebars', hbs.engine); app.set('view engine', 'handlebars'); diff --git a/views/index.handlebars b/views/index.handlebars index 3a82aecd..5a78a2b1 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -1,7 +1,7 @@
{{> topBar}}
- {{> publish}} + {{> publishForm}} {{> learnMore}}
{{> footer}} diff --git a/views/login.handlebars b/views/login.handlebars new file mode 100644 index 00000000..84b3515c --- /dev/null +++ b/views/login.handlebars @@ -0,0 +1,11 @@ +
+ {{> topBar}} +
+ {{#if isAuthenticated}} + {{> profile }} + {{else}} + {{> loginForm}} + {{/if}} +
+ {{> footer}} +
\ No newline at end of file diff --git a/views/partials/publish.handlebars b/views/partials/publishForm.handlebars similarity index 100% rename from views/partials/publish.handlebars rename to views/partials/publishForm.handlebars diff --git a/views/profile.handlebars b/views/profile.handlebars new file mode 100644 index 00000000..84b3515c --- /dev/null +++ b/views/profile.handlebars @@ -0,0 +1,11 @@ +
+ {{> topBar}} +
+ {{#if isAuthenticated}} + {{> profile }} + {{else}} + {{> loginForm}} + {{/if}} +
+ {{> footer}} +
\ No newline at end of file diff --git a/views/setup.handlebars b/views/setup.handlebars new file mode 100644 index 00000000..70cd3d08 --- /dev/null +++ b/views/setup.handlebars @@ -0,0 +1,19 @@ +
+ {{> topBar}} +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ {{> footer}} +
\ No newline at end of file From 0e9147d9d6e9c9e67929450982bb1d749b976cef Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 18 Sep 2017 10:14:06 -0700 Subject: [PATCH 04/43] basic passport structures --- models/user.js | 12 ++++---- package.json | 1 + passport/local-login.js | 12 ++++---- passport/local-signup.js | 18 +++++++----- routes/page-routes.js | 29 ++++++++++++------- speech.js | 18 +++++++++++- views/login.handlebars | 29 ++++++++++++------- views/{setup.handlebars => signup.handlebars} | 3 +- 8 files changed, 82 insertions(+), 40 deletions(-) rename views/{setup.handlebars => signup.handlebars} (86%) diff --git a/models/user.js b/models/user.js index 76043b11..96886925 100644 --- a/models/user.js +++ b/models/user.js @@ -21,18 +21,18 @@ module.exports = (sequelize, { STRING }) => { }, { freezeTableName: true, - instanceMethods: { - validPassword: function (password) { - return (password === this.password); - }, - }, } - ); + ); User.associate = db => { User.hasMany(db.File); User.hasOne(db.Certificate); }; + User.prototype.validPassword = (givenpassword, thispassword) => { + console.log(`${givenpassword} === ${thispassword}`); + return (givenpassword === thispassword); + }; + return User; }; diff --git a/package.json b/package.json index 97d90dd1..a89ac83b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "connect-multiparty": "^2.0.0", "express": "^4.15.2", "express-handlebars": "^3.0.0", + "express-session": "^1.15.5", "helmet": "^3.8.1", "mysql2": "^1.3.5", "nodemon": "^1.11.0", diff --git a/passport/local-login.js b/passport/local-login.js index 4dd50517..6fe29e2b 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -1,5 +1,6 @@ const PassportLocalStrategy = require('passport-local').Strategy; -const db = require('./models'); +const db = require('../models'); +const logger = require('winston'); module.exports = new PassportLocalStrategy( { @@ -8,20 +9,21 @@ module.exports = new PassportLocalStrategy( session : false, passReqToCallback: true, }, - (username, password, done) => { + (req, username, password, done) => { return db.User .findOne({where: {channelName: username}}) .then(user => { + logger.debug('user', user.dataValues); if (!user) { return done(null, false, {message: 'Incorrect username or password.'}); } - if (!user.validPassword(password)) { + if (!user.validPassword(password, user.password)) { return done(null, false, {message: 'Incorrect username or password.'}); } - return done(null, user); + return done(null, user.dataValues); }) .catch(error => { return done(error); }); - }, + } ); diff --git a/passport/local-signup.js b/passport/local-signup.js index ea3fa9b6..2da08450 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -1,32 +1,36 @@ -const db = require('./models'); +const db = require('../models'); const PassportLocalStrategy = require('passport-local').Strategy; const lbryApi = require('../helpers/lbryApi.js'); +const logger = require('winston'); module.exports = new PassportLocalStrategy( { - usernameField : 'email', // sets the custom name of parameters in the POST body message + usernameField : 'username', // sets the custom name of parameters in the POST body message passwordField : 'password', // sets the custom name of parameters in the POST body message session : false, // set to false because we will use token approach to auth passReqToCallback: true, // we want to be able to read the post body message parameters in the callback }, (req, username, password, done) => { + console.log('inside local-signup'); // create the channel and retrieve the metadata lbryApi.createChannel(username) .then(channelInfo => { // define an object that contains all the user data const userData = { - channelName: username, - channelId : channelInfo.claim_Id, - password : password, - email : req.body.email.trim(), + channelName : username, + channelClaimId: channelInfo.claim_id, + password : password, + email : 'test email', // req.body.email.trim(), }; return db.User.create(userData); }) .then(user => { + logger.debug('User record was created successfully'); return done(null); }) .catch(error => { + logger.debug(error); return done(error); }); - }, + } ); diff --git a/routes/page-routes.js b/routes/page-routes.js index 0aace52f..94d75adb 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -2,9 +2,23 @@ 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 { deAuthenticate } = require('../auth/authentication.js'); 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. + // `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. + // `req.user` contains the authenticated user. + res.redirect('/@' + req.user.channelName); + }); + // route to display login page app.get('/login', (req, res) => { res.status(200).render('login'); @@ -12,19 +26,14 @@ module.exports = (app) => { app.get('/signup', (req, res) => { res.status(200).render('signup'); }); - // route for auth - app.post('/login', passport.authenticate('local-login'), (req, res) => { - // If this function gets called, authentication was successful. - // `req.user` contains the authenticated user. - res.redirect('/@' + req.user.username); - }); // route to display login page // app.get('/users/:name', isAuthenticated, (req, res) => { // res.status(200).render('profile'); // }); - app.get('/logout', deAuthenticate, (req, res) => { - res.status(200).render('/'); - }); + // app.get('/logout', deAuthenticate, (req, res) => { + // res.status(200).render('/'); + // }); + // route to show 'about' page for spee.ch app.get('/about', (req, res) => { // get and render the content diff --git a/speech.js b/speech.js index 4c18b8e7..2fbe3a90 100644 --- a/speech.js +++ b/speech.js @@ -13,6 +13,7 @@ const PORT = 3000; // set port const app = express(); // create an Express application const db = require('./models'); // require our models for syncing const passport = require('passport'); +const session = require('express-session'); // configure logging const logLevel = config.get('Logging.LogLevel'); @@ -31,9 +32,24 @@ app.use((req, res, next) => { // custom logging middleware to log all incomming 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) { + done(null, user.id); +}); + +passport.deserializeUser(function (id, done) { + db.User.findOne({ where: { id } }) + .then(user => { + done(null, user.dataValues); + }) + .catch(error => { + logger.error('sequelize error', error); + }); +}); + // Load passport strategies const localSignupStrategy = require('./passport/local-signup.js'); const localLoginStrategy = require('./passport/local-login.js'); diff --git a/views/login.handlebars b/views/login.handlebars index 84b3515c..81fe1046 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -1,11 +1,20 @@
- {{> topBar}} -
- {{#if isAuthenticated}} - {{> profile }} - {{else}} - {{> loginForm}} - {{/if}} -
- {{> footer}} -
\ No newline at end of file + {{> topBar}} +
+

Log In

+
+
+ + +
+
+ + +
+
+ +
+
+
+ {{> footer}} +
diff --git a/views/setup.handlebars b/views/signup.handlebars similarity index 86% rename from views/setup.handlebars rename to views/signup.handlebars index 70cd3d08..7a327202 100644 --- a/views/setup.handlebars +++ b/views/signup.handlebars @@ -1,7 +1,8 @@
{{> topBar}}
-
+

Sign up

+
From f64961446f71796521af1e81fc4917c939af31e2 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 19 Sep 2017 08:47:24 -0700 Subject: [PATCH 05/43] 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 From 53981b861803b8e22003706ba6a35f5aa2248e6b Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 19 Sep 2017 12:54:23 -0700 Subject: [PATCH 06/43] DRY input validations --- .gitignore | 3 +- controllers/publishController.js | 2 +- helpers/publishHelpers.js | 24 +--- migrations/Add-Address-To-User.js | 20 +++ migrations/Add-UserId-To-Certificate.js | 20 +++ migrations/Add-UserId-To-File.js | 20 +++ models/user.js | 4 + package.json | 1 + public/assets/css/componentStyle.css | 9 -- public/assets/css/generalStyle.css | 30 ++++- public/assets/js/generalFunctions.js | 12 -- public/assets/js/validationFunctions.js | 148 ++++++++++------------- routes/api-routes.js | 8 +- views/login.handlebars | 8 +- views/partials/publishChannel.handlebars | 45 ++++--- views/partials/publishDetails.handlebars | 2 +- views/partials/publishForm.handlebars | 8 +- 17 files changed, 208 insertions(+), 156 deletions(-) create mode 100644 migrations/Add-Address-To-User.js create mode 100644 migrations/Add-UserId-To-Certificate.js create mode 100644 migrations/Add-UserId-To-File.js diff --git a/.gitignore b/.gitignore index 34977ee7..761ac590 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -.idea \ No newline at end of file +.idea +config/config.json \ No newline at end of file diff --git a/controllers/publishController.js b/controllers/publishController.js index fd54d347..099675ba 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -8,7 +8,7 @@ module.exports = { return new Promise((resolve, reject) => { let publishResults = {}; // 1. make sure the name is available - publishHelpers.checkNameAvailability(publishParams.name) + publishHelpers.checkClaimNameAvailability(publishParams.name) // 2. publish the file .then(result => { if (result === true) { diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 3462dfa2..a14ce408 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -100,7 +100,7 @@ module.exports = { logger.debug(`successfully deleted ${filePath}`); }); }, - checkNameAvailability (name) { + checkClaimNameAvailability (name) { return new Promise((resolve, reject) => { // find any records where the name is used db.File.findAll({ where: { name } }) @@ -133,27 +133,13 @@ module.exports = { checkChannelAvailability (name) { return new Promise((resolve, reject) => { // find any records where the name is used - db.Certificate.findAll({ where: { name } }) + db.User.findAll({ where: { channelName: name } }) .then(result => { + logger.debug('sequelize result:', 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); + return resolve(false); } + resolve(true); }) .catch(error => { reject(error); diff --git a/migrations/Add-Address-To-User.js b/migrations/Add-Address-To-User.js new file mode 100644 index 00000000..b8f88d6f --- /dev/null +++ b/migrations/Add-Address-To-User.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + return queryInterface.addColumn( + 'User', + 'Address', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + return queryInterface.removeColumn( + 'User', + 'Address' + ); + }, +}; diff --git a/migrations/Add-UserId-To-Certificate.js b/migrations/Add-UserId-To-Certificate.js new file mode 100644 index 00000000..f1f60684 --- /dev/null +++ b/migrations/Add-UserId-To-Certificate.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + queryInterface.addColumn( + 'Certificate', + 'UserId', + { + type : Sequelize.STRING, + allowNull: false, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + queryInterface.removeColumn( + 'Certificate', + 'UserId' + ); + }, +}; diff --git a/migrations/Add-UserId-To-File.js b/migrations/Add-UserId-To-File.js new file mode 100644 index 00000000..bde92b42 --- /dev/null +++ b/migrations/Add-UserId-To-File.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + return queryInterface.addColumn( + 'File', + 'UserId', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + return queryInterface.removeColumn( + 'File', + 'UserId' + ); + }, +}; diff --git a/models/user.js b/models/user.js index 96886925..32620407 100644 --- a/models/user.js +++ b/models/user.js @@ -18,6 +18,10 @@ module.exports = (sequelize, { STRING }) => { type : STRING, allowNull: false, }, + address: { + type : STRING, + allowNull: false, + }, }, { freezeTableName: true, diff --git a/package.json b/package.json index a89ac83b..e1aa4a85 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "passport": "^0.4.0", "passport-local": "^1.0.0", "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", diff --git a/public/assets/css/componentStyle.css b/public/assets/css/componentStyle.css index 32696fc7..3f908c29 100644 --- a/public/assets/css/componentStyle.css +++ b/public/assets/css/componentStyle.css @@ -54,16 +54,7 @@ width: 90% } -.input-text--primary { - border: 0px; - background-color: #ffffff; - border-bottom: 1px solid grey; -} -.input-text--primary:focus { - outline: none; - border-bottom: 1px solid blue; -} /* show routes */ .show-asset { diff --git a/public/assets/css/generalStyle.css b/public/assets/css/generalStyle.css index 70cfcf0b..d1d0c1c7 100644 --- a/public/assets/css/generalStyle.css +++ b/public/assets/css/generalStyle.css @@ -123,12 +123,36 @@ table { word-wrap: break-word; } -.input-error { +/* BEM */ + +.info-message { font-weight: bold; - color: red; - font-size: small; } +.info-message--success { + color: green; +} + +.info-message--failure { + color: red; +} + +.input-text { + outline: none; + border: 0px; + background-color: #ffffff; +} + +.input-text--primary { + border-bottom: 1px solid grey; +} + +.input-text--primary:focus { + border-bottom: 1px solid blue; +} + +/* MEDIA QUERIES */ + @media (max-width: 1250px) { .wrapper { diff --git a/public/assets/js/generalFunctions.js b/public/assets/js/generalFunctions.js index 3c3a60d9..4e5ba82c 100644 --- a/public/assets/js/generalFunctions.js +++ b/public/assets/js/generalFunctions.js @@ -55,18 +55,6 @@ function dataURItoBlob(dataURI) { return new Blob([ia], {type:mimeString}); } -function showError(elementId, errorMsg) { - var errorDisplay = document.getElementById(elementId); - errorDisplay.hidden = false; - errorDisplay.innerText = errorMsg; -} - -function clearError(elementId) { - var errorDisplay = document.getElementById(elementId); - errorDisplay.hidden = true; - errorDisplay.innerText = ''; -} - // Create new error objects, that prototypically inherit from the Error constructor function FileError(message) { this.name = 'FileError'; diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index b73b16c3..2f180822 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -27,56 +27,6 @@ function validateFile(file) { 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 not already claimed -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 @@ -95,51 +45,85 @@ function cleanseClaimName(name) { name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-' return name; } -// validaiton function to check claim name as the input changes -function checkClaimName(name){ - try { - // check to make sure the characters are valid - validateClaimName(name); - clearError('input-error-claim-name'); - // check to make sure it is availabe - isNameAvailable(name) - .then(function() { - document.getElementById('claim-name-available').hidden = false; - }) - .catch(function(error) { - document.getElementById('claim-name-available').hidden = true; - showError('input-error-claim-name', error.message); - }); - } catch (error) { - showError('input-error-claim-name', error.message); - document.getElementById('claim-name-available').hidden = true; - } + +// validation functions to check claim & channel name eligibility as the inputs change + +function isNameAvailable (name, apiUrl) { + return new Promise(function(resolve, reject) { + // make sure the claim name is still available + var xhttp; + xhttp = new XMLHttpRequest(); + xhttp.open('GET', apiUrl + 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 showError(errorDisplay, errorMsg) { + errorDisplay.hidden = false; + errorDisplay.innerText = errorMsg; +} -// 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) +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, isNameAvailable, apiUrl) { try { + // check to make sure the characters are valid + validateClaimName(name); // check to make sure it is available - isChannelAvailable(name) + isNameAvailable(name, apiUrl) .then(function() { - errorDisplay.hidden = false; + hideError(errorDisplayElement); + showSuccess(successDisplayElement) }) .catch(function(error) { - errorDisplay.hidden = false; - showError(errorDisplay.getAttribute('id'), error.message); + hideSuccess(successDisplayElement); + showError(errorDisplayElement, error.message); }); } catch (error) { - console.log(error.message); - document.getElementById(errorDisplay.getAttribute('id')).hidden = true; + hideSuccess(successDisplayElement); + showError(errorDisplayElement, error.message); } } +function checkClaimName(name){ + const successDisplayElement = document.getElementById('claim-name-success'); + const errorDisplayElement = document.getElementById('claim-name-error'); + checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isClaimAvailable/'); +} + +function checkChannelName(name){ + const successDisplayElement = document.getElementById('channel-name-success'); + const errorDisplayElement = document.getElementById('channel-name-error'); + checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isChannelAvailable/'); +} + // validation function which checks all aspects of the publish submission function validateSubmission(stagedFiles, name){ return new Promise(function (resolve, reject) { diff --git a/routes/api-routes.js b/routes/api-routes.js index 28a54daa..d8baaf64 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, checkChannelAvailability } = require('../helpers/publishHelpers.js'); +const { createPublishParams, validateFile, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); @@ -25,7 +25,7 @@ module.exports = (app, hostedContentPath) => { // route to check whether spee.ch has published to a claim app.get('/api/isClaimAvailable/:name', ({ ip, originalUrl, params }, res) => { // send response - checkNameAvailability(params.name) + checkClaimNameAvailability(params.name) .then(result => { if (result === true) { res.status(200).json(true); @@ -39,8 +39,7 @@ module.exports = (app, hostedContentPath) => { }); }); // route to check whether spee.ch has published to a channel - app.get('/api/isChannelAvailable/:name', ({ ip, originalUrl, params }, res) => { - // send response + app.get('/api/isChannelAvailable/:name', ({ params }, res) => { checkChannelAvailability(params.name) .then(result => { if (result === true) { @@ -51,6 +50,7 @@ module.exports = (app, hostedContentPath) => { } }) .catch(error => { + logger.debug('api/isChannelAvailable/ error', error); res.status(500).json(error); }); }); diff --git a/views/login.handlebars b/views/login.handlebars index 60f5df49..f662d36f 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -7,11 +7,11 @@
- @ + @
- +
@@ -23,11 +23,11 @@
- @ + @
- +
diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index 221ddaad..cd3b2356 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -1,21 +1,34 @@
- - - From 86a2c6fe03a40bd5f297cb3982a100a759a6daf7 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 19 Sep 2017 15:39:54 -0700 Subject: [PATCH 07/43] added channel validation --- helpers/publishHelpers.js | 1 - public/assets/js/generalFunctions.js | 30 +++++++----------------- public/assets/js/publishFunctions.js | 18 ++++++++------ public/assets/js/validationFunctions.js | 25 ++++++++++++-------- views/index.handlebars | 4 +++- views/partials/publishChannel.handlebars | 27 +++++++++------------ views/partials/publishForm.handlebars | 8 +++---- 7 files changed, 53 insertions(+), 60 deletions(-) diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index a14ce408..186fabec 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -135,7 +135,6 @@ module.exports = { // find any records where the name is used db.User.findAll({ where: { channelName: name } }) .then(result => { - logger.debug('sequelize result:', result); if (result.length >= 1) { return resolve(false); } diff --git a/public/assets/js/generalFunctions.js b/public/assets/js/generalFunctions.js index 4e5ba82c..0841822d 100644 --- a/public/assets/js/generalFunctions.js +++ b/public/assets/js/generalFunctions.js @@ -35,26 +35,6 @@ function createProgressBar(element, size){ setInterval(addOne, 300); } -function dataURItoBlob(dataURI) { - // convert base64/URLEncoded data component to raw binary data held in a string - var byteString; - if (dataURI.split(',')[0].indexOf('base64') >= 0) - byteString = atob(dataURI.split(',')[1]); - else - byteString = unescape(dataURI.split(',')[1]); - - // separate out the mime component - var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; - - // write the bytes of the string to a typed array - var ia = new Uint8Array(byteString.length); - for (var i = 0; i < byteString.length; i++) { - ia[i] = byteString.charCodeAt(i); - } - - return new Blob([ia], {type:mimeString}); -} - // Create new error objects, that prototypically inherit from the Error constructor function FileError(message) { this.name = 'FileError'; @@ -70,4 +50,12 @@ function NameError(message) { this.stack = (new Error()).stack; } NameError.prototype = Object.create(Error.prototype); -NameError.prototype.constructor = NameError; \ No newline at end of file +NameError.prototype.constructor = NameError; + +function ChannelError(message) { + this.name = 'ChannelError'; + this.message = message || 'Default Message'; + this.stack = (new Error()).stack; +} +ChannelError.prototype = Object.create(Error.prototype); +ChannelError.prototype.constructor = ChannelError; \ No newline at end of file diff --git a/public/assets/js/publishFunctions.js b/public/assets/js/publishFunctions.js index 552ecd0d..cef074eb 100644 --- a/public/assets/js/publishFunctions.js +++ b/public/assets/js/publishFunctions.js @@ -19,7 +19,7 @@ function previewAndStageFile(selectedFile){ showError('input-error-file-selection', error.message); return; } - // set the image preview, if a preview was provided + // set the image preview, if an image was provided if (selectedFile.type !== 'video/mp4') { previewReader.readAsDataURL(selectedFile); previewReader.onloadend = function () { @@ -41,18 +41,22 @@ function previewAndStageFile(selectedFile){ // Validate the publish submission and then trigger publishing. function publishSelectedImage(event) { event.preventDefault(); - var name = document.getElementById('claim-name-input').value; - validateSubmission(stagedFiles, name) + var claimName = document.getElementById('claim-name-input').value; + var channelName = document.getElementById('channel-name-select').value; + validateSubmission(stagedFiles, claimName, channelName) .then(function() { uploader.submitFiles(stagedFiles); }) .catch(function(error) { - if (error.name === 'FileError'){ - showError('input-error-file-selection', error.message); + if (error.name === 'FileError') { + showError(document.getElementById('input-error-file-selection'), error.message); } else if (error.name === 'NameError') { - showError('input-error-claim-name', error.message); + showError(document.getElementById('input-error-claim-name'), error.message); + } else if (error.name === 'ChannelError'){ + console.log(error); + showError(document.getElementById('input-error-channel-select'), error.message); } else { - showError('input-error-publish-submit', error.message); + showError(document.getElementById('input-error-publish-submit'), error.message); } return; }) diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index 2f180822..cfd0c780 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -113,39 +113,44 @@ function checkAvailability(name, successDisplayElement, errorDisplayElement, isN } function checkClaimName(name){ - const successDisplayElement = document.getElementById('claim-name-success'); - const errorDisplayElement = document.getElementById('claim-name-error'); + const successDisplayElement = document.getElementById('input-success-claim-name'); + const errorDisplayElement = document.getElementById('input-error-claim-name'); checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isClaimAvailable/'); } function checkChannelName(name){ - const successDisplayElement = document.getElementById('channel-name-success'); - const errorDisplayElement = document.getElementById('channel-name-error'); + const successDisplayElement = document.getElementById('input-success-channel-name'); + const errorDisplayElement = document.getElementById('input-error-channel-name'); checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isChannelAvailable/'); } // validation function which checks all aspects of the publish submission -function validateSubmission(stagedFiles, name){ +function validateSubmission(stagedFiles, claimName, channelName){ return new Promise(function (resolve, reject) { - // make sure only 1 file was selected + // 1. make sure only 1 file was selected if (!stagedFiles) { reject(new FileError("Please select a file")); } else if (stagedFiles.length > 1) { reject(new FileError("Only one file is allowed at a time")); } - // validate the file's name, type, and size + // 2. validate the file's name, type, and size try { validateFile(stagedFiles[0]); } catch (error) { reject(error); } - // make sure the claim name has not already been used + // 3. validate that a channel was chosen + if (channelName === 'new') { + reject(new ChannelError("Please select a valid channel")); + }; + // 4. validate the claim name try { - validateClaimName(name); + validateClaimName(claimName); // validate the formatting } catch (error) { reject(error); } - isNameAvailable(name) + console.log(claimName); + isNameAvailable(claimName, '/api/isClaimAvailable/') // validate the availability .then(function() { resolve(); }) diff --git a/views/index.handlebars b/views/index.handlebars index 5a78a2b1..7607a81a 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -24,12 +24,14 @@ var description = document.getElementById('publish-description').value; var license = document.getElementById('publish-license').value; var nsfw = document.getElementById('publish-nsfw').checked; + var channel = document.getElementById('channel-name-select').value; event.file.meta.name = name; event.file.meta.title = title; event.file.meta.description = description; event.file.meta.license = license; event.file.meta.nsfw = nsfw; event.file.meta.type = stagedFiles[0].type; + event.file.meta.channel = channel; // re-set the html in the publish area document.getElementById('publish-active-area').innerHTML = '
'; // start a progress animation @@ -54,7 +56,7 @@ }); socket.on('publish-complete', function(msg){ var publishResults; - var showUrl = msg.name + '/' + msg.result.claim_id; + var showUrl = msg.result.claim_id + "/" + msg.name; // build new publish area publishResults = '

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

'; publishResults += '

If you do not get redirected, click here.

'; diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index cd3b2356..f5e33615 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -1,8 +1,8 @@
- +

- - {{#if user}} {{/if}} @@ -12,23 +12,19 @@

-
From 0dd8e19a267ccbacc733d58ff62ea64dccbc4a8f Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 19 Sep 2017 18:50:25 -0700 Subject: [PATCH 08/43] finished basic loop for channel creation --- migrations/Add-UserId-To-Certificate.js | 2 +- migrations/remove-Email-from-User.js | 20 ++++++++++ models/user.js | 4 -- passport/local-login.js | 1 + passport/local-signup.js | 31 ++++++++++++---- public/assets/js/channelFunctions.js | 26 +++++++++++++ public/assets/js/validationFunctions.js | 31 +++++++++++++--- routes/api-routes.js | 10 +++++ routes/page-routes.js | 6 +-- speech.js | 2 + views/index.handlebars | 1 - views/login.handlebars | 47 +++++++++++++++++++++--- views/partials/publishChannel.handlebars | 4 +- views/partials/publishDetails.handlebars | 3 +- views/partials/topBar.handlebars | 2 +- 15 files changed, 158 insertions(+), 32 deletions(-) create mode 100644 migrations/remove-Email-from-User.js create mode 100644 public/assets/js/channelFunctions.js diff --git a/migrations/Add-UserId-To-Certificate.js b/migrations/Add-UserId-To-Certificate.js index f1f60684..49217435 100644 --- a/migrations/Add-UserId-To-Certificate.js +++ b/migrations/Add-UserId-To-Certificate.js @@ -6,7 +6,7 @@ module.exports = { 'UserId', { type : Sequelize.STRING, - allowNull: false, + allowNull: true, } ); }, diff --git a/migrations/remove-Email-from-User.js b/migrations/remove-Email-from-User.js new file mode 100644 index 00000000..4d6a39e3 --- /dev/null +++ b/migrations/remove-Email-from-User.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + return queryInterface.removeColumn( + 'User', + 'Email' + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + return queryInterface.addColumn( + 'User', + 'Email', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + }, +}; diff --git a/models/user.js b/models/user.js index 32620407..da7457d8 100644 --- a/models/user.js +++ b/models/user.js @@ -14,10 +14,6 @@ module.exports = (sequelize, { STRING }) => { type : STRING, allowNull: false, }, - email: { - type : STRING, - allowNull: false, - }, address: { type : STRING, allowNull: false, diff --git a/passport/local-login.js b/passport/local-login.js index 9420e1fa..dff8a2ac 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -10,6 +10,7 @@ module.exports = new PassportLocalStrategy( passReqToCallback: true, }, (req, username, password, done) => { + username = `@${username}`; return db.User .findOne({where: {channelName: username}}) .then(user => { diff --git a/passport/local-signup.js b/passport/local-signup.js index 0ae9ea94..1bed5485 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -2,6 +2,7 @@ const db = require('../models'); const PassportLocalStrategy = require('passport-local').Strategy; const lbryApi = require('../helpers/lbryApi.js'); const logger = require('winston'); +const config = require('config'); module.exports = new PassportLocalStrategy( { @@ -11,20 +12,34 @@ module.exports = new PassportLocalStrategy( passReqToCallback: true, // we want to be able to read the post body message parameters in the callback }, (req, username, password, done) => { - console.log('inside local-signup'); + logger.debug('new channel signup request'); + const address = config.get('WalletConfig.LbryClaimAddress'); + // validate raw inputs (username, password) + username = '@' + username; // create the channel and retrieve the metadata - lbryApi.createChannel(username) - .then(channelInfo => { - // define an object that contains all the user data + return lbryApi.createChannel(username) + .then(channelTx => { + // create certificate record + const certificateData = { + address, + claimId: channelTx.claim_id, + name : username, + }; + logger.debug('certificateData >', certificateData); + return db.Certificate.create(certificateData); + }) + .then(certificate => { + logger.debug('certificate result >', certificate.dataValues); + logger.debug('Certificate record was created successfully'); + // define an object that contains all the user data const userData = { channelName : username, - channelClaimId: channelInfo.claim_id, + channelClaimId: certificate.claimId, password : password, - email : 'test email', // req.body.email.trim(), + address, }; return db.User.create(userData); - }) - .then(user => { + }).then(user => { logger.debug('User record was created successfully'); return done(null, user); // user.datavalues? }) diff --git a/public/assets/js/channelFunctions.js b/public/assets/js/channelFunctions.js new file mode 100644 index 00000000..49bc7abb --- /dev/null +++ b/public/assets/js/channelFunctions.js @@ -0,0 +1,26 @@ +function sendSignupRequest (channelName, password) { + return new Promise(function(resolve, reject) { + // make sure the claim name is still available + let xhttp; + const params = `username=${channelName}&password=${password}`; + console.log(params); + xhttp = new XMLHttpRequest(); + xhttp.open('POST', '/api/signup', true); + xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhttp.responseType = 'json'; + xhttp.onreadystatechange = function() { + if (this.readyState == 4 ) { + if ( this.status == 200) { + if (this.response == true) { + resolve(); + } else { + reject( new NameError("Your request could not be completed")); + } + } else { + reject("createChannel request failed with status:" + this.status); + }; + } + }; + xhttp.send(params); + }); +} \ No newline at end of file diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index cfd0c780..5abe027d 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -40,6 +40,26 @@ function validateClaimName (name) { } } +function validateChannelName (name) { + name = name.substring(name.indexOf('@') + 1); + console.log(name); + // ensure a name was entered + if (name.length < 1) { + throw new ChannelError("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 ChannelError('"' + invalidCharacters + '" characters are not allowed in the channel name.'); + } +} + +function validatePassword (password) { + if (password.length < 1) { + throw new ChannelError("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 '-' @@ -92,10 +112,10 @@ function hideSuccess (successElement) { successElement.innerHTML = ""; } -function checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, apiUrl) { +function checkAvailability(name, successDisplayElement, errorDisplayElement, validateName, isNameAvailable, apiUrl) { try { // check to make sure the characters are valid - validateClaimName(name); + validateName(name); // check to make sure it is available isNameAvailable(name, apiUrl) .then(function() { @@ -115,13 +135,14 @@ function checkAvailability(name, successDisplayElement, errorDisplayElement, isN function checkClaimName(name){ const successDisplayElement = document.getElementById('input-success-claim-name'); const errorDisplayElement = document.getElementById('input-error-claim-name'); - checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isClaimAvailable/'); + checkAvailability(name, successDisplayElement, errorDisplayElement, validateClaimName, isNameAvailable, '/api/isClaimAvailable/'); } function checkChannelName(name){ const successDisplayElement = document.getElementById('input-success-channel-name'); const errorDisplayElement = document.getElementById('input-error-channel-name'); - checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isChannelAvailable/'); + name = `@${name}`; + checkAvailability(name, successDisplayElement, errorDisplayElement, validateChannelName, isNameAvailable, '/api/isChannelAvailable/'); } // validation function which checks all aspects of the publish submission @@ -158,4 +179,4 @@ function validateSubmission(stagedFiles, claimName, channelName){ reject(error); }); }); -} +} \ No newline at end of file diff --git a/routes/api-routes.js b/routes/api-routes.js index d8baaf64..9895a21a 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -6,8 +6,18 @@ const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); const { createPublishParams, validateFile, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); +const passport = require('passport'); module.exports = (app, hostedContentPath) => { + // route for auth + app.post('/api/signup', passport.authenticate('local-signup'), (req, res) => { + logger.debug('successful signup'); + res.status(200).json(true); + }); + app.post('/api/login', passport.authenticate('local-login'), (req, res) => { + logger.debug('successful login'); + res.status(200).json(true); + }); // route to run a claim_list request on the daemon app.get('/api/claim_list/:name', ({ headers, ip, originalUrl, params }, res) => { // google analytics diff --git a/routes/page-routes.js b/routes/page-routes.js index 2aba1bd8..4630bd6c 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -9,13 +9,13 @@ module.exports = (app) => { 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); + res.redirect(`/${req.user.channelName}`); }); app.post('/login', passport.authenticate('local-login'), (req, res) => { 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); + res.redirect(`/${req.user.channelName}`); }); // route to log out app.get('/logout', (req, res) => { @@ -26,7 +26,7 @@ module.exports = (app) => { // route to display login page app.get('/login', (req, res) => { if (req.user) { - res.status(200).redirect(`/@${req.user.channelName}`); + res.status(200).redirect(`/${req.user.channelName}`); } else { res.status(200).render('login'); } diff --git a/speech.js b/speech.js index fb7d9f9f..f5663baf 100644 --- a/speech.js +++ b/speech.js @@ -31,6 +31,7 @@ app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing app.use(siofu.router); // 'socketio-file-upload' router for uploading with socket.io app.use((req, res, next) => { // custom logging middleware to log all incoming http requests logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`); + logger.debug(req.body); next(); }); @@ -75,6 +76,7 @@ app.use((req, res, next) => { } next(); }); + // start the server db.sequelize .sync() // sync sequelize diff --git a/views/index.handlebars b/views/index.handlebars index 7607a81a..d58cc37e 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -64,5 +64,4 @@ document.getElementById('publish-active-area').innerHTML = publishResults; window.location.href = showUrl; }); - diff --git a/views/login.handlebars b/views/login.handlebars index f662d36f..05f160f1 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -3,7 +3,7 @@

Log In

-

Log in to an existing channel

+

Log in to an existing channel:

@@ -19,21 +19,56 @@

Create New

-

Create a brand new channel

-
+

Create a brand new channel:

+
+
- @ + @ +
+
- +
- +
{{> footer}}
+ + + + + diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index f5e33615..aea12fd3 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -4,7 +4,7 @@ + @
diff --git a/views/partials/publishDetails.handlebars b/views/partials/publishDetails.handlebars index bebe8b74..6e96a543 100644 --- a/views/partials/publishDetails.handlebars +++ b/views/partials/publishDetails.handlebars @@ -25,4 +25,5 @@ -
\ No newline at end of file +
+ diff --git a/views/partials/topBar.handlebars b/views/partials/topBar.handlebars index f627f4df..f9115c09 100644 --- a/views/partials/topBar.handlebars +++ b/views/partials/topBar.handlebars @@ -6,7 +6,7 @@ help {{#if user}} - @{{user.channelName}} + {{user.channelName}} logout {{else}} login From 0575e9853f9dd6c89c66dc9982510d9d5fd7a6bf Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 09:49:05 -0700 Subject: [PATCH 09/43] finished validation on login page --- public/assets/js/channelFunctions.js | 26 ------ public/assets/js/generalFunctions.js | 16 +++- public/assets/js/publishChannelFunctions.js | 56 +++++++++++++ ...shFunctions.js => publishFileFunctions.js} | 80 ++++++++++--------- public/assets/js/validationFunctions.js | 50 +++++++++--- views/index.handlebars | 2 +- views/login.handlebars | 34 +------- 7 files changed, 151 insertions(+), 113 deletions(-) delete mode 100644 public/assets/js/channelFunctions.js create mode 100644 public/assets/js/publishChannelFunctions.js rename public/assets/js/{publishFunctions.js => publishFileFunctions.js} (71%) diff --git a/public/assets/js/channelFunctions.js b/public/assets/js/channelFunctions.js deleted file mode 100644 index 49bc7abb..00000000 --- a/public/assets/js/channelFunctions.js +++ /dev/null @@ -1,26 +0,0 @@ -function sendSignupRequest (channelName, password) { - return new Promise(function(resolve, reject) { - // make sure the claim name is still available - let xhttp; - const params = `username=${channelName}&password=${password}`; - console.log(params); - xhttp = new XMLHttpRequest(); - xhttp.open('POST', '/api/signup', true); - xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhttp.responseType = 'json'; - xhttp.onreadystatechange = function() { - if (this.readyState == 4 ) { - if ( this.status == 200) { - if (this.response == true) { - resolve(); - } else { - reject( new NameError("Your request could not be completed")); - } - } else { - reject("createChannel request failed with status:" + this.status); - }; - } - }; - xhttp.send(params); - }); -} \ No newline at end of file diff --git a/public/assets/js/generalFunctions.js b/public/assets/js/generalFunctions.js index 0841822d..3b4f1925 100644 --- a/public/assets/js/generalFunctions.js +++ b/public/assets/js/generalFunctions.js @@ -52,10 +52,18 @@ function NameError(message) { NameError.prototype = Object.create(Error.prototype); NameError.prototype.constructor = NameError; -function ChannelError(message) { - this.name = 'ChannelError'; +function ChannelNameError(message) { + this.name = 'ChannelNameError'; this.message = message || 'Default Message'; this.stack = (new Error()).stack; } -ChannelError.prototype = Object.create(Error.prototype); -ChannelError.prototype.constructor = ChannelError; \ No newline at end of file +ChannelNameError.prototype = Object.create(Error.prototype); +ChannelNameError.prototype.constructor = ChannelNameError; + +function ChannelPasswordError(message) { + this.name = 'ChannelPasswordError'; + this.message = message || 'Default Message'; + this.stack = (new Error()).stack; +} +ChannelPasswordError.prototype = Object.create(Error.prototype); +ChannelPasswordError.prototype.constructor = ChannelPasswordError; \ No newline at end of file diff --git a/public/assets/js/publishChannelFunctions.js b/public/assets/js/publishChannelFunctions.js new file mode 100644 index 00000000..aa07e9e7 --- /dev/null +++ b/public/assets/js/publishChannelFunctions.js @@ -0,0 +1,56 @@ +function sendSignupRequest (channelName, password) { + return new Promise(function(resolve, reject) { + // make sure the claim name is still available + let xhttp; + const params = `username=${channelName}&password=${password}`; + console.log(params); + xhttp = new XMLHttpRequest(); + xhttp.open('POST', '/api/signup', true); + xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhttp.responseType = 'json'; + xhttp.onreadystatechange = function() { + if (this.readyState == 4 ) { + if ( this.status == 200) { + if (this.response == true) { + resolve(); + } else { + reject( new NameError("Your request could not be completed")); + } + } else { + reject("createChannel request failed with status:" + this.status); + }; + } + }; + xhttp.send(params); + }); +} + +function publishNewChannel (event) { + const channelName = document.getElementById('new-channel-name').value; + const password = document.getElementById('new-channel-password').value; + const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name'); + const passwordErrorDisplayElement = document.getElementById('input-error-password'); + // prevent default so this script can handle submission + event.preventDefault(); + // validate submission + validateNewChannelSubmission(channelName, password) + .then(() => { + return sendSignupRequest(channelName, password) // post the request + }) + .then(() => { + console.log('success'); + document.getElementById('signup-form').innerHTML = '

Your channel has been successfully created! Redirecting you now...

'; + window.location.href = '/channelName'; + }) + .catch(error => { + if (error.name === 'ChannelNameError'){ + showError(channelNameErrorDisplayElement, error.message); + } else if (error.name === 'ChannelPasswordError'){ + showError(passwordErrorDisplayElement, error.message); + } else { + console.log('failure:', error); + } + }) + + +} \ No newline at end of file diff --git a/public/assets/js/publishFunctions.js b/public/assets/js/publishFileFunctions.js similarity index 71% rename from public/assets/js/publishFunctions.js rename to public/assets/js/publishFileFunctions.js index cef074eb..c535d1a6 100644 --- a/public/assets/js/publishFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -1,9 +1,38 @@ -// update the publish status -function updatePublishStatus(msg){ - document.getElementById('publish-status').innerHTML = msg; +/* drop zone functions */ + +function drop_handler(ev) { + ev.preventDefault(); + // if dropped items aren't files, reject them + var dt = ev.dataTransfer; + if (dt.items) { + if (dt.items[0].kind == 'file') { + var droppedFile = dt.items[0].getAsFile(); + previewAndStageFile(droppedFile); + } + } } -/* publish helper functions */ +function dragover_handler(ev) { + ev.preventDefault(); +} + +function dragend_handler(ev) { + var dt = ev.dataTransfer; + if (dt.items) { + for (var i = 0; i < dt.items.length; i++) { + dt.items.remove(i); + } + } else { + ev.dataTransfer.clearData(); + } +} + +/* publish functions */ + +// update the publish status +function updatePublishStatus(msg){ + document.getElementById('publish-status').innerHTML = msg; +} // When a file is selected for publish, validate that file and // stage it so it will be ready when the publish button is clicked. @@ -40,19 +69,21 @@ function previewAndStageFile(selectedFile){ // Validate the publish submission and then trigger publishing. function publishSelectedImage(event) { - event.preventDefault(); var claimName = document.getElementById('claim-name-input').value; var channelName = document.getElementById('channel-name-select').value; - validateSubmission(stagedFiles, claimName, channelName) - .then(function() { + // prevent default so this script can handle submission + event.preventDefault(); + // validate, submit, and handle response + validateFilePublishSubmission(stagedFiles, claimName, channelName) + .then(() => { uploader.submitFiles(stagedFiles); }) - .catch(function(error) { + .catch(error => { if (error.name === 'FileError') { showError(document.getElementById('input-error-file-selection'), error.message); } else if (error.name === 'NameError') { showError(document.getElementById('input-error-claim-name'), error.message); - } else if (error.name === 'ChannelError'){ + } else if (error.name === 'ChannelNameError'){ console.log(error); showError(document.getElementById('input-error-channel-select'), error.message); } else { @@ -60,33 +91,4 @@ function publishSelectedImage(event) { } return; }) -}; - -/* drop zone functions */ - -function drop_handler(ev) { - ev.preventDefault(); - // if dropped items aren't files, reject them - var dt = ev.dataTransfer; - if (dt.items) { - if (dt.items[0].kind == 'file') { - var droppedFile = dt.items[0].getAsFile(); - previewAndStageFile(droppedFile); - } - } -} - -function dragover_handler(ev) { - ev.preventDefault(); -} - -function dragend_handler(ev) { - var dt = ev.dataTransfer; - if (dt.items) { - for (var i = 0; i < dt.items.length; i++) { - dt.items.remove(i); - } - } else { - ev.dataTransfer.clearData(); - } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index 5abe027d..fe16607a 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -45,18 +45,18 @@ function validateChannelName (name) { console.log(name); // ensure a name was entered if (name.length < 1) { - throw new ChannelError("You must enter a name for your channel"); + 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 ChannelError('"' + invalidCharacters + '" characters are not allowed in the channel name.'); + throw new ChannelNameError('"' + invalidCharacters + '" characters are not allowed in the channel name.'); } } function validatePassword (password) { if (password.length < 1) { - throw new ChannelError("You must enter a password for you channel"); + throw new ChannelNameError("You must enter a password for you channel"); } } @@ -146,32 +146,32 @@ function checkChannelName(name){ } // validation function which checks all aspects of the publish submission -function validateSubmission(stagedFiles, claimName, channelName){ +function validateFilePublishSubmission(stagedFiles, claimName, channelName){ return new Promise(function (resolve, reject) { // 1. make sure only 1 file was selected if (!stagedFiles) { - reject(new FileError("Please select a file")); + return reject(new FileError("Please select a file")); } else if (stagedFiles.length > 1) { - reject(new FileError("Only one file is allowed at a time")); + return reject(new FileError("Only one file is allowed at a time")); } // 2. validate the file's name, type, and size try { validateFile(stagedFiles[0]); } catch (error) { - reject(error); + return reject(error); } // 3. validate that a channel was chosen if (channelName === 'new') { - reject(new ChannelError("Please select a valid channel")); + return reject(new ChannelNameError("Please select a valid channel")); }; // 4. validate the claim name try { - validateClaimName(claimName); // validate the formatting + validateClaimName(claimName); } catch (error) { - reject(error); + return reject(error); } - console.log(claimName); - isNameAvailable(claimName, '/api/isClaimAvailable/') // validate the availability + // if all validation passes, check availability of the name + isNameAvailable(claimName, '/api/isClaimAvailable/') .then(function() { resolve(); }) @@ -179,4 +179,30 @@ function validateSubmission(stagedFiles, claimName, channelName){ reject(error); }); }); +} + +// validation function which checks all aspects of the publish submission +function validateNewChannelSubmission(channelName, password){ + 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(function() { + resolve(); + }) + .catch(function(error) { + reject(error); + }); + }); } \ No newline at end of file diff --git a/views/index.handlebars b/views/index.handlebars index d58cc37e..31662191 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -11,7 +11,7 @@ - + - - + From 1a502c6ad88f3342fcd42d3b13d50f10cbdb8095 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 10:14:00 -0700 Subject: [PATCH 10/43] updated login page validation --- passport/local-login.js | 4 ++-- passport/local-signup.js | 4 ++-- public/assets/js/publishChannelFunctions.js | 4 ++-- public/assets/js/validationFunctions.js | 12 +++++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/passport/local-login.js b/passport/local-login.js index dff8a2ac..b27d6aea 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -14,14 +14,14 @@ module.exports = new PassportLocalStrategy( return db.User .findOne({where: {channelName: username}}) .then(user => { - logger.debug('user', user.dataValues); if (!user) { return done(null, false, {message: 'Incorrect username or password.'}); } if (!user.validPassword(password, user.password)) { return done(null, false, {message: 'Incorrect username or password.'}); } - return done(null, user); // user.dataValues? + logger.debug('user', user.dataValues); + return done(null, user); }) .catch(error => { return done(error); diff --git a/passport/local-signup.js b/passport/local-signup.js index 1bed5485..3e1f4787 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -14,8 +14,8 @@ module.exports = new PassportLocalStrategy( (req, username, password, done) => { logger.debug('new channel signup request'); const address = config.get('WalletConfig.LbryClaimAddress'); - // validate raw inputs (username, password) - username = '@' + username; + // server-side validaton of raw inputs (username, password) + // create the channel and retrieve the metadata return lbryApi.createChannel(username) .then(channelTx => { diff --git a/public/assets/js/publishChannelFunctions.js b/public/assets/js/publishChannelFunctions.js index aa07e9e7..42e5078e 100644 --- a/public/assets/js/publishChannelFunctions.js +++ b/public/assets/js/publishChannelFunctions.js @@ -26,7 +26,7 @@ function sendSignupRequest (channelName, password) { } function publishNewChannel (event) { - const channelName = document.getElementById('new-channel-name').value; + const channelName = `@${document.getElementById('new-channel-name').value}`; const password = document.getElementById('new-channel-password').value; const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name'); const passwordErrorDisplayElement = document.getElementById('input-error-password'); @@ -40,7 +40,7 @@ function publishNewChannel (event) { .then(() => { console.log('success'); document.getElementById('signup-form').innerHTML = '

Your channel has been successfully created! Redirecting you now...

'; - window.location.href = '/channelName'; + window.location.href = `/${channelName}`; }) .catch(error => { if (error.name === 'ChannelNameError'){ diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index fe16607a..d2555dba 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -56,7 +56,7 @@ function validateChannelName (name) { function validatePassword (password) { if (password.length < 1) { - throw new ChannelNameError("You must enter a password for you channel"); + throw new ChannelPasswordError("You must enter a password for you channel"); } } @@ -172,10 +172,10 @@ function validateFilePublishSubmission(stagedFiles, claimName, channelName){ } // if all validation passes, check availability of the name isNameAvailable(claimName, '/api/isClaimAvailable/') - .then(function() { + .then(() => { resolve(); }) - .catch(function(error) { + .catch(error => { reject(error); }); }); @@ -198,10 +198,12 @@ function validateNewChannelSubmission(channelName, password){ } // 3. if all validation passes, check availability of the name isNameAvailable(channelName, '/api/isChannelAvailable/') // validate the availability - .then(function() { + .then(() => { + console.log('channel is avaliable'); resolve(); }) - .catch(function(error) { + .catch( error => { + console.log('error: channel is not avaliable'); reject(error); }); }); From 8fab84de73f7c758f2365f9e9da9c8c00229a7fd Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 11:36:20 -0700 Subject: [PATCH 11/43] fixed error location and messages --- controllers/publishController.js | 2 +- public/assets/js/publishChannelFunctions.js | 30 ----------- public/assets/js/validationFunctions.js | 6 +-- views/login.handlebars | 40 +++++++++++--- views/partials/publishChannel.handlebars | 60 +++++++++++++++------ views/partials/publishForm.handlebars | 2 +- 6 files changed, 83 insertions(+), 57 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 099675ba..3c1ca453 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -14,7 +14,7 @@ module.exports = { if (result === true) { return lbryApi.publishClaim(publishParams); } else { - return new Error('That name has already been claimed by spee.ch. Please choose a new claim name.'); + return new Error('That name has already been claimed by spee.ch.'); } }) // 3. upsert File record (update is in case the claim has been published before by this daemon) diff --git a/public/assets/js/publishChannelFunctions.js b/public/assets/js/publishChannelFunctions.js index 42e5078e..49bc7abb 100644 --- a/public/assets/js/publishChannelFunctions.js +++ b/public/assets/js/publishChannelFunctions.js @@ -23,34 +23,4 @@ function sendSignupRequest (channelName, password) { }; xhttp.send(params); }); -} - -function publishNewChannel (event) { - const channelName = `@${document.getElementById('new-channel-name').value}`; - const password = document.getElementById('new-channel-password').value; - const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name'); - const passwordErrorDisplayElement = document.getElementById('input-error-password'); - // prevent default so this script can handle submission - event.preventDefault(); - // validate submission - validateNewChannelSubmission(channelName, password) - .then(() => { - return sendSignupRequest(channelName, password) // post the request - }) - .then(() => { - console.log('success'); - document.getElementById('signup-form').innerHTML = '

Your channel has been successfully created! Redirecting you now...

'; - window.location.href = `/${channelName}`; - }) - .catch(error => { - if (error.name === 'ChannelNameError'){ - showError(channelNameErrorDisplayElement, error.message); - } else if (error.name === 'ChannelPasswordError'){ - showError(passwordErrorDisplayElement, error.message); - } else { - console.log('failure:', error); - } - }) - - } \ No newline at end of file diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index d2555dba..e8b05c0c 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -31,12 +31,12 @@ function validateFile(file) { function validateClaimName (name) { // ensure a name was entered if (name.length < 1) { - throw new NameError("You must enter a name for your claim"); + 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 in the title.'); + throw new NameError('"' + invalidCharacters + '" characters are not allowed in the url.'); } } @@ -81,7 +81,7 @@ function isNameAvailable (name, apiUrl) { if (this.response == true) { resolve(); } else { - reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); + reject( new NameError("That name has already been claimed by someone else.")); } } else { reject("request to check claim name failed with status:" + this.status); diff --git a/views/login.handlebars b/views/login.handlebars index 5f3ff6a9..daa6de4b 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -20,23 +20,20 @@

Create New

Create a brand new channel:

-
+
-
+
@
-
+
-
- -
- +
{{> footer}}
@@ -44,3 +41,32 @@ + diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index aea12fd3..48c9da5c 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -1,8 +1,8 @@
-

+

- {{#if user}} {{/if}} @@ -12,25 +12,30 @@

\ No newline at end of file diff --git a/views/partials/publishForm.handlebars b/views/partials/publishForm.handlebars index 0a767866..7c80e3f2 100644 --- a/views/partials/publishForm.handlebars +++ b/views/partials/publishForm.handlebars @@ -12,8 +12,8 @@
-

+

Spee.ch/ From 3acd89f93dd84b6af67dea69e9e96fcc4f091090 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 14:39:20 -0700 Subject: [PATCH 12/43] changed login to http request --- passport/local-login.js | 1 + public/assets/css/generalStyle.css | 28 +++++++- ...shChannelFunctions.js => authFunctions.js} | 6 +- routes/api-routes.js | 12 +--- routes/auth-routes.js | 15 +++++ routes/page-routes.js | 24 ------- speech.js | 3 +- views/login.handlebars | 39 +++++++---- views/partials/publishChannel.handlebars | 66 +++++++++++++++---- views/partials/publishForm.handlebars | 4 +- 10 files changed, 131 insertions(+), 67 deletions(-) rename public/assets/js/{publishChannelFunctions.js => authFunctions.js} (85%) create mode 100644 routes/auth-routes.js diff --git a/passport/local-login.js b/passport/local-login.js index b27d6aea..719ce1ee 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -10,6 +10,7 @@ module.exports = new PassportLocalStrategy( passReqToCallback: true, }, (req, username, password, done) => { + logger.debug('verifying loggin attempt'); username = `@${username}`; return db.User .findOne({where: {channelName: username}}) diff --git a/public/assets/css/generalStyle.css b/public/assets/css/generalStyle.css index d1d0c1c7..8d2babcc 100644 --- a/public/assets/css/generalStyle.css +++ b/public/assets/css/generalStyle.css @@ -2,6 +2,7 @@ body, button, input, textarea, label, select, option { font-family: serif; } /* Containters */ + .wrapper { margin-left: 20%; width:60%; @@ -76,6 +77,7 @@ a, a:visited { color: blue; text-decoration: none; } + h1 { font-size: x-large; } @@ -111,6 +113,26 @@ table { text-align: left; } +button { + border: 1px solid black; + padding: 0.5em; + margin: 0.5em 0 0.5em 0; + color: black; + background-color: white; +} + +button:hover, button:focus { + border: 1px solid blue; + color: white; + background-color: blue; +} + +button:active{ + border: 1px solid blue; + color: white; + background-color: white; +} + .stop-float { clear: both; } @@ -137,10 +159,14 @@ table { color: red; } +input:-webkit-autofill { + -webkit-box-shadow: 0 0 0px 1000px white inset; +} + .input-text { outline: none; border: 0px; - background-color: #ffffff; + background-color: white; } .input-text--primary { diff --git a/public/assets/js/publishChannelFunctions.js b/public/assets/js/authFunctions.js similarity index 85% rename from public/assets/js/publishChannelFunctions.js rename to public/assets/js/authFunctions.js index 49bc7abb..5b52ee4c 100644 --- a/public/assets/js/publishChannelFunctions.js +++ b/public/assets/js/authFunctions.js @@ -1,11 +1,11 @@ -function sendSignupRequest (channelName, password) { +function sendAuthRequest (channelName, password, url) { // url === /signup or /login return new Promise(function(resolve, reject) { // make sure the claim name is still available let xhttp; const params = `username=${channelName}&password=${password}`; - console.log(params); + console.log(params, url); xhttp = new XMLHttpRequest(); - xhttp.open('POST', '/api/signup', true); + xhttp.open('POST', url, true); xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhttp.responseType = 'json'; xhttp.onreadystatechange = function() { diff --git a/routes/api-routes.js b/routes/api-routes.js index 9895a21a..8ed5858b 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -6,18 +6,8 @@ const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); const { createPublishParams, validateFile, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); -const passport = require('passport'); -module.exports = (app, hostedContentPath) => { - // route for auth - app.post('/api/signup', passport.authenticate('local-signup'), (req, res) => { - logger.debug('successful signup'); - res.status(200).json(true); - }); - app.post('/api/login', passport.authenticate('local-login'), (req, res) => { - logger.debug('successful login'); - res.status(200).json(true); - }); +module.exports = (app) => { // route to run a claim_list request on the daemon app.get('/api/claim_list/:name', ({ headers, ip, originalUrl, params }, res) => { // google analytics diff --git a/routes/auth-routes.js b/routes/auth-routes.js new file mode 100644 index 00000000..39c0ef0f --- /dev/null +++ b/routes/auth-routes.js @@ -0,0 +1,15 @@ +const logger = require('winston'); +const passport = require('passport'); + +module.exports = (app) => { + // route for sign up + app.post('/signup', passport.authenticate('local-signup'), (req, res) => { + logger.debug('successful signup'); + res.status(200).json(true); + }); + // route for log in + app.post('/login', passport.authenticate('local-login'), (req, res) => { + logger.debug('successful login'); + res.status(200).json(true); + }); +}; diff --git a/routes/page-routes.js b/routes/page-routes.js index 4630bd6c..fee208a8 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -1,28 +1,12 @@ const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, getStatsSummary, getTrendingClaims, getRecentClaims } = require('../controllers/statsController.js'); -const passport = require('passport'); -const logger = require('winston'); module.exports = (app) => { - // route for auth - app.post('/signup', passport.authenticate('local-signup'), (req, res) => { - 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) => { - 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) => { if (req.user) { @@ -31,14 +15,6 @@ module.exports = (app) => { res.status(200).render('login'); } }); - // route to display login page - // app.get('/users/:name', isAuthenticated, (req, res) => { - // res.status(200).render('profile'); - // }); - // app.get('/logout', deAuthenticate, (req, res) => { - // res.status(200).render('/'); - // }); - // route to show 'about' page for spee.ch app.get('/about', (req, res) => { // get and render the content diff --git a/speech.js b/speech.js index f5663baf..2f8066c1 100644 --- a/speech.js +++ b/speech.js @@ -88,7 +88,8 @@ db.sequelize // add the hosted content folder at a static path app.use('/media', express.static(hostedContentPath)); // require routes & wrap in socket.io - require('./routes/api-routes.js')(app, hostedContentPath); + require('./routes/auth-routes.js')(app); + require('./routes/api-routes.js')(app); require('./routes/page-routes.js')(app); require('./routes/serve-routes.js')(app); require('./routes/home-routes.js')(app); diff --git a/views/login.handlebars b/views/login.handlebars index daa6de4b..b415c9bf 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -4,19 +4,17 @@

Log In

Log in to an existing channel:

-
+
- @ + @
- -
-
- +
+

Create New

Create a brand new channel:

@@ -24,23 +22,23 @@
- @ + @
- +
- +
{{> footer}}
- + diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index 48c9da5c..97cfb7e4 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -7,41 +7,65 @@ {{/if}} +

+ +
+ \ No newline at end of file diff --git a/views/partials/publishForm.handlebars b/views/partials/publishForm.handlebars index 7c80e3f2..79eab66a 100644 --- a/views/partials/publishForm.handlebars +++ b/views/partials/publishForm.handlebars @@ -4,7 +4,7 @@

Drag and drop your file here, or choose your file below.

-
+
@@ -40,7 +40,7 @@ // reset file selection area document.getElementById('file-selection-area').innerHTML = `

Drag and drop your file here, or choose your file below.

- +
`; From ef475ba951d8ccff253b011150f7fb9a0ea819c4 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 15:43:42 -0700 Subject: [PATCH 13/43] login completed --- passport/local-login.js | 7 ++++--- public/assets/js/authFunctions.js | 6 ++++-- views/login.handlebars | 12 ++++++++---- views/partials/publishChannel.handlebars | 9 ++++++--- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/passport/local-login.js b/passport/local-login.js index 719ce1ee..5812abae 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -10,18 +10,19 @@ module.exports = new PassportLocalStrategy( passReqToCallback: true, }, (req, username, password, done) => { - logger.debug('verifying loggin attempt'); - username = `@${username}`; + logger.debug(`verifying loggin attempt ${username} ${password}`); return db.User .findOne({where: {channelName: username}}) .then(user => { if (!user) { + logger.debug('no user found'); return done(null, false, {message: 'Incorrect username or password.'}); } if (!user.validPassword(password, user.password)) { + logger.debug('incorrect password'); return done(null, false, {message: 'Incorrect username or password.'}); } - logger.debug('user', user.dataValues); + logger.debug('user found:', user.dataValues); return done(null, user); }) .catch(error => { diff --git a/public/assets/js/authFunctions.js b/public/assets/js/authFunctions.js index 5b52ee4c..3cc7a85f 100644 --- a/public/assets/js/authFunctions.js +++ b/public/assets/js/authFunctions.js @@ -14,10 +14,12 @@ function sendAuthRequest (channelName, password, url) { // url === /signup or / if (this.response == true) { resolve(); } else { - reject( new NameError("Your request could not be completed")); + reject(new NameError('Your request succedded but could not be completed')); } + } else if (this.status == 401) { + reject('Incorrect username or password') } else { - reject("createChannel request failed with status:" + this.status); + reject('Auth request failed with status:' + this.status); }; } }; diff --git a/views/login.handlebars b/views/login.handlebars index b415c9bf..7823a498 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -5,6 +5,7 @@

Log In

Log in to an existing channel:

+
@ @@ -50,12 +51,13 @@ // validate submission validateNewChannelSubmission(channelName, password) .then(() => { + document.getElementById('publish-channel-form').innerHTML = '

Creating your new channel...

'; return sendAuthRequest(channelName, password, '/signup'); // post the request }) .then(() => { console.log('signup success'); - document.getElementById('publish-channel-form').innerHTML = '

Your channel has been successfully created! Redirecting you now...

'; - window.location.href = `/${channelName}`; + document.getElementById('publish-channel-form').innerHTML = '

Your channel has been successfully created! Signing you in now...

'; + window.location.href = '/'; }) .catch(error => { if (error.name === 'ChannelNameError'){ @@ -70,15 +72,17 @@ function loginToChannel (event) { const channelName = `@${document.getElementById('login-channel-name').value}`; const password = document.getElementById('login-channel-password').value; + const loginErrorDisplayElement = document.getElementById('login-error-display-element'); // prevent default action event.preventDefault() // send request sendAuthRequest(channelName, password, '/login') - .then(() => { + .then( () => { console.log('login success'); - //window.location.href = `/${channelName}`; + window.location.href = `/${channelName}`; }) .catch(error => { + showError(loginErrorDisplayElement, error); console.log('login failure:', error); }) } diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index 97cfb7e4..32bf8c32 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -15,6 +15,7 @@ @@ -40,50 +15,3 @@ - diff --git a/views/partials/channelCreation.handlebars b/views/partials/channelCreation.handlebars new file mode 100644 index 00000000..e0f3c692 --- /dev/null +++ b/views/partials/channelCreation.handlebars @@ -0,0 +1,51 @@ +
+

+

+
+
+ + @ + +
+
+
+ + +
+
+

+ +
+ + \ No newline at end of file diff --git a/views/partials/channelLogin.handlebars b/views/partials/channelLogin.handlebars new file mode 100644 index 00000000..fe715c5c --- /dev/null +++ b/views/partials/channelLogin.handlebars @@ -0,0 +1,35 @@ +

+

+
+
+ + @ +
+
+ + +
+ +
+

+ + + \ No newline at end of file diff --git a/views/partials/channelSelection.handlebars b/views/partials/channelSelection.handlebars new file mode 100644 index 00000000..1056d17a --- /dev/null +++ b/views/partials/channelSelection.handlebars @@ -0,0 +1,43 @@ +
+

+

+ + +

+ + + + +
+ + + \ No newline at end of file diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars deleted file mode 100644 index 32bf8c32..00000000 --- a/views/partials/publishChannel.handlebars +++ /dev/null @@ -1,121 +0,0 @@ -
-

-

- - -

- - - - -
- - - \ No newline at end of file diff --git a/views/partials/publishForm.handlebars b/views/partials/publishForm.handlebars index 79eab66a..91da57ad 100644 --- a/views/partials/publishForm.handlebars +++ b/views/partials/publishForm.handlebars @@ -20,7 +20,7 @@

- {{> publishChannel}} + {{> channelSelection}}
{{> publishDetails}} From 5228b55a0f1ac5357b14f377f698dbbdb8b99287 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 17:14:33 -0700 Subject: [PATCH 15/43] fixed publish --- migrations/Add-FileId-To-Claim.js | 20 ++++++++++++++++++++ public/assets/css/componentStyle.css | 2 -- public/assets/css/generalStyle.css | 5 +++-- routes/api-routes.js | 4 ++-- 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 migrations/Add-FileId-To-Claim.js diff --git a/migrations/Add-FileId-To-Claim.js b/migrations/Add-FileId-To-Claim.js new file mode 100644 index 00000000..0cea1ea8 --- /dev/null +++ b/migrations/Add-FileId-To-Claim.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + return queryInterface.addColumn( + 'Claim', + 'FileId', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + return queryInterface.removeColumn( + 'Claim', + 'FileId' + ); + }, +}; diff --git a/public/assets/css/componentStyle.css b/public/assets/css/componentStyle.css index 3f908c29..b5514d39 100644 --- a/public/assets/css/componentStyle.css +++ b/public/assets/css/componentStyle.css @@ -54,8 +54,6 @@ width: 90% } - - /* show routes */ .show-asset { width: 100%; diff --git a/public/assets/css/generalStyle.css b/public/assets/css/generalStyle.css index 5a777639..c417203d 100644 --- a/public/assets/css/generalStyle.css +++ b/public/assets/css/generalStyle.css @@ -1,6 +1,7 @@ body, button, input, textarea, label, select, option { font-family: serif; } + /* Containters */ .wrapper { @@ -170,11 +171,11 @@ input:-webkit-autofill { } .input-text--primary { - border-bottom: 1px solid grey; + border-bottom: 1px solid blue; } .input-text--primary:focus { - border-bottom: 1px solid blue; + border-bottom: 1px solid grey; } /* MEDIA QUERIES */ diff --git a/routes/api-routes.js b/routes/api-routes.js index 8ed5858b..2b504b69 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -30,7 +30,7 @@ module.exports = (app) => { if (result === true) { res.status(200).json(true); } else { - logger.debug(`Rejecting publish request because ${params.name} has already been published via spee.ch`); + logger.debug(`Rejecting '${params.name}' because that name has already been claimed on spee.ch`); res.status(200).json(false); } }) @@ -45,7 +45,7 @@ module.exports = (app) => { if (result === true) { res.status(200).json(true); } else { - logger.debug(`Rejecting publish request because ${params.name} has already been published via spee.ch`); + logger.debug(`Rejecting '${params.name}' because that channel has already been claimed on spee.ch`); res.status(200).json(false); } }) From 9041d4d4dcbfd91b1c1f94bc3683523fd62304f0 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Wed, 20 Sep 2017 17:49:05 -0700 Subject: [PATCH 16/43] added lbryapi createChannel functionality --- helpers/lbryApi.js | 65 +++++++++++++++++++++++----------------- passport/local-signup.js | 1 + 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/helpers/lbryApi.js b/helpers/lbryApi.js index d04b9f9c..e1b2bad2 100644 --- a/helpers/lbryApi.js +++ b/helpers/lbryApi.js @@ -1,6 +1,22 @@ const axios = require('axios'); const logger = require('winston'); +function handleResponse ({ data }, resolve, reject) { + logger.debug('handling lbry api response'); + if (data.result) { + // check for an error + if (data.result.error) { + reject(data.result.error); + return; + }; + logger.debug('data.result', data.result); + resolve(data.result); + return; + } + // fallback in case the just timed out + reject(JSON.stringify(data)); +} + module.exports = { getWalletList () { logger.debug('lbryApi >> getting wallet list'); @@ -10,8 +26,7 @@ module.exports = { method: 'wallet_list', }) .then(response => { - const result = response.data.result; - resolve(result); + handleResponse(response, resolve, reject); }) .catch(error => { reject(error); @@ -27,8 +42,7 @@ module.exports = { params: publishParams, }) .then(response => { - const result = response.data.result; - resolve(result); + handleResponse(response, resolve, reject); }) .catch(error => { reject(error); @@ -43,18 +57,8 @@ module.exports = { method: 'get', params: { uri, timeout: 20 }, }) - .then(({ data }) => { - // check to make sure the daemon didn't just time out - if (!data.result) { - reject(JSON.stringify(data)); - } - if (data.result.error) { - reject(data.result.error); - } - /* - note: put in a check to make sure we do not resolve until the download is actually complete (response.data.completed === true)? - */ - resolve(data.result); + .then(response => { + handleResponse(response, resolve, reject); }) .catch(error => { reject(error); @@ -69,8 +73,8 @@ module.exports = { method: 'claim_list', params: { name: claimName }, }) - .then(({ data }) => { - resolve(data.result); + .then(response => { + handleResponse(response, resolve, reject); }) .catch(error => { reject(error); @@ -94,7 +98,6 @@ module.exports = { } }) .catch(error => { - console.log('error with resolve', error); reject(error); }); }); @@ -110,7 +113,6 @@ module.exports = { if (data.result) { resolve(data.result.download_directory); } else { - // reject(new Error('Successfully connected to lbry daemon, but unable to retrieve the download directory.')); return new Error('Successfully connected to lbry daemon, but unable to retrieve the download directory.'); } }) @@ -120,15 +122,22 @@ module.exports = { }); }); }, - createChannel (channelName) { + createChannel (name) { return new Promise((resolve, reject) => { - resolve({ - tx : 'test', - txid : 'test', - nout : 'test', - fee : 'test', - claim_id: 'xxxxxxxxxxxxxxxxxx', - }); + axios + .post('http://localhost:5279/lbryapi', { + method: 'channel_new', + params: { + channel_name: name, + amount : 0.1, + }, + }) + .then(response => { + handleResponse(response, resolve, reject); + }) + .catch(error => { + reject(error); + }); }); }, }; diff --git a/passport/local-signup.js b/passport/local-signup.js index 3e1f4787..f7f8dcce 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -37,6 +37,7 @@ module.exports = new PassportLocalStrategy( channelClaimId: certificate.claimId, password : password, address, + CertificateId : certificate.id, }; return db.User.create(userData); }).then(user => { From 18f16e4cce8c20c4098a77299ba81bba62765291 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 00:18:34 -0700 Subject: [PATCH 17/43] switched UserId and CertificateId --- migrations/Remove-UserId-Add-CertificateId.js | 34 +++++++++++++++++++ passport/local-signup.js | 4 ++- public/assets/css/generalStyle.css | 6 ++-- ...n.handlebars => publishChannel.handlebars} | 2 +- views/partials/publishDetails.handlebars | 2 +- views/partials/publishForm.handlebars | 2 +- 6 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 migrations/Remove-UserId-Add-CertificateId.js rename views/partials/{channelSelection.handlebars => publishChannel.handlebars} (91%) diff --git a/migrations/Remove-UserId-Add-CertificateId.js b/migrations/Remove-UserId-Add-CertificateId.js new file mode 100644 index 00000000..fa90482f --- /dev/null +++ b/migrations/Remove-UserId-Add-CertificateId.js @@ -0,0 +1,34 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + const p1 = queryInterface.addColumn( + 'User', + 'CertificateId', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + const p2 = queryInterface.removeColumn( + 'Certificate', + 'UserId' + ); + return Promise.all([p1, p2]); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + const p1 = queryInterface.removeColumn( + 'Certificate', + 'UserId' + ); + const p2 = queryInterface.addColumn( + 'User', + 'CertificateId', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + return Promise.all([p1, p2]); + }, +}; diff --git a/passport/local-signup.js b/passport/local-signup.js index f7f8dcce..0f7b8a0c 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -39,10 +39,12 @@ module.exports = new PassportLocalStrategy( address, CertificateId : certificate.id, }; + logger.debug('userData >', userData); return db.User.create(userData); }).then(user => { + logger.debug('user result >', user.dataValues); logger.debug('User record was created successfully'); - return done(null, user); // user.datavalues? + return done(null, user); }) .catch(error => { logger.debug(error); diff --git a/public/assets/css/generalStyle.css b/public/assets/css/generalStyle.css index c417203d..acedc6a2 100644 --- a/public/assets/css/generalStyle.css +++ b/public/assets/css/generalStyle.css @@ -164,17 +164,17 @@ input:-webkit-autofill { -webkit-box-shadow: 0 0 0px 1000px white inset; } -.input-text { +.input-text, .select, .textarea { outline: none; border: 0px; background-color: white; } -.input-text--primary { +.input-text--primary, .select--primary, .textarea--primary { border-bottom: 1px solid blue; } -.input-text--primary:focus { +.input-text--primary:focus, .select--primary:focus, .textarea--primary:focus { border-bottom: 1px solid grey; } diff --git a/views/partials/channelSelection.handlebars b/views/partials/publishChannel.handlebars similarity index 91% rename from views/partials/channelSelection.handlebars rename to views/partials/publishChannel.handlebars index 1056d17a..478cc7f2 100644 --- a/views/partials/channelSelection.handlebars +++ b/views/partials/publishChannel.handlebars @@ -2,7 +2,7 @@

- {{#if user}} {{/if}} diff --git a/views/partials/publishDetails.handlebars b/views/partials/publishDetails.handlebars index 6e96a543..282fe65f 100644 --- a/views/partials/publishDetails.handlebars +++ b/views/partials/publishDetails.handlebars @@ -9,7 +9,7 @@ - + diff --git a/views/partials/publishForm.handlebars b/views/partials/publishForm.handlebars index 91da57ad..79eab66a 100644 --- a/views/partials/publishForm.handlebars +++ b/views/partials/publishForm.handlebars @@ -20,7 +20,7 @@

- {{> channelSelection}} + {{> publishChannel}}
{{> publishDetails}} From b1f4bbeaf38591aeda5795fa8b195da48c946797 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 09:18:19 -0700 Subject: [PATCH 18/43] added channel_name to publish params --- controllers/publishController.js | 4 ++-- helpers/publishHelpers.js | 9 +++++---- public/assets/js/validationFunctions.js | 2 +- routes/sockets-routes.js | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 3c1ca453..39bc43ef 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -7,14 +7,14 @@ module.exports = { publish (publishParams, fileName, fileType) { return new Promise((resolve, reject) => { let publishResults = {}; - // 1. make sure the name is available + // 1. make sure the name is (still) available publishHelpers.checkClaimNameAvailability(publishParams.name) // 2. publish the file .then(result => { if (result === true) { return lbryApi.publishClaim(publishParams); } else { - return new Error('That name has already been claimed by spee.ch.'); + return new Error('That name has already been claimed on spee.ch.'); } }) // 3. upsert File record (update is in case the claim has been published before by this daemon) diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 186fabec..66a7f690 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -51,7 +51,7 @@ module.exports = { throw new Error('NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"'); } }, - createPublishParams (name, filePath, title, description, license, nsfw) { + createPublishParams (name, filePath, title, description, license, nsfw, channel) { logger.debug(`Creating Publish Parameters for "${name}"`); const claimAddress = config.get('WalletConfig.LbryClaimAddress'); // filter nsfw and ensure it is a boolean @@ -72,7 +72,7 @@ module.exports = { if (title === '' || title === null) { title = name; } - if (description === '' || title === null) { + if (description === '' || description === null) { description = `${name} published via spee.ch`; } // create the publish params @@ -83,10 +83,11 @@ module.exports = { metadata : { description, title, - author : 'spee.ch', - language: 'en', + author : 'spee.ch', + language : 'en', license, nsfw, + channel_name: channel, }, claim_address: claimAddress, // change_address: changeAddress, diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index e8b05c0c..4a1f6915 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -161,7 +161,7 @@ function validateFilePublishSubmission(stagedFiles, claimName, channelName){ return reject(error); } // 3. validate that a channel was chosen - if (channelName === 'new') { + if (channelName === 'new' || channelName === 'login') { return reject(new ChannelNameError("Please select a valid channel")); }; // 4. validate the claim name diff --git a/routes/sockets-routes.js b/routes/sockets-routes.js index cd653e37..9a376f81 100644 --- a/routes/sockets-routes.js +++ b/routes/sockets-routes.js @@ -36,7 +36,7 @@ module.exports = (app, siofu, hostedContentPath) => { logger.debug(`Client successfully uploaded ${file.name}`); socket.emit('publish-status', 'File upload successfully completed. Your image is being published to LBRY (this might take a second)...'); // prepare the publish parameters - const publishParams = publishHelpers.createPublishParams(file.meta.name, file.pathName, file.meta.title, file.meta.description, file.meta.license, file.meta.nsfw); + const publishParams = publishHelpers.createPublishParams(file.meta.name, file.pathName, file.meta.title, file.meta.description, file.meta.license, file.meta.nsfw, file.meta.channel); // publish the file publishController.publish(publishParams, file.name, file.meta.type) .then(result => { @@ -52,7 +52,7 @@ module.exports = (app, siofu, hostedContentPath) => { logger.error(`An error occurred in uploading the client's file`); socket.emit('publish-failure', 'File uploaded, but with errors'); postToStats('PUBLISH', '/', null, null, null, 'File uploaded, but with errors'); - // to-do: remove the file if not done automatically + // to-do: remove the file, if not done automatically } }); // handle disconnect From ba7e19a49893893650b104018ef1c527e80019ce Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 11:00:06 -0700 Subject: [PATCH 19/43] fixed associations between User and Certificate --- controllers/publishController.js | 11 ++++-- migrations/Remove-UserId-Add-CertificateId.js | 8 ++-- passport/local-signup.js | 39 +++++++++++-------- speech.js | 4 +- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 39bc43ef..d9de1d43 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -7,14 +7,14 @@ module.exports = { publish (publishParams, fileName, fileType) { return new Promise((resolve, reject) => { let publishResults = {}; - // 1. make sure the name is (still) available + // 1. make sure the name is available publishHelpers.checkClaimNameAvailability(publishParams.name) // 2. publish the file .then(result => { if (result === true) { return lbryApi.publishClaim(publishParams); } else { - return new Error('That name has already been claimed on spee.ch.'); + return new Error('That name has already been claimed by spee.ch.'); } }) // 3. upsert File record (update is in case the claim has been published before by this daemon) @@ -40,10 +40,15 @@ module.exports = { name : publishParams.name, claimId: publishResults.claim_id, }; + // create the records return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, fileRecord, upsertCriteria, 'Claim')]); }) - .then(() => { + .then((file, claim) => { logger.debug('File and Claim records successfully created'); + return Promise.all([file.setClaims(claim), claim.setFiles(file)]); + }) + .then(() => { + logger.debug('File and Claim records successfully associated'); resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim; }) .catch(error => { diff --git a/migrations/Remove-UserId-Add-CertificateId.js b/migrations/Remove-UserId-Add-CertificateId.js index fa90482f..942ac792 100644 --- a/migrations/Remove-UserId-Add-CertificateId.js +++ b/migrations/Remove-UserId-Add-CertificateId.js @@ -18,12 +18,12 @@ module.exports = { down: (queryInterface, Sequelize) => { // logic for reverting the changes const p1 = queryInterface.removeColumn( - 'Certificate', - 'UserId' + 'User', + 'CertificateId' ); const p2 = queryInterface.addColumn( - 'User', - 'CertificateId', + 'Certificate', + 'UserId', { type : Sequelize.STRING, allowNull: true, diff --git a/passport/local-signup.js b/passport/local-signup.js index 0f7b8a0c..1700a3e0 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -14,11 +14,21 @@ module.exports = new PassportLocalStrategy( (req, username, password, done) => { logger.debug('new channel signup request'); const address = config.get('WalletConfig.LbryClaimAddress'); - // server-side validaton of raw inputs (username, password) + let user; + let certificate; + // server-side validaton of inputs (username, password) // create the channel and retrieve the metadata return lbryApi.createChannel(username) .then(channelTx => { + // create user record + const userData = { + channelName : username, + channelClaimId: channelTx.claim_id, + password : password, + address, + }; + logger.debug('userData >', userData); // create certificate record const certificateData = { address, @@ -26,24 +36,19 @@ module.exports = new PassportLocalStrategy( name : username, }; logger.debug('certificateData >', certificateData); - return db.Certificate.create(certificateData); + // save user and certificate to db + return Promise.all([db.User.create(userData), db.Certificate.create(certificateData)]); }) - .then(certificate => { - logger.debug('certificate result >', certificate.dataValues); - logger.debug('Certificate record was created successfully'); - // define an object that contains all the user data - const userData = { - channelName : username, - channelClaimId: certificate.claimId, - password : password, - address, - CertificateId : certificate.id, - }; - logger.debug('userData >', userData); - return db.User.create(userData); - }).then(user => { + .then(result => { + user = result[0]; + certificate = result[1]; + logger.debug('user and certificate successfully created'); logger.debug('user result >', user.dataValues); - logger.debug('User record was created successfully'); + logger.debug('certificate result >', certificate.dataValues); + // associate the instances + return Promise.all([certificate.setUser(user), user.setCertificate(certificate)]); + }).then(result => { + logger.debug('user and certificate successfully associated'); return done(null, user); }) .catch(error => { diff --git a/speech.js b/speech.js index 2f8066c1..d6df24f0 100644 --- a/speech.js +++ b/speech.js @@ -45,8 +45,10 @@ passport.serializeUser((user, done) => { passport.deserializeUser((id, done) => { db.User.findOne({ where: { id } }) .then(user => { - done(null, user); // user.dataValues? + done(null, user); + return null; }) + .then() .catch(error => { logger.error('sequelize error', error); done(error, null); From 99dc8573804c62bf72cbd5be44f598cb655483eb Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 12:05:04 -0700 Subject: [PATCH 20/43] fixed publish associations and db.Claim info --- controllers/publishController.js | 39 ++++++++++++++++++++++++-------- helpers/publishHelpers.js | 6 ++--- passport/local-signup.js | 8 +++---- routes/sockets-routes.js | 6 +++++ 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index d9de1d43..d18a5153 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -7,6 +7,8 @@ module.exports = { publish (publishParams, fileName, fileType) { return new Promise((resolve, reject) => { let publishResults = {}; + let file; + let claim; // 1. make sure the name is available publishHelpers.checkClaimNameAvailability(publishParams.name) // 2. publish the file @@ -18,12 +20,14 @@ module.exports = { } }) // 3. upsert File record (update is in case the claim has been published before by this daemon) - .then(result => { - let fileRecord; - let upsertCriteria; - publishResults = result; - logger.info(`Successfully published ${fileName}`, publishResults); - fileRecord = { + .then(tx => { + logger.info(`Successfully published ${fileName}`, tx); + publishResults = tx; + return db.User.findOne({where: {channelName: publishParams.channel_name}}); + }) + .then(user => { + logger.debug('found user', user.datavalues); + const fileRecord = { name : publishParams.name, claimId : publishResults.claim_id, title : publishParams.metadata.title, @@ -36,16 +40,31 @@ module.exports = { fileType, nsfw : publishParams.metadata.nsfw, }; - upsertCriteria = { + const claimRecord = { + name : publishParams.name, + claimId : publishResults.claim_id, + title : publishParams.metadata.title, + description : publishParams.metadata.description, + address : publishParams.claim_address, + outpoint : `${publishResults.txid}:${publishResults.nout}`, + height : 0, + contentType : fileType, + nsfw : publishParams.metadata.nsfw, + certificateId: user.channelClaimId, + amount : publishParams.bid, + }; + const upsertCriteria = { name : publishParams.name, claimId: publishResults.claim_id, }; // create the records - return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, fileRecord, upsertCriteria, 'Claim')]); + return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim')]); }) - .then((file, claim) => { + .then(result => { + file = result[0]; + claim = result[1]; logger.debug('File and Claim records successfully created'); - return Promise.all([file.setClaims(claim), claim.setFiles(file)]); + return Promise.all([file.setClaim(claim), claim.setFile(file)]); }) .then(() => { logger.debug('File and Claim records successfully associated'); diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 66a7f690..7d801130 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -83,12 +83,12 @@ module.exports = { metadata : { description, title, - author : 'spee.ch', - language : 'en', + author : 'spee.ch', + language: 'en', license, nsfw, - channel_name: channel, }, + channel_name : channel, claim_address: claimAddress, // change_address: changeAddress, }; diff --git a/passport/local-signup.js b/passport/local-signup.js index 1700a3e0..258c554f 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -20,11 +20,11 @@ module.exports = new PassportLocalStrategy( // create the channel and retrieve the metadata return lbryApi.createChannel(username) - .then(channelTx => { + .then(tx => { // create user record const userData = { channelName : username, - channelClaimId: channelTx.claim_id, + channelClaimId: tx.claim_id, password : password, address, }; @@ -32,7 +32,7 @@ module.exports = new PassportLocalStrategy( // create certificate record const certificateData = { address, - claimId: channelTx.claim_id, + claimId: tx.claim_id, name : username, }; logger.debug('certificateData >', certificateData); @@ -47,7 +47,7 @@ module.exports = new PassportLocalStrategy( logger.debug('certificate result >', certificate.dataValues); // associate the instances return Promise.all([certificate.setUser(user), user.setCertificate(certificate)]); - }).then(result => { + }).then(() => { logger.debug('user and certificate successfully associated'); return done(null, user); }) diff --git a/routes/sockets-routes.js b/routes/sockets-routes.js index 9a376f81..d14160af 100644 --- a/routes/sockets-routes.js +++ b/routes/sockets-routes.js @@ -35,6 +35,12 @@ module.exports = (app, siofu, hostedContentPath) => { if (file.success) { logger.debug(`Client successfully uploaded ${file.name}`); socket.emit('publish-status', '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 client side. + */ + // prepare the publish parameters const publishParams = publishHelpers.createPublishParams(file.meta.name, file.pathName, file.meta.title, file.meta.description, file.meta.license, file.meta.nsfw, file.meta.channel); // publish the file From 761aa1daf7b5ef3a566f3ea0ee4ea5daa71cd325 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 16:03:45 -0700 Subject: [PATCH 21/43] removed address from User model --- config/default.json | 7 ++-- config/development.json | 5 +-- config/production.json | 5 +-- helpers/lbryApi.js | 2 +- helpers/publishHelpers.js | 14 +++++--- migrations/Add-Address-To-User.js | 20 ----------- ...Id-To-Claim.js => Add-FileId-To-Claim2.js} | 0 migrations/Add-UserId-To-Certificate.js | 4 +-- migrations/Remove-UserId-Add-CertificateId.js | 34 ------------------- migrations/remove-Email-from-User.js | 20 ----------- models/user.js | 4 --- passport/local-signup.js | 6 ++-- speech.js | 5 +-- views/partials/assetInfo.handlebars | 4 +-- views/partials/publishChannel.handlebars | 2 +- views/partials/topBar.handlebars | 2 +- 16 files changed, 30 insertions(+), 104 deletions(-) delete mode 100644 migrations/Add-Address-To-User.js rename migrations/{Add-FileId-To-Claim.js => Add-FileId-To-Claim2.js} (100%) delete mode 100644 migrations/Remove-UserId-Add-CertificateId.js delete mode 100644 migrations/remove-Email-from-User.js diff --git a/config/default.json b/config/default.json index cdab2d23..47764010 100644 --- a/config/default.json +++ b/config/default.json @@ -1,6 +1,7 @@ { "WalletConfig": { - "LbryClaimAddress": "none" + "LbryClaimAddress": "none", + "DefaultChannel": "none" }, "AnalyticsConfig":{ "GoogleId": "none" @@ -11,6 +12,8 @@ "Password": "none" }, "Logging": { - "LogLevel": "none" + "LogLevel": "none", + "SlackErrorChannel": "none", + "SlackInfoChannel": "none" } } \ No newline at end of file diff --git a/config/development.json b/config/development.json index 839e5f35..4beca9ff 100644 --- a/config/development.json +++ b/config/development.json @@ -1,13 +1,10 @@ { "WalletConfig": { - "LbryClaimAddress": "none" + "DefaultChannel": "@speechDev" }, "AnalyticsConfig":{ "GoogleId": "UA-100747990-1" }, - "Database": { - "MySqlConnectionUri": "none" - }, "Logging": { "LogLevel": "silly", "SlackErrorChannel": "#staging_speech-errors", diff --git a/config/production.json b/config/production.json index caa0b328..a5bc5074 100644 --- a/config/production.json +++ b/config/production.json @@ -1,13 +1,10 @@ { "WalletConfig": { - "LbryClaimAddress": "none" + "DefaultChannel": "@speech" }, "AnalyticsConfig":{ "GoogleId": "UA-60403362-3" }, - "Database": { - "MySqlConnectionUri": "none" - }, "Logging": { "LogLevel": "verbose", "SlackErrorChannel": "#speech-errors", diff --git a/helpers/lbryApi.js b/helpers/lbryApi.js index e1b2bad2..2b9c1fba 100644 --- a/helpers/lbryApi.js +++ b/helpers/lbryApi.js @@ -122,7 +122,7 @@ module.exports = { }); }); }, - createChannel (name) { + createChannel (name, claimAddress) { return new Promise((resolve, reject) => { axios .post('http://localhost:5279/lbryapi', { diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 7d801130..44890fce 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -32,7 +32,7 @@ module.exports = { throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"'); } // validate license - if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1) && (license.indecOf('CC Attribution-NonCommercial 4.0 International') === -1)) { + if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { throw new Error('Only posts with a "Public Domain" license, or one of the Creative Commons licenses are eligible for publishing through spee.ch'); } switch (nsfw) { @@ -54,6 +54,7 @@ module.exports = { createPublishParams (name, filePath, title, description, license, nsfw, channel) { logger.debug(`Creating Publish Parameters for "${name}"`); const claimAddress = config.get('WalletConfig.LbryClaimAddress'); + const defaultChannel = config.get('WalletConfig.DefaultChannel'); // filter nsfw and ensure it is a boolean if (nsfw === false) { nsfw = false; @@ -72,7 +73,7 @@ module.exports = { if (title === '' || title === null) { title = name; } - if (description === '' || description === null) { + if (description === ' ' || description === null) { description = `${name} published via spee.ch`; } // create the publish params @@ -88,10 +89,15 @@ module.exports = { license, nsfw, }, - channel_name : channel, claim_address: claimAddress, - // change_address: changeAddress, }; + // add channel if applicable + if (channel !== 'none'){ + publishParams['channel_name'] = channel; + } else { + publishParams['channel_name'] = defaultChannel; + } + logger.debug('publishParams:', publishParams); return publishParams; }, diff --git a/migrations/Add-Address-To-User.js b/migrations/Add-Address-To-User.js deleted file mode 100644 index b8f88d6f..00000000 --- a/migrations/Add-Address-To-User.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - // logic for transforming into the new state - return queryInterface.addColumn( - 'User', - 'Address', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - }, - down: (queryInterface, Sequelize) => { - // logic for reverting the changes - return queryInterface.removeColumn( - 'User', - 'Address' - ); - }, -}; diff --git a/migrations/Add-FileId-To-Claim.js b/migrations/Add-FileId-To-Claim2.js similarity index 100% rename from migrations/Add-FileId-To-Claim.js rename to migrations/Add-FileId-To-Claim2.js diff --git a/migrations/Add-UserId-To-Certificate.js b/migrations/Add-UserId-To-Certificate.js index 49217435..9005dee6 100644 --- a/migrations/Add-UserId-To-Certificate.js +++ b/migrations/Add-UserId-To-Certificate.js @@ -1,7 +1,7 @@ module.exports = { up: (queryInterface, Sequelize) => { // logic for transforming into the new state - queryInterface.addColumn( + return queryInterface.addColumn( 'Certificate', 'UserId', { @@ -12,7 +12,7 @@ module.exports = { }, down: (queryInterface, Sequelize) => { // logic for reverting the changes - queryInterface.removeColumn( + return queryInterface.removeColumn( 'Certificate', 'UserId' ); diff --git a/migrations/Remove-UserId-Add-CertificateId.js b/migrations/Remove-UserId-Add-CertificateId.js deleted file mode 100644 index 942ac792..00000000 --- a/migrations/Remove-UserId-Add-CertificateId.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - // logic for transforming into the new state - const p1 = queryInterface.addColumn( - 'User', - 'CertificateId', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - const p2 = queryInterface.removeColumn( - 'Certificate', - 'UserId' - ); - return Promise.all([p1, p2]); - }, - down: (queryInterface, Sequelize) => { - // logic for reverting the changes - const p1 = queryInterface.removeColumn( - 'User', - 'CertificateId' - ); - const p2 = queryInterface.addColumn( - 'Certificate', - 'UserId', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - return Promise.all([p1, p2]); - }, -}; diff --git a/migrations/remove-Email-from-User.js b/migrations/remove-Email-from-User.js deleted file mode 100644 index 4d6a39e3..00000000 --- a/migrations/remove-Email-from-User.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - // logic for transforming into the new state - return queryInterface.removeColumn( - 'User', - 'Email' - ); - }, - down: (queryInterface, Sequelize) => { - // logic for reverting the changes - return queryInterface.addColumn( - 'User', - 'Email', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - }, -}; diff --git a/models/user.js b/models/user.js index da7457d8..9368d89d 100644 --- a/models/user.js +++ b/models/user.js @@ -14,10 +14,6 @@ module.exports = (sequelize, { STRING }) => { type : STRING, allowNull: false, }, - address: { - type : STRING, - allowNull: false, - }, }, { freezeTableName: true, diff --git a/passport/local-signup.js b/passport/local-signup.js index 258c554f..f71332af 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -19,21 +19,21 @@ module.exports = new PassportLocalStrategy( // server-side validaton of inputs (username, password) // create the channel and retrieve the metadata - return lbryApi.createChannel(username) + return lbryApi.createChannel(username, address) .then(tx => { // create user record const userData = { channelName : username, channelClaimId: tx.claim_id, password : password, - address, + // address, }; logger.debug('userData >', userData); // create certificate record const certificateData = { - address, claimId: tx.claim_id, name : username, + // address, }; logger.debug('certificateData >', certificateData); // save user and certificate to db diff --git a/speech.js b/speech.js index d6df24f0..56d1225a 100644 --- a/speech.js +++ b/speech.js @@ -72,8 +72,9 @@ app.set('view engine', 'handlebars'); app.use((req, res, next) => { if (req.user) { res.locals.user = { - id : req.user.id, - channelName: req.user.channelName, + id : req.user.id, + channelName : req.user.channelName, + channelClaimId: req.user.channelClaimId, }; } next(); diff --git a/views/partials/assetInfo.handlebars b/views/partials/assetInfo.handlebars index b9406f9d..b66fc6f7 100644 --- a/views/partials/assetInfo.handlebars +++ b/views/partials/assetInfo.handlebars @@ -45,8 +45,8 @@ {{/ifConditional}}
-

Description

-

{{fileInfo.description}} +

Description

+

{{fileInfo.description}}

Metadata

diff --git a/views/partials/publishChannel.handlebars b/views/partials/publishChannel.handlebars index 478cc7f2..ca178afe 100644 --- a/views/partials/publishChannel.handlebars +++ b/views/partials/publishChannel.handlebars @@ -6,7 +6,7 @@ {{#if user}} {{/if}} - + diff --git a/views/partials/topBar.handlebars b/views/partials/topBar.handlebars index f9115c09..b89a5ea0 100644 --- a/views/partials/topBar.handlebars +++ b/views/partials/topBar.handlebars @@ -6,7 +6,7 @@ help {{#if user}} - {{user.channelName}} + {{user.channelName}} logout {{else}} login From 0216263a8af52251dbde76ec5dc7d23544439633 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 16:03:58 -0700 Subject: [PATCH 22/43] removed address from User model --- helpers/publishHelpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 44890fce..c154967d 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -92,7 +92,7 @@ module.exports = { claim_address: claimAddress, }; // add channel if applicable - if (channel !== 'none'){ + if (channel !== 'none') { publishParams['channel_name'] = channel; } else { publishParams['channel_name'] = defaultChannel; From d15c6f076b60cb6461404491527b94a254da87a4 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 21 Sep 2017 16:09:57 -0700 Subject: [PATCH 23/43] updated migrations --- migrations/Add-FileId-To-Claim2.js | 20 -------------------- migrations/Add-UserId-To-Certificate.js | 20 -------------------- migrations/Add-UserId-To-File.js | 20 -------------------- 3 files changed, 60 deletions(-) delete mode 100644 migrations/Add-FileId-To-Claim2.js delete mode 100644 migrations/Add-UserId-To-Certificate.js delete mode 100644 migrations/Add-UserId-To-File.js diff --git a/migrations/Add-FileId-To-Claim2.js b/migrations/Add-FileId-To-Claim2.js deleted file mode 100644 index 0cea1ea8..00000000 --- a/migrations/Add-FileId-To-Claim2.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - // logic for transforming into the new state - return queryInterface.addColumn( - 'Claim', - 'FileId', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - }, - down: (queryInterface, Sequelize) => { - // logic for reverting the changes - return queryInterface.removeColumn( - 'Claim', - 'FileId' - ); - }, -}; diff --git a/migrations/Add-UserId-To-Certificate.js b/migrations/Add-UserId-To-Certificate.js deleted file mode 100644 index 9005dee6..00000000 --- a/migrations/Add-UserId-To-Certificate.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - // logic for transforming into the new state - return queryInterface.addColumn( - 'Certificate', - 'UserId', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - }, - down: (queryInterface, Sequelize) => { - // logic for reverting the changes - return queryInterface.removeColumn( - 'Certificate', - 'UserId' - ); - }, -}; diff --git a/migrations/Add-UserId-To-File.js b/migrations/Add-UserId-To-File.js deleted file mode 100644 index bde92b42..00000000 --- a/migrations/Add-UserId-To-File.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - up: (queryInterface, Sequelize) => { - // logic for transforming into the new state - return queryInterface.addColumn( - 'File', - 'UserId', - { - type : Sequelize.STRING, - allowNull: true, - } - ); - }, - down: (queryInterface, Sequelize) => { - // logic for reverting the changes - return queryInterface.removeColumn( - 'File', - 'UserId' - ); - }, -}; From 7d0fda64f7dfcfeff58501bf7ab73da99a25613a Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 08:49:28 -0700 Subject: [PATCH 24/43] added check for slack env vars --- config/default.json | 17 ++++++++------- config/loggerConfig.js | 4 ---- config/slackLoggerConfig.js | 43 +++++++++++++++++++++---------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/config/default.json b/config/default.json index 47764010..e094bc44 100644 --- a/config/default.json +++ b/config/default.json @@ -1,19 +1,20 @@ { "WalletConfig": { - "LbryClaimAddress": "none", - "DefaultChannel": "none" + "LbryClaimAddress": null, + "DefaultChannel": null }, "AnalyticsConfig":{ - "GoogleId": "none" + "GoogleId": null }, "Database": { "Database": "lbry", - "Username": "none", - "Password": "none" + "Username": null, + "Password": null }, "Logging": { - "LogLevel": "none", - "SlackErrorChannel": "none", - "SlackInfoChannel": "none" + "LogLevel": null, + "SlackWebHook": null, + "SlackErrorChannel": null, + "SlackInfoChannel": null } } \ No newline at end of file diff --git a/config/loggerConfig.js b/config/loggerConfig.js index 4efbbb02..ffd4c970 100644 --- a/config/loggerConfig.js +++ b/config/loggerConfig.js @@ -12,10 +12,6 @@ module.exports = (winston, logLevel) => { ], }); - // winston.on('error', (err) => { - // console.log('unhandled exception in winston >> ', err); - // }); - winston.error('Level 0'); winston.warn('Level 1'); winston.info('Level 2'); diff --git a/config/slackLoggerConfig.js b/config/slackLoggerConfig.js index 5f49e001..4bd88200 100644 --- a/config/slackLoggerConfig.js +++ b/config/slackLoggerConfig.js @@ -5,23 +5,28 @@ const SLACK_INFO_CHANNEL = config.get('Logging.SlackInfoChannel'); const winstonSlackWebHook = require('winston-slack-webhook').SlackWebHook; module.exports = (winston) => { - // add a transport for errors - winston.add(winstonSlackWebHook, { - name : 'slack-errors-transport', - level : 'error', - webhookUrl: SLACK_WEB_HOOK, - channel : SLACK_ERROR_CHANNEL, - 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, - username : 'spee.ch', - iconEmoji : ':nerd_face:', - }); - // send test message - winston.error('Testing slack logging... slack logging is online.'); + if (SLACK_WEB_HOOK) { + // add a transport for errors to slack + winston.add(winstonSlackWebHook, { + name : 'slack-errors-transport', + level : 'error', + webhookUrl: SLACK_WEB_HOOK, + channel : SLACK_ERROR_CHANNEL, + 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, + username : 'spee.ch', + iconEmoji : ':nerd_face:', + }); + // send test message + winston.error('Slack error logging is online.'); + winston.info('Slack info logging is online.'); + } else { + winston.error('Slack logging is not enabled because no SLACK_WEB_HOOK env var provided.'); + } }; From 959f57ffc5a466c976156220e475ca36b52838b0 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 09:13:18 -0700 Subject: [PATCH 25/43] added config variable check on startup --- helpers/configVarCheck.js | 18 ++++++++++++++++++ speech.js | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 helpers/configVarCheck.js diff --git a/helpers/configVarCheck.js b/helpers/configVarCheck.js new file mode 100644 index 00000000..527d5811 --- /dev/null +++ b/helpers/configVarCheck.js @@ -0,0 +1,18 @@ +const config = require('config'); +const logger = require('winston'); + +module.exports = function () { + let configVars = []; + configVars.push(config.get('Logging')); + configVars.push(config.get('Database')); + configVars.push(config.get('AnalyticsConfig')); + configVars.push(config.get('WalletConfig')); + + for (let i = 0; i < configVars.length; i++) { + for (let key in configVars[i]) { + if (configVars[i].hasOwnProperty(key)) { + logger.debug(`CONFIG CHECK: ${key} === ${configVars[i][key]}`); + } + } + } +}; diff --git a/speech.js b/speech.js index 56d1225a..0f6841b6 100644 --- a/speech.js +++ b/speech.js @@ -20,6 +20,9 @@ const logLevel = config.get('Logging.LogLevel'); require('./config/loggerConfig.js')(logger, logLevel); require('./config/slackLoggerConfig.js')(logger); +// check for global config variables +require('./helpers/configVarCheck.js')(); + // trust the proxy to get ip address for us app.enable('trust proxy'); @@ -68,7 +71,7 @@ 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 +// middleware to pass user info back to client (for handlebars access), if user is logged in app.use((req, res, next) => { if (req.user) { res.locals.user = { From 250ead165bd51c4b9812d514c724d320f526d90c Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 09:31:26 -0700 Subject: [PATCH 26/43] generalized config variable check --- helpers/configVarCheck.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/helpers/configVarCheck.js b/helpers/configVarCheck.js index 527d5811..b4b40e51 100644 --- a/helpers/configVarCheck.js +++ b/helpers/configVarCheck.js @@ -1,17 +1,20 @@ const config = require('config'); const logger = require('winston'); +const fs = require('fs'); module.exports = function () { - let configVars = []; - configVars.push(config.get('Logging')); - configVars.push(config.get('Database')); - configVars.push(config.get('AnalyticsConfig')); - configVars.push(config.get('WalletConfig')); + // get the config file + const defaultConfigFile = JSON.parse(fs.readFileSync('./config/default.json')); - for (let i = 0; i < configVars.length; i++) { - for (let key in configVars[i]) { - if (configVars[i].hasOwnProperty(key)) { - logger.debug(`CONFIG CHECK: ${key} === ${configVars[i][key]}`); + for (let configCategoryKey in defaultConfigFile) { + if (defaultConfigFile.hasOwnProperty(configCategoryKey)) { + // get the final variables for each config category + const configVariables = config.get(configCategoryKey); + for (let configVarKey in configVariables) { + if (configVariables.hasOwnProperty(configVarKey)) { + // print each variable + logger.debug(`CONFIG CHECK: ${configCategoryKey}.${configVarKey} === ${configVariables[configVarKey]}`); + } } } } From ede4d4804f5cff896bec7a3d60f1f3f459187402 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 11:55:56 -0700 Subject: [PATCH 27/43] updated input msgs and destructuring --- controllers/publishController.js | 8 ++------ helpers/publishHelpers.js | 2 +- passport/local-signup.js | 14 ++++++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index d18a5153..71c731d9 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -7,8 +7,6 @@ module.exports = { publish (publishParams, fileName, fileType) { return new Promise((resolve, reject) => { let publishResults = {}; - let file; - let claim; // 1. make sure the name is available publishHelpers.checkClaimNameAvailability(publishParams.name) // 2. publish the file @@ -16,7 +14,7 @@ module.exports = { if (result === true) { return lbryApi.publishClaim(publishParams); } else { - return new Error('That name has already been claimed by spee.ch.'); + return new Error('That name is already in use by spee.ch.'); } }) // 3. upsert File record (update is in case the claim has been published before by this daemon) @@ -60,9 +58,7 @@ module.exports = { // create the records return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim')]); }) - .then(result => { - file = result[0]; - claim = result[1]; + .then(([file, claim]) => { logger.debug('File and Claim records successfully created'); return Promise.all([file.setClaim(claim), claim.setFile(file)]); }) diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index c154967d..4177ed39 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -29,7 +29,7 @@ module.exports = { // validate claim name const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); if (invalidCharacters) { - throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"'); + throw new Error('The url name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"'); } // validate license if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { diff --git a/passport/local-signup.js b/passport/local-signup.js index f71332af..35e52742 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -15,7 +15,6 @@ module.exports = new PassportLocalStrategy( logger.debug('new channel signup request'); const address = config.get('WalletConfig.LbryClaimAddress'); let user; - let certificate; // server-side validaton of inputs (username, password) // create the channel and retrieve the metadata @@ -26,7 +25,6 @@ module.exports = new PassportLocalStrategy( channelName : username, channelClaimId: tx.claim_id, password : password, - // address, }; logger.debug('userData >', userData); // create certificate record @@ -39,16 +37,16 @@ module.exports = new PassportLocalStrategy( // save user and certificate to db return Promise.all([db.User.create(userData), db.Certificate.create(certificateData)]); }) - .then(result => { - user = result[0]; - certificate = result[1]; + .then(([newUser, newCertificate]) => { + user = newUser; // save outside scope of this function logger.debug('user and certificate successfully created'); - logger.debug('user result >', user.dataValues); - logger.debug('certificate result >', certificate.dataValues); + logger.debug('user result >', newUser.dataValues); + logger.debug('certificate result >', newCertificate.dataValues); // associate the instances - return Promise.all([certificate.setUser(user), user.setCertificate(certificate)]); + return Promise.all([newCertificate.setUser(newUser), newUser.setCertificate(newCertificate)]); }).then(() => { logger.debug('user and certificate successfully associated'); + logger.debug('user ===', user.dataValues); return done(null, user); }) .catch(error => { From 6e60e0aa2f2ef886d5c7515ce31f9da6b6979a74 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 20:30:45 -0700 Subject: [PATCH 28/43] updated models --- migrations/UpdateAssociationColumns.js | 79 ++++++++++++++++++++++++++ migrations/UpdateUserAndChannel.js | 46 +++++++++++++++ models/certificate.js | 1 + models/channel.js | 26 +++++++++ models/claim.js | 1 + models/file.js | 1 + models/request.js | 1 + models/user.js | 8 +-- 8 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 migrations/UpdateAssociationColumns.js create mode 100644 migrations/UpdateUserAndChannel.js create mode 100644 models/channel.js diff --git a/migrations/UpdateAssociationColumns.js b/migrations/UpdateAssociationColumns.js new file mode 100644 index 00000000..22c6def9 --- /dev/null +++ b/migrations/UpdateAssociationColumns.js @@ -0,0 +1,79 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + const p1 = queryInterface.removeColumn( + 'Certificate', + 'UserId' + ); + const p2 = queryInterface.addColumn( + 'Certificate', + 'ChannelId', + { + type : Sequelize.INTEGER, + allowNull: true, + } + ); + const p3 = queryInterface.addConstraint( + 'Certificate', + ['ChannelId'], + { + type : 'FOREIGN KEY', + name : 'Certificate_ibfk_1', + references: { + table: 'Channel', + field: 'id', + }, + onUpdate: 'cascade', + onDelete: 'cascade', + } + ); + const p4 = queryInterface.changeColumn( + 'Claim', + 'FileId', + { + type : Sequelize.INTEGER, + allowNull: true, + } + ); + const p5 = queryInterface.addConstraint( + 'Claim', + ['FileId'], + { + type : 'FOREIGN KEY', + name : 'Claim_ibfk_1', + references: { + table: 'File', + field: 'id', + }, + onUpdate: 'cascade', + onDelete: 'cascade', + } + ); + const p6 = queryInterface.removeColumn( + 'File', + 'UserId' + ); + + return Promise.all([p1, p2, p3, p4, p5, p6]); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + const p1 = queryInterface.addColumn( + 'Certificate', + 'UserId', + { + type : Sequelize.INTEGER, + allowNull: true, + } + ); + const p2 = queryInterface.addColumn( + 'File', + 'UserId', + { + type : Sequelize.INTEGER, + allowNull: true, + } + ); + return Promise.all([p1, p2]); + }, +}; diff --git a/migrations/UpdateUserAndChannel.js b/migrations/UpdateUserAndChannel.js new file mode 100644 index 00000000..32c22097 --- /dev/null +++ b/migrations/UpdateUserAndChannel.js @@ -0,0 +1,46 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + const p1 = queryInterface.addColumn( + 'User', + 'userName', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + const p2 = queryInterface.removeColumn( + 'User', + 'channelName' + ); + const p3 = queryInterface.removeColumn( + 'User', + 'channelClaimId' + ); + return Promise.all([p1, p2, p3]); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + const p1 = queryInterface.removeColumn( + 'User', + 'userName' + ); + const p2 = queryInterface.addColumn( + 'User', + 'channelName', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + const p3 = queryInterface.addColumn( + 'User', + 'channelClaimId', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + return Promise.all([p1, p2, p3]); + }, +}; diff --git a/models/certificate.js b/models/certificate.js index f8e8784d..04fee274 100644 --- a/models/certificate.js +++ b/models/certificate.js @@ -85,6 +85,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D }, { freezeTableName: true, + underscored : true, } ); diff --git a/models/channel.js b/models/channel.js new file mode 100644 index 00000000..5478088f --- /dev/null +++ b/models/channel.js @@ -0,0 +1,26 @@ +module.exports = (sequelize, { STRING }) => { + const Channel = sequelize.define( + 'Channel', + { + channelName: { + type : STRING, + allowNull: false, + }, + channelClaimId: { + type : STRING, + allowNull: false, + }, + }, + { + freezeTableName: true, + underscored : true, + } + ); + + Channel.associate = db => { + Channel.belongsTo(db.User); + Channel.hasOne(db.Certificate); + }; + + return Channel; +}; diff --git a/models/claim.js b/models/claim.js index ab35576b..d4e09303 100644 --- a/models/claim.js +++ b/models/claim.js @@ -137,6 +137,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D }, { freezeTableName: true, + underscored : true, } ); diff --git a/models/file.js b/models/file.js index dbe74cf4..75a5db8c 100644 --- a/models/file.js +++ b/models/file.js @@ -47,6 +47,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => { }, { freezeTableName: true, + underscored : true, } ); diff --git a/models/request.js b/models/request.js index 846e12c6..b436d23e 100644 --- a/models/request.js +++ b/models/request.js @@ -22,6 +22,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, TEXT }) => { }, { freezeTableName: true, + underscored : true, } ); diff --git a/models/user.js b/models/user.js index 9368d89d..cb44acd5 100644 --- a/models/user.js +++ b/models/user.js @@ -2,11 +2,7 @@ module.exports = (sequelize, { STRING }) => { const User = sequelize.define( 'User', { - channelName: { - type : STRING, - allowNull: false, - }, - channelClaimId: { + userName: { type : STRING, allowNull: false, }, @@ -17,11 +13,11 @@ module.exports = (sequelize, { STRING }) => { }, { freezeTableName: true, + underscored : true, } ); User.associate = db => { - User.hasMany(db.File); User.hasOne(db.Certificate); }; From a2b3ed1ab29a1d5b2d6a773983f068943877fc82 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 21:03:43 -0700 Subject: [PATCH 29/43] updated db.User to db.Channel refs --- controllers/publishController.js | 2 +- helpers/publishHelpers.js | 2 +- models/certificate.js | 3 +-- models/channel.js | 1 - models/claim.js | 1 - models/file.js | 7 ------- models/request.js | 1 - models/user.js | 1 - passport/local-login.js | 8 ++++++-- passport/local-signup.js | 20 ++++++++++++++------ speech.js | 1 + 11 files changed, 24 insertions(+), 23 deletions(-) diff --git a/controllers/publishController.js b/controllers/publishController.js index 71c731d9..1c3c82c7 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -21,7 +21,7 @@ module.exports = { .then(tx => { logger.info(`Successfully published ${fileName}`, tx); publishResults = tx; - return db.User.findOne({where: {channelName: publishParams.channel_name}}); + return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); }) .then(user => { logger.debug('found user', user.datavalues); diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 4177ed39..b308f764 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -140,7 +140,7 @@ module.exports = { checkChannelAvailability (name) { return new Promise((resolve, reject) => { // find any records where the name is used - db.User.findAll({ where: { channelName: name } }) + db.Channel.findAll({ where: { channelName: name } }) .then(result => { if (result.length >= 1) { return resolve(false); diff --git a/models/certificate.js b/models/certificate.js index 04fee274..3cf35e7b 100644 --- a/models/certificate.js +++ b/models/certificate.js @@ -85,12 +85,11 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D }, { freezeTableName: true, - underscored : true, } ); Certificate.associate = db => { - Certificate.belongsTo(db.User, { + Certificate.belongsTo(db.Channel, { onDelete : 'cascade', foreignKey: { allowNull: true, diff --git a/models/channel.js b/models/channel.js index 5478088f..9421ee52 100644 --- a/models/channel.js +++ b/models/channel.js @@ -13,7 +13,6 @@ module.exports = (sequelize, { STRING }) => { }, { freezeTableName: true, - underscored : true, } ); diff --git a/models/claim.js b/models/claim.js index d4e09303..ab35576b 100644 --- a/models/claim.js +++ b/models/claim.js @@ -137,7 +137,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D }, { freezeTableName: true, - underscored : true, } ); diff --git a/models/file.js b/models/file.js index 75a5db8c..c1e86772 100644 --- a/models/file.js +++ b/models/file.js @@ -47,18 +47,11 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER }) => { }, { freezeTableName: true, - underscored : true, } ); File.associate = db => { File.hasMany(db.Request); - File.belongsTo(db.User, { - onDelete : 'cascade', - foreignKey: { - allowNull: true, - }, - }); File.hasOne(db.Claim); }; diff --git a/models/request.js b/models/request.js index b436d23e..846e12c6 100644 --- a/models/request.js +++ b/models/request.js @@ -22,7 +22,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, TEXT }) => { }, { freezeTableName: true, - underscored : true, } ); diff --git a/models/user.js b/models/user.js index cb44acd5..56f71002 100644 --- a/models/user.js +++ b/models/user.js @@ -13,7 +13,6 @@ module.exports = (sequelize, { STRING }) => { }, { freezeTableName: true, - underscored : true, } ); diff --git a/passport/local-login.js b/passport/local-login.js index 5812abae..bc03eda3 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -12,7 +12,7 @@ module.exports = new PassportLocalStrategy( (req, username, password, done) => { logger.debug(`verifying loggin attempt ${username} ${password}`); return db.User - .findOne({where: {channelName: username}}) + .findOne({where: {userName: username}}) .then(user => { if (!user) { logger.debug('no user found'); @@ -23,7 +23,11 @@ module.exports = new PassportLocalStrategy( return done(null, false, {message: 'Incorrect username or password.'}); } logger.debug('user found:', user.dataValues); - return done(null, user); + return user.getChannel().then(channel => { + user['channelName'] = channel.channelClaimId; + user['channelClaimId'] = channel.channelClaimId; + return done(null, user); + }); }) .catch(error => { return done(error); diff --git a/passport/local-signup.js b/passport/local-signup.js index 35e52742..0158f715 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -22,11 +22,16 @@ module.exports = new PassportLocalStrategy( .then(tx => { // create user record const userData = { - channelName : username, - channelClaimId: tx.claim_id, - password : password, + userName: username, + password: password, }; logger.debug('userData >', userData); + // create user record + const channelData = { + channelName : `@${username}`, + channelClaimId: tx.claim_id, + }; + logger.debug('channelData >', channelData); // create certificate record const certificateData = { claimId: tx.claim_id, @@ -35,15 +40,18 @@ module.exports = new PassportLocalStrategy( }; logger.debug('certificateData >', certificateData); // save user and certificate to db - return Promise.all([db.User.create(userData), db.Certificate.create(certificateData)]); + return Promise.all([db.User.create(userData), db.Channel.create(channelData), db.Certificate.create(certificateData)]); }) - .then(([newUser, newCertificate]) => { + .then(([newUser, newChannel, newCertificate]) => { user = newUser; // save outside scope of this function + user['channelName'] = newChannel.channelClaimId; + user['channelClaimId'] = newChannel.channelClaimId; logger.debug('user and certificate successfully created'); logger.debug('user result >', newUser.dataValues); + logger.debug('user result >', newChannel.dataValues); logger.debug('certificate result >', newCertificate.dataValues); // associate the instances - return Promise.all([newCertificate.setUser(newUser), newUser.setCertificate(newCertificate)]); + return Promise.all([newCertificate.setChannel(newChannel), newChannel.setUser(newUser)]); }).then(() => { logger.debug('user and certificate successfully associated'); logger.debug('user ===', user.dataValues); diff --git a/speech.js b/speech.js index 0f6841b6..24667437 100644 --- a/speech.js +++ b/speech.js @@ -76,6 +76,7 @@ app.use((req, res, next) => { if (req.user) { res.locals.user = { id : req.user.id, + userName : req.user.userName, channelName : req.user.channelName, channelClaimId: req.user.channelClaimId, }; From 84c525dcc09e13de7e1459da1c504667fa2b4ec0 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Mon, 25 Sep 2017 22:49:27 -0700 Subject: [PATCH 30/43] fixed view for Channel and User tables --- helpers/lbryApi.js | 8 ++++---- models/user.js | 2 +- passport/local-login.js | 2 -- passport/local-signup.js | 15 +++++---------- speech.js | 13 +++++++++++-- views/partials/channelCreation.handlebars | 2 +- views/partials/channelLogin.handlebars | 2 +- views/partials/publishChannel.handlebars | 2 +- views/partials/topBar.handlebars | 2 +- 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/helpers/lbryApi.js b/helpers/lbryApi.js index 2b9c1fba..df101b55 100644 --- a/helpers/lbryApi.js +++ b/helpers/lbryApi.js @@ -122,7 +122,7 @@ module.exports = { }); }); }, - createChannel (name, claimAddress) { + createChannel (name) { return new Promise((resolve, reject) => { axios .post('http://localhost:5279/lbryapi', { @@ -135,9 +135,9 @@ module.exports = { .then(response => { handleResponse(response, resolve, reject); }) - .catch(error => { - reject(error); - }); + .catch(error => { + reject(error); + }); }); }, }; diff --git a/models/user.js b/models/user.js index 56f71002..bce93d91 100644 --- a/models/user.js +++ b/models/user.js @@ -17,7 +17,7 @@ module.exports = (sequelize, { STRING }) => { ); User.associate = db => { - User.hasOne(db.Certificate); + User.hasOne(db.Channel); }; User.prototype.validPassword = (givenpassword, thispassword) => { diff --git a/passport/local-login.js b/passport/local-login.js index bc03eda3..5ccb822c 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -24,8 +24,6 @@ module.exports = new PassportLocalStrategy( } logger.debug('user found:', user.dataValues); return user.getChannel().then(channel => { - user['channelName'] = channel.channelClaimId; - user['channelClaimId'] = channel.channelClaimId; return done(null, user); }); }) diff --git a/passport/local-signup.js b/passport/local-signup.js index 0158f715..23273180 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -2,7 +2,6 @@ const db = require('../models'); const PassportLocalStrategy = require('passport-local').Strategy; const lbryApi = require('../helpers/lbryApi.js'); const logger = require('winston'); -const config = require('config'); module.exports = new PassportLocalStrategy( { @@ -12,13 +11,12 @@ module.exports = new PassportLocalStrategy( passReqToCallback: true, // we want to be able to read the post body message parameters in the callback }, (req, username, password, done) => { - logger.debug('new channel signup request'); - const address = config.get('WalletConfig.LbryClaimAddress'); + logger.debug(`new channel signup request: ${username} ${password}`); let user; // server-side validaton of inputs (username, password) // create the channel and retrieve the metadata - return lbryApi.createChannel(username, address) + return lbryApi.createChannel(`@${username}`) .then(tx => { // create user record const userData = { @@ -35,7 +33,7 @@ module.exports = new PassportLocalStrategy( // create certificate record const certificateData = { claimId: tx.claim_id, - name : username, + name : `@${username}`, // address, }; logger.debug('certificateData >', certificateData); @@ -43,9 +41,7 @@ module.exports = new PassportLocalStrategy( return Promise.all([db.User.create(userData), db.Channel.create(channelData), db.Certificate.create(certificateData)]); }) .then(([newUser, newChannel, newCertificate]) => { - user = newUser; // save outside scope of this function - user['channelName'] = newChannel.channelClaimId; - user['channelClaimId'] = newChannel.channelClaimId; + user = newUser; logger.debug('user and certificate successfully created'); logger.debug('user result >', newUser.dataValues); logger.debug('user result >', newChannel.dataValues); @@ -54,11 +50,10 @@ module.exports = new PassportLocalStrategy( return Promise.all([newCertificate.setChannel(newChannel), newChannel.setUser(newUser)]); }).then(() => { logger.debug('user and certificate successfully associated'); - logger.debug('user ===', user.dataValues); return done(null, user); }) .catch(error => { - logger.debug(error); + logger.error('signup error', error); return done(error); }); } diff --git a/speech.js b/speech.js index 24667437..500f2132 100644 --- a/speech.js +++ b/speech.js @@ -45,10 +45,18 @@ app.use(passport.session()); passport.serializeUser((user, done) => { done(null, user.id); }); -passport.deserializeUser((id, done) => { +passport.deserializeUser((id, done) => { // this populates req.user db.User.findOne({ where: { id } }) .then(user => { - done(null, user); + user.getChannel().then(channel => { + let userInfo = {}; + userInfo['id'] = user.id; + userInfo['userName'] = user.userName; + userInfo['channelName'] = channel.channelName; + userInfo['channelClaimId'] = channel.channelClaimId; + done(null, userInfo); + }); + // done(null, user); return null; }) .then() @@ -74,6 +82,7 @@ app.set('view engine', 'handlebars'); // middleware to pass user info back to client (for handlebars access), if user is logged in app.use((req, res, next) => { if (req.user) { + logger.verbose(req.user); res.locals.user = { id : req.user.id, userName : req.user.userName, diff --git a/views/partials/channelCreation.handlebars b/views/partials/channelCreation.handlebars index e0f3c692..1f538a7f 100644 --- a/views/partials/channelCreation.handlebars +++ b/views/partials/channelCreation.handlebars @@ -19,7 +19,7 @@ diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars index 41bd4daf..ea4184b4 100644 --- a/views/layouts/main.handlebars +++ b/views/layouts/main.handlebars @@ -5,8 +5,10 @@ Spee.ch - + + + diff --git a/views/layouts/show.handlebars b/views/layouts/show.handlebars index 0bf410fb..d0130e4b 100644 --- a/views/layouts/show.handlebars +++ b/views/layouts/show.handlebars @@ -5,8 +5,10 @@ Spee.ch - - + + + + {{#unless fileInfo.nsfw}} {{{addTwitterCard fileInfo.fileType openGraphInfo.source openGraphInfo.embedUrl openGraphInfo.directFileUrl}}} diff --git a/views/login.handlebars b/views/login.handlebars index ff0f9ecd..f0e4ec41 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -1,17 +1,22 @@
{{> topBar}} -
- +

Log In

-

Log in to an existing channel:

- {{>channelLogin}} +
+

Log in to an existing channel:

+ {{>channelLoginForm}} +
+
+

Create New

-

Create a brand new channel:

- {{>channelCreation}} +
+

Create a brand new channel:

+ {{>channelCreationForm}} +
{{> footer}}
- + \ No newline at end of file diff --git a/views/partials/asset.handlebars b/views/partials/asset.handlebars index eb607d24..a963b07d 100644 --- a/views/partials/asset.handlebars +++ b/views/partials/asset.handlebars @@ -1,4 +1,4 @@ -
+
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} diff --git a/views/partials/channelCreation.handlebars b/views/partials/channelCreationForm.handlebars similarity index 58% rename from views/partials/channelCreation.handlebars rename to views/partials/channelCreationForm.handlebars index 1f538a7f..058b28f5 100644 --- a/views/partials/channelCreation.handlebars +++ b/views/partials/channelCreationForm.handlebars @@ -1,22 +1,35 @@ -
-

-

-
-
- - @ - -
-
-
- - -
-
-

+ +
+ +
+ +
+
+
+
+ @ + + +
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+ + \ No newline at end of file From f0b248ba9c317a29e5b8dd4563aaee6957517cb5 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 26 Sep 2017 16:05:45 -0700 Subject: [PATCH 32/43] url updates dynamically from channel selection --- public/assets/css/BEM.css | 5 +++++ views/login.handlebars | 5 +++-- views/partials/publishForm-Channel.handlebars | 19 +++++++++++++++++-- views/partials/publishForm-Url.handlebars | 3 +-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/public/assets/css/BEM.css b/public/assets/css/BEM.css index 15c3cc99..92b4beaf 100644 --- a/public/assets/css/BEM.css +++ b/public/assets/css/BEM.css @@ -9,6 +9,11 @@ p { padding-left: 0.3em; } +.url-text { + margin:0px; + padding:0px; +} + /* CONTAINERS */ .wrapper { diff --git a/views/login.handlebars b/views/login.handlebars index f0e4ec41..7309d518 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -1,14 +1,15 @@
{{> topBar}} +

Log In

-

Log In

+

Log in to an existing channel:

{{>channelLoginForm}}
+

Create New

-

Create New

Create a brand new channel:

{{>channelCreationForm}} diff --git a/views/partials/publishForm-Channel.handlebars b/views/partials/publishForm-Channel.handlebars index 1488d01e..09f6df63 100644 --- a/views/partials/publishForm-Channel.handlebars +++ b/views/partials/publishForm-Channel.handlebars @@ -2,9 +2,9 @@
-
+
- {{#if user}} @@ -34,16 +34,31 @@ const createChannelTool = document.getElementById('channel-create-details'); const loginToChannelTool = document.getElementById('channel-login-details'); const selectedOption = event.target.selectedOptions[0].value; + const urlChannel = document.getElementById('url-channel'); if (selectedOption === 'new') { + // show/hide the login and new channel forms createChannelTool.hidden = false; loginToChannelTool.hidden = true; + // update URL + urlChannel.innerText = ''; } else if (selectedOption === 'login') { + // show/hide the login and new channel forms loginToChannelTool.hidden = false; createChannelTool.hidden = true; + // update URL + urlChannel.innerText = ''; } else { + // hide the login and new channel forms loginToChannelTool.hidden = true; createChannelTool.hidden = true; hideError(document.getElementById('input-error-channel-select')); + // update URL + if (selectedOption === 'none'){ + urlChannel.innerText = ''; + } else { + urlChannel.innerText = `${selectedOption}/`; + } + } } \ No newline at end of file diff --git a/views/partials/publishForm-Url.handlebars b/views/partials/publishForm-Url.handlebars index eba519f1..82b3e753 100644 --- a/views/partials/publishForm-Url.handlebars +++ b/views/partials/publishForm-Url.handlebars @@ -5,8 +5,7 @@
- Spee.ch/{{user.channelName}}/ - + Spee.ch/{{#if user.channelName}}{{user.channelName}}/{{/if}}
From b78e881ebcc12990c542576fe2c974e40349d231 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 26 Sep 2017 18:49:37 -0700 Subject: [PATCH 33/43] updated top free claim to use effective Amount --- models/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/index.js b/models/index.js index e4e870c5..e87a147d 100644 --- a/models/index.js +++ b/models/index.js @@ -66,7 +66,7 @@ function getLongClaimIdFromShortClaimId (name, shortId) { function getTopFreeClaimIdByClaimName (name) { return new Promise((resolve, reject) => { db - .sequelize.query(`SELECT claimId FROM Claim WHERE name = '${name}' ORDER BY amount DESC, height ASC LIMIT 1`, { type: db.sequelize.QueryTypes.SELECT }) + .sequelize.query(`SELECT claimId FROM Claim WHERE name = '${name}' ORDER BY effectiveAmount DESC, height ASC LIMIT 1`, { type: db.sequelize.QueryTypes.SELECT }) .then(result => { switch (result.length) { case 0: From 4f3c29c6ca4578e174295153914b85a8e497a295 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Tue, 26 Sep 2017 19:06:07 -0700 Subject: [PATCH 34/43] updated create-channel progress display --- views/partials/channelCreationForm.handlebars | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/views/partials/channelCreationForm.handlebars b/views/partials/channelCreationForm.handlebars index 058b28f5..743aa534 100644 --- a/views/partials/channelCreationForm.handlebars +++ b/views/partials/channelCreationForm.handlebars @@ -21,13 +21,20 @@
+
+ +
-
- + + + - -
+ \ No newline at end of file From a0d1cb4f5c8311e7505db30d0ee40b6065d02040 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Thu, 28 Sep 2017 16:37:20 -0700 Subject: [PATCH 42/43] changed so shortChannelId is part of user creds --- speech.js | 24 +++++++++++-------- views/index.handlebars | 3 ++- views/partials/publishForm-Channel.handlebars | 10 +------- views/partials/publishForm-Url.handlebars | 5 ++-- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/speech.js b/speech.js index 500f2132..082c64a6 100644 --- a/speech.js +++ b/speech.js @@ -46,20 +46,23 @@ passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { // this populates req.user + let userInfo = {}; db.User.findOne({ where: { id } }) .then(user => { - user.getChannel().then(channel => { - let userInfo = {}; - userInfo['id'] = user.id; - userInfo['userName'] = user.userName; - userInfo['channelName'] = channel.channelName; - userInfo['channelClaimId'] = channel.channelClaimId; - done(null, userInfo); - }); - // done(null, user); + userInfo['id'] = user.id; + userInfo['userName'] = user.userName; + return user.getChannel(); + }) + .then(channel => { + userInfo['channelName'] = channel.channelName; + userInfo['channelClaimId'] = channel.channelClaimId; + return db.getShortChannelIdFromLongChannelId(channel.channelClaimId, channel.channelName); + }) + .then(shortChannelId => { + userInfo['shortChannelId'] = shortChannelId; + done(null, userInfo); return null; }) - .then() .catch(error => { logger.error('sequelize error', error); done(error, null); @@ -88,6 +91,7 @@ app.use((req, res, next) => { userName : req.user.userName, channelName : req.user.channelName, channelClaimId: req.user.channelClaimId, + shortChannelId: req.user.shortChannelId, }; } next(); diff --git a/views/index.handlebars b/views/index.handlebars index 1e117a51..1e1bc2e1 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -1,3 +1,5 @@ + +
@@ -10,7 +12,6 @@ -