281 unfurl show links #303

Merged
bones7242 merged 12 commits from 281-unfurl-show-links into master 2017-12-15 17:01:23 +01:00
17 changed files with 164 additions and 109 deletions
Showing only changes of commit 3accf40db2 - Show all commits

View file

@ -10,7 +10,7 @@ module.exports = {
// publish the file // publish the file
return lbryApi.publishClaim(publishParams) return lbryApi.publishClaim(publishParams)
.then(tx => { .then(tx => {
logger.info(`Successfully published ${fileName}`, tx); logger.info(`Successfully published ${publishParams.name} ${fileName}`, tx);
publishResults = tx; publishResults = tx;
// get the channel information // get the channel information
if (publishParams.channel_name) { if (publishParams.channel_name) {
@ -79,6 +79,7 @@ module.exports = {
resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim; resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim;
}) })
.catch(error => { .catch(error => {
logger.error('PUBLISH ERROR', error);
publishHelpers.deleteTemporaryFile(publishParams.file_path); // delete the local file publishHelpers.deleteTemporaryFile(publishParams.file_path); // delete the local file
reject(error); reject(error);
}); });

View file

@ -1,9 +1,10 @@
const db = require('../models'); // require our models for syncing // const db = require('../models'); // require our models for syncing
const logger = require('winston'); const logger = require('winston');
module.exports = { module.exports = {
populateLocalsDotUser (req, res, next) { populateLocalsDotUser (req, res, next) {
if (req.user) { if (req.user) {
logger.debug('populating res.locals.user');
res.locals.user = { res.locals.user = {
id : req.user.id, id : req.user.id,
userName : req.user.userName, userName : req.user.userName,
@ -14,31 +15,12 @@ module.exports = {
} }
next(); next();
}, },
serializeSpeechUser (user, done) { serializeSpeechUser (user, done) { // returns user data to be serialized into session
done(null, user.id); logger.debug('serializing user');
done(null, user);
}, },
deserializeSpeechUser (id, done) { deserializeSpeechUser (user, done) { // deserializes session and populates additional info to req.user
let userInfo = {}; logger.debug('deserializing user');
db.User.findOne({ where: { id } }) done(null, user);
.then(user => {
userInfo['id'] = user.id;
userInfo['userName'] = user.userName;
return user.getChannel();
})
.then(channel => {
userInfo['channelName'] = channel.channelName;
userInfo['channelClaimId'] = channel.channelClaimId;
return db.Certificate.getShortChannelIdFromLongChannelId(channel.channelClaimId, channel.channelName);
})
.then(shortChannelId => {
userInfo['shortChannelId'] = shortChannelId;
// return done(null, userInfo);
done(null, userInfo);
return null;
})
.catch(error => {
logger.error(error);
done(error, null);
});
}, },
}; };

View file

@ -70,16 +70,12 @@ 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 "-"'); throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"');
} }
}, },
validateLicense (license) {
if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) {
throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing.');
}
},
cleanseChannelName (channelName) { cleanseChannelName (channelName) {
if (channelName) { if (!channelName) {
if (channelName.indexOf('@') !== 0) { return null;
channelName = `@${channelName}`; }
} if (channelName.indexOf('@') !== 0) {
channelName = `@${channelName}`;
} }
return channelName; return channelName;
}, },

View file

@ -93,7 +93,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
Certificate.associate = db => { Certificate.associate = db => {
Certificate.belongsTo(db.Channel, { Certificate.belongsTo(db.Channel, {
onDelete : 'cascade',
foreignKey: { foreignKey: {
allowNull: true, allowNull: true,
}, },

View file

@ -235,7 +235,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
Claim.associate = db => { Claim.associate = db => {
Claim.belongsTo(db.File, { Claim.belongsTo(db.File, {
onDelete : 'cascade',
foreignKey: { foreignKey: {
allowNull: true, allowNull: true,
}, },

View file

@ -27,7 +27,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, TEXT }) => {
Request.associate = db => { Request.associate = db => {
Request.belongsTo(db.File, { Request.belongsTo(db.File, {
onDelete : 'cascade',
foreignKey: { foreignKey: {
allowNull: true, allowNull: true,
}, },

View file

@ -28,9 +28,40 @@ module.exports = (sequelize, { STRING }) => {
bcrypt.compare(password, this.password, callback); bcrypt.compare(password, this.password, callback);
}; };
User.prototype.changePassword = function (newPassword) {
return new Promise((resolve, reject) => {
// generate a salt string to use for hashing
bcrypt.genSalt((saltError, salt) => {
if (saltError) {
logger.error('salt error', saltError);
reject(saltError);
return;
}
// generate a hashed version of the user's password
bcrypt.hash(newPassword, salt, (hashError, hash) => {
// if there is an error with the hash generation return the error
if (hashError) {
logger.error('hash error', hashError);
reject(hashError);
return;
}
// replace the current password with the new hash
this
.update({password: hash})
.then(() => {
resolve();
})
.catch(error => {
reject(error);
});
});
});
});
};
// pre-save hook method to hash the user's password before the user's info is saved to the db. // pre-save hook method to hash the user's password before the user's info is saved to the db.
User.hook('beforeCreate', (user, options) => { User.hook('beforeCreate', (user, options) => {
logger.debug('...beforeCreate hook...'); logger.debug('User.beforeCreate hook...');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// generate a salt string to use for hashing // generate a salt string to use for hashing
bcrypt.genSalt((saltError, salt) => { bcrypt.genSalt((saltError, salt) => {

View file

@ -1,53 +1,60 @@
const PassportLocalStrategy = require('passport-local').Strategy; const PassportLocalStrategy = require('passport-local').Strategy;
const db = require('../models'); const db = require('../models');
const logger = require('winston'); const logger = require('winston');
function returnUserAndChannelInfo (userInstance) {
return new Promise((resolve, reject) => {
let userInfo = {};
userInfo['id'] = userInstance.id;
userInfo['userName'] = userInstance.userName;
userInstance
.getChannel()
.then(({channelName, channelClaimId}) => {
userInfo['channelName'] = channelName;
userInfo['channelClaimId'] = channelClaimId;
return db.Certificate.getShortChannelIdFromLongChannelId(channelClaimId, channelName);
})
.then(shortChannelId => {
userInfo['shortChannelId'] = shortChannelId;
resolve(userInfo);
})
.catch(error => {
reject(error);
});
});
}
module.exports = new PassportLocalStrategy( module.exports = new PassportLocalStrategy(
{ {
usernameField : 'username', // username key in the request body usernameField: 'username',
passwordField : 'password', // password key in the request body passwordField: 'password',
session : false,
passReqToCallback: true,
}, },
(req, username, password, done) => { (username, password, done) => {
logger.debug(`verifying loggin attempt ${username} ${password}`); logger.debug('logging user in');
let userInfo = {}; return db
return db.User .User
.findOne({where: {userName: username}}) .findOne({where: {userName: username}})
.then(user => { .then(user => {
if (!user) { if (!user) {
logger.debug('no user found'); // logger.debug('no user found');
return done(null, false, {message: 'Incorrect username or password.'}); return done(null, false, {message: 'Incorrect username or password.'});
} }
logger.debug('user found:', user.dataValues); user.comparePassword(password, (passwordErr, isMatch) => {
logger.debug('...comparing password...');
return user.comparePassword(password, (passwordErr, isMatch) => {
if (passwordErr) { if (passwordErr) {
logger.error('passwordErr:', passwordErr); logger.error('passwordErr:', passwordErr);
return done(passwordErr); return done(null, false, {message: passwordErr});
} }
if (!isMatch) { if (!isMatch) {
logger.debug('incorrect password'); // logger.debug('incorrect password');
return done(null, false, {message: 'Incorrect username or password.'}); return done(null, false, {message: 'Incorrect username or password.'});
} }
logger.debug('...password was a match...'); logger.debug('Password was a match, returning User');
userInfo['id'] = user.id; return returnUserAndChannelInfo(user)
userInfo['userName'] = user.userName; .then((userInfo) => {
// get the User's channel info
return user.getChannel()
.then(channel => {
userInfo['channelName'] = channel.channelName;
userInfo['channelClaimId'] = channel.channelClaimId;
return db.Certificate.getShortChannelIdFromLongChannelId(channel.channelClaimId, channel.channelName);
})
.then(shortChannelId => {
userInfo['shortChannelId'] = shortChannelId;
return done(null, userInfo); return done(null, userInfo);
}) })
.catch(error => { .catch(error => {
throw error; return done(error);
}); });
}); });
}) })

View file

@ -5,12 +5,10 @@ const logger = require('winston');
module.exports = new PassportLocalStrategy( module.exports = new PassportLocalStrategy(
{ {
usernameField : 'username', // sets the custom name of parameters in the POST body message usernameField: 'username',
passwordField : 'password', // sets the custom name of parameters in the POST body message passwordField: 'password',
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) => { (username, password, done) => {
logger.verbose(`new channel signup request. user: ${username} pass: ${password} .`); logger.verbose(`new channel signup request. user: ${username} pass: ${password} .`);
let userInfo = {}; let userInfo = {};
// server-side validaton of inputs (username, password) // server-side validaton of inputs (username, password)

View file

@ -466,6 +466,10 @@ table {
display: inline-block; display: inline-block;
} }
.nav-bar-logo {
cursor: pointer;
}
/* PUBLISH FORM */ /* PUBLISH FORM */
.dropzone { .dropzone {

View file

@ -33,15 +33,13 @@ function publishNewChannel (event) {
return sendAuthRequest(userName, password, '/signup') // post the request return sendAuthRequest(userName, password, '/signup') // post the request
}) })
.then(result => { .then(result => {
setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId);
showChannelCreateDoneDisplay(); showChannelCreateDoneDisplay();
// refresh window logged in as the channel // if user is on the home page, update the needed elements without reloading
setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId); // set cookies
})
.then(() => {
if (window.location.pathname === '/') { if (window.location.pathname === '/') {
// remove old channel and replace with new one & select it replaceChannelOptionInPublishChannelSelect(result.channelName);
replaceChannelOptionInPublishChannelSelect(); replaceChannelOptionInNavBarChannelSelect(result.channelName);
replaceChannelOptionInNavBarChannelSelect(); // if user is not on home page, redirect to home page
} else { } else {
window.location = '/'; window.location = '/';
} }

View file

@ -1,11 +1,9 @@
function replaceChannelOptionInPublishChannelSelect() { function replaceChannelOptionInPublishChannelSelect(loggedInChannel) {
// remove the old channel option // remove the old channel option
const oldChannel = document.getElementById('publish-channel-select-channel-option') const oldChannel = document.getElementById('publish-channel-select-channel-option')
if (oldChannel){ if (oldChannel){
oldChannel.parentNode.removeChild(oldChannel); oldChannel.parentNode.removeChild(oldChannel);
} }
// get channel details from cookies
const loggedInChannel = getCookie('channel_name');
// create new channel option // create new channel option
const newChannelOption = document.createElement('option'); const newChannelOption = document.createElement('option');
newChannelOption.setAttribute('value', loggedInChannel); newChannelOption.setAttribute('value', loggedInChannel);
@ -19,14 +17,12 @@ function replaceChannelOptionInPublishChannelSelect() {
toggleSelectedChannel(loggedInChannel); toggleSelectedChannel(loggedInChannel);
} }
function replaceChannelOptionInNavBarChannelSelect () { function replaceChannelOptionInNavBarChannelSelect (loggedInChannel) {
// remove the old channel option // remove the old channel option
const oldChannel = document.getElementById('nav-bar-channel-select-channel-option'); const oldChannel = document.getElementById('nav-bar-channel-select-channel-option');
if (oldChannel){ if (oldChannel){
oldChannel.parentNode.removeChild(oldChannel); oldChannel.parentNode.removeChild(oldChannel);
} }
// get channel details from cookies
const loggedInChannel = getCookie('channel_name');
// create new channel option & select it // create new channel option & select it
const newChannelOption = document.createElement('option'); const newChannelOption = document.createElement('option');
newChannelOption.setAttribute('value', loggedInChannel); newChannelOption.setAttribute('value', loggedInChannel);
@ -49,20 +45,15 @@ function loginToChannel (event) {
event.preventDefault() event.preventDefault()
validationFunctions.validateNewChannelLogin(userName, password) validationFunctions.validateNewChannelLogin(userName, password)
.then(() => { .then(() => {
// send request
return sendAuthRequest(userName, password, '/login') return sendAuthRequest(userName, password, '/login')
}) })
.then(result => { .then(result => {
// update session cookie with new channel name and id's setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId);
setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId); // replace the current cookies // if user is on the home page, update the needed elements without reloading
})
.then(() => {
// update channel selection
if (window.location.pathname === '/') { if (window.location.pathname === '/') {
// remove old channel and replace with new one & select it replaceChannelOptionInPublishChannelSelect(result.channelName);
replaceChannelOptionInPublishChannelSelect(); replaceChannelOptionInNavBarChannelSelect(result.channelName);
// remove old channel and replace with new one & select it // if user is not on home page, redirect to home page
replaceChannelOptionInNavBarChannelSelect();
} else { } else {
window.location = '/'; window.location = '/';
} }

View file

@ -123,12 +123,14 @@ const publishFileFunctions = {
xhr.open("POST", uri, true); xhr.open("POST", uri, true);
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
console.log('publish response:', xhr.response)
if (xhr.status == 200) { if (xhr.status == 200) {
console.log('publish complete!'); console.log('publish complete!');
that.showFilePublishComplete(JSON.parse(xhr.response).message); that.showFilePublishComplete(JSON.parse(xhr.response).message);
} else if (xhr.status == 502){
that.showFilePublishFailure('Spee.ch was not able to get a response from the LBRY network.');
} else { } else {
console.log(xhr.response); that.showFilePublishFailure(JSON.parse(xhr.response).message);
that.showFilePublishFailure(JSON.parse(xhr.response).message);
} }
} else { } else {
console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status); console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status);

View file

@ -134,6 +134,7 @@ module.exports = (app) => {
res.status(400).json({success: false, message: error.message}); res.status(400).json({success: false, message: error.message});
return; return;
} }
logger.debug('publish req.files:', files);
// validate file, name, license, and nsfw // validate file, name, license, and nsfw
file = files.file; file = files.file;
fileName = file.path.substring(file.path.lastIndexOf('/') + 1); fileName = file.path.substring(file.path.lastIndexOf('/') + 1);

View file

@ -14,7 +14,7 @@ module.exports = (app) => {
}); });
// route for log in // route for log in
app.post('/login', passport.authenticate('local-login'), (req, res) => { app.post('/login', passport.authenticate('local-login'), (req, res) => {
logger.debug('req.user:', req.user); // logger.debug('req.user:', req.user); // req.user contains the authenticated user's info
logger.debug('successful login'); logger.debug('successful login');
res.status(200).json({ res.status(200).json({
success : true, success : true,

View file

@ -33,10 +33,16 @@ 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(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing application/x-www-form-urlencoded
app.use((req, res, next) => { // custom logging middleware to log all incoming 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}`); logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`);
// logger.debug('req.headers:', req.headers);
next(); next();
}); });
// configure passport
passport.serializeUser(serializeSpeechUser);
passport.deserializeUser(deserializeSpeechUser);
const localSignupStrategy = require('./passport/local-signup.js');
const localLoginStrategy = require('./passport/local-login.js');
passport.use('local-signup', localSignupStrategy);
passport.use('local-login', localLoginStrategy);
// initialize passport // initialize passport
app.use(cookieSession({ app.use(cookieSession({
name : 'session', name : 'session',
@ -45,12 +51,6 @@ app.use(cookieSession({
})); }));
app.use(passport.initialize()); app.use(passport.initialize());
app.use(passport.session()); app.use(passport.session());
passport.serializeUser(serializeSpeechUser); // takes the user id from the db and serializes it
passport.deserializeUser(deserializeSpeechUser); // this deserializes id then populates req.user with info
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 // configure handlebars & register it with express app
const hbs = expressHandlebars.create({ const hbs = expressHandlebars.create({

View file

@ -0,0 +1,47 @@
// load dependencies
const logger = require('winston');
const db = require('../models/index'); // require our models for syncing
// configure logging
const config = require('../config/speechConfig.js');
const logLevel = config.logging.logLevel;
require('../config/loggerConfig.js')(logger, logLevel);
const userName = process.argv[2];
logger.debug('user name:', userName);
const oldPassword = process.argv[3];
logger.debug('old password:', oldPassword);
const newPassword = process.argv[4];
logger.debug('new password:', newPassword);
db.sequelize.sync() // sync sequelize
.then(() => {
logger.info('finding user profile');
return db.User.findOne({
where: {
userName: userName,
},
});
})
.then(user => {
if (!user) {
throw new Error('no user found');
}
return new Promise((resolve, reject) => {
user.comparePassword(oldPassword, (passwordErr, isMatch) => {
if (passwordErr) {
return reject(passwordErr);
}
if (!isMatch) {
return reject('Incorrect old password.');
}
logger.debug('Password was a match, updating password');
return resolve(user.changePassword(newPassword));
});
});
})
.then(() => {
logger.debug('Password successfully updated');
})
.catch((error) => {
logger.error(error);
});