From 0b8fed3e2709469d85c3c53c364632b9b7c78766 Mon Sep 17 00:00:00 2001 From: Paul Bienkowski Date: Fri, 20 Nov 2020 11:30:12 +0100 Subject: [PATCH] chore: make everything async instead of manual promise chaining --- _helpers/wrapRoute.js | 9 ++ _middleware/validate-request.js | 8 +- accounts/accounts.controller.js | 157 +++++++++++++++----------------- config/passport.js | 25 ++--- models/Article.js | 11 +-- routes/api/profiles.js | 103 +++++++++++---------- routes/api/tags.js | 16 ++-- routes/api/tracks.js | 77 ++++++++-------- routes/api/users.js | 84 ++++++++--------- 9 files changed, 242 insertions(+), 248 deletions(-) create mode 100644 _helpers/wrapRoute.js diff --git a/_helpers/wrapRoute.js b/_helpers/wrapRoute.js new file mode 100644 index 0000000..3b65377 --- /dev/null +++ b/_helpers/wrapRoute.js @@ -0,0 +1,9 @@ +const wrapRoute = (fn) => async (req, res, next) => { + try { + return await fn(req, res); + } catch (err) { + next(err); + } +}; + +module.exports = wrapRoute; diff --git a/_middleware/validate-request.js b/_middleware/validate-request.js index ece8138..be64dc5 100644 --- a/_middleware/validate-request.js +++ b/_middleware/validate-request.js @@ -1,6 +1,4 @@ -module.exports = validateRequest; - -function validateRequest(req, next, schema) { +const validateRequest = (schema) => (req, res, next) => { console.log('validateRequest'); const options = { @@ -16,4 +14,6 @@ function validateRequest(req, next, schema) { req.body = value; next(); } -} +}; + +module.exports = validateRequest; diff --git a/accounts/accounts.controller.js b/accounts/accounts.controller.js index 7b0585e..afa1785 100644 --- a/accounts/accounts.controller.js +++ b/accounts/accounts.controller.js @@ -1,93 +1,78 @@ -const express = require('express'); +const express = require('express'); const router = express.Router(); const Joi = require('joi'); +const wrapRoute = require('../_helpers/wrapRoute'); const validateRequest = require('../_middleware/validate-request'); const accountService = require('./account.service'); -// routes -router.post('/register', registerSchema, register); -router.post('/verify-email', verifyEmailSchema, verifyEmail); -router.post('/forgot-password', forgotPasswordSchema, forgotPassword); -router.post('/validate-reset-token', validateResetTokenSchema, validateResetToken); -router.post('/reset-password', resetPasswordSchema, resetPassword); +router.post( + '/register', + validateRequest( + Joi.object({ + username: Joi.string().required(), + email: Joi.string().email().required(), + password: Joi.string().min(6).required(), + confirmPassword: Joi.string().valid(Joi.ref('password')).required(), + }), + ), + wrapRoute(async (req, res) => { + await accountService.register(req.body, req.get('origin')); + res.json({ message: 'Registration successful, please check your email for verification instructions' }); + }), +); + +router.post( + '/verify-email', + validateRequest( + Joi.object({ + token: Joi.string().required(), + }), + ), + wrapRoute(async (req, res) => { + await accountService.verifyEmail(req.body); + res.json({ message: 'Verification successful, you can now login' }); + }), +); + +router.post( + '/forgot-password', + validateRequest( + Joi.object({ + email: Joi.string().email().required(), + }), + ), + wrapRoute(async (req, res) => { + await accountService.forgotPassword(req.body, req.get('origin')); + res.json({ message: 'Please check your email for password reset instructions' }); + }), +); + +router.post( + '/validate-reset-token', + validateRequest( + Joi.object({ + token: Joi.string().required(), + }), + ), + wrapRoute(async (req, res) => { + await accountService.validateResetToken(req.body); + res.json({ message: 'Token is valid' }); + }), +); + +router.post( + '/reset-password', + validateRequest( + Joi.object({ + token: Joi.string().required(), + password: Joi.string().min(6).required(), + confirmPassword: Joi.string().valid(Joi.ref('password')).required(), + }), + ), + wrapRoute(async (req, res) => { + await accountService.resetPassword(req.body); + res.json({ message: 'Password reset successful, you can now login' }); + }), +); module.exports = router; - -function registerSchema(req, res, next) { - const schema = Joi.object({ - username: Joi.string().required(), - email: Joi.string().email().required(), - password: Joi.string().min(6).required(), - confirmPassword: Joi.string().valid(Joi.ref('password')).required(), - }); - - validateRequest(req, next, schema); -} - -function register(req, res, next) { - accountService - .register(req.body, req.get('origin')) - .then(() => res.json({ message: 'Registration successful, please check your email for verification instructions' })) - .catch((err) => { - console.log(err); - next(err); - }); -} - -function verifyEmailSchema(req, res, next) { - const schema = Joi.object({ - token: Joi.string().required(), - }); - validateRequest(req, next, schema); -} - -function verifyEmail(req, res, next) { - accountService - .verifyEmail(req.body) - .then(() => res.json({ message: 'Verification successful, you can now login' })) - .catch(next); -} - -function forgotPasswordSchema(req, res, next) { - const schema = Joi.object({ - email: Joi.string().email().required(), - }); - validateRequest(req, next, schema); -} - -function forgotPassword(req, res, next) { - accountService - .forgotPassword(req.body, req.get('origin')) - .then(() => res.json({ message: 'Please check your email for password reset instructions' })) - .catch(next); -} - -function validateResetTokenSchema(req, res, next) { - const schema = Joi.object({ - token: Joi.string().required(), - }); - validateRequest(req, next, schema); -} - -function validateResetToken(req, res, next) { - accountService - .validateResetToken(req.body) - .then(() => res.json({ message: 'Token is valid' })) - .catch(next); -} - -function resetPasswordSchema(req, res, next) { - const schema = Joi.object({ - token: Joi.string().required(), - password: Joi.string().min(6).required(), - confirmPassword: Joi.string().valid(Joi.ref('password')).required(), - }); - validateRequest(req, next, schema); -} - -function resetPassword(req, res, next) { - accountService - .resetPassword(req.body) - .then(() => res.json({ message: 'Password reset successful, you can now login' })) - .catch(next); -} diff --git a/config/passport.js b/config/passport.js index 91bd44f..5e11f0b 100644 --- a/config/passport.js +++ b/config/passport.js @@ -9,20 +9,21 @@ passport.use( usernameField: 'user[email]', passwordField: 'user[password]', }, - function (email, password, done) { - User.findOne({ email: email }) - .then(function (user) { - if (!user || !user.validPassword(password)) { - return done(null, false, { errors: { 'email or password': 'is invalid' } }); - } + async function (email, password, done) { + try { + const user = await User.findOne({ email: email }); + if (!user || !user.validPassword(password)) { + return done(null, false, { errors: { 'email or password': 'is invalid' } }); + } - if (user && user.needsEmailValidation) { - return done(null, false, { errors: { 'E-Mail-Bestätigung': 'noch nicht erfolgt' } }); - } + if (user.needsEmailValidation) { + return done(null, false, { errors: { 'E-Mail-Bestätigung': 'noch nicht erfolgt' } }); + } - return done(null, user); - }) - .catch(done); + return done(null, user); + } catch (err) { + done(err); + } }, ), ); diff --git a/models/Article.js b/models/Article.js index 9ebb5df..f08a008 100644 --- a/models/Article.js +++ b/models/Article.js @@ -31,14 +31,9 @@ ArticleSchema.methods.slugify = function () { this.slug = slug(this.title) + '-' + ((Math.random() * Math.pow(36, 6)) | 0).toString(36); }; -ArticleSchema.methods.updateFavoriteCount = function () { - const article = this; - - return User.count({ favorites: { $in: [article._id] } }).then(function (count) { - article.favoritesCount = count; - - return article.save(); - }); +ArticleSchema.methods.updateFavoriteCount = async function () { + this.favoritesCount = await User.count({ favorites: { $in: [this._id] } }); + return await this.save(); }; ArticleSchema.methods.toJSONFor = function (user) { diff --git a/routes/api/profiles.js b/routes/api/profiles.js index 16932e6..ae98b59 100644 --- a/routes/api/profiles.js +++ b/routes/api/profiles.js @@ -1,67 +1,72 @@ const router = require('express').Router(); const mongoose = require('mongoose'); const User = mongoose.model('User'); +const wrapRoute = require('../../_helpers/wrapRoute'); const auth = require('../auth'); // Preload user profile on routes with ':username' -router.param('username', function (req, res, next, username) { - User.findOne({ username: username }) - .then(function (user) { - if (!user) { - return res.sendStatus(404); - } +router.param('username', async function (req, res, next, username) { + try { + const user = await User.findOne({ username: username }); + if (!user) { + return res.sendStatus(404); + } - req.profile = user; + req.profile = user; - return next(); - }) - .catch(next); -}); - -router.get('/:username', auth.optional, function (req, res, next) { - if (req.payload) { - User.findById(req.payload.id).then(function (user) { - if (!user) { - return res.json({ profile: req.profile.toProfileJSONFor(false) }); - } - - return res.json({ profile: req.profile.toProfileJSONFor(user) }); - }); - } else { - return res.json({ profile: req.profile.toProfileJSONFor(false) }); + return next(); + } catch (err) { + next(err); } }); -router.post('/:username/follow', auth.required, function (req, res, next) { - const profileId = req.profile._id; +router.get( + '/:username', + auth.optional, + wrapRoute(async (req, res) => { + if (!req.payload) { + return res.json({ profile: req.profile.toProfileJSONFor(false) }); + } - User.findById(req.payload.id) - .then(function (user) { - if (!user) { - return res.sendStatus(401); - } + const user = await User.findById(req.payload.id); + if (!user) { + return res.json({ profile: req.profile.toProfileJSONFor(false) }); + } - return user.follow(profileId).then(function () { - return res.json({ profile: req.profile.toProfileJSONFor(user) }); - }); - }) - .catch(next); -}); + return res.json({ profile: req.profile.toProfileJSONFor(user) }); + }), +); -router.delete('/:username/follow', auth.required, function (req, res, next) { - const profileId = req.profile._id; +router.post( + '/:username/follow', + auth.required, + wrapRoute(async (req, res) => { + const profileId = req.profile._id; - User.findById(req.payload.id) - .then(function (user) { - if (!user) { - return res.sendStatus(401); - } + const user = await User.findById(req.payload.id); + if (!user) { + return res.sendStatus(401); + } - return user.unfollow(profileId).then(function () { - return res.json({ profile: req.profile.toProfileJSONFor(user) }); - }); - }) - .catch(next); -}); + await user.follow(profileId); + return res.json({ profile: req.profile.toProfileJSONFor(user) }); + }), +); + +router.delete( + '/:username/follow', + auth.required, + wrapRoute(async (req, res) => { + const profileId = req.profile._id; + + const user = User.findById(req.payload.id); + if (!user) { + return res.sendStatus(401); + } + + await user.unfollow(profileId); + return res.json({ profile: req.profile.toProfileJSONFor(user) }); + }), +); module.exports = router; diff --git a/routes/api/tags.js b/routes/api/tags.js index 858e76a..b357df2 100644 --- a/routes/api/tags.js +++ b/routes/api/tags.js @@ -1,15 +1,15 @@ const router = require('express').Router(); const mongoose = require('mongoose'); const Track = mongoose.model('Track'); +const wrapRoute = require('../../_helpers/wrapRoute'); // return a list of tags -router.get('/', function (req, res, next) { - Track.find() - .distinct('tagList') - .then(function (tags) { - return res.json({ tags: tags }); - }) - .catch(next); -}); +router.get( + '/', + wrapRoute(async (req, res) => { + const tags = await Track.find().distinct('tagList'); + return res.json({ tags: tags }); + }), +); module.exports = router; diff --git a/routes/api/tracks.js b/routes/api/tracks.js index 6a52aa4..1a1da6d 100644 --- a/routes/api/tracks.js +++ b/routes/api/tracks.js @@ -8,14 +8,7 @@ const auth = require('../auth'); const currentTracks = new Map(); const TrackInfo = require('../../logic/TrackInfo'); const { addPointsToTrack } = require('../../logic/tracks'); - -const wrapRoute = (fn) => async (req, res, next) => { - try { - return await fn(req, res); - } catch (err) { - next(err); - } -}; +const wrapRoute = require('../../_helpers/wrapRoute'); // Preload track objects on routes with ':track' router.param('track', async (req, res, next, slug) => { @@ -296,42 +289,46 @@ router.get( ); // update track -router.put('/:track', auth.required, async function (req, res, next) { - const user = await User.findById(req.payload.id); +router.put( + '/:track', + auth.required, + wrapRoute(async (req, res) => { + const user = await User.findById(req.payload.id); - if (req.track.author._id.toString() !== req.payload.id.toString()) { - return res.sendStatus(403); - } - - if (typeof req.body.track.title !== 'undefined') { - req.track.title = req.body.track.title; - } - - if (typeof req.body.track.description !== 'undefined') { - req.track.description = req.body.track.description; - } - - if (req.body.track.body && req.body.track.body.trim()) { - req.track.body = req.body.track.body.trim(); - - let trackData = await TrackData.findById(req.track.trackData); - if (!trackData) { - trackData = new TrackData(); - req.track.trackData = trackData._id; + if (req.track.author._id.toString() !== req.payload.id.toString()) { + return res.sendStatus(403); } - trackData.points = []; - addPointsToTrack({ trackData }, req.track.body); - await trackData.save(); - } - if (typeof req.body.track.tagList !== 'undefined') { - req.track.tagList = req.body.track.tagList; - } - req.track.visible = req.body.track.visible; + if (typeof req.body.track.title !== 'undefined') { + req.track.title = req.body.track.title; + } - const track = await req.track.save(); - return res.json({ track: track.toJSONFor(user) }); -}); + if (typeof req.body.track.description !== 'undefined') { + req.track.description = req.body.track.description; + } + + if (req.body.track.body && req.body.track.body.trim()) { + req.track.body = req.body.track.body.trim(); + + let trackData = await TrackData.findById(req.track.trackData); + if (!trackData) { + trackData = new TrackData(); + req.track.trackData = trackData._id; + } + trackData.points = []; + addPointsToTrack({ trackData }, req.track.body); + await trackData.save(); + } + + if (typeof req.body.track.tagList !== 'undefined') { + req.track.tagList = req.body.track.tagList; + } + req.track.visible = req.body.track.visible; + + const track = await req.track.save(); + return res.json({ track: track.toJSONFor(user) }); + }), +); // delete track router.delete( diff --git a/routes/api/users.js b/routes/api/users.js index 14c2a5b..0aa35fa 100644 --- a/routes/api/users.js +++ b/routes/api/users.js @@ -2,53 +2,55 @@ const mongoose = require('mongoose'); const router = require('express').Router(); const passport = require('passport'); const User = mongoose.model('User'); +const wrapRoute = require('../../_helpers/wrapRoute'); const auth = require('../auth'); -router.get('/user', auth.required, function (req, res, next) { - User.findById(req.payload.id) - .then(function (user) { - if (!user) { - return res.sendStatus(401); - } +router.get( + '/user', + auth.required, + wrapRoute(async (req, res) => { + const user = await User.findById(req.payload.id); + if (!user) { + return res.sendStatus(401); + } - return res.json({ user: user.toAuthJSON() }); - }) - .catch(next); -}); + return res.json({ user: user.toAuthJSON() }); + }), +); -router.put('/user', auth.required, function (req, res, next) { - User.findById(req.payload.id) - .then(function (user) { - if (!user) { - return res.sendStatus(401); - } +router.put( + '/user', + auth.required, + wrapRoute(async (req, res) => { + const user = await User.findById(req.payload.id); + if (!user) { + return res.sendStatus(401); + } - // only update fields that were actually passed... - if (typeof req.body.user.username !== 'undefined') { - user.username = req.body.user.username; - } - if (typeof req.body.user.email !== 'undefined') { - user.email = req.body.user.email; - } - if (typeof req.body.user.bio !== 'undefined') { - user.bio = req.body.user.bio; - } - if (typeof req.body.user.image !== 'undefined') { - user.image = req.body.user.image; - } - if (typeof req.body.user.areTracksVisibleForAll !== 'undefined') { - user.areTracksVisibleForAll = req.body.user.areTracksVisibleForAll; - } - if (typeof req.body.user.password === 'string' && req.body.user.password !== '') { - user.setPassword(req.body.user.password); - } + // only update fields that were actually passed... + if (typeof req.body.user.username !== 'undefined') { + user.username = req.body.user.username; + } + if (typeof req.body.user.email !== 'undefined') { + user.email = req.body.user.email; + } + if (typeof req.body.user.bio !== 'undefined') { + user.bio = req.body.user.bio; + } + if (typeof req.body.user.image !== 'undefined') { + user.image = req.body.user.image; + } + if (typeof req.body.user.areTracksVisibleForAll !== 'undefined') { + user.areTracksVisibleForAll = req.body.user.areTracksVisibleForAll; + } + if (typeof req.body.user.password === 'string' && req.body.user.password !== '') { + user.setPassword(req.body.user.password); + } - return user.save().then(function () { - return res.json({ user: user.toAuthJSON() }); - }); - }) - .catch(next); -}); + await user.save(); + return res.json({ user: user.toAuthJSON() }); + }), +); router.post('/users/login', function (req, res, next) { if (!req.body.user.email) {