diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 3b546826e9..48e8721209 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -147,12 +147,14 @@ exports.returnCurrentChallenge = function(req, res, next) { exports.returnIndividualChallenge = function(req, res, next) { var dashedName = req.params.challengeName; - var challengeName = /^(bonfire|waypoint|zipline|basejump)/i.test(dashedName) ? dashedName - .replace(/\-/g, ' ') - .split(' ') - .slice(1) - .join(' ') - : dashedName.replace(/\-/g, ' '); + var challengeName = + (/^(bonfire|waypoint|zipline|basejump)/i).test(dashedName) ? + dashedName + .replace(/\-/g, ' ') + .split(' ') + .slice(1) + .join(' ') : + dashedName.replace(/\-/g, ' '); Challenge.find({'name': new RegExp(challengeName, 'i')}, function(err, challengeFromMongo) { @@ -175,23 +177,21 @@ exports.returnIndividualChallenge = function(req, res, next) { .replace(/[^a-z0-9\-\.]/gi, ''); if (dashedNameFull !== dashedName) { return res.redirect('../challenges/' + dashedNameFull); - } else { - if (req.user) { - req.user.currentChallenge = { - challengeId: challenge._id, - challengeName: challenge.name, - challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds). - map(function (key) { - return challengeMapWithIds[key] - .filter(function (elem) { - return String(elem) === String(challenge._id); - }).map(function () { - return key; - }); - }) - )) - }; - } + } else if (req.user) { + req.user.currentChallenge = { + challengeId: challenge._id, + challengeName: challenge.name, + challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds). + map(function (key) { + return challengeMapWithIds[key] + .filter(function (elem) { + return String(elem) === String(challenge._id); + }).map(function () { + return key; + }); + }) + )) + }; } var challengeType = { @@ -379,13 +379,11 @@ exports.completedBonfire = function (req, res, next) { return next(err); } if (user && paired) { - res.send(true); + return res.send(true); } }); - } else { - if (user) { - res.send(true); - } + } else if (user) { + res.send(true); } }); } diff --git a/server/boot/challengeMap.js b/server/boot/challengeMap.js index c61c6785a0..6a4a79dba2 100644 --- a/server/boot/challengeMap.js +++ b/server/boot/challengeMap.js @@ -1,5 +1,5 @@ var R = require('ramda'), - debug = require('debug')('freecc:cntr:challengeMap'), //eslint-disable-line + // debug = require('debug')('freecc:cntr:challengeMap'), User = require('../../models/User'), resources = require('./../resources/resources'), middleware = require('../resources/middleware'), @@ -19,8 +19,6 @@ router.get('/about', function(req, res) { res.redirect(301, '/map'); }); - - function challengeMap(req, res, next) { var completedList = []; diff --git a/server/boot/fieldGuide.js b/server/boot/fieldGuide.js index c74e993dbd..f433bf3ed2 100644 --- a/server/boot/fieldGuide.js +++ b/server/boot/fieldGuide.js @@ -1,7 +1,7 @@ var R = require('ramda'), + // debug = require('debug')('freecc:fieldguides'), FieldGuide = require('./../../models/FieldGuide'), - resources = require('./../resources/resources'), - debug = require('debug')('freecc:fieldguides'); + resources = require('./../resources/resources'); exports.returnIndividualFieldGuide = function(req, res, next) { var dashedName = req.params.fieldGuideName; diff --git a/server/boot/home.js b/server/boot/home.js index 44efd6c958..9360c0823b 100644 --- a/server/boot/home.js +++ b/server/boot/home.js @@ -5,8 +5,6 @@ var router = express.Router(); router.get('/', index); - - function index(req, res, next) { if (req.user && !req.user.profile.picture) { req.user.profile.picture = diff --git a/server/boot/jobs.js b/server/boot/jobs.js index 1802f4cdf3..d92843a465 100644 --- a/server/boot/jobs.js +++ b/server/boot/jobs.js @@ -1,6 +1,4 @@ -var moment = require('moment'), - Job = require('./../../models/Job'), - resources = require('./../resources/resources'); +var Job = require('./../../models/Job'); exports.jobsDirectory = function(req, res, next) { Job.find({}, function(err, jobs) { diff --git a/server/boot/nonprofits.js b/server/boot/nonprofits.js index 176c50cfff..12e0dff04f 100644 --- a/server/boot/nonprofits.js +++ b/server/boot/nonprofits.js @@ -1,9 +1,13 @@ -var moment = require('moment'), - Nonprofit = require('./../../models/Nonprofit'), - resources = require('./../resources/resources'); +var express = require('express'), + Nonprofit = require('./../../models/Nonprofit'); -exports.nonprofitsDirectory = function(req, res, next) { - Nonprofit.find({estimatedHours: { $gt: 0 } }, function(err, nonprofits) { +var router = express.Router(); + +router.get('/nonprofits/directory', nonprofitsDirectory); +router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit); + +function nonprofitsDirectory(req, res, next) { + Nonprofit.find({ estimatedHours: { $gt: 0 } }, function(err, nonprofits) { if (err) { return next(err); } res.render('nonprofits/directory', { @@ -11,9 +15,9 @@ exports.nonprofitsDirectory = function(req, res, next) { nonprofits: nonprofits }); }); -}; +} -exports.returnIndividualNonprofit = function(req, res, next) { +function returnIndividualNonprofit(req, res, next) { var dashedName = req.params.nonprofitName; var nonprofitName = dashedName.replace(/\-/g, ' '); @@ -91,9 +95,10 @@ exports.returnIndividualNonprofit = function(req, res, next) { }); } ); -}; +} -exports.interestedInNonprofit = function(req, res, next) { +/* +function interestedInNonprofit(req, res, next) { if (req.user) { Nonprofit.findOne( { name: new RegExp(req.params.nonprofitName.replace(/-/, ' '), 'i') }, @@ -115,4 +120,7 @@ exports.interestedInNonprofit = function(req, res, next) { } ); } -}; +} +*/ + +module.exports = router; diff --git a/server/boot/redirects.js b/server/boot/redirects.js index 674ae9750b..c5422b6986 100644 --- a/server/boot/redirects.js +++ b/server/boot/redirects.js @@ -1,9 +1,11 @@ var express = require('express'); var router = express.Router(); - router.get('/nonprofit-project-instructions', function(req, res) { - res.redirect(301, '/field-guide/how-do-free-code-camp\'s-nonprofit-projects-work'); + res.redirect( + 301, + '/field-guide/how-do-free-code-camp\'s-nonprofit-projects-work' + ); }); router.get('/agile', function(req, res) { diff --git a/server/boot/story.js b/server/boot/story.js index 0902c756ab..f69a2a7f32 100755 --- a/server/boot/story.js +++ b/server/boot/story.js @@ -1,18 +1,16 @@ -/* eslint-disable no-catch-shadow, no-unused-vars */ -var R = require('ramda'), - debug = require('debug')('freecc:cntr:story'), - Story = require('./../../models/Story'), - Comment = require('./../../models/Comment'), - User = require('./../../models/User'), - moment = require('moment'), - resources = require('./../resources/resources'), - mongodb = require('mongodb'), - MongoClient = mongodb.MongoClient, - secrets = require('../../config/secrets'), - nodemailer = require('nodemailer'), - sanitizeHtml = require('sanitize-html'), - express = require('express'), - router = express.Router(); +var nodemailer = require('nodemailer'), + sanitizeHtml = require('sanitize-html'), + express = require('express'), + moment = require('moment'), + // debug = require('debug')('freecc:cntr:story'), + Story = require('./../../models/Story'), + Comment = require('./../../models/Comment'), + User = require('./../../models/User'), + resources = require('./../resources/resources'), + mongodb = require('mongodb'), + MongoClient = mongodb.MongoClient, + secrets = require('../../config/secrets'), + router = express.Router(); router.get('/stories/hotStories', hotJSON); router.get('/stories/recentStories', recentJSON); @@ -88,6 +86,8 @@ function submitNew(req, res) { }); } +/* + * no used anywhere function search(req, res) { return res.render('stories/index', { title: 'Search the archives of Camper News', @@ -101,6 +101,7 @@ function recent(req, res) { page: 'recent' }); } +*/ function preSubmit(req, res) { @@ -250,16 +251,16 @@ function upvote(req, res, next) { ); story.markModified('rank'); story.save(); + // NOTE(Berks): This logic is full of wholes and race conditions + // this could be the source of many 'can't set headers after they are sent' + // errors. This needs cleaning User.findOne({'_id': story.author.userId}, function(err, user) { - if (err) { - return next(err); - } + if (err) { return next(err); } + user.progressTimestamps.push(Date.now() || 0); - user.save(function (err, user) { - req.user.save(function (err, user) { - if (err) { - return next(err); - } + user.save(function (err) { + req.user.save(function (err) { + if (err) { return next(err); } }); req.user.progressTimestamps.push(Date.now() || 0); if (err) { @@ -351,13 +352,18 @@ function storySubmission(req, res, next) { .replace(/\s+/g, ' ') .toLowerCase() .trim(); + var link = data.link; + if (link.search(/^https?:\/\//g) === -1) { link = 'http://' + link; } - Story.count({ storyLink: new RegExp('^' + storyLink + '(?: [0-9]+)?$', 'i')}, function (err, storyCount) { + + Story.count({ + storyLink: new RegExp('^' + storyLink + '(?: [0-9]+)?$', 'i') + }, function (err, storyCount) { if (err) { - return res.status(500); + return next(err); } // if duplicate storyLink add unique number @@ -397,17 +403,17 @@ function storySubmission(req, res, next) { }); story.save(function (err) { if (err) { - return res.status(500); + return next(err); } req.user.progressTimestamps.push(Date.now() || 0); - req.user.save(function (err, user) { + req.user.save(function (err) { if (err) { return next(err); } + res.send(JSON.stringify({ + storyLink: story.storyLink.replace(/\s+/g, '-').toLowerCase() + })); }); - res.send(JSON.stringify({ - storyLink: story.storyLink.replace(/\s+/g, '-').toLowerCase() - })); }); }); } @@ -531,7 +537,9 @@ function commentSave(comment, Context, res, next) { try { // Based on the context retrieve the parent // object of the comment (Story/Comment) - Context.find({'_id': data.associatedPost}, function (err, associatedContext) { + Context.find({ + '_id': data.associatedPost + }, function (err, associatedContext) { if (err) { return next(err); } @@ -546,7 +554,9 @@ function commentSave(comment, Context, res, next) { }); } // Find the author of the parent object - User.findOne({'profile.username': associatedContext.author.username}, function(err, recipient) { + User.findOne({ + 'profile.username': associatedContext.author.username + }, function(err, recipient) { if (err) { return next(err); } diff --git a/server/boot/user.js b/server/boot/user.js index affcf95e51..f6ad37002a 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -37,7 +37,8 @@ router.post('/account/password', postUpdatePassword); router.post('/account/delete', postDeleteAccount); router.get('/account/unlink/:provider', getOauthUnlink); router.get('/account', getAccount); -router.get('/:username', returnUser); // Ensure this is the last route! +// Ensure this is the last route! +router.get('/:username', returnUser); /** * GET /signin @@ -273,6 +274,7 @@ function checkUniqueUsername (req, res, next) { /** * Existing username check */ + function checkExistingUsername (req, res, next) { User.count( { 'profile.username': req.params.username.toLowerCase() }, @@ -748,7 +750,8 @@ function postForgot (req, res, next) { } user.resetPasswordToken = token; - user.resetPasswordExpires = Date.now() + 3600000; // 1 hour + // 3600000 = 1 hour + user.resetPasswordExpires = Date.now() + 3600000; user.save(function(err) { if (err) { return done(err); } diff --git a/server/boot/utility.js b/server/boot/utility.js index 2565d64b16..3e347b9242 100644 --- a/server/boot/utility.js +++ b/server/boot/utility.js @@ -151,13 +151,22 @@ function getPair(req, res) { var userName = req.user.profile.username; var challenge = req.body.payload.challenge; slack.send({ - text: 'Anyone want to pair with *@' + userName + '* on ' + challenge + - '?\nMake sure you install Screen Hero here: ' + - 'http://freecodecamp.com/field-guide/how-do-i-install-screenhero\n' + - 'Then start your pair program session with *@' + userName + - '* by typing \"/hero @' + userName + '\" into Slack.\n And *@'+ userName + - '*, be sure to launch Screen Hero, then keep coding. ' + - 'Another camper may pair with you soon.', + text: [ + 'Anyone want to pair with *@', + userName, + '* on ', + challenge, + '?\nMake sure you install Screen Hero here: ', + 'http://freecodecamp.com/field-guide/how-do-i-install-screenhero\n', + 'Then start your pair program session with *@', + userName, + '* by typing \"/hero @', + userName, + '\" into Slack.\n And *@', + userName, + '*, be sure to launch Screen Hero, then keep coding. ', + 'Another camper may pair with you soon.' + ].join(''), channel: '#letspair', username: 'Companion Cube', 'icon_url': 'https://lh3.googleusercontent.com/-f6xDPDV2rPE/AAAAAAAAAAI/' + diff --git a/server/server.js b/server/server.js index 107d155398..e826fa5bc5 100755 --- a/server/server.js +++ b/server/server.js @@ -6,9 +6,7 @@ process.on('uncaughtException', function (err) { err.message ); console.error(err.stack); - /* eslint-disable no-process-exit */ - process.exit(1); - /* eslint-enable no-process-exit */ + process.exit(1); // eslint-disable-line }); var express = require('express'), @@ -21,32 +19,29 @@ var express = require('express'), methodOverride = require('method-override'), bodyParser = require('body-parser'), helmet = require('helmet'), - //frameguard = require('frameguard'), - //csp = require('helmet-csp'), MongoStore = require('connect-mongo')(session), flash = require('express-flash'), path = require('path'), mongoose = require('mongoose'), passport = require('passport'), expressValidator = require('express-validator'), - request = require('request'), + // request = require('request'), forceDomain = require('forcedomain'), lessMiddleware = require('less-middleware'), /** - * Controllers (route handlers). + * routers. */ - homeController = require('./boot/home'), - resourcesController = require('./resources/resources'), - userController = require('./boot/user'), - nonprofitController = require('./boot/nonprofits'), - fieldGuideController = require('./boot/fieldGuide'), - challengeMapController = require('./boot/challengeMap'), - challengeController = require('./boot/challenge'), - jobsController = require('./boot/jobs'), - redirects = require('./boot/redirects'), - utility = require('./boot/utility'), - storyController = require('./boot/story'), + homeRouter = require('./boot/home'), + resourcesRouter = require('./resources/resources'), + userRouter = require('./boot/user'), + fieldGuideRouter = require('./boot/fieldGuide'), + challengeMapRouter = require('./boot/challengeMap'), + challengeRouter = require('./boot/challenge'), + jobsRouter = require('./boot/jobs'), + redirectsRouter = require('./boot/redirects'), + utilityRouter = require('./boot/utility'), + storyRouter = require('./boot/story'), /** * API keys and Passport configuration. @@ -85,10 +80,10 @@ if (process.env.NODE_ENV === 'production') { } app.use(compress()); -app.use(lessMiddleware(__dirname + '/public')); +app.use(lessMiddleware(path.join(__dirname, '/public'))); app.use(logger('dev')); app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: true})); +app.use(bodyParser.urlencoded({ extended: true })); app.use(expressValidator({ customValidators: { matchRegex: function (param, regex) { @@ -189,9 +184,12 @@ app.use(helmet.csp({ '*.twitter.com', '*.ghbtns.com' ].concat(trusted), - reportOnly: false, // set to true if you only want to report errors - setAllHeaders: false, // set to true if you want to set all headers - safari5: false // set to true if you want to force buggy CSP in Safari 5 + // set to true if you only want to report errors + reportOnly: false, + // set to true if you want to set all headers + setAllHeaders: false, + // set to true if you want to force buggy CSP in Safari 5 + safari5: false })); app.use(function (req, res, next) { @@ -200,7 +198,9 @@ app.use(function (req, res, next) { next(); }); -app.use(express.static(__dirname + '/public', {maxAge: 86400000 })); +app.use( + express.static(path.join(__dirname, '/public'), { maxAge: 86400000 }) +); app.use(function (req, res, next) { // Remember original destination before login. @@ -214,89 +214,50 @@ app.use(function (req, res, next) { next(); }); +// add sub routers +app.use(homeRouter); +app.use(resourcesRouter); +app.use(userRouter); +app.use(fieldGuideRouter); +app.use(challengeMapRouter); +app.use(challengeRouter); +app.use(jobsRouter); +app.use(redirectsRouter); +app.use(utilityRouter); +app.use(storyRouter); - - - -/** - * Nonprofit Project routes. - */ - -app.get('/nonprofits/directory', nonprofitController.nonprofitsDirectory); - -app.get( - '/nonprofits/:nonprofitName', - nonprofitController.returnIndividualNonprofit -); - +/* app.get( '/jobs', jobsController.jobsDirectory ); - - - - - - -/** - * Camper News routes. - */ - - - app.all('/account', passportConf.isAuthenticated); - - -/** - * API routes - */ - - -/** - * Field Guide related routes - */ app.get('/field-guide/all-articles', fieldGuideController.showAllFieldGuides); - app.get('/field-guide/:fieldGuideName', fieldGuideController.returnIndividualFieldGuide ); - app.get('/field-guide/', fieldGuideController.returnNextFieldGuide); - app.post('/completed-field-guide/', fieldGuideController.completedFieldGuide); - - -/** - * Challenge related routes - */ - app.get('/challenges/next-challenge', userController.userMigration, challengeController.returnNextChallenge ); - app.get( '/challenges/:challengeName', userController.userMigration, challengeController.returnIndividualChallenge ); - app.get('/challenges/', userController.userMigration, challengeController.returnCurrentChallenge); + // todo refactor these routes app.post('/completed-challenge/', challengeController.completedChallenge); - app.post('/completed-zipline-or-basejump', challengeController.completedZiplineOrBasejump); - app.post('/completed-bonfire', challengeController.completedBonfire); - -// Unique Check API route - - +*/ /** * OAuth sign-in routes. @@ -369,8 +330,9 @@ app.get( if (process.env.NODE_ENV === 'development') { app.use(errorHandler({ log: true })); } else { - // error handling in production - app.use(function(err, req, res, next) { + // error handling in production disabling eslint due to express parity rules + // for error handlers + app.use(function(err, req, res, next) { // eslint-disable-line // respect err.status if (err.status) {