Major refactor. Add error handlers everywhere.
This commit is contained in:
209
app.js
209
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.
|
||||
|
29
config/bootstrap.js
vendored
29
config/bootstrap.js
vendored
@ -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 );
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
});
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
));
|
||||
*/
|
||||
|
@ -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
|
||||
});
|
||||
});
|
||||
};
|
@ -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
|
||||
});
|
||||
});
|
||||
};
|
@ -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) {
|
||||
|
@ -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'
|
||||
});
|
||||
}
|
||||
};
|
@ -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 <a href='http://www.teamviewer.com/en/index.aspx' target='_blank'>http://www.teamviewer.com/en/index.aspx</a> 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.",
|
||||
"<strong>First, you will pair program on your pair's computer.</strong> 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.",
|
||||
"<strong>Next, you will pair program on your computer.</strong> 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 <a href='http://coderbyte.com/sl/' target='_blank'>http://coderbyte.com/sl/</a>",
|
||||
"Now go to <a href='http://coderbyte.com/CodingArea/Challenges/#easyChals' target='_blank'>http://coderbyte.com/CodingArea/Challenges/#easyChals</a> 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!'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
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"
|
||||
}
|
||||
]);
|
||||
interviewQuestions: function(req, res) {
|
||||
res.json(questions);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
148
controllers/resources.json
Normal file
148
controllers/resources.json
Normal file
@ -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 <a href='http://www.teamviewer.com/en/index.aspx' target='_blank'>http://www.teamviewer.com/en/index.aspx</a> 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.",
|
||||
"<strong>First, you will pair program on your pair's computer.</strong> 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.",
|
||||
"<strong>Next, you will pair program on your computer.</strong> 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 <a href='http://coderbyte.com/sl/' target='_blank'>http://coderbyte.com/sl/</a>",
|
||||
"Now go to <a href='http://coderbyte.com/CodingArea/Challenges/#easyChals' target='_blank'>http://coderbyte.com/CodingArea/Challenges/#easyChals</a> 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."
|
||||
]
|
||||
}
|
@ -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');
|
||||
});
|
||||
};
|
||||
|
20
gulpfile.js
20
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
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
module.exports = mongoose.model('Challenge', challengeSchema);
|
||||
|
347
models/User.js
347
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';
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
215
setup.js
215
setup.js
@ -50,7 +50,8 @@ var footer = blessed.text({
|
||||
fg: 'white',
|
||||
bg: 'blue',
|
||||
tags: true,
|
||||
content: ' {cyan-fg}<Up/Down>{/cyan-fg} moves | {cyan-fg}<Enter>{/cyan-fg} selects | {cyan-fg}<q>{/cyan-fg} exits'
|
||||
content: ' {cyan-fg}<Up/Down>{/cyan-fg} moves |' +
|
||||
' {cyan-fg}<Enter>{/cyan-fg} selects | {cyan-fg}<q>{/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;
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user