diff --git a/app.js b/app.js index cb0abde460..6243d927b6 100644 --- a/app.js +++ b/app.js @@ -1,63 +1,61 @@ +require('newrelic'); /** * Module dependencies. */ -require('newrelic'); -var express = require('express'); -var debug = require('debug')('freecc:server'); -var cookieParser = require('cookie-parser'); -var compress = require('compression'); -var session = require('express-session'); -var bodyParser = require('body-parser'); -var logger = require('morgan'); -var errorHandler = require('errorhandler'); -var methodOverride = require('method-override'); -var bodyParser = require('body-parser'); -var helmet = require('helmet'); +var express = require('express'), + debug = require('debug')('freecc:server'), + cookieParser = require('cookie-parser'), + compress = require('compression'), + session = require('express-session'), + bodyParser = require('body-parser'), + logger = require('morgan'), + errorHandler = require('errorhandler'), + methodOverride = require('method-override'), + bodyParser = require('body-parser'), + helmet = require('helmet'), -var _ = require('lodash'); -var MongoStore = require('connect-mongo')(session); -var flash = require('express-flash'); -var path = require('path'); -var mongoose = require('mongoose'); -var passport = require('passport'); -var expressValidator = require('express-validator'); -var connectAssets = require('connect-assets'); + _ = require('lodash'), + MongoStore = require('connect-mongo')(session), + flash = require('express-flash'), + path = require('path'), + mongoose = require('mongoose'), + passport = require('passport'), + expressValidator = require('express-validator'), + connectAssets = require('connect-assets'), -/** - * Controllers (route handlers). - */ + /** + * Controllers (route handlers). + */ + homeController = require('./controllers/home'), + challengesController = require('./controllers/challenges'), + resourcesController = require('./controllers/resources'), + userController = require('./controllers/user'), + contactController = require('./controllers/contact'), -var homeController = require('./controllers/home'); -var challengesController = require('./controllers/challenges'); -var resourcesController = require('./controllers/resources'); -var userController = require('./controllers/user'); -var apiController = require('./controllers/api'); -var contactController = require('./controllers/contact'); + /** + * User model + */ + User = require('./models/User'), -/** - * User model - */ -var User = require('./models/User'); -/** - * API keys and Passport configuration. - */ - -var secrets = require('./config/secrets'); -var passportConf = require('./config/passport'); + /** + * API keys and Passport configuration. + */ + secrets = require('./config/secrets'), + passportConf = require('./config/passport'); /** * Create Express server. */ - var app = express(); /** * Connect to MongoDB. */ - mongoose.connect(secrets.db); mongoose.connection.on('error', function() { - console.error('MongoDB Connection Error. Please make sure that MongoDB is running.'); + console.error( + 'MongoDB Connection Error. Please make sure that MongoDB is running.' + ); }); /** @@ -69,7 +67,10 @@ app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); app.use(compress()); app.use(connectAssets({ - paths: [path.join(__dirname, 'public/css'), path.join(__dirname, 'public/js')], + paths: [ + path.join(__dirname, 'public/css'), + path.join(__dirname, 'public/js') + ], helperContext: app.locals })); app.use(logger('dev')); @@ -91,38 +92,41 @@ app.use(passport.initialize()); app.use(passport.session()); app.use(flash()); app.disable('x-powered-by'); + app.use(helmet.xssFilter()); app.use(helmet.xframe()); + var trusted = [ - "'self'", + '"self"', '*.freecodecamp.com', - "*.google-analytics.com", - "*.googleapis.com", - "*.gstatic.com", - "*.doubleclick.net", - "*.twitter.com", + '*.google-analytics.com', + '*.googleapis.com', + '*.gstatic.com', + '*.doubleclick.net', + '*.twitter.com', '*.twimg.com', - "*.githubusercontent.com", - "'unsafe-eval'", - "'unsafe-inline'" + '*.githubusercontent.com', + '"unsafe-eval"', + '"unsafe-inline"' ]; -//var connectSrc; -//if (process.env.NODE_ENV === 'development') { -// debug('Pushing'); -// connectSrc = ['"self"', 'ws://localhost:3001/']; -//} else { -// debug('Not'); -// connectSrc = []; -//} +//TODO(Berks): conditionally add localhost domains to csp; +/*var connectSrc; +if (process.env.NODE_ENV === 'development') { + debug('Pushing'); + connectSrc = [''self'', 'ws://localhost:3001/']; +} else { + debug('Not'); + connectSrc = []; +}*/ debug(trusted); app.use(helmet.contentSecurityPolicy({ defaultSrc: trusted, scriptSrc: ['*.optimizely.com'].concat(trusted), - 'connect-src': process.env.NODE_ENV === 'development' ? ['ws://localhost:3001/', 'http://localhost:3001/'] : [], + 'connect-src': ['ws://localhost:3001/', 'http://localhost:3001/'], styleSrc: trusted, - imgSrc: ['*.evernote.com', '*.amazonaws.com', "data:"].concat(trusted), - fontSrc: ["'self", '*.googleapis.com'].concat(trusted), + imgSrc: ['*.evernote.com', '*.amazonaws.com', 'data:'].concat(trusted), + fontSrc: ['"self"', '*.googleapis.com'].concat(trusted), mediaSrc: ['*.amazonaws.com', '*.twitter.com'], frameSrc: ['*.gitter.im', '*.vimeo.com', '*.twitter.com'], // sandbox: ['allow-forms', 'allow-scripts'], @@ -148,7 +152,9 @@ app.use(function(req, res, next) { next(); }); -app.use(express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 })); +app.use( + express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }) +); /** * Main routes. @@ -164,9 +170,19 @@ app.get('/jquery-exercises', resourcesController.jqueryExercises); app.get('/live-pair-programming', resourcesController.livePairProgramming); app.get('/javascript-in-your-inbox', resourcesController.javaScriptInYourInbox); app.get('/chromebook', resourcesController.chromebook); -app.get('/pair-program-with-team-viewer', resourcesController.pairProgramWithTeamViewer); -app.get('/done-with-first-100-hours', resourcesController.doneWithFirst100Hours); -app.get('/programmer-interview-questions-app', resourcesController.programmerInterviewQuestionsApp); + +app.get( + '/pair-program-with-team-viewer', + resourcesController.pairProgramWithTeamViewer +); +app.get( + '/done-with-first-100-hours', + resourcesController.doneWithFirst100Hours +); +app.get( + '/programmer-interview-questions-app', + resourcesController.programmerInterviewQuestionsApp +); app.get('/about', resourcesController.about); app.get('/login', userController.getLogin); @@ -185,14 +201,17 @@ app.post('/nonprofits', contactController.postContact); // # Protected routes, user must be logged in. app.post( - '/update-progress', - passportConf.isAuthenticated, - userController.updateProgress); + '/update-progress', + passportConf.isAuthenticated, + userController.updateProgress +); app.get( - '/challenges/:challengeNumber', - passportConf.isAuthenticated, - challengesController.returnChallenge); + '/challenges/:challengeNumber', + passportConf.isAuthenticated, + challengesController.returnChallenge +); + app.all('/account', passportConf.isAuthenticated); app.get('/account', userController.getAccount); app.post('/account/profile', userController.postUpdateProfile); @@ -207,11 +226,12 @@ app.get('/account/unlink/:provider', userController.getOauthUnlink); * */ app.post('/completed_challenge', function(req, res) { - req.user.challengesHash[parseInt(req.body.challengeNumber)] = Math.round(+new Date() / 1000); + req.user.challengesHash[parseInt(req.body.challengeNumber)] = + Math.round(+ new Date() / 1000); var ch = req.user.challengesHash; var p = 0; - for (k in ch) { - if (ch[k] > 0) { p += 1} + for (var k in ch) { + if (ch[k] > 0) { p += 1; } } req.user.points = p; req.user.save(); @@ -220,7 +240,6 @@ app.post('/completed_challenge', function(req, res) { /** * OAuth sign-in routes. */ - app.get('/auth/twitter', passport.authenticate('twitter')); app.get( '/auth/twitter/callback', @@ -246,20 +265,40 @@ app.get( res.redirect(req.session.returnTo || '/'); }); -app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email', 'user_location'] })); -app.get('/auth/facebook/callback', passport.authenticate('facebook', { successRedirect: '/',failureRedirect: '/login' }), function(req, res) { +app.get( + '/auth/facebook', + passport.authenticate('facebook', { scope: ['email', 'user_location'] }) +); + +var passportOptions = { + successRedirect: '/', + failureRedirect: '/login' +}; +app.get( + '/auth/facebook/callback', + passport.authenticate('facebook', passportOptions), function(req, res) { res.redirect(req.session.returnTo || '/'); -}); + } +); app.get('/auth/github', passport.authenticate('github')); -app.get('/auth/github/callback', passport.authenticate('github', { successRedirect: '/',failureRedirect: '/login' }), function(req, res) { +app.get( + '/auth/github/callback', + passport.authenticate('github', passportOptions), function(req, res) { res.redirect(req.session.returnTo || '/'); -}); + } +); -app.get('/auth/google', passport.authenticate('google', { scope: 'profile email' })); -app.get('/auth/google/callback', passport.authenticate('google', { successRedirect: '/',failureRedirect: '/login' }), function(req, res) { +app.get( + '/auth/google', + passport.authenticate('google', { scope: 'profile email' }) +); +app.get( + '/auth/google/callback', + passport.authenticate('google', passportOptions), function(req, res) { res.redirect(req.session.returnTo || '/'); -}); + } +); /** * 500 Error Handler. diff --git a/config/bootstrap.js b/config/bootstrap.js index c7845e061c..c38ffac08e 100644 --- a/config/bootstrap.js +++ b/config/bootstrap.js @@ -1,22 +1,29 @@ -var mongoose = require('mongoose'); -var secrets = require('./secrets'); +var mongoose = require('mongoose'), + debug = require('debug')('freecc:config:boot'), + secrets = require('./secrets'), + courses = require('../seed_data/courses.json'), + Course = require('./../models/Course'), + challenges = require('../seed_data/challenges.json'), + Challenge = require('./../models/Challenge'); + mongoose.connect(secrets.db); mongoose.connection.on('error', function() { console.error('MongoDB Connection Error. Make sure MongoDB is running.'); }); -var courses = require('../seed_data/courses.json'); -var challenges = require('../seed_data/challenges.json'); - -Challenge = require ('./../models/Challenge'); -Course = require ('./../models/Course'); Course.create(courses, function(err, data) { - if (err) console.log(err); - else console.log('Saved ', data ); + if (err) { + debug(err); + } else { + debug('Saved ', data); + } }); Challenge.create(challenges, function(err, data) { - if (err) console.log(err); - else console.log('Saved ', data ); -}); \ No newline at end of file + if (err) { + console.log(err); + } else { + console.log('Saved ', data); + } +}); diff --git a/config/passport.js b/config/passport.js index 814a4ec7d0..6157ffa1c6 100644 --- a/config/passport.js +++ b/config/passport.js @@ -1,25 +1,31 @@ -var _ = require('lodash'); -var passport = require('passport'); -var InstagramStrategy = require('passport-instagram').Strategy; -var LocalStrategy = require('passport-local').Strategy; -var FacebookStrategy = require('passport-facebook').Strategy; -var TwitterStrategy = require('passport-twitter').Strategy; -var GitHubStrategy = require('passport-github').Strategy; -var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy; -var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy; -var OAuthStrategy = require('passport-oauth').OAuthStrategy; -var OAuth2Strategy = require('passport-oauth').OAuth2Strategy; -var User = require('../models/User'); -var secrets = require('./secrets'); +var _ = require('lodash'), + passport = require('passport'), + LocalStrategy = require('passport-local').Strategy, + FacebookStrategy = require('passport-facebook').Strategy, + TwitterStrategy = require('passport-twitter').Strategy, + GitHubStrategy = require('passport-github').Strategy, + GoogleStrategy = require('passport-google-oauth').OAuth2Strategy, + LinkedInStrategy = require('passport-linkedin-oauth2').Strategy, + OAuthStrategy = require('passport-oauth').OAuthStrategy, + OAuth2Strategy = require('passport-oauth').OAuth2Strategy, + User = require('../models/User'), + secrets = require('./secrets'); + +// Login Required middleware. +module.exports = { + isAuthenticated: isAuthenticated, + isAuthorized: isAuthorized +}; passport.serializeUser(function(user, done) { - done(null, user.id); + done(null, user.id); }); passport.deserializeUser(function(id, done) { - User.findById(id, function(err, user) { - done(err, user); - }); + User.findById(id, function(err, user) { + if (err) { return done(err); } + done(null, user); + }); }); //function sendWelcomeEmail(u) { @@ -55,247 +61,452 @@ passport.deserializeUser(function(id, done) { */ // Sign in with Twitter. -passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) { - if (req.user) { +passport.use( + new TwitterStrategy( + secrets.twitter, function(req, accessToken, tokenSecret, profile, done) { + if (req.user) { User.findOne({ twitter: profile.id }, function(err, 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.' }); - done(err); - } else { - User.findById(req.user.id, function(err, user) { - user.twitter = profile.id; - user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.location = user.profile.location || profile._json.location; - user.profile.picture = user.profile.picture || profile._json.profile_image_url_https; - user.profile.username = profile.username; - user.save(function(err) { - req.flash('info', { msg: 'Twitter account has been linked.' }); - done(err, user); - }); - }); - } + if (err) { return done(err); } + + 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.' + ].join('') + }); + done(); + } else { + User.findById(req.user.id, function(err, user) { + if (err) { return done(err); } + + user.twitter = profile.id; + user.tokens.push({ + kind: 'twitter', + accessToken: accessToken, + tokenSecret: tokenSecret + }); + user.profile.name = user.profile.name || profile.displayName; + + user.profile.location = + user.profile.location || profile._json.location; + + user.profile.picture = + user.profile.picture || profile._json.profile_image_url_https; + + user.profile.username = profile.username; + user.save(function(err) { + req.flash('info', { msg: 'Twitter account has been linked.' }); + done(null, user); + }); + }); + } }); - } else { + } else { User.findOne({ twitter: profile.id }, function(err, existingUser) { - //if (existingUser) return done(null, existingUser); - // Twitter will not provide an email address. Period. - // But a person’s twitter username is guaranteed to be unique - // so we can "fake" a twitter email address as follows: - //user.email = profile.username + "@twitter.com"; - var user = existingUser || new User; - user.twitter = profile.id; - user.email = user.email || ''; - user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.location = user.profile.location || profile._json.location; - user.profile.picture = user.profile.picture || profile._json.profile_image_url_https; - user.save(function(err) { - done(err, user); - }); - //if (!user.email) { - // req.redirect('/account'); - // req.flash('errors', { msg: 'OK, you are signed in. Please add your email address to your profile.' }); - //} + if (err) { return done(err); } + + //if (existingUser) return done(null, existingUser); + // Twitter will not provide an email address. Period. + // But a person’s twitter username is guaranteed to be unique + // so we can "fake" a twitter email address as follows: + //user.email = profile.username + "@twitter.com"; + var user = existingUser || new User(); + user.twitter = profile.id; + user.email = user.email || ''; + user.tokens.push({ + kind: 'twitter', + accessToken: accessToken, + tokenSecret: tokenSecret + }); + user.profile.name = user.profile.name || profile.displayName; + + user.profile.location = + user.profile.location || profile._json.location; + user.profile.picture = + user.profile.picture || profile._json.profile_image_url_https; + + user.save(function(err) { + if (err) { return done(err); } + done(null, user); + }); + //TODO: Twitter redirect to capture user email. + //if (!user.email) { + // req.redirect('/account'); + // req.flash('errors', { + // msg: + // 'OK, you are signed in. ' + + // 'Please add your email address to your profile.' + // }); + //} }); - } -})); + } + }) +); // Sign in with LinkedIn. -passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) { - if (req.user) { +passport.use( + new LinkedInStrategy( + secrets.linkedin, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { User.findOne({ linkedin: profile.id }, function(err, 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.' }); - done(err); - } else { - User.findById(req.user.id, function(err, user) { - user.linkedin = profile.id; - user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.location = user.profile.location || profile._json.location.name; - user.profile.picture = user.profile.picture || profile._json.pictureUrl; - user.profile.website = user.profile.website || profile._json.publicProfileUrl; - user.save(function(err) { - req.flash('info', { msg: 'LinkedIn account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ linkedin: profile.id }, function(err, existingUser) { - if (existingUser) return done(null, existingUser); - User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) { - var user = existingEmailUser || new User; - user.linkedin = profile.id; - user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); - user.email = user.email || profile._json.emailAddress; - user.profile.name = user.profile.name || profile.displayName; - user.profile.location = user.profile.location || profile._json.location.name; - user.profile.picture = user.profile.picture || profile._json.pictureUrl; - user.profile.website = user.profile.website || profile._json.publicProfileUrl; - user.challengesComplete = user.challengesCompleted || []; - user.save(function(err) { - done(err, user); - }); + if (err) { return done(err); } + + 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.' + ].join('') }); + done(); + } else { + User.findById(req.user.id, function(err, user) { + if (err) { return done(err); } + + user.linkedin = profile.id; + user.tokens.push({ + kind: 'linkedin', + accessToken: accessToken + }); + user.profile.name = user.profile.name || profile.displayName; + + user.profile.location = + user.profile.location || profile._json.location.name; + + user.profile.picture = + user.profile.picture || profile._json.pictureUrl; + + user.profile.website = + user.profile.website || profile._json.publicProfileUrl; + + user.save(function(err, _user) { + if (err) { return done(err); } + req.flash( + 'info', { msg: 'LinkedIn account has been linked.' } + ); + done(null, user); + }); + }); + } }); - } + } else { + User.findOne({ linkedin: profile.id }, function(err, existingUser) { + if (err) { return done(err); } + + if (existingUser) { return done(null, existingUser); } + User.findOne( + { email: profile._json.emailAddress }, + function(err, existingEmailUser) { + if (err) { return done(err); } + + var user = existingEmailUser || new User(); + + user.linkedin = profile.id; + user.tokens.push({ + kind: 'linkedin', + accessToken: accessToken + }); + user.email = user.email || profile._json.emailAddress; + user.profile.name = user.profile.name || profile.displayName; + + user.profile.location = + user.profile.location || profile._json.location.name; + + user.profile.picture = + user.profile.picture || profile._json.pictureUrl; + + user.profile.website = + user.profile.website || profile._json.publicProfileUrl; + + user.challengesComplete = user.challengesCompleted || []; + user.save(function(err) { + if (err) { return done(err); } + done(null, user); + }); + }); + + }); + } })); // Sign in using Email and Password. -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) { - if (!user) return done(null, false, { message: 'Email ' + email + ' not found'}); - user.comparePassword(password, function(err, isMatch) { - if (isMatch) { - return done(null, user); - } else { - return done(null, false, { message: 'Invalid email or password.' }); - } - }); + if (err) { return done(err); } + + if (!user) { + return done(null, false, { message: 'Email ' + email + ' not found'}); + } + user.comparePassword(password, function(err, isMatch) { + if (err) { return done(err); } + + if (isMatch) { + return done(null, user); + } else { + return done(null, false, { message: 'Invalid email or password.' }); + } + }); }); })); // Sign in with Facebook. +passport.use( + new FacebookStrategy( + secrets.facebook, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ facebook: profile.id }, function(err, existingUser) { + if (err) { return done(err); } -passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { - if (req.user) { - User.findOne({ facebook: profile.id }, function(err, 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.' }); - done(err); - } else { - User.findById(req.user.id, function(err, user) { - user.facebook = profile.id; - user.tokens.push({ kind: 'facebook', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.gender = user.profile.gender || profile._json.gender; - user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large'; - user.save(function(err) { - req.flash('info', { msg: 'Facebook account has been linked.' }); - done(err, user); - }); - }); - } + 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.' + ].join(' ') + }); + done(); + } else { + User.findById(req.user.id, function(err, user) { + if (err) { return done(err); } + + user.facebook = profile.id; + user.tokens.push({ + kind: 'facebook', + accessToken: accessToken + }); + + user.profile.name = user.profile.name || profile.displayName; + user.profile.gender = user.profile.gender || profile._json.gender; + + user.profile.picture = + user.profile.picture || + 'https://graph.facebook.com/' + + profile.id + + '/picture?type=large'; + + user.save(function(err) { + if (err) { return done(err); } + + req.flash( + 'info', { msg: 'Facebook account has been linked.' }); + done(null, user); + }); + }); + } }); - } else { + } else { User.findOne({ facebook: profile.id }, function(err, existingUser) { - if (existingUser) return done(null, existingUser); - User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { - var user = existingEmailUser || new User; - user.email = user.email || profile._json.email; - user.facebook = profile.id; - user.tokens.push({ kind: 'facebook', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.gender = user.profile.gender || profile._json.gender; - user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large'; - user.profile.location = user.profile.location || (profile._json.location) ? profile._json.location.name : ''; - user.challengesComplete = user.challengesCompleted || []; - user.save(function(err) { - done(err, user); - }); + if (err) { return done(err); } + + if (existingUser) { return done(null, existingUser); } + + User.findOne( + { email: profile._json.email }, function(err, existingEmailUser) { + if (err) { return done(err); } + + var user = existingEmailUser || new User(); + user.email = user.email || profile._json.email; + user.facebook = profile.id; + user.tokens.push({ + kind: 'facebook', + accessToken: accessToken + }); + user.profile.name = user.profile.name || profile.displayName; + + user.profile.gender = + user.profile.gender || profile._json.gender; + + user.profile.picture = + user.profile.picture || + 'https://graph.facebook.com/' + + profile.id + + '/picture?type=large'; + + user.profile.location = + user.profile.location || + (profile._json.location) ? profile._json.location.name : ''; + + user.challengesComplete = user.challengesCompleted || []; + user.save(function(err) { + if (err) { return done(err); } + done(null, user); + }); }); }); - } + } })); // Sign in with GitHub. -passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refreshToken, profile, done) { - if (req.user) { +passport.use( + new GitHubStrategy( + secrets.github, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { User.findOne({ github: profile.id }, function(err, 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.' }); - done(err); - } else { - User.findById(req.user.id, function(err, user) { - user.github = profile.id; - user.tokens.push({ kind: 'github', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.picture = user.profile.picture || profile._json.avatar_url; - user.profile.location = user.profile.location || profile._json.location; - user.profile.website = user.profile.website || profile._json.blog; - user.save(function(err) { - req.flash('info', { msg: 'GitHub account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ github: profile.id }, function(err, existingUser) { - if (existingUser) return done(null, existingUser); - User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { - var user = existingEmailUser || new User; - user.email = user.email || profile._json.email; - user.github = profile.id; - user.tokens.push({ kind: 'github', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.picture = user.profile.picture || profile._json.avatar_url; - user.profile.location = user.profile.location || profile._json.location; - user.profile.website = user.profile.website || profile._json.blog; - user.save(function(err) { - done(err, user); - }); + if (err) { return done(err); } + + 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.' + ].join(' ') }); + done(); + } else { + User.findById(req.user.id, function(err, user) { + if (err) { return done(err); } + + user.github = profile.id; + user.tokens.push({ kind: 'github', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + + user.profile.picture = + user.profile.picture || profile._json.avatar_url; + + user.profile.location = + user.profile.location || profile._json.location; + + user.profile.website = + user.profile.website || profile._json.blog; + + user.save(function(err) { + if (err) { return done(err); } + + req.flash('info', { msg: 'GitHub account has been linked.' }); + done(null, user); + }); + }); + } }); - } + } else { + User.findOne({ github: profile.id }, function(err, existingUser) { + if (err) { return done(err); } + + if (existingUser) { return done(null, existingUser); } + User.findOne( + { email: profile._json.email }, function(err, existingEmailUser) { + if (err) { return done(err); } + + var user = existingEmailUser || new User(); + user.email = user.email || profile._json.email; + user.github = profile.id; + user.tokens.push({ + kind: 'github', + accessToken: accessToken + }); + user.profile.name = user.profile.name || profile.displayName; + + user.profile.picture = + user.profile.picture || profile._json.avatar_url; + + user.profile.location = + user.profile.location || profile._json.location; + + user.profile.website = + user.profile.website || profile._json.blog; + + user.save(function(err) { + if (err) { return done(err); } + done(null, user); + }); + }); + }); + } })); // Sign in with Google. -passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) { - if (req.user) { +passport.use( + new GoogleStrategy( + secrets.google, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { User.findOne({ google: profile.id }, function(err, 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.' }); - done(err); - } else { - User.findById(req.user.id, function(err, user) { - user.google = profile.id; - user.tokens.push({ kind: 'google', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.gender = user.profile.gender || profile._json.gender; - user.profile.picture = user.profile.picture || profile._json.picture; - user.save(function(err) { - req.flash('info', { msg: 'Google account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ google: profile.id }, function(err, existingUser) { - if (existingUser) return done(null, existingUser); - User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { - var user = existingEmailUser || new User; - user.email = user.email || profile._json.email; - user.google = profile.id; - user.tokens.push({ kind: 'google', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.gender = user.profile.gender || profile._json.gender; - user.profile.picture = user.profile.picture || profile._json.picture; - //if (!existingEmailUser) { - // sendWelcomeEmail(user); - //} - user.save(function(err) { - done(err, user); - }); + if (err) { return done(err); } + + 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.' + ].join(' ') }); + done(); + } else { + User.findById(req.user.id, function(err, user) { + if (err) { return done(err); } + + user.google = profile.id; + user.tokens.push({ + kind: 'google', + accessToken: accessToken + }); + + user.profile.name = user.profile.name || profile.displayName; + + user.profile.gender = + user.profile.gender || profile._json.gender; + + user.profile.picture = + user.profile.picture || profile._json.picture; + + user.save(function(err) { + if (err) { return done(err); } + req.flash('info', { msg: 'Google account has been linked.' }); + done(null, user); + }); + }); + } }); - } + } else { + User.findOne({ google: profile.id }, function(err, existingUser) { + if (err) { return done(err); } + + if (existingUser) { return done(null, existingUser); } + User.findOne( + { email: profile._json.email }, function(err, existingEmailUser) { + if (err) { return done(err); } + + var user = existingEmailUser || new User(); + user.email = user.email || profile._json.email; + user.google = profile.id; + user.tokens.push({ + kind: 'google', + accessToken: accessToken + }); + + user.profile.name = + user.profile.name || profile.displayName; + + user.profile.gender = + user.profile.gender || profile._json.gender; + + user.profile.picture = + user.profile.picture || profile._json.picture; + + //TODO: Greeting email + //if (!existingEmailUser) { + // sendWelcomeEmail(user); + //} + user.save(function(err) { + if (err) { return done(err); } + done(null, user); + }); + }); + }); + } })); -// Login Required middleware. - -module.exports = { - isAuthenticated: isAuthenticated, - isAuthorized: isAuthorized -}; function isAuthenticated(req, res, next) { if (req.isAuthenticated()) return next(); @@ -307,113 +518,8 @@ function isAuthorized(req, res, next) { var provider = req.path.split('/').slice(-1)[0]; if (_.find(req.user.tokens, { kind: provider })) { - next(); + next(); } else { - res.redirect('/auth/' + provider); + res.redirect('/auth/' + provider); } } - -/* -passport.use(new InstagramStrategy(secrets.instagram,function(req, accessToken, refreshToken, profile, done) { - if (req.user) { - User.findOne({ instagram: profile.id }, function(err, existingUser) { - if (existingUser) { - req.flash('errors', { msg: 'There is already an Instagram account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); - done(err); - } else { - User.findById(req.user.id, function(err, user) { - user.instagram = profile.id; - user.tokens.push({ kind: 'instagram', accessToken: accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.picture = user.profile.picture || profile._json.data.profile_picture; - user.profile.website = user.profile.website || profile._json.data.website; - user.save(function(err) { - req.flash('info', { msg: 'Instagram account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ instagram: profile.id }, function(err, existingUser) { - if (existingUser) return done(null, existingUser); - - var user = new User(); - user.instagram = profile.id; - user.tokens.push({ kind: 'instagram', accessToken: accessToken }); - user.profile.name = profile.displayName; - // Similar to Twitter API, assigns a temporary e-mail address - // to get on with the registration process. It can be changed later - // to a valid e-mail address in Profile Management. - user.email = profile.username + "@instagram.com"; - user.profile.website = profile._json.data.website; - user.profile.picture = profile._json.data.profile_picture; - user.save(function(err) { - done(err, user); - }); - }); - } -})); - - -// Tumblr API setup. - -passport.use('tumblr', new OAuthStrategy({ - requestTokenURL: 'http://www.tumblr.com/oauth/request_token', - accessTokenURL: 'http://www.tumblr.com/oauth/access_token', - userAuthorizationURL: 'http://www.tumblr.com/oauth/authorize', - consumerKey: secrets.tumblr.consumerKey, - consumerSecret: secrets.tumblr.consumerSecret, - callbackURL: secrets.tumblr.callbackURL, - passReqToCallback: true - }, - function(req, token, tokenSecret, profile, done) { - User.findById(req.user._id, function(err, user) { - user.tokens.push({ kind: 'tumblr', accessToken: token, tokenSecret: tokenSecret }); - user.save(function(err) { - done(err, user); - }); - }); - } -)); - -// Foursquare API setup. - -passport.use('foursquare', new OAuth2Strategy({ - authorizationURL: 'https://foursquare.com/oauth2/authorize', - tokenURL: 'https://foursquare.com/oauth2/access_token', - clientID: secrets.foursquare.clientId, - clientSecret: secrets.foursquare.clientSecret, - callbackURL: secrets.foursquare.redirectUrl, - passReqToCallback: true - }, - function(req, accessToken, refreshToken, profile, done) { - User.findById(req.user._id, function(err, user) { - user.tokens.push({ kind: 'foursquare', accessToken: accessToken }); - user.save(function(err) { - done(err, user); - }); - }); - } -)); - -// Venmo API setup. - -passport.use('venmo', new OAuth2Strategy({ - authorizationURL: 'https://api.venmo.com/v1/oauth/authorize', - tokenURL: 'https://api.venmo.com/v1/oauth/access_token', - clientID: secrets.venmo.clientId, - clientSecret: secrets.venmo.clientSecret, - callbackURL: secrets.venmo.redirectUrl, - passReqToCallback: true - }, - function(req, accessToken, refreshToken, profile, done) { - User.findById(req.user._id, function(err, user) { - user.tokens.push({ kind: 'venmo', accessToken: accessToken }); - user.save(function(err) { - done(err, user); - }); - }); - } -)); -*/ diff --git a/controllers/api.js b/controllers/api.js deleted file mode 100644 index b395a756a6..0000000000 --- a/controllers/api.js +++ /dev/null @@ -1,602 +0,0 @@ -var secrets = require('../config/secrets'); -var User = require('../models/User'); -var querystring = require('querystring'); -var validator = require('validator'); -var async = require('async'); -var cheerio = require('cheerio'); -var request = require('request'); -var graph = require('fbgraph'); -var LastFmNode = require('lastfm').LastFmNode; -var tumblr = require('tumblr.js'); -var foursquare = require('node-foursquare')({ secrets: secrets.foursquare }); -var Github = require('github-api'); -var Twit = require('twit'); -var stripe = require('stripe')(secrets.stripe.secretKey); -var twilio = require('twilio')(secrets.twilio.sid, secrets.twilio.token); -var Linkedin = require('node-linkedin')(secrets.linkedin.clientID, secrets.linkedin.clientSecret, secrets.linkedin.callbackURL); -var clockwork = require('clockwork')({key: secrets.clockwork.apiKey}); -var ig = require('instagram-node').instagram(); -var Y = require('yui/yql'); -var _ = require('lodash'); - -/** - * GET /api - * List of API examples. - */ - -exports.getApi = function(req, res) { - res.render('api/index', { - title: 'API Examples' - }); -}; - -/** - * GET /api/foursquare - * Foursquare API example. - */ - -exports.getFoursquare = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'foursquare' }); - async.parallel({ - trendingVenues: function(callback) { - foursquare.Venues.getTrending('40.7222756', '-74.0022724', { limit: 50 }, token.accessToken, function(err, results) { - callback(err, results); - }); - }, - venueDetail: function(callback) { - foursquare.Venues.getVenue('49da74aef964a5208b5e1fe3', token.accessToken, function(err, results) { - callback(err, results); - }); - }, - userCheckins: function(callback) { - foursquare.Users.getCheckins('self', null, token.accessToken, function(err, results) { - callback(err, results); - }); - } - }, - function(err, results) { - if (err) return next(err); - res.render('api/foursquare', { - title: 'Foursquare API', - trendingVenues: results.trendingVenues, - venueDetail: results.venueDetail, - userCheckins: results.userCheckins - }); - }); -}; - -/** - * GET /api/tumblr - * Tumblr API example. - */ - -exports.getTumblr = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'tumblr' }); - var client = tumblr.createClient({ - consumer_key: secrets.tumblr.consumerKey, - consumer_secret: secrets.tumblr.consumerSecret, - token: token.accessToken, - token_secret: token.tokenSecret - }); - client.posts('withinthisnightmare.tumblr.com', { type: 'photo' }, function(err, data) { - if (err) return next(err); - res.render('api/tumblr', { - title: 'Tumblr API', - blog: data.blog, - photoset: data.posts[0].photos - }); - }); -}; - -/** - * GET /api/facebook - * Facebook API example. - */ - -exports.getFacebook = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'facebook' }); - graph.setAccessToken(token.accessToken); - async.parallel({ - getMe: function(done) { - graph.get(req.user.facebook, function(err, me) { - done(err, me); - }); - }, - getMyFriends: function(done) { - graph.get(req.user.facebook + '/friends', function(err, friends) { - done(err, friends.data); - }); - } - }, - function(err, results) { - if (err) return next(err); - res.render('api/facebook', { - title: 'Facebook API', - me: results.getMe, - friends: results.getMyFriends - }); - }); -}; - -/** - * GET /api/scraping - * Web scraping example using Cheerio library. - */ - -exports.getScraping = function(req, res, next) { - request.get('https://news.ycombinator.com/', function(err, request, body) { - if (err) return next(err); - var $ = cheerio.load(body); - var links = []; - $('.title a[href^="http"], a[href^="https"]').each(function() { - links.push($(this)); - }); - res.render('api/scraping', { - title: 'Web Scraping', - links: links - }); - }); -}; - -/** - * GET /api/github - * GitHub API Example. - */ - -exports.getGithub = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'github' }); - var github = new Github({ token: token.accessToken }); - var repo = github.getRepo('sahat', 'requirejs-library'); - repo.show(function(err, repo) { - if (err) return next(err); - res.render('api/github', { - title: 'GitHub API', - repo: repo - }); - }); - -}; - -/** - * GET /api/aviary - * Aviary image processing example. - */ - -exports.getAviary = function(req, res) { - res.render('api/aviary', { - title: 'Aviary API' - }); -}; - -/** - * GET /api/nyt - * New York Times API example. - */ - -exports.getNewYorkTimes = function(req, res, next) { - var query = querystring.stringify({ 'api-key': secrets.nyt.key, 'list-name': 'young-adult' }); - var url = 'http://api.nytimes.com/svc/books/v2/lists?' + query; - request.get(url, function(err, request, body) { - if (err) return next(err); - if (request.statusCode === 403) return next(Error('Missing or Invalid New York Times API Key')); - var bestsellers = JSON.parse(body); - res.render('api/nyt', { - title: 'New York Times API', - books: bestsellers.results - }); - }); -}; - -/** - * GET /api/lastfm - * Last.fm API example. - */ - -exports.getLastfm = function(req, res, next) { - var lastfm = new LastFmNode(secrets.lastfm); - async.parallel({ - artistInfo: function(done) { - lastfm.request('artist.getInfo', { - artist: 'The Pierces', - handlers: { - success: function(data) { - done(null, data); - }, - error: function(err) { - done(err); - } - } - }); - }, - artistTopTracks: function(done) { - lastfm.request('artist.getTopTracks', { - artist: 'The Pierces', - handlers: { - success: function(data) { - var tracks = []; - _.each(data.toptracks.track, function(track) { - tracks.push(track); - }); - done(null, tracks.slice(0,10)); - }, - error: function(err) { - done(err); - } - } - }); - }, - artistTopAlbums: function(done) { - lastfm.request('artist.getTopAlbums', { - artist: 'The Pierces', - handlers: { - success: function(data) { - var albums = []; - _.each(data.topalbums.album, function(album) { - albums.push(album.image.slice(-1)[0]['#text']); - }); - done(null, albums.slice(0, 4)); - }, - error: function(err) { - done(err); - } - } - }); - } - }, - function(err, results) { - if (err) return next(err.message); - var artist = { - name: results.artistInfo.artist.name, - image: results.artistInfo.artist.image.slice(-1)[0]['#text'], - tags: results.artistInfo.artist.tags.tag, - bio: results.artistInfo.artist.bio.summary, - stats: results.artistInfo.artist.stats, - similar: results.artistInfo.artist.similar.artist, - topAlbums: results.artistTopAlbums, - topTracks: results.artistTopTracks - }; - res.render('api/lastfm', { - title: 'Last.fm API', - artist: artist - }); - }); -}; - -/** - * GET /api/twitter - * Twiter API example. - */ - -exports.getTwitter = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'twitter' }); - var T = new Twit({ - consumer_key: secrets.twitter.consumerKey, - consumer_secret: secrets.twitter.consumerSecret, - access_token: token.accessToken, - access_token_secret: token.tokenSecret - }); - T.get('search/tweets', { q: 'nodejs since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 10 }, function(err, reply) { - if (err) return next(err); - res.render('api/twitter', { - title: 'Twitter API', - tweets: reply.statuses - }); - }); -}; - -/** - * POST /api/twitter - * Post a tweet. - */ - -exports.postTwitter = function(req, res, next) { - req.assert('tweet', 'Tweet cannot be empty.').notEmpty(); - var errors = req.validationErrors(); - if (errors) { - req.flash('errors', errors); - return res.redirect('/api/twitter'); - } - var token = _.find(req.user.tokens, { kind: 'twitter' }); - var T = new Twit({ - consumer_key: secrets.twitter.consumerKey, - consumer_secret: secrets.twitter.consumerSecret, - access_token: token.accessToken, - access_token_secret: token.tokenSecret - }); - T.post('statuses/update', { status: req.body.tweet }, function(err, data, response) { - if (err) return next(err); - req.flash('success', { msg: 'Tweet has been posted.'}); - res.redirect('/api/twitter'); - }); -}; - -/** - * GET /api/steam - * Steam API example. - */ - -exports.getSteam = function(req, res, next) { - var steamId = '76561197982488301'; - var query = { l: 'english', steamid: steamId, key: secrets.steam.apiKey }; - async.parallel({ - playerAchievements: function(done) { - query.appid = '49520'; - var qs = querystring.stringify(query); - request.get({ url: 'http://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?' + qs, json: true }, function(error, request, body) { - if (request.statusCode === 401) return done(new Error('Missing or Invalid Steam API Key')); - done(error, body); - }); - }, - playerSummaries: function(done) { - query.steamids = steamId; - var qs = querystring.stringify(query); - request.get({ url: 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?' + qs, json: true }, function(err, request, body) { - if (request.statusCode === 401) return done(new Error('Missing or Invalid Steam API Key')); - done(err, body); - }); - }, - ownedGames: function(done) { - query.include_appinfo = 1; - query.include_played_free_games = 1; - var qs = querystring.stringify(query); - request.get({ url: 'http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?' + qs, json: true }, function(err, request, body) { - if (request.statusCode === 401) return done(new Error('Missing or Invalid Steam API Key')); - done(err, body); - }); - } - }, - function(err, results) { - if (err) return next(err); - res.render('api/steam', { - title: 'Steam Web API', - ownedGames: results.ownedGames.response.games, - playerAchievemments: results.playerAchievements.playerstats, - playerSummary: results.playerSummaries.response.players[0] - }); - }); -}; - -/** - * GET /api/stripe - * Stripe API example. - */ - -exports.getStripe = function(req, res) { - res.render('api/stripe', { - title: 'Stripe API', - publishableKey: secrets.stripe.publishableKey - }); -}; - -/** - * POST /api/stripe - * Make a payment. - */ - -exports.postStripe = function(req, res, next) { - var stripeToken = req.body.stripeToken; - var stripeEmail = req.body.stripeEmail; - stripe.charges.create({ - amount: 395, - currency: 'usd', - card: stripeToken, - description: stripeEmail - }, function(err, charge) { - if (err && err.type === 'StripeCardError') { - req.flash('errors', { msg: 'Your card has been declined.' }); - res.redirect('/api/stripe'); - } - req.flash('success', { msg: 'Your card has been charged successfully.' }); - res.redirect('/api/stripe'); - }); -}; - -/** - * GET /api/twilio - * Twilio API example. - */ - -exports.getTwilio = function(req, res) { - res.render('api/twilio', { - title: 'Twilio API' - }); -}; - -/** - * POST /api/twilio - * Send a text message using Twilio. - */ - -exports.postTwilio = function(req, res, next) { - req.assert('number', 'Phone number is required.').notEmpty(); - req.assert('message', 'Message cannot be blank.').notEmpty(); - var errors = req.validationErrors(); - if (errors) { - req.flash('errors', errors); - return res.redirect('/api/twilio'); - } - var message = { - to: req.body.number, - from: '+13472235148', - body: req.body.message - }; - twilio.sendMessage(message, function(err, responseData) { - if (err) return next(err.message); - req.flash('success', { msg: 'Text sent to ' + responseData.to + '.'}); - res.redirect('/api/twilio'); - }); -}; - -/** - * GET /api/clockwork - * Clockwork SMS API example. - */ - -exports.getClockwork = function(req, res) { - res.render('api/clockwork', { - title: 'Clockwork SMS API' - }); -}; - -/** - * POST /api/clockwork - * Send a text message using Clockwork SMS - */ - -exports.postClockwork = function(req, res, next) { - var message = { - To: req.body.telephone, - From: 'Hackathon', - Content: 'Hello from the Hackathon Starter' - }; - clockwork.sendSms(message, function(err, responseData) { - if (err) return next(err.errDesc); - req.flash('success', { msg: 'Text sent to ' + responseData.responses[0].to }); - res.redirect('/api/clockwork'); - }); -}; - -/** - * GET /api/venmo - * Venmo API example. - */ - -exports.getVenmo = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'venmo' }); - var query = querystring.stringify({ access_token: token.accessToken }); - async.parallel({ - getProfile: function(done) { - request.get({ url: 'https://api.venmo.com/v1/me?' + query, json: true }, function(err, request, body) { - done(err, body); - }); - }, - getRecentPayments: function(done) { - request.get({ url: 'https://api.venmo.com/v1/payments?' + query, json: true }, function(err, request, body) { - done(err, body); - }); - } - }, - function(err, results) { - if (err) return next(err); - res.render('api/venmo', { - title: 'Venmo API', - profile: results.getProfile.data, - recentPayments: results.getRecentPayments.data - }); - }); -}; - -/** - * POST /api/venmo - * Send money. - */ - -exports.postVenmo = function(req, res, next) { - req.assert('user', 'Phone, Email or Venmo User ID cannot be blank').notEmpty(); - req.assert('note', 'Please enter a message to accompany the payment').notEmpty(); - req.assert('amount', 'The amount you want to pay cannot be blank').notEmpty(); - var errors = req.validationErrors(); - if (errors) { - req.flash('errors', errors); - return res.redirect('/api/venmo'); - } - var token = _.find(req.user.tokens, { kind: 'venmo' }); - var formData = { - access_token: token.accessToken, - note: req.body.note, - amount: req.body.amount - }; - if (validator.isEmail(req.body.user)) { - formData.email = req.body.user; - } else if (validator.isNumeric(req.body.user) && - validator.isLength(req.body.user, 10, 11)) { - formData.phone = req.body.user; - } else { - formData.user_id = req.body.user; - } - request.post('https://api.venmo.com/v1/payments', { form: formData }, function(err, request, body) { - if (err) return next(err); - if (request.statusCode !== 200) { - req.flash('errors', { msg: JSON.parse(body).error.message }); - return res.redirect('/api/venmo'); - } - req.flash('success', { msg: 'Venmo money transfer complete' }); - res.redirect('/api/venmo'); - }); -}; - -/** - * GET /api/linkedin - * LinkedIn API example. - */ - -exports.getLinkedin = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'linkedin' }); - var linkedin = Linkedin.init(token.accessToken); - linkedin.people.me(function(err, $in) { - if (err) return next(err); - res.render('api/linkedin', { - title: 'LinkedIn API', - profile: $in - }); - }); -}; - -/** - * GET /api/instagram - * Instagram API example. - */ - -exports.getInstagram = function(req, res, next) { - var token = _.find(req.user.tokens, { kind: 'instagram' }); - ig.use({ client_id: secrets.instagram.clientID, client_secret: secrets.instagram.clientSecret }); - ig.use({ access_token: token.accessToken }); - async.parallel({ - searchByUsername: function(done) { - ig.user_search('richellemead', function(err, users, limit) { - done(err, users); - }); - }, - searchByUserId: function(done) { - ig.user('175948269', function(err, user) { - done(err, user); - }); - }, - popularImages: function(done) { - ig.media_popular(function(err, medias) { - done(err, medias); - }); - }, - myRecentMedia: function(done) { - ig.user_self_media_recent(function(err, medias, pagination, limit) { - done(err, medias); - }); - } - }, function(err, results) { - if (err) return next(err); - res.render('api/instagram', { - title: 'Instagram API', - usernames: results.searchByUsername, - userById: results.searchByUserId, - popularImages: results.popularImages, - myRecentMedia: results.myRecentMedia - }); - }); -}; - -/** - * GET /api/yahoo - * Yahoo API example. - */ - -exports.getYahoo = function(req, res) { - Y.YQL('SELECT * FROM weather.forecast WHERE (location = 10007)', function(response) { - var location = response.query.results.channel.location; - var condition = response.query.results.channel.item.condition; - res.render('api/yahoo', { - title: 'Yahoo API', - location: location, - condition: condition - }); - }); -}; \ No newline at end of file diff --git a/controllers/challenges.js b/controllers/challenges.js index 382aa369a5..841b4941e9 100644 --- a/controllers/challenges.js +++ b/controllers/challenges.js @@ -2,24 +2,29 @@ * GET / * Challenges. */ -var Challenge = require('./../models/Challenge'); -var _ = require('lodash'); +var _ = require('lodash'), + debug = require('debug')('freecc:cntr:challenges'), + Challenge = require('./../models/Challenge'); exports.returnChallenge = function(req, res, next) { - var challengeNumber = parseInt(req.params.challengeNumber) || 0; - if (challengeNumber > 59) { challengeNumber = 0; } - Challenge.findOne({challengeNumber: challengeNumber}, function (err, c) { - if (err) { - console.error('Challenge err: ', err); - next(err); - } - res.render('challenges/show', { - title: 'Challenge: ' + c.name, - name: c.name, - video: c.video, - time: c.time, - steps: c.steps, - cc: req.user.challengesHash - }); + var challengeNumber = parseInt(req.params.challengeNumber) || 0; + + if (challengeNumber > 59) { + challengeNumber = 0; + } + + Challenge.findOne({ challengeNumber: challengeNumber }, function(err, c) { + if (err) { + debug('Challenge err: ', err); + return next(err); + } + res.render('challenges/show', { + title: 'Challenge: ' + c.name, + name: c.name, + video: c.video, + time: c.time, + steps: c.steps, + cc: req.user.challengesHash }); -}; \ No newline at end of file + }); +}; diff --git a/controllers/contact.js b/controllers/contact.js index a66424d6f8..c35b6e5d6b 100644 --- a/controllers/contact.js +++ b/controllers/contact.js @@ -1,5 +1,7 @@ -var secrets = require('../config/secrets'); -var nodemailer = require("nodemailer"); +var nodemailer = require('nodemailer'), + debug = require('debug')('freecc:cntr:contact'), + secrets = require('../config/secrets'); + var transporter = nodemailer.createTransport({ service: 'Mandrill', auth: { @@ -22,9 +24,6 @@ exports.getContact = function(req, res) { /** * POST /contact * Send a contact form via Nodemailer. - * @param email - * @param name - * @param message */ exports.postContact = function(req, res) { @@ -32,24 +31,17 @@ exports.postContact = function(req, res) { req.assert('email', 'Email is not valid').isEmail(); req.assert('message', 'Message cannot be blank').notEmpty(); - var errors = req.validationErrors(); - - if (errors) { + if (req.validationErrors()) { req.flash('errors', errors); return res.redirect('/nonprofits'); } - var from = req.body.email; - var name = req.body.name; - var body = req.body.message; - var to = 'team@freecodecamp.com'; - var subject = "CodeNonprofit Project Idea from " + name; - var mailOptions = { - to: to, - from: from, - subject: subject, - text: body + to: 'team@freecodecamp.com', + name: req.body.name, + from: req.body.email, + subject: 'CodeNonprofit Project Idea from ' + name, + text: req.body.message }; transporter.sendMail(mailOptions, function(err) { diff --git a/controllers/home.js b/controllers/home.js index 049a710008..343a28a9c5 100644 --- a/controllers/home.js +++ b/controllers/home.js @@ -5,19 +5,19 @@ exports.index = function(req, res) { if (req.user) { - ch = req.user.challengesHash; - if (req.user.challengesHash[0] > 0) { - var max = Object.keys(ch).reduce(function(max,key){ - return (max === undefined || ch[key] > ch[max]) ? +key : max; - }); - nextChallenge = max + 1; - res.redirect("challenges/" + nextChallenge); - } else { - res.redirect("challenges/0"); - } - } else { - res.render('home', { - title: 'Learn to Code and Become a Software Engineer' + ch = req.user.challengesHash; + if (req.user.challengesHash[0] > 0) { + var max = Object.keys(ch).reduce(function(max, key) { + return (max === undefined || ch[key] > ch[max]) ? +key : max; }); + nextChallenge = max + 1; + res.redirect('challenges/' + nextChallenge); + } else { + res.redirect('challenges/0'); + } + } else { + res.render('home', { + title: 'Learn to Code and Become a Software Engineer' + }); } -}; \ No newline at end of file +}; diff --git a/controllers/resources.js b/controllers/resources.js index e43d665bf9..016b3c7f13 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -1,271 +1,98 @@ +var User = require('../models/User'), + resources = require('./resources.json'), + questions = resources.questions, + steps = resources.steps; -var User = require('../models/User'); - -var totalUsers = User.count({}, function( err, count){ - count; +//NOTE(BERKS): Async, total users may not available before it is used. +var totalUsers = 0; +User.count({}, function(err, count) { + totalUsers = count; }); -//var usersOverTenChallenges = User.where: "this.challengesCompleted && this.challengesCompleted.length >= 10" - /** * GET / * Resources. */ - -exports.learnToCode = function(req, res) { - res.render('learn-to-code', { - title: 'Learn to Code' +//TODO: Stats view +module.exports = { + learnToCode: function(req, res) { + res.render('learn-to-code', { + title: 'Learn to Code' }); -} + }, -exports.privacy = function(req, res) { - res.render('privacy', { - title: 'Privacy' + privacy: function privacy(req, res) { + res.render('privacy', { + title: 'Privacy' }); -} + }, -exports.statistics = function(req, res) { - res.render('statistics', { - title: 'Code Camper Statistics', - //totalUsers: totalUsers, - //usersOverTenChallenges: usersOverTenChallenges + statistics: function statistics(req, res) { + res.render('statistics', { + title: 'Code Camper Statistics' + //totalUsers: totalUsers, + //usersOverTenChallenges: usersOverTenChallenges }); -} + }, -exports.chromebook = function(req, res) { - res.render('chromebook', { - title: 'Win a Chromebook' + chromebook: function chromebook(req, res) { + res.render('chromebook', { + title: 'Win a Chromebook' }); -} + }, -exports.jqueryExercises = function(req, res) { - res.render('jquery-exercises', { - title: 'jQuery Exercises' + jqueryExercises: function jqueryExercises(req, res) { + res.render('jquery-exercises', { + title: 'jQuery Exercises' }); -} + }, -exports.livePairProgramming = function(req, res) { - res.render('live-pair-programming', { - title: 'Live Pair Programming' + livePairProgramming: function(req, res) { + res.render('live-pair-programming', { + title: 'Live Pair Programming' }); -} + }, -exports.javaScriptInYourInbox = function(req, res) { - res.render('javascript-in-your-inbox', { - title: 'JavaScript in your Inbox' + javaScriptInYourInbox: function(req, res) { + res.render('javascript-in-your-inbox', { + title: 'JavaScript in your Inbox' }); -} + }, -exports.programmerInterviewQuestionsApp = function(req, res) { - res.render('programmer-interview-questions-app', { - title: 'Programmer Interview Questions App' + programmerInterviewQuestionsApp: function(req, res) { + res.render('programmer-interview-questions-app', { + title: 'Programmer Interview Questions App' }); -} + }, -exports.pairProgramWithTeamViewer = function(req, res) { + pairProgramWithTeamViewer: function(req, res) { res.render('pair-program-with-team-viewer', { - title: 'Challenge: Pair Program with Team Viewer', - name: "Pair Program with Team Viewer", - video: '', - time: 30, - steps: ["In the spirit of keeping Free Code Camp 100% free, we've created directions for using a totally free pair programming tool called Team Viewer. It's not as user-friendly as Screen Hero, but it gets the job done (and works on Linux!).", - "Go to http://www.teamviewer.com/en/index.aspx and download Team Viewer. Be sure not to download the beta version, which isn't compatible with the current stable version everyone else will be using.", - "Install it and launch it", - "Go to the chat room and ask if anyone wants to pair program with you on a CoderByte challenge.", - "If someone is interested, they will be your \"pair\" - the person you pair programming with.", - "First, you will pair program on your pair's computer. Ask your pair for his or her Team Viewer ID and password.", - "Enter this id, and then password, to start the session.", - "Once the Team Viewer session starts, look at the the top of the screen. You will see 6 buttons. If you hover your cursor over the buttons, they will slide down so that you can read them. Click the audio/video button. This will allow you to turn on Voice Over IP and unmute your microphone, opening up your voice channel.", - "Note that you can now control your pair's keyboard and mouse, enabling you to step in and code yourself on your pair's computer when desired", - "Now you can click the X to end the pair programming session.", - "Next, you will pair program on your computer. Copy your Team Viewer ID and paste it into the private chat, so that your pair can use it to connect with you.", - "You will need to share your randomly generated password with your pair as well.", - "Once your pair connects, you will see a Team Viewer side menu. ", - "Click the audio button that drops down.", - "Click the headset icon and choose Voice over IP", - "Click the microphone button to unmute your microphone. Once your pair does the same, you two will have an open voice channel.", - "Now you can click the X to end the pair programming session.", - "Now it's time to tackle CoderByte.", - "Create a CoderByte account at http://coderbyte.com/sl/", - "Now go to http://coderbyte.com/CodingArea/Challenges/#easyChals and start working through Coderbyte's easy algorithm scripting challenges using JavaScript.", - "When you are finished pair programming, click the X to end the session.", - "Congratulations! You have completed your first pair programming session.", - "You should pair program with different Code Campers until you've completed all the Easy, Medium and Hard CoderByte challenges. This is a big time investment, but the JavaScript practice you'll get, along with the scripting and algorithm experience, are well worth it!", - "You can complete CoderByte problems while you continue to work through Free Code Camp's challenges.", - "Be sure to pair program on these challenges, and remember to apply the RSAP methodology.", - "Click the button below to return to the Pair Programming challenge, then mark it complete."], - cc: req.user.challengesHash + title: 'Challenge: Pair Program with Team Viewer', + name: 'Pair Program with Team Viewer', + video: '', + time: 30, + steps: steps, + cc: req.user.challengesHash }); -} -exports.about = function(req, res) { + }, + + about: function(req, res) { res.render('about', { - title: 'About Free Code Camp and Our Team of Volunteers' + title: 'About Free Code Camp and Our Team of Volunteers' }); -} + }, -exports.doneWithFirst100Hours = function(req, res) { + doneWithFirst100Hours: function(req, res) { res.render('done-with-first-100-hours', { - title: 'Congratulations on finishing the first 100 hours of Free Code Camp!' + title: + 'Congratulations on finishing the first 100 hours of Free Code Camp!' }); -} + }, + + interviewQuestions: function(req, res) { + res.json(questions); + } +}; + + -exports.interviewQuestions = function(req, res) { - res.json([ - { - question: "Time Complexity of Accessing Array Index (int a = ARR[5];)", - answer: "O(1)" - }, - { - question: "Time Complexity of Inserting a node in Linked List", - answer: "O(1)" - }, - { - question: "Time Complexity of Pushing and Popping on Stack", - answer: "O(1)" - }, - { - question: "Time Complexity of Insertion and Removal from Queue", - answer: "O(1)" - }, - { - question: "Time Complexity of Finding out the parent or left/right child of a node in a tree stored in Array", - answer: "O(1)" - }, - { - question: "Time Complexity of Jumping to Next/Previous element in Doubly Linked List", - answer: "O(1)" - }, - { - question: "Time Complexity of Traversing an array", - answer: "O(n)" - }, - { - question: "Time Complexity of Traversing a linked list", - answer: "O(n)" - }, - { - question: "Time Complexity of Linear Search", - answer: "O(n)" - }, - { - question: "Time Complexity of Deletion of a specific element in a Linked List (Not sorted)", - answer: "O(n)" - }, - { - question: "Time Complexity of Comparing two strings", - answer: "O(n)" - }, - { - question: "Time Complexity of Checking for Palindrome", - answer: "O(n)" - }, - { - question: "Time Complexity of Counting/Bucket Sort", - answer: "O(n)" - }, - { - question: "Time Complexity of Binary Search", - answer: "O(log n)" - }, - { - question: "Time Complexity of Finding largest/smallest number in a binary search tree", - answer: "O(log n)" - }, - { - question: "Time Complexity of Certain Divide and Conquer Algorithms based on Linear functionality", - answer: "O(log n)" - }, - { - question: "Time Complexity of Calculating Fibonacci Numbers - Best Method", - answer: "O(log n)" - }, - { - question: "Time Complexity of Merge Sort", - answer: "O(nlogn)" - }, - { - question: "Time Complexity of Heap Sort", - answer: "O(nlogn)" - }, - { - question: "Time Complexity of Quick Sort", - answer: "O(nlogn)" - }, - { - question: "Time Complexity of Certain Divide and Conquer Algorithms based on optimizing O(n^2) algorithms", - answer: "O(nlogn)" - }, - { - question: "Time Complexity of Bubble Sort", - answer: "O(n^2)" - }, - { - question: "Time Complexity of Insertion Sort", - answer: "O(n^2)" - }, - { - question: "Time Complexity of Selection Sort", - answer: "O(n^2)" - }, - { - question: "Time Complexity of Traversing a simple 2D array", - answer: "O(n^2)" - }, - { - question: "Latency of L1 cache reference", - answer: "0.5 nanoseconds" - }, - { - question: "Latency of Branch mispredict", - answer: "5 nanoseconds" - }, - { - question: "Latency of L2 cache reference", - answer: "7 nanoseconds" - }, - { - question: "Latency of Mutex lock/unlock", - answer: "25 nanoseconds" - }, - { - question: "Latency of Main memory reference", - answer: "100 nanoseconds" - }, - { - question: "Latency of Compressing 1K bytes with Zippy", - answer: "3,000 nanoseconds" - }, - { - question: "Latency of Sending 1K bytes over a 1 Gbps network", - answer: "10,000 nanoseconds" - }, - { - question: "Latency of Reading 4K randomly from SSD", - answer: "150,000 nanoseconds" - }, - { - question: "Latency of Reading 1 MB sequentially from memory", - answer: "250,000 nanoseconds" - }, - { - question: "Latency of a Round trip within the same datacenter", - answer: "500,000 nanoseconds" - }, - { - question: "Latency of Reading 1 MB sequentially from SSD", - answer: "1,000,000 nanoseconds" - }, - { - question: "Latency of Disk seek", - answer: "10,000,000 nanoseconds" - }, - { - question: "Latency of Reading 1 MB sequentially from disk", - answer: "20,000,000 nanoseconds" - }, - { - question: "Latency of Sending a packet from California to the Netherlands and back", - answer: "150,000,000 nanoseconds" - } - ]); -}; \ No newline at end of file diff --git a/controllers/resources.json b/controllers/resources.json new file mode 100644 index 0000000000..3b8504c88a --- /dev/null +++ b/controllers/resources.json @@ -0,0 +1,148 @@ +{ + "questions": [{ + "question": "Time Complexity of Accessing Array Index (int a = ARR[5];)", + "answer": "O(1)" + }, { + "question": "Time Complexity of Inserting a node in Linked List", + "answer": "O(1)" + }, { + "question": "Time Complexity of Pushing and Popping on Stack", + "answer": "O(1)" + }, { + "question": "Time Complexity of Insertion and Removal from Queue", + "answer": "O(1)" + }, { + "question": "Time Complexity of Finding out the parent or left/right child of a node in a tree stored in Array", + "answer": "O(1)" + }, { + "question": "Time Complexity of Jumping to Next/Previous element in Doubly Linked List", + "answer": "O(1)" + }, { + "question": "Time Complexity of Traversing an array", + "answer": "O(n)" + }, { + "question": "Time Complexity of Traversing a linked list", + "answer": "O(n)" + }, { + "question": "Time Complexity of Linear Search", + "answer": "O(n)" + }, { + "question": "Time Complexity of Deletion of a specific element in a Linked List (Not sorted)", + "answer": "O(n)" + }, { + "question": "Time Complexity of Comparing two strings", + "answer": "O(n)" + }, { + "question": "Time Complexity of Checking for Palindrome", + "answer": "O(n)" + }, { + "question": "Time Complexity of Counting/Bucket Sort", + "answer": "O(n)" + }, { + "question": "Time Complexity of Binary Search", + "answer": "O(log n)" + }, { + "question": "Time Complexity of Finding largest/smallest number in a binary search tree", + "answer": "O(log n)" + }, { + "question": "Time Complexity of Certain Divide and Conquer Algorithms based on Linear functionality", + "answer": "O(log n)" + }, { + "question": "Time Complexity of Calculating Fibonacci Numbers - Best Method", + "answer": "O(log n)" + }, { + "question": "Time Complexity of Merge Sort", + "answer": "O(nlogn)" + }, { + "question": "Time Complexity of Heap Sort", + "answer": "O(nlogn)" + }, { + "question": "Time Complexity of Quick Sort", + "answer": "O(nlogn)" + }, { + "question": "Time Complexity of Certain Divide and Conquer Algorithms based on optimizing O(n^2) algorithms", + "answer": "O(nlogn)" + }, { + "question": "Time Complexity of Bubble Sort", + "answer": "O(n^2)" + }, { + "question": "Time Complexity of Insertion Sort", + "answer": "O(n^2)" + }, { + "question": "Time Complexity of Selection Sort", + "answer": "O(n^2)" + }, { + "question": "Time Complexity of Traversing a simple 2D array", + "answer": "O(n^2)" + }, { + "question": "Latency of L1 cache reference", + "answer": "0.5 nanoseconds" + }, { + "question": "Latency of Branch mispredict", + "answer": "5 nanoseconds" + }, { + "question": "Latency of L2 cache reference", + "answer": "7 nanoseconds" + }, { + "question": "Latency of Mutex lock/unlock", + "answer": "25 nanoseconds" + }, { + "question": "Latency of Main memory reference", + "answer": "100 nanoseconds" + }, { + "question": "Latency of Compressing 1K bytes with Zippy", + "answer": "3,000 nanoseconds" + }, { + "question": "Latency of Sending 1K bytes over a 1 Gbps network", + "answer": "10,000 nanoseconds" + }, { + "question": "Latency of Reading 4K randomly from SSD", + "answer": "150,000 nanoseconds" + }, { + "question": "Latency of Reading 1 MB sequentially from memory", + "answer": "250,000 nanoseconds" + }, { + "question": "Latency of a Round trip within the same datacenter", + "answer": "500,000 nanoseconds" + }, { + "question": "Latency of Reading 1 MB sequentially from SSD", + "answer": "1,000,000 nanoseconds" + }, { + "question": "Latency of Disk seek", + "answer": "10,000,000 nanoseconds" + }, { + "question": "Latency of Reading 1 MB sequentially from disk", + "answer": "20,000,000 nanoseconds" + }, { + "question": "Latency of Sending a packet from California to the Netherlands and back", + "answer": "150,000,000 nanoseconds" + }], + "steps": [ + "In the spirit of keeping Free Code Camp 100% free, we've created directions for using a totally free pair programming tool called Team Viewer. It's not as user-friendly as Screen Hero, but it gets the job done (and works on Linux!).", + "Go to http://www.teamviewer.com/en/index.aspx and download Team Viewer. Be sure not to download the beta version, which isn't compatible with the current stable version everyone else will be using.", + "Install it and launch it", + "Go to the chat room and ask if anyone wants to pair program with you on a CoderByte challenge.", + "If someone is interested, they will be your \"pair\" - the person you pair programming with.", + "First, you will pair program on your pair's computer. Ask your pair for his or her Team Viewer ID and password.", + "Enter this id, and then password, to start the session.", + "Once the Team Viewer session starts, look at the the top of the screen. You will see 6 buttons. If you hover your cursor over the buttons, they will slide down so that you can read them. Click the audio/video button. This will allow you to turn on Voice Over IP and unmute your microphone, opening up your voice channel.", + "Note that you can now control your pair's keyboard and mouse, enabling you to step in and code yourself on your pair's computer when desired", + "Now you can click the X to end the pair programming session.", + "Next, you will pair program on your computer. Copy your Team Viewer ID and paste it into the private chat, so that your pair can use it to connect with you.", + "You will need to share your randomly generated password with your pair as well.", + "Once your pair connects, you will see a Team Viewer side menu. ", + "Click the audio button that drops down.", + "Click the headset icon and choose Voice over IP", + "Click the microphone button to unmute your microphone. Once your pair does the same, you two will have an open voice channel.", + "Now you can click the X to end the pair programming session.", + "Now it's time to tackle CoderByte.", + "Create a CoderByte account at http://coderbyte.com/sl/", + "Now go to http://coderbyte.com/CodingArea/Challenges/#easyChals and start working through Coderbyte's easy algorithm scripting challenges using JavaScript.", + "When you are finished pair programming, click the X to end the session.", + "Congratulations! You have completed your first pair programming session.", + "You should pair program with different Code Campers until you've completed all the Easy, Medium and Hard CoderByte challenges. This is a big time investment, but the JavaScript practice you'll get, along with the scripting and algorithm experience, are well worth it!", + "You can complete CoderByte problems while you continue to work through Free Code Camp's challenges.", + "Be sure to pair program on these challenges, and remember to apply the RSAP methodology.", + "Click the button below to return to the Pair Programming challenge, then mark it complete." + ] +} diff --git a/controllers/user.js b/controllers/user.js index 48048b0c7c..45dc0fc6a7 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -7,6 +7,7 @@ var User = require('../models/User'); var secrets = require('../config/secrets'); var moment = require('moment'); +//TODO(Berks): Refactor to use module.exports = {} pattern. /** * GET /login * Login page. @@ -22,8 +23,6 @@ exports.getLogin = function(req, res) { /** * POST /login * Sign in using email and password. - * @param email - * @param password */ exports.postLogin = function(req, res, next) { @@ -88,14 +87,13 @@ exports.getEmailSignup = function(req, res) { /** * POST /email-signup * Create a new local account. - * @param email - * @param password */ exports.postEmailSignup = function(req, res, next) { req.assert('email', 'Email is not valid').isEmail(); req.assert('password', 'Password must be at least 4 characters long').len(4); - req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password); + req.assert('confirmPassword', 'Passwords do not match') + .equals(req.body.password); var errors = req.validationErrors(); @@ -110,14 +108,19 @@ exports.postEmailSignup = function(req, res, next) { }); User.findOne({ email: req.body.email }, function(err, existingUser) { + if (err) { return next(err); } + if (existingUser) { - req.flash('errors', { msg: 'Account with that email address already exists.' }); + req.flash('errors', { + msg: 'Account with that email address already exists.' + }); return res.redirect('/email-signup'); } user.save(function(err) { - if (err) return next(err); + if (err) { return next(err); } + req.logIn(user, function(err) { - if (err) return next(err); + if (err) { return next(err); } res.redirect('/email-signup'); }); }); @@ -188,12 +191,12 @@ exports.postUpdateProfile = function(req, res, next) { /** * POST /account/password * Update current password. - * @param password */ exports.postUpdatePassword = function(req, res, next) { req.assert('password', 'Password must be at least 4 characters long').len(4); - req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password); + req.assert('confirmPassword', 'Passwords do not match') + .equals(req.body.password); var errors = req.validationErrors(); @@ -203,12 +206,13 @@ exports.postUpdatePassword = function(req, res, next) { } User.findById(req.user.id, function(err, user) { - if (err) return next(err); + if (err) { return next(err); } user.password = req.body.password; user.save(function(err) { - if (err) return next(err); + if (err) { return next(err); } + req.flash('success', { msg: 'Password has been changed.' }); res.redirect('/account'); }); @@ -222,7 +226,7 @@ exports.postUpdatePassword = function(req, res, next) { exports.postDeleteAccount = function(req, res, next) { User.remove({ _id: req.user.id }, function(err) { - if (err) return next(err); + if (err) { return next(err); } req.logout(); req.flash('info', { msg: 'Your account has been deleted.' }); res.redirect('/'); @@ -232,19 +236,21 @@ exports.postDeleteAccount = function(req, res, next) { /** * GET /account/unlink/:provider * Unlink OAuth provider. - * @param provider */ exports.getOauthUnlink = function(req, res, next) { var provider = req.params.provider; User.findById(req.user.id, function(err, user) { - if (err) return next(err); + if (err) { return next(err); } user[provider] = undefined; - user.tokens = _.reject(user.tokens, function(token) { return token.kind === provider; }); + user.tokens = + _.reject(user.tokens, function(token) { + return token.kind === provider; + }); user.save(function(err) { - if (err) return next(err); + if (err) { return next(err); } req.flash('info', { msg: provider + ' account has been unlinked.' }); res.redirect('/account'); }); @@ -264,8 +270,11 @@ exports.getReset = function(req, res) { .findOne({ resetPasswordToken: req.params.token }) .where('resetPasswordExpires').gt(Date.now()) .exec(function(err, user) { + if (err) { return next(err); } if (!user) { - req.flash('errors', { msg: 'Password reset token is invalid or has expired.' }); + req.flash('errors', { + msg: 'Password reset token is invalid or has expired.' + }); return res.redirect('/forgot'); } res.render('account/reset', { @@ -277,7 +286,6 @@ exports.getReset = function(req, res) { /** * POST /reset/:token * Process the reset password request. - * @param token */ exports.postReset = function(req, res, next) { @@ -297,8 +305,11 @@ exports.postReset = function(req, res, next) { .findOne({ resetPasswordToken: req.params.token }) .where('resetPasswordExpires').gt(Date.now()) .exec(function(err, user) { + if (err) { return next(err); } if (!user) { - req.flash('errors', { msg: 'Password reset token is invalid or has expired.' }); + req.flash('errors', { + msg: 'Password reset token is invalid or has expired.' + }); return res.redirect('back'); } @@ -307,7 +318,7 @@ exports.postReset = function(req, res, next) { user.resetPasswordExpires = undefined; user.save(function(err) { - if (err) return next(err); + if (err) { return done(err); } req.logIn(user, function(err) { done(err, user); }); @@ -326,16 +337,25 @@ exports.postReset = function(req, res, next) { to: user.email, from: 'Team@freecodecamp.com', subject: 'Your Free Code Camp password has been changed', - text: 'Hello,\n\n' + - 'This email is confirming that you requested to reset your password for your Free Code Camp account. This is your email: ' + user.email + '\n' + text: [ + 'Hello,\n\n', + 'This email is confirming that you requested to', + 'reset your password for your Free Code Camp account.', + 'This is your email:', + user.email, + '\n' + ].join(' ') }; transporter.sendMail(mailOptions, function(err) { - req.flash('success', { msg: 'Success! Your password has been changed.' }); - done(err); + if (err) { return done(err); } + req.flash('success', { + msg: 'Success! Your password has been changed.' + }); + done(); }); } ], function(err) { - if (err) return next(err); + if (err) { return next(err); } res.redirect('/'); }); }; @@ -357,7 +377,6 @@ exports.getForgot = function(req, res) { /** * POST /forgot * Create a random token, then the send user an email with a reset link. - * @param email */ exports.postForgot = function(req, res, next) { @@ -373,14 +392,20 @@ exports.postForgot = function(req, res, next) { async.waterfall([ function(done) { crypto.randomBytes(16, function(err, buf) { + if (err) { return done(err); } var token = buf.toString('hex'); - done(err, token); + done(null, token); }); }, function(token, done) { - User.findOne({ email: req.body.email.toLowerCase() }, function(err, user) { + User.findOne({ + email: req.body.email.toLowerCase() + }, function(err, user) { + if (err) { return done(err); } if (!user) { - req.flash('errors', { msg: 'No account with that email address exists.' }); + req.flash('errors', { + msg: 'No account with that email address exists.' + }); return res.redirect('/forgot'); } @@ -388,7 +413,8 @@ exports.postForgot = function(req, res, next) { user.resetPasswordExpires = Date.now() + 3600000; // 1 hour user.save(function(err) { - done(err, token, user); + if (err) { return done(err); } + done(null, token, user); }); }); }, @@ -404,18 +430,32 @@ exports.postForgot = function(req, res, next) { to: user.email, from: 'Team@freecodecamp.com', subject: 'Reset your Free Code Camp password', - text: "You are receiving this email because you (or someone else) requested we reset your Free Code Camp account's password.\n\n" + - 'Please click on the following link, or paste this into your browser to complete the process:\n\n' + - 'http://' + req.headers.host + '/reset/' + token + '\n\n' + - 'If you did not request this, please ignore this email and your password will remain unchanged.\n' + text: [ + 'You are receiving this email because you (or someone else)', + 'requested we reset your Free Code Camp account\'s password.\n\n', + 'Please click on the following link, or paste this into your', + 'browser to complete the process:\n\n', + 'http://', + req.headers.host, + '/reset/', + token, + '\n\n', + 'If you did not request this, please ignore this email and', + 'your password will remain unchanged.\n' + ].join(' ') }; transporter.sendMail(mailOptions, function(err) { - req.flash('info', { msg: 'An e-mail has been sent to ' + user.email + ' with further instructions.' }); - done(err, 'done'); + if (err) { return done(err); } + req.flash('info', { + msg: 'An e-mail has been sent to ' + + user.email + + ' with further instructions.' + }); + done(null, 'done'); }); } ], function(err) { - if (err) return next(err); + if (err) { return next(err); } res.redirect('/forgot'); }); }; diff --git a/gulpfile.js b/gulpfile.js index faf61f7339..cc488c5d05 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,8 +9,9 @@ var gulp = require('gulp'), var paths = { server: './app.js', - serverIgnore: [], -} + serverIgnore: [] +}; + gulp.task('inject', function() { gulp.src('views/home.jade') .pipe(inject(gulp.src(bower()), { @@ -51,15 +52,12 @@ gulp.task('serve', function(cb) { gulp.task('sync', ['serve'], function() { sync.init(null, { - proxy: 'http://localhost:3000', - logLeval: 'debug', - files: [ - 'public/**/*', - ], - port: 3001, - open: true, - browser: ['safari', 'google chrome'], - reloadDelay: reloadDelay + proxy: 'http://localhost:3000', + logLeval: 'debug', + files: ['public/**/*'], + port: 3001, + open: true, + reloadDelay: reloadDelay }); }); diff --git a/models/Challenge.js b/models/Challenge.js index 28759a81ad..63e2f85ef5 100644 --- a/models/Challenge.js +++ b/models/Challenge.js @@ -2,12 +2,15 @@ var mongoose = require('mongoose'); var secrets = require('../config/secrets'); var challengeSchema = new mongoose.Schema({ - name: { type: String, unique: true }, - link: String, - time: String, - challengeNumber: Number, - video: String, - steps: Array + name: { + type: String, + unique: true + }, + link: String, + time: String, + challengeNumber: Number, + video: String, + steps: Array }); -var Challenge = module.exports = mongoose.model('Challenge', challengeSchema); \ No newline at end of file +module.exports = mongoose.model('Challenge', challengeSchema); diff --git a/models/User.js b/models/User.js index 27794012a5..5d789e0e9c 100644 --- a/models/User.js +++ b/models/User.js @@ -3,7 +3,12 @@ var crypto = require('crypto'); var mongoose = require('mongoose'); var userSchema = new mongoose.Schema({ - //email: { type: String, unique: true, lowercase: true, match: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/ }, + /*email: { + type: String, + unique: true, + lowercase: true, + match: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/ + },*/ email: String, password: String, @@ -14,77 +19,277 @@ var userSchema = new mongoose.Schema({ instagram: String, linkedin: String, tokens: Array, - points: { type: Number, default: 0 }, + points: { + type: Number, + default: 0 + }, challengesCompleted: { type: Array, default: [] }, challengesHash: { - 0: { type: Number, default: 0 }, - 1: { type: Number, default: 0 }, - 2: { type: Number, default: 0 }, - 3: { type: Number, default: 0 }, - 4: { type: Number, default: 0 }, - 5: { type: Number, default: 0 }, - 6: { type: Number, default: 0 }, - 7: { type: Number, default: 0 }, - 8: { type: Number, default: 0 }, - 9: { type: Number, default: 0 }, - 10: { type: Number, default: 0 }, - 11: { type: Number, default: 0 }, - 12: { type: Number, default: 0 }, - 13: { type: Number, default: 0 }, - 14: { type: Number, default: 0 }, - 15: { type: Number, default: 0 }, - 16: { type: Number, default: 0 }, - 17: { type: Number, default: 0 }, - 18: { type: Number, default: 0 }, - 19: { type: Number, default: 0 }, - 20: { type: Number, default: 0 }, - 21: { type: Number, default: 0 }, - 22: { type: Number, default: 0 }, - 23: { type: Number, default: 0 }, - 24: { type: Number, default: 0 }, - 25: { type: Number, default: 0 }, - 26: { type: Number, default: 0 }, - 27: { type: Number, default: 0 }, - 28: { type: Number, default: 0 }, - 29: { type: Number, default: 0 }, - 30: { type: Number, default: 0 }, - 31: { type: Number, default: 0 }, - 32: { type: Number, default: 0 }, - 33: { type: Number, default: 0 }, - 34: { type: Number, default: 0 }, - 35: { type: Number, default: 0 }, - 36: { type: Number, default: 0 }, - 37: { type: Number, default: 0 }, - 38: { type: Number, default: 0 }, - 39: { type: Number, default: 0 }, - 40: { type: Number, default: 0 }, - 41: { type: Number, default: 0 }, - 42: { type: Number, default: 0 }, - 43: { type: Number, default: 0 }, - 44: { type: Number, default: 0 }, - 45: { type: Number, default: 0 }, - 46: { type: Number, default: 0 }, - 47: { type: Number, default: 0 }, - 48: { type: Number, default: 0 }, - 49: { type: Number, default: 0 }, - 50: { type: Number, default: 0 }, - 51: { type: Number, default: 0 }, - 52: { type: Number, default: 0 }, - 53: { type: Number, default: 0 }, - 54: { type: Number, default: 0 }, - 55: { type: Number, default: 0 }, - 56: { type: Number, default: 0 }, - 57: { type: Number, default: 0 }, - 58: { type: Number, default: 0 }, - 59: { type: Number, default: 0 } + 0: { + type: Number, + default: 0 + }, + 1: { + type: Number, + default: 0 + }, + 2: { + type: Number, + default: 0 + }, + 3: { + type: Number, + default: 0 + }, + 4: { + type: Number, + default: 0 + }, + 5: { + type: Number, + default: 0 + }, + 6: { + type: Number, + default: 0 + }, + 7: { + type: Number, + default: 0 + }, + 8: { + type: Number, + default: 0 + }, + 9: { + type: Number, + default: 0 + }, + 10: { + type: Number, + default: 0 + }, + 11: { + type: Number, + default: 0 + }, + 12: { + type: Number, + default: 0 + }, + 13: { + type: Number, + default: 0 + }, + 14: { + type: Number, + default: 0 + }, + 15: { + type: Number, + default: 0 + }, + 16: { + type: Number, + default: 0 + }, + 17: { + type: Number, + default: 0 + }, + 18: { + type: Number, + default: 0 + }, + 19: { + type: Number, + default: 0 + }, + 20: { + type: Number, + default: 0 + }, + 21: { + type: Number, + default: 0 + }, + 22: { + type: Number, + default: 0 + }, + 23: { + type: Number, + default: 0 + }, + 24: { + type: Number, + default: 0 + }, + 25: { + type: Number, + default: 0 + }, + 26: { + type: Number, + default: 0 + }, + 27: { + type: Number, + default: 0 + }, + 28: { + type: Number, + default: 0 + }, + 29: { + type: Number, + default: 0 + }, + 30: { + type: Number, + default: 0 + }, + 31: { + type: Number, + default: 0 + }, + 32: { + type: Number, + default: 0 + }, + 33: { + type: Number, + default: 0 + }, + 34: { + type: Number, + default: 0 + }, + 35: { + type: Number, + default: 0 + }, + 36: { + type: Number, + default: 0 + }, + 37: { + type: Number, + default: 0 + }, + 38: { + type: Number, + default: 0 + }, + 39: { + type: Number, + default: 0 + }, + 40: { + type: Number, + default: 0 + }, + 41: { + type: Number, + default: 0 + }, + 42: { + type: Number, + default: 0 + }, + 43: { + type: Number, + default: 0 + }, + 44: { + type: Number, + default: 0 + }, + 45: { + type: Number, + default: 0 + }, + 46: { + type: Number, + default: 0 + }, + 47: { + type: Number, + default: 0 + }, + 48: { + type: Number, + default: 0 + }, + 49: { + type: Number, + default: 0 + }, + 50: { + type: Number, + default: 0 + }, + 51: { + type: Number, + default: 0 + }, + 52: { + type: Number, + default: 0 + }, + 53: { + type: Number, + default: 0 + }, + 54: { + type: Number, + default: 0 + }, + 55: { + type: Number, + default: 0 + }, + 56: { + type: Number, + default: 0 + }, + 57: { + type: Number, + default: 0 + }, + 58: { + type: Number, + default: 0 + }, + 59: { + type: Number, + default: 0 + } }, profile: { - name: { type: String, default: '' }, - gender: { type: String, default: '' }, - location: { type: String, default: '' }, - website: { type: String, default: '' }, - picture: { type: String, default: '' } - //username: { type: String, default: '', unique: true, match: /^[a-zA-Z0-9_]+$/ } + name: { + type: String, default: '' + }, + gender: { + type: String, default: '' + }, + location: { + type: String, default: '' + }, + website: { + type: String, + default: '' + }, + picture: { + type: String, + default: '' + } + /*username: { + type: String, + default: '', + unique: true, + match: /^[a-zA-Z0-9_]+$/ + }*/ }, resetPasswordToken: String, @@ -133,7 +338,11 @@ userSchema.methods.gravatar = function(size) { return 'https://gravatar.com/avatar/?s=' + size + '&d=retro'; } - var md5 = crypto.createHash('md5').update(this.email).digest('hex'); + var md5 = crypto + .createHash('md5') + .update(this.email) + .digest('hex'); + return 'https://gravatar.com/avatar/' + md5 + '?s=' + size + '&d=retro'; }; diff --git a/public/js/main.js b/public/js/main.js index c4d98b043d..f1d9507b1b 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,40 +1,44 @@ $(document).ready(function() { - var CSRF_HEADER = 'X-CSRF-Token'; + var CSRF_HEADER = 'X-CSRF-Token'; - var setCSRFToken = function(securityToken) { - jQuery.ajaxPrefilter(function(options, _, xhr) { - if ( !xhr.crossDomain ) - xhr.setRequestHeader(CSRF_HEADER, securityToken); - }); - }; - - setCSRFToken($('meta[name="csrf-token"]').attr('content')); - - $('.start-challenge').on("click", function() { - $(this).parent().remove(); - $('.challenge-content').removeClass('hidden-element').addClass('animated fadeInDown'); + var setCSRFToken = function(securityToken) { + jQuery.ajaxPrefilter(function(options, _, xhr) { + if (!xhr.crossDomain) { + xhr.setRequestHeader(CSRF_HEADER, securityToken); + } }); + }; - $('.completed-challenge').on("click", function() { - $('#complete-dialog').modal('show'); - l = location.pathname.split('/'); - cn = l[l.length - 1] - console.log(cn); - $.ajax({ - type: 'POST', - data: {challengeNumber: cn}, - url: '/completed_challenge/' - }); - }); + setCSRFToken($('meta[name="csrf-token"]').attr('content')); - $('.skip-challenge').on("click", function() { - $('#skip-dialog').modal('show'); - }); + $('.start-challenge').on('click', function() { + $(this).parent().remove(); + $('.challenge-content') + .removeClass('hidden-element') + .addClass('animated fadeInDown'); + }); - $('.next-button').on("click", function() { - l = location.pathname.split('/'); - window.location = "/challenges/" + (parseInt(l[l.length-1]) + 1) - }); + $('.completed-challenge').on('click', function() { + $('#complete-dialog').modal('show'); + + l = location.pathname.split('/'); + cn = l[l.length - 1]; + console.log(cn); + $.ajax({ + type: 'POST', + data: {challengeNumber: cn}, + url: '/completed_challenge/' + }); + }); + + $('.skip-challenge').on('click', function() { + $('#skip-dialog').modal('show'); + }); + + $('.next-button').on('click', function() { + l = location.pathname.split('/'); + window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); + }); }); diff --git a/seed_data/seed_challenges.js b/seed_data/seed_challenges.js index b54e9c2f68..3d1e9897a3 100644 --- a/seed_data/seed_challenges.js +++ b/seed_data/seed_challenges.js @@ -1,15 +1,22 @@ -var Challenge = require('../models/Challenge.js') -var mongoose = require('mongoose'); -var secrets = require('../config/secrets'); -var challenges = require('./challenges.json'); +var Challenge = require('../models/Challenge.js'), + mongoose = require('mongoose'), + secrets = require('../config/secrets'), + challenges = require('./challenges.json'); mongoose.connect(secrets.db); -Challenge.remove({}, function(err, data){ - if (err) console.log(err); - else console.log('Deleted ', data ); + +Challenge.remove({}, function(err, data) { + if (err) { + console.error(err); + } else { + console.log('Deleted ', data); + } Challenge.create(challenges, function(err, data) { - if (err) console.log(err); - else console.log('Saved ', data ); + if (err) { + console.log(err); + } else { + console.log('Saved ', data); + } process.exit(0); }); }); diff --git a/setup.js b/setup.js index bc0456afd3..775853162e 100644 --- a/setup.js +++ b/setup.js @@ -50,7 +50,8 @@ var footer = blessed.text({ fg: 'white', bg: 'blue', tags: true, - content: ' {cyan-fg}{/cyan-fg} moves | {cyan-fg}{/cyan-fg} selects | {cyan-fg}{/cyan-fg} exits' + content: ' {cyan-fg}{/cyan-fg} moves |' + + ' {cyan-fg}{/cyan-fg} selects | {cyan-fg}{/cyan-fg} exits' }); var inner = blessed.form({ @@ -97,7 +98,8 @@ var clusterText = blessed.text({ bg: 'red', fg: 'white', tags: true, - content: 'Take advantage of multi-core systems using built-in {underline}cluster{/underline} module.' + content: 'Take advantage of multi-core systems' + + ' using built-in {underline}cluster{/underline} module.' }); @@ -186,38 +188,54 @@ var authForm = blessed.form({ }); authForm.on('submit', function() { - var passportConfig = fs.readFileSync('config/passport.js').toString().split(os.EOL); - var loginTemplate = fs.readFileSync('views/account/login.jade').toString().split(os.EOL); - var profileTemplate = fs.readFileSync('views/account/profile.jade').toString().split(os.EOL); + var passportConfig = fs.readFileSync('config/passport.js') + .toString().split(os.EOL); + var loginTemplate = fs.readFileSync('views/account/login.jade') + .toString().split(os.EOL); + var profileTemplate = fs.readFileSync('views/account/profile.jade') + .toString().split(os.EOL); var userModel = fs.readFileSync('models/User.js').toString().split(os.EOL); var app = fs.readFileSync('app.js').toString().split(os.EOL); var secrets = fs.readFileSync('config/secrets.js').toString().split(os.EOL); - var index = passportConfig.indexOf("var FacebookStrategy = require('passport-facebook').Strategy;"); + var index = passportConfig + .indexOf('var FacebookStrategy = require("passport-facebook").Strategy;'); if (facebookCheckbox.checked && index !== -1) { passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with Facebook.'); passportConfig.splice(index, 47); fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL)); - index = loginTemplate.indexOf(" a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook')"); + index = loginTemplate.indexOf( + ' a.btn.btn-block.btn-facebook.btn-social(href="/auth/facebook")' + ); loginTemplate.splice(index, 3); fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL)); - index = profileTemplate.indexOf(" if user.facebook"); + index = profileTemplate.indexOf(' if user.facebook'); profileTemplate.splice(index - 1, 5); - fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL)); + fs.writeFileSync( + 'views/account/profile.jade', + profileTemplate.join(os.EOL) + ); index = userModel.indexOf(' facebook: String,'); userModel.splice(index, 1); fs.writeFileSync('models/User.js', userModel.join(os.EOL)); - index = app.indexOf("app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email', 'user_location'] }));"); + index = app.indexOf([ + 'app.get("/auth/facebook"', + 'passport.authenticate("facebook",', + ' { scope: ["email", "user_location"] }));' + ].join(' ')); + app.splice(index, 4); fs.writeFileSync('app.js', app.join(os.EOL)); } - index = passportConfig.indexOf("var GitHubStrategy = require('passport-github').Strategy;"); + index = passportConfig.indexOf( + "var GitHubStrategy = require('passport-github').Strategy;" + ); if (githubCheckbox.checked && index !== -1) { console.log(index); passportConfig.splice(index, 1); @@ -225,111 +243,162 @@ authForm.on('submit', function() { passportConfig.splice(index, 48); fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL)); - index = loginTemplate.indexOf(" a.btn.btn-block.btn-github.btn-social(href='/auth/github')"); + index = loginTemplate.indexOf( + " a.btn.btn-block.btn-github.btn-social(href='/auth/github')" + ); loginTemplate.splice(index, 3); fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL)); index = profileTemplate.indexOf(' if user.github'); profileTemplate.splice(index - 1, 5); - fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL)); + fs.writeFileSync( + 'views/account/profile.jade', + profileTemplate.join(os.EOL) + ); index = userModel.indexOf(' github: String,'); userModel.splice(index, 1); fs.writeFileSync('models/User.js', userModel.join(os.EOL)); - index = app.indexOf("app.get('/auth/github', passport.authenticate('github'));"); + index = app.indexOf( + 'app.get("/auth/github", passport.authenticate("github"));' + ); app.splice(index, 4); fs.writeFileSync('app.js', app.join(os.EOL)); } - index = passportConfig.indexOf("var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;"); + index = passportConfig.indexOf( + 'var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;' + ); if (googleCheckbox.checked && index !== -1) { passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with Google.'); passportConfig.splice(index, 46); fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL)); - index = loginTemplate.indexOf(" a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google')"); + index = loginTemplate.indexOf( + " a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google')" + ); loginTemplate.splice(index, 3); fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL)); index = profileTemplate.indexOf(' if user.google'); profileTemplate.splice(index - 1, 5); - fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL)); + fs.writeFileSync( + 'views/account/profile.jade', + profileTemplate.join(os.EOL) + ); index = userModel.indexOf(' google: String,'); userModel.splice(index, 1); fs.writeFileSync('models/User.js', userModel.join(os.EOL)); - index = app.indexOf("app.get('/auth/google', passport.authenticate('google', { scope: 'profile email' }));"); + index = app.indexOf( + 'app.get("/auth/google",' + + ' passport.authenticate("google", { scope: "profile email" }));' + ); app.splice(index, 4); fs.writeFileSync('app.js', app.join(os.EOL)); } - index = passportConfig.indexOf("var TwitterStrategy = require('passport-twitter').Strategy;"); + index = passportConfig.indexOf( + 'var TwitterStrategy = require("passport-twitter").Strategy;' + ); if (twitterCheckbox.checked && index !== -1) { passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with Twitter.'); passportConfig.splice(index, 43); fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL)); - index = loginTemplate.indexOf(" a.btn.btn-block.btn-twitter.btn-social(href='/auth/twitter')"); + index = loginTemplate.indexOf( + ' a.btn.btn-block.btn-twitter.btn-social(href="/auth/twitter")' + ); loginTemplate.splice(index, 3); fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL)); index = profileTemplate.indexOf(' if user.twitter'); profileTemplate.splice(index - 1, 5); - fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL)); + fs.writeFileSync( + 'views/account/profile.jade', profileTemplate.join(os.EOL) + ); index = userModel.indexOf(' twitter: String,'); userModel.splice(index, 1); fs.writeFileSync('models/User.js', userModel.join(os.EOL)); - index = app.indexOf("app.get('/auth/twitter', passport.authenticate('twitter'));"); + index = app.indexOf( + "app.get('/auth/twitter', passport.authenticate('twitter'));" + ); app.splice(index, 4); fs.writeFileSync('app.js', app.join(os.EOL)); } - index = passportConfig.indexOf("var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;"); + index = passportConfig.indexOf( + 'var LinkedInStrategy = require("passport-linkedin-oauth2").Strategy;' + ); if (linkedinCheckbox.checked && index !== -1) { passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with LinkedIn.'); passportConfig.splice(index, 47); fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL)); - index = loginTemplate.indexOf(" a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin')"); + index = loginTemplate.indexOf( + ' a.btn.btn-block.btn-linkedin.btn-social(href="/auth/linkedin")' + ); loginTemplate.splice(index, 3); fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL)); index = profileTemplate.indexOf(' if user.linkedin'); profileTemplate.splice(index - 1, 5); - fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL)); + fs.writeFileSync( + 'views/account/profile.jade', + profileTemplate.join(os.EOL) + ); index = userModel.indexOf(' linkedin: String,'); userModel.splice(index, 1); fs.writeFileSync('models/User.js', userModel.join(os.EOL)); - index = app.indexOf("app.get('/auth/linkedin', passport.authenticate('linkedin', { state: 'SOME STATE' }));"); + index = app.indexOf( + 'app.get("/auth/linkedin",', + ' passport.authenticate("linkedin", { state: "SOME STATE" }));' + ); app.splice(index, 4); fs.writeFileSync('app.js', app.join(os.EOL)); } - index = passportConfig.indexOf("var InstagramStrategy = require('passport-instagram').Strategy;"); + index = passportConfig.indexOf( + 'var InstagramStrategy = require("passport-instagram").Strategy;' + ); if (instagramCheckbox.checked && index !== -1) { passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with Instagram.'); passportConfig.splice(index, 43); - fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL)); + fs.writeFileSync( + 'config/passport.js', + passportConfig.join(os.EOL) + ); - index = loginTemplate.indexOf(" a.btn.btn-block.btn-instagram.btn-social(href='/auth/instagram')"); + index = loginTemplate.indexOf( + ' a.btn.btn-block.btn-instagram.btn-social(href="/auth/instagram")' + ); loginTemplate.splice(index, 3); - fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL)); + + fs.writeFileSync( + 'views/account/login.jade', + loginTemplate.join(os.EOL) + ); index = profileTemplate.indexOf(' if user.instagram'); profileTemplate.splice(index - 1, 5); - fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL)); + fs.writeFileSync( + 'views/account/profile.jade', + profileTemplate.join(os.EOL) + ); - index = app.indexOf("app.get('/auth/instagram', passport.authenticate('instagram'));"); + index = app.indexOf( + 'app.get("/auth/instagram", passport.authenticate("instagram"));' + ); app.splice(index, 4); fs.writeFileSync('app.js', app.join(os.EOL)); @@ -339,14 +408,18 @@ authForm.on('submit', function() { home.remove(authForm); home.append(success); - success.setContent('Selected authentication providers have been removed from passportConfig.js, User.js, app.js, login.jade and profile.jade!'); + success.setContent( + 'Selected authentication providers have been removed' + + 'from passportConfig.js, User.js, app.js, login.jade and profile.jade!' + ); success.focus(); screen.render(); }); var authText = blessed.text({ parent: authForm, - content: 'Selecting a checkbox removes an authentication provider. If authentication provider is already removed, no action will be taken.', + content: 'Selecting a checkbox removes an authentication provider.' + + ' If authentication provider is already removed, no action will be taken.', padding: 1, bg: 'magenta', fg: 'white' @@ -460,8 +533,10 @@ var emailForm = blessed.form({ }); emailForm.on('submit', function() { - var contactCtrl = fs.readFileSync('controllers/contact.js').toString().split(os.EOL); - var userCtrl = fs.readFileSync('controllers/user.js').toString().split(os.EOL); + var contactCtrl = fs.readFileSync('controllers/contact.js') + .toString().split(os.EOL); + var userCtrl = fs.readFileSync('controllers/user.js') + .toString().split(os.EOL); var choice = null; if (sendgridRadio.checked) { @@ -472,20 +547,57 @@ emailForm.on('submit', function() { choice = 'Mandrill'; } - var index = contactCtrl.indexOf('var transporter = nodemailer.createTransport({'); + var index = contactCtrl.indexOf( + 'var transporter = nodemailer.createTransport({' + ); contactCtrl.splice(index + 1, 1, " service: '" + choice + "',"); - contactCtrl.splice(index + 3, 1, ' user: secrets.' + choice.toLowerCase() +'.user,'); - contactCtrl.splice(index + 4, 1, ' pass: secrets.' + choice.toLowerCase() + '.password'); + contactCtrl.splice( + index + 3, + 1, + ' user: secrets.' + choice.toLowerCase() + '.user,' + ); + contactCtrl.splice( + index + 4, + 1, + ' pass: secrets.' + choice.toLowerCase() + '.password' + ); fs.writeFileSync('controllers/contact.js', contactCtrl.join(os.EOL)); - index = userCtrl.indexOf(' var transporter = nodemailer.createTransport({'); + index = userCtrl.indexOf( + ' var transporter = nodemailer.createTransport({' + ); userCtrl.splice(index + 1, 1, " service: '" + choice + "',"); - userCtrl.splice(index + 3, 1, ' user: secrets.' + choice.toLowerCase() + '.user,'); - userCtrl.splice(index + 4, 1, ' pass: secrets.' + choice.toLowerCase() + '.password'); - index = userCtrl.indexOf(' var transporter = nodemailer.createTransport({', (index + 1)); - userCtrl.splice(index + 1, 1, " service: '" + choice + "',"); - userCtrl.splice(index + 3, 1, ' user: secrets.' + choice.toLowerCase() + '.user,'); - userCtrl.splice(index + 4, 1, ' pass: secrets.' + choice.toLowerCase() + '.password'); + userCtrl.splice( + index + 3, + 1, + ' user: secrets.' + choice.toLowerCase() + '.user,' + ); + userCtrl.splice( + index + 4, + 1, + ' pass: secrets.' + choice.toLowerCase() + '.password' + ); + + index = userCtrl.indexOf( + ' var transporter = nodemailer.createTransport({', + (index + 1) + ); + userCtrl.splice( + index + 1, + 1, + ' service: "' + choice + '",' + ); + + userCtrl.splice( + index + 3, + 1, + ' user: secrets.' + choice.toLowerCase() + '.user,' + ); + userCtrl.splice( + index + 4, + 1, + ' pass: secrets.' + choice.toLowerCase() + '.password' + ); fs.writeFileSync('controllers/user.js', userCtrl.join(os.EOL)); home.remove(emailForm); @@ -497,7 +609,9 @@ emailForm.on('submit', function() { var emailText = blessed.text({ parent: emailForm, - content: 'Select one of the following email service providers for {underline}contact form{/underline} and {underline}password reset{/underline}.', + content: 'Select one of the following email service providers ' + + 'for {underline}contact form{/underline}' + + ' and {underline}password reset{/underline}.', padding: 1, bg: 'red', fg: 'white', @@ -592,7 +706,12 @@ home.on('select', function(child, index) { case 2: addClusterSupport(); home.append(success); - success.setContent('New file {underline}cluster_app.js{/underline} has been created. Your app is now able to use more than 1 CPU by running {underline}node cluster_app.js{/underline}, which in turn spawns multiple instances of {underline}app.js{/underline}'); + success.setContent([ + 'New file {underline}cluster_app.js{/underline} has been created.', + 'Your app is now able to use more than 1 CPU by running', + '{underline}node cluster_app.js{/underline}, which in turn', + 'spawns multiple instances of {underline}app.js{/underline}' + ].join(' ')); success.focus(); screen.render(); break; diff --git a/test/app.js b/test/app.js index 18bf953a71..324fa4291f 100644 --- a/test/app.js +++ b/test/app.js @@ -47,4 +47,4 @@ describe('GET /random-url', function() { .get('/reset') .expect(404, done); }); -}); \ No newline at end of file +}); diff --git a/test/models.js b/test/models.js index 7fc38b03e0..b4666b5e6e 100644 --- a/test/models.js +++ b/test/models.js @@ -11,7 +11,7 @@ describe('User Model', function() { user.save(function(err) { if (err) return done(err); done(); - }) + }); }); it('should not create a user with the unique email', function(done) {