Merge branch 'master' of https://github.com/FreeCodeCamp/freecodecamp into ux-improvements

Conflicts:
	app.js
This commit is contained in:
Michael Q Larson
2015-03-25 14:20:34 -07:00
5 changed files with 168 additions and 98 deletions

60
app.js
View File

@ -2,11 +2,20 @@ if (process.env.NODE_ENV !== 'development') {
require('newrelic'); require('newrelic');
} }
require('dotenv').load(); require('dotenv').load();
/** // handle uncaught exceptions. Forever will restart process on shutdown
* Module dependencies. process.on('uncaughtException', function (err) {
*/ console.error(
(new Date()).toUTCString() + ' uncaughtException:',
err.message
);
console.error(err.stack);
/* eslint-disable no-process-exit */
process.exit(1);
/* eslint-enable no-process-exit */
});
var express = require('express'), var express = require('express'),
accepts = require('accepts'),
cookieParser = require('cookie-parser'), cookieParser = require('cookie-parser'),
compress = require('compression'), compress = require('compression'),
session = require('express-session'), session = require('express-session'),
@ -442,8 +451,6 @@ app.post('/account/delete', userController.postDeleteAccount);
app.get('/account/unlink/:provider', userController.getOauthUnlink); app.get('/account/unlink/:provider', userController.getOauthUnlink);
app.get('/sitemap.xml', resourcesController.sitemap); app.get('/sitemap.xml', resourcesController.sitemap);
/** /**
* OAuth sign-in routes. * OAuth sign-in routes.
*/ */
@ -505,17 +512,54 @@ app.get(
} }
); );
//put this route last app.get('/induce-vomiting', function(req, res, next) {
next(new Error('vomiting induced'));
});
// put this route last
app.get( app.get(
'/:username', '/:username',
userController.returnUser userController.returnUser
); );
/** /**
* 500 Error Handler. * 500 Error Handler.
*/ */
app.use(errorHandler()); if (process.env.NODE_ENV === 'development') {
app.use(errorHandler({ log: true }));
} else {
// error handling in production
app.use(function(err, req, res, next) {
// respect err.status
if (err.status) {
res.statusCode = err.status;
}
// default status code to 500
if (res.statusCode < 400) {
res.statusCode = 500;
}
// parse res type
var accept = accepts(req);
var type = accept.type('html', 'json', 'text');
var message = 'opps! Something went wrong. Please try again later';
if (type === 'html') {
req.flash('errors', { msg: message });
return res.redirect('/');
// json
} else if (type === 'json') {
res.setHeader('Content-Type', 'application/json');
return res.send({ message: message });
// plain text
} else {
res.setHeader('Content-Type', 'text/plain');
return res.send(message);
}
});
}
/** /**
* Start Express server. * Start Express server.

View File

@ -27,6 +27,7 @@ passport.deserializeUser(function(id, done) {
passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, password, done) { passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, password, done) {
User.findOne({ email: email }, function(err, user) { User.findOne({ email: email }, function(err, user) {
if (err) { return done(err); }
if (!user) return done(null, false, { message: 'Email ' + email + ' not found'}); if (!user) return done(null, false, { message: 'Email ' + email + ' not found'});
user.comparePassword(password, function(err, isMatch) { user.comparePassword(password, function(err, isMatch) {
if (isMatch) { if (isMatch) {
@ -60,17 +61,19 @@ passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, r
User.findOne({ facebook: profile.id }, function(err, existingUser) { User.findOne({ facebook: profile.id }, function(err, existingUser) {
if (existingUser) { if (existingUser) {
req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
done(err); done();
} else { } else {
User.findById(req.user.id, function(err, user) { User.findById(req.user.id, function(err, user) {
if (err) { return done(err); }
user.facebook = profile.id; user.facebook = profile.id;
user.tokens.push({ kind: 'facebook', accessToken: accessToken }); user.tokens.push({ kind: 'facebook', accessToken: accessToken });
user.profile.name = user.profile.name || profile.displayName; user.profile.name = user.profile.name || profile.displayName;
user.profile.gender = user.profile.gender || profile._json.gender; user.profile.gender = user.profile.gender || profile._json.gender;
user.profile.picture = user.profile.picture || 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png'; user.profile.picture = user.profile.picture || 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png';
user.save(function(err) { user.save(function(err) {
if (err) { return done(err); }
req.flash('info', { msg: 'Facebook account has been linked.' }); req.flash('info', { msg: 'Facebook account has been linked.' });
done(err, user); done(null, user);
}); });
}); });
} }
@ -81,7 +84,7 @@ passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, r
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
if (existingEmailUser) { if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' }); req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' });
done(err); done();
} else { } else {
var user = new User(); var user = new User();
user.email = profile._json.email; user.email = profile._json.email;
@ -128,9 +131,10 @@ passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, r
passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refreshToken, profile, done) { passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refreshToken, profile, done) {
if (req.user) { if (req.user) {
User.findOne({ github: profile.id }, function(err, existingUser) { User.findOne({ github: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) { if (existingUser) {
req.flash('errors', { msg: 'There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); req.flash('errors', { msg: 'There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
done(err); done();
} else { } else {
User.findById(req.user.id, function(err, user) { User.findById(req.user.id, function(err, user) {
user.github = profile.id; user.github = profile.id;
@ -140,19 +144,22 @@ passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refre
user.profile.location = user.profile.location || profile._json.location; user.profile.location = user.profile.location || profile._json.location;
user.profile.website = user.profile.website || profile._json.blog; user.profile.website = user.profile.website || profile._json.blog;
user.save(function(err) { user.save(function(err) {
if (err) { return done(err); }
req.flash('info', { msg: 'GitHub account has been linked.' }); req.flash('info', { msg: 'GitHub account has been linked.' });
done(err, user); done(null, user);
}); });
}); });
} }
}); });
} else { } else {
User.findOne({ github: profile.id }, function(err, existingUser) { User.findOne({ github: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) return done(null, existingUser); if (existingUser) return done(null, existingUser);
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
if (err) { return done(err); }
if (existingEmailUser) { if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' });
done(err); done(null);
} else { } else {
var user = new User(); var user = new User();
user.email = profile._json.email; user.email = profile._json.email;
@ -163,30 +170,31 @@ passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refre
user.profile.location = profile._json.location; user.profile.location = profile._json.location;
user.profile.website = profile._json.blog; user.profile.website = profile._json.blog;
user.save(function(err) { user.save(function(err) {
done(err, user); if (err) { return done(err); }
}); var transporter = nodemailer.createTransport({
var transporter = nodemailer.createTransport({ service: 'Mandrill',
service: 'Mandrill', auth: {
auth: { user: secrets.mandrill.user,
user: secrets.mandrill.user, pass: secrets.mandrill.password
pass: secrets.mandrill.password }
} });
}); var mailOptions = {
var mailOptions = { to: user.email,
to: user.email, from: 'Team@freecodecamp.com',
from: 'Team@freecodecamp.com', subject: 'Welcome to Free Code Camp!',
subject: 'Welcome to Free Code Camp!', text: [
text: [ 'Greetings from San Francisco!\n\n',
'Greetings from San Francisco!\n\n', 'Thank you for joining our community.\n',
'Thank you for joining our community.\n', 'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
'Feel free to email us at this address if you have any questions about Free Code Camp.\n', "And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n", 'Good luck with the challenges!\n\n',
'Good luck with the challenges!\n\n', '- the Volunteer Camp Counselor Team'
'- the Volunteer Camp Counselor Team' ].join('')
].join('') };
}; transporter.sendMail(mailOptions, function(err) {
transporter.sendMail(mailOptions, function(err) { if (err) { return done(err); }
if (err) { return err; } done(null, user);
});
}); });
} }
}); });
@ -199,9 +207,10 @@ passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refre
passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) { passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) {
if (req.user) { if (req.user) {
User.findOne({ twitter: profile.id }, function(err, existingUser) { User.findOne({ twitter: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) { if (existingUser) {
req.flash('errors', { msg: 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); req.flash('errors', { msg: 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
done(err); done();
} else { } else {
User.findById(req.user.id, function(err, user) { User.findById(req.user.id, function(err, user) {
user.twitter = profile.id; user.twitter = profile.id;
@ -212,8 +221,9 @@ passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tok
user.profile.picture = user.profile.picture || profile._json.profile_image_url_https.replace('_normal', ''); user.profile.picture = user.profile.picture || profile._json.profile_image_url_https.replace('_normal', '');
user.profile.twitterHandle = user.profile.twitterHandle || profile.username.toLowerCase(); user.profile.twitterHandle = user.profile.twitterHandle || profile.username.toLowerCase();
user.save(function(err) { user.save(function(err) {
if (err) { return done(err); }
req.flash('info', { msg: 'Twitter account has been linked.' }); req.flash('info', { msg: 'Twitter account has been linked.' });
done(err, user); done(null, user);
}); });
}); });
} }
@ -221,6 +231,7 @@ passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tok
} else { } else {
User.findOne({ twitter: profile.id }, function(err, existingUser) { User.findOne({ twitter: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) return done(null, existingUser); if (existingUser) return done(null, existingUser);
var user = new User(); var user = new User();
user.profile.username = profile.username.toLowerCase(); user.profile.username = profile.username.toLowerCase();
@ -231,7 +242,8 @@ passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tok
user.profile.picture = profile._json.profile_image_url_https.replace('_normal', ''); user.profile.picture = profile._json.profile_image_url_https.replace('_normal', '');
user.profile.twitterHandle = user.profile.twitterHandle || profile.username.toLowerCase(); user.profile.twitterHandle = user.profile.twitterHandle || profile.username.toLowerCase();
user.save(function(err) { user.save(function(err) {
done(err, user); if (err) { return done(err); }
done(null, user);
}); });
}); });
} }
@ -242,30 +254,35 @@ passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tok
passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) { passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) {
if (req.user) { if (req.user) {
User.findOne({ google: profile.id }, function(err, existingUser) { User.findOne({ google: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) { if (existingUser) {
req.flash('errors', { msg: 'There is already a Google account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); req.flash('errors', { msg: 'There is already a Google account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
done(err); done();
} else { } else {
User.findById(req.user.id, function(err, user) { User.findById(req.user.id, function(err, user) {
if (err) { return done(err); }
user.google = profile.id; user.google = profile.id;
user.tokens.push({ kind: 'google', accessToken: accessToken }); user.tokens.push({ kind: 'google', accessToken: accessToken });
user.profile.name = user.profile.name || profile.displayName; user.profile.name = user.profile.name || profile.displayName;
user.profile.gender = user.profile.gender || profile._json.gender; user.profile.gender = user.profile.gender || profile._json.gender;
user.profile.picture = user.profile.picture || profile._json.picture; user.profile.picture = user.profile.picture || profile._json.picture;
user.save(function(err) { user.save(function(err) {
if (err) { return done(err); }
req.flash('info', { msg: 'Google account has been linked.' }); req.flash('info', { msg: 'Google account has been linked.' });
done(err, user); done(null, user);
}); });
}); });
} }
}); });
} else { } else {
User.findOne({ google: profile.id }, function(err, existingUser) { User.findOne({ google: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) return done(null, existingUser); if (existingUser) return done(null, existingUser);
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
if (err) { return done(err); }
if (existingEmailUser) { if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' }); req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' });
done(err); done();
} else { } else {
var user = new User(); var user = new User();
user.email = profile._json.email; user.email = profile._json.email;
@ -275,30 +292,31 @@ passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refre
user.profile.gender = profile._json.gender; user.profile.gender = profile._json.gender;
user.profile.picture = profile._json.picture; user.profile.picture = profile._json.picture;
user.save(function(err) { user.save(function(err) {
done(err, user); if (err) { return done(err); }
}); var transporter = nodemailer.createTransport({
var transporter = nodemailer.createTransport({ service: 'Mandrill',
service: 'Mandrill', auth: {
auth: { user: secrets.mandrill.user,
user: secrets.mandrill.user, pass: secrets.mandrill.password
pass: secrets.mandrill.password }
} });
}); var mailOptions = {
var mailOptions = { to: user.email,
to: user.email, from: 'Team@freecodecamp.com',
from: 'Team@freecodecamp.com', subject: 'Welcome to Free Code Camp!',
subject: 'Welcome to Free Code Camp!', text: [
text: [ 'Greetings from San Francisco!\n\n',
'Greetings from San Francisco!\n\n', 'Thank you for joining our community.\n',
'Thank you for joining our community.\n', 'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
'Feel free to email us at this address if you have any questions about Free Code Camp.\n', "And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n", 'Good luck with the challenges!\n\n',
'Good luck with the challenges!\n\n', '- the Volunteer Camp Counselor Team'
'- the Volunteer Camp Counselor Team' ].join('')
].join('') };
}; transporter.sendMail(mailOptions, function(err) {
transporter.sendMail(mailOptions, function(err) { if (err) { return err; }
if (err) { return err; } done(null, user);
});
}); });
} }
}); });
@ -311,11 +329,13 @@ passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refre
passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) { passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) {
if (req.user) { if (req.user) {
User.findOne({ linkedin: profile.id }, function(err, existingUser) { User.findOne({ linkedin: profile.id }, function(err, existingUser) {
if (err) { return done(err); }
if (existingUser) { if (existingUser) {
req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
done(err); done();
} else { } else {
User.findById(req.user.id, function(err, user) { User.findById(req.user.id, function(err, user) {
if (err) { return done(err); }
user.linkedin = profile.id; user.linkedin = profile.id;
user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
user.profile.name = user.profile.name || profile.displayName; user.profile.name = user.profile.name || profile.displayName;
@ -323,8 +343,9 @@ passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, r
user.profile.picture = user.profile.picture || profile._json.pictureUrl; user.profile.picture = user.profile.picture || profile._json.pictureUrl;
user.profile.website = user.profile.website || profile._json.publicProfileUrl; user.profile.website = user.profile.website || profile._json.publicProfileUrl;
user.save(function(err) { user.save(function(err) {
if (err) { return done(err); }
req.flash('info', { msg: 'LinkedIn account has been linked.' }); req.flash('info', { msg: 'LinkedIn account has been linked.' });
done(err, user); done(null, user);
}); });
}); });
} }
@ -333,9 +354,10 @@ passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, r
User.findOne({ linkedin: profile.id }, function(err, existingUser) { User.findOne({ linkedin: profile.id }, function(err, existingUser) {
if (existingUser) return done(null, existingUser); if (existingUser) return done(null, existingUser);
User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) { User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) {
if (err) { return done(err); }
if (existingEmailUser) { if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' }); req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' });
done(err); done();
} else { } else {
var user = new User(); var user = new User();
user.linkedin = profile.id; user.linkedin = profile.id;
@ -346,30 +368,31 @@ passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, r
user.profile.picture = profile._json.pictureUrl; user.profile.picture = profile._json.pictureUrl;
user.profile.website = profile._json.publicProfileUrl; user.profile.website = profile._json.publicProfileUrl;
user.save(function(err) { user.save(function(err) {
done(err, user); if (err) { return done(err); }
}); var transporter = nodemailer.createTransport({
var transporter = nodemailer.createTransport({ service: 'Mandrill',
service: 'Mandrill', auth: {
auth: { user: secrets.mandrill.user,
user: secrets.mandrill.user, pass: secrets.mandrill.password
pass: secrets.mandrill.password }
} });
}); var mailOptions = {
var mailOptions = { to: user.email,
to: user.email, from: 'Team@freecodecamp.com',
from: 'Team@freecodecamp.com', subject: 'Welcome to Free Code Camp!',
subject: 'Welcome to Free Code Camp!', text: [
text: [ 'Greetings from San Francisco!\n\n',
'Greetings from San Francisco!\n\n', 'Thank you for joining our community.\n',
'Thank you for joining our community.\n', 'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
'Feel free to email us at this address if you have any questions about Free Code Camp.\n', "And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n", 'Good luck with the challenges!\n\n',
'Good luck with the challenges!\n\n', '- the Volunteer Camp Counselor Team'
'- the Volunteer Camp Counselor Team' ].join('')
].join('') };
}; transporter.sendMail(mailOptions, function(err) {
transporter.sendMail(mailOptions, function(err) { if (err) { return err; }
if (err) { return err; } done(null, user);
});
}); });
} }
}); });
@ -380,7 +403,7 @@ passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, r
// Login Required middleware. // Login Required middleware.
exports.isAuthenticated = function(req, res, next) { exports.isAuthenticated = function(req, res, next) {
if (req.isAuthenticated()) return next(); if (req.isAuthenticated()) { return next(); }
res.redirect('/login'); res.redirect('/login');
}; };
@ -394,4 +417,4 @@ exports.isAuthorized = function(req, res, next) {
} else { } else {
res.redirect('/auth/' + provider); res.redirect('/auth/' + provider);
} }
}; };

View File

@ -15,9 +15,11 @@
"postinstall": "node seed_data/seed.js" "postinstall": "node seed_data/seed.js"
}, },
"dependencies": { "dependencies": {
"accepts": "^1.2.5",
"async": "^0.9.0", "async": "^0.9.0",
"bcrypt-nodejs": "^0.0.3", "bcrypt-nodejs": "^0.0.3",
"body-parser": "^1.9.3", "body-parser": "^1.9.3",
"chai-jquery": "^2.0.0",
"cheerio": "^0.18.0", "cheerio": "^0.18.0",
"clockwork": "^0.1.1", "clockwork": "^0.1.1",
"compression": "^1.2.1", "compression": "^1.2.1",

View File

@ -17,6 +17,7 @@ var run = function(code) {
} }
application.remote.output(result); application.remote.output(result);
self.close();
}; };

View File

@ -176,7 +176,7 @@
"tests": [ "tests": [
"assert.deepEqual(chunk(['a', 'b', 'c', 'd'], 2), [['a', 'b'], ['c', 'd']], 'should return chunked arrays');", "assert.deepEqual(chunk(['a', 'b', 'c', 'd'], 2), [['a', 'b'], ['c', 'd']], 'should return chunked arrays');",
"assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 3), [[0, 1, 2], [3, 4, 5]], 'should return chunked arrays');", "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 3), [[0, 1, 2], [3, 4, 5]], 'should return chunked arrays');",
"assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 4), [[0, 1, 2, 3], [4, 5]], 'should return cthe last chunk as remaining elements');" "assert.deepEqual(chunk([0, 1, 2, 3, 4, 5], 4), [[0, 1, 2, 3], [4, 5]], 'should return the last chunk as remaining elements');"
] ]
}, },
{ {