From 55defd3dbaa2adb27486610b104d7a12f423a0a9 Mon Sep 17 00:00:00 2001 From: Yashar Fakhari Date: Mon, 24 Jul 2023 01:03:34 -0700 Subject: [PATCH] Mongoose 7 upgrade --- .env.example | 4 - config/passport.js | 902 ++++++++++++++++++++------------------------ controllers/api.js | 4 +- controllers/user.js | 120 +++--- package-lock.json | 141 ++++--- package.json | 4 +- 6 files changed, 569 insertions(+), 606 deletions(-) diff --git a/.env.example b/.env.example index d9e9f806f8..35572a8734 100644 --- a/.env.example +++ b/.env.example @@ -59,12 +59,9 @@ TUMBLR_SECRET=QpCTs5IMMCsCImwdvFiqyGtIZwowF5o3UXonjPoNp4HVtJAL4o FOURSQUARE_ID=2STROLSFBMZLAHG3IBA141EM2HGRF0IRIBB4KXMOGA2EH3JG FOURSQUARE_SECRET=UAABFAWTIHIUFBL0PDC3TDMSXJF2GTGWLD3BES1QHXKAIYQB -FOURSQUARE_REDIRECT_URL=http://localhost:8080/auth/foursquare/callback PAYPAL_ID=AdGE8hDyixVoHmbhASqAThfbBcrbcgiJPBwlAM7u7Kfq3YU-iPGc6BXaTppt PAYPAL_SECRET=EPN0WxB5PaRaumTB1ZpCuuTqLqIlF6_EWUcAbZV99Eu86YeNBVm9KVsw_Ez5 -PAYPAL_RETURN_URL=http://localhost:8080/api/paypal/success -PAYPAL_CANCEL_URL=http://localhost:8080/api/paypal/cancel RECAPTCHA_SITE_KEY=JKHGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxJG RECAPTCHA_SECRET_KEY=87ehxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx3298 @@ -73,7 +70,6 @@ LOB_KEY=test_814e892b199d65ef6dbb3e4ad24689559ca PINTEREST_ID=4819282851912494691 PINTEREST_SECRET=b32f578ad83d94c058c6682329220feda7e5817e043a1cc4a5a1e28f51c70301 -PINTEREST_REDIRECT_URL=https://localhost:8080/auth/pinterest/callback GOOGLE_MAP_API_KEY=google-map-api-key diff --git a/config/passport.js b/config/passport.js index 9355b0104d..cbd7415a2b 100644 --- a/config/passport.js +++ b/config/passport.js @@ -22,32 +22,35 @@ passport.serializeUser((user, done) => { done(null, user.id); }); -passport.deserializeUser((id, done) => { - User.findById(id, (err, user) => { - done(err, user); - }); +passport.deserializeUser(async (id, done) => { + try { + return done(null, await User.findById(id)); + } catch (error) { + return done(error); + } }); /** * Sign in using Email and Password. */ passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => { - User.findOne({ email: email.toLowerCase() }, (err, user) => { - if (err) { return done(err); } - if (!user) { - return done(null, false, { msg: `Email ${email} not found.` }); - } - if (!user.password) { - return done(null, false, { msg: 'Your account was registered using a sign-in provider. To enable password login, sign in using a provider, and then set a password under your user profile.' }); - } - user.comparePassword(password, (err, isMatch) => { - if (err) { return done(err); } - if (isMatch) { - return done(null, user); + User.findOne({ email: email.toLowerCase() }) + .then((user) => { + if (!user) { + return done(null, false, { msg: `Email ${email} not found.` }); } - return done(null, false, { msg: 'Invalid email or password.' }); - }); - }); + if (!user.password) { + return done(null, false, { msg: 'Your account was registered using a sign-in provider. To enable password login, sign in using a provider, and then set a password under your user profile.' }); + } + user.comparePassword(password, (err, isMatch) => { + if (err) { return done(err); } + if (isMatch) { + return done(null, user); + } + return done(null, false, { msg: 'Invalid email or password.' }); + }); + }) + .catch((err) => done(err)); })); /** @@ -75,46 +78,40 @@ passport.use(new SnapchatStrategy({ profileFields: ['id', 'displayName', 'bitmoji'], scope: ['user.display_name', 'user.bitmoji.avatar'], passReqToCallback: true -}, (req, accessToken, refreshToken, profile, done) => { - if (req.user) { - User.findOne({ snapchat: profile.id }, (err, existingUser) => { - if (err) { return done(err); } +}, async (req, accessToken, refreshToken, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ snapchat: profile.id }); if (existingUser) { req.flash('errors', { msg: 'There is already a Snapchat 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, (err, user) => { - if (err) { return done(err); } - user.snapchat = profile.id; - user.tokens.push({ kind: 'snapchat', accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.picture = user.profile.picture || profile.bitmoji.avatarUrl; - user.save((err) => { - req.flash('info', { msg: 'Snapchat account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ snapchat: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - const user = new User(); - // Similar to Twitter & Instagram APIs, assign 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.id}@snapchat.com`; + const user = await User.findById(req.user.id); user.snapchat = profile.id; user.tokens.push({ kind: 'snapchat', accessToken }); - user.profile.name = profile.displayName; - user.profile.picture = profile.bitmoji.avatarUrl; - user.save((err) => { - done(err, user); - }); - }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.picture = user.profile.picture || profile.bitmoji.avatarUrl; + await user.save(); + req.flash('info', { msg: 'Snapchat account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ snapchat: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const user = new User(); + // Similar to Twitter & Instagram APIs, assign 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.id}@snapchat.com`; + user.snapchat = profile.id; + user.tokens.push({ kind: 'snapchat', accessToken }); + user.profile.name = profile.displayName; + user.profile.picture = profile.bitmoji.avatarUrl; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } })); @@ -127,54 +124,45 @@ passport.use(new FacebookStrategy({ callbackURL: `${process.env.BASE_URL}/auth/facebook/callback`, profileFields: ['name', 'email', 'link', 'locale', 'timezone', 'gender'], passReqToCallback: true -}, (req, accessToken, refreshToken, profile, done) => { - if (req.user) { - User.findOne({ facebook: profile.id }, (err, existingUser) => { - if (err) { return done(err); } +}, async (req, accessToken, refreshToken, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ facebook: profile.id }); 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, (err, user) => { - if (err) { return done(err); } - user.facebook = profile.id; - user.tokens.push({ kind: 'facebook', accessToken }); - user.profile.name = user.profile.name || `${profile.name.givenName} ${profile.name.familyName}`; - 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((err) => { - req.flash('info', { msg: 'Facebook account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ facebook: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - User.findOne({ email: profile._json.email }, (err, existingEmailUser) => { - if (err) { return done(err); } - if (existingEmailUser) { - req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' }); - done(err); - } else { - const user = new User(); - user.email = profile._json.email; - user.facebook = profile.id; - user.tokens.push({ kind: 'facebook', accessToken }); - user.profile.name = `${profile.name.givenName} ${profile.name.familyName}`; - user.profile.gender = profile._json.gender; - user.profile.picture = `https://graph.facebook.com/${profile.id}/picture?type=large`; - user.profile.location = (profile._json.location) ? profile._json.location.name : ''; - user.save((err) => { - done(err, user); - }); - } - }); - }); + const user = await User.findById(req.user.id); + user.facebook = profile.id; + user.tokens.push({ kind: 'facebook', accessToken }); + user.profile.name = user.profile.name || `${profile.name.givenName} ${profile.name.familyName}`; + user.profile.gender = user.profile.gender || profile._json.gender; + user.profile.picture = user.profile.picture || `https://graph.facebook.com/${profile.id}/picture?type=large`; + await user.save(); + req.flash('info', { msg: 'Facebook account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ facebook: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const existingEmailUser = await User.findOne({ email: profile._json.email }); + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' }); + return done(null, existingEmailUser); + } + const user = new User(); + user.email = profile._json.email; + user.facebook = profile.id; + user.tokens.push({ kind: 'facebook', accessToken }); + user.profile.name = `${profile.name.givenName} ${profile.name.familyName}`; + user.profile.gender = profile._json.gender; + user.profile.picture = `https://graph.facebook.com/${profile.id}/picture?type=large`; + user.profile.location = profile._json.location ? profile._json.location.name : ''; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } })); @@ -187,76 +175,56 @@ passport.use(new GitHubStrategy({ callbackURL: `${process.env.BASE_URL}/auth/github/callback`, passReqToCallback: true, scope: ['user:email'] -}, (req, accessToken, refreshToken, profile, done) => { - if (req.user) { - User.findOne({ github: profile.id }, (err, existingUser) => { +}, async (req, accessToken, refreshToken, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ github: profile.id }); 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, (err, user) => { - if (err) { return done(err); } - user.github = profile.id; - user.tokens.push({ kind: 'github', 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((err) => { - req.flash('info', { msg: 'GitHub account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ github: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - if (profile._json.email === null) { - User.findOne({ email: profile.emails[0].value }, (err, existingEmailUser) => { - if (err) { return done(err); } - if (existingEmailUser) { - req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); - done(err); - } else { - const user = new User(); - user.email = _.get(_.orderBy(profile.emails, ['primary', 'verified'], ['desc', 'desc']), [0, 'value'], null); - user.github = profile.id; - user.tokens.push({ kind: 'github', accessToken }); - user.profile.name = profile.displayName; - user.profile.picture = profile._json.avatar_url; - user.profile.location = profile._json.location; - user.profile.website = profile._json.blog; - user.save((err) => { - done(err, user); - }); - } - }); - } else { - User.findOne({ email: profile._json.email }, (err, existingEmailUser) => { - if (err) { return done(err); } - if (existingEmailUser) { - req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); - done(err); - } else { - const user = new User(); - user.email = _.get(_.orderBy(profile.emails, ['primary', 'verified'], ['desc', 'desc']), [0, 'value'], null); - user.github = profile.id; - user.tokens.push({ kind: 'github', accessToken }); - user.profile.name = profile.displayName; - user.profile.picture = profile._json.avatar_url; - user.profile.location = profile._json.location; - user.profile.website = profile._json.blog; - user.save((err) => { - done(err, user); - }); - } - }); + const user = await User.findById(req.user.id); + user.github = profile.id; + user.tokens.push({ kind: 'github', 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; + await user.save(); + req.flash('info', { msg: 'GitHub account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ github: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const emailValue = _.get(_.orderBy(profile.emails, ['primary', 'verified'], ['desc', 'desc']), [0, 'value'], null); + if (profile._json.email === null) { + const existingEmailUser = await User.findOne({ email: emailValue }); + + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); + return done(null, existingEmailUser); } - }); + } else { + const existingEmailUser = await User.findOne({ email: profile._json.email }); + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); + return done(null, existingEmailUser); + } + } + const user = new User(); + user.email = emailValue; + user.github = profile.id; + user.tokens.push({ kind: 'github', accessToken }); + user.profile.name = profile.displayName; + user.profile.picture = profile._json.avatar_url; + user.profile.location = profile._json.location; + user.profile.website = profile._json.blog; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } })); @@ -268,49 +236,42 @@ passport.use(new TwitterStrategy({ consumerSecret: process.env.TWITTER_SECRET, callbackURL: `${process.env.BASE_URL}/auth/twitter/callback`, passReqToCallback: true -}, (req, accessToken, tokenSecret, profile, done) => { - if (req.user) { - User.findOne({ twitter: profile.id }, (err, existingUser) => { - if (err) { return done(err); } +}, async (req, accessToken, tokenSecret, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ twitter: profile.id }); 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, (err, user) => { - if (err) { return done(err); } - user.twitter = profile.id; - user.tokens.push({ kind: 'twitter', accessToken, 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((err) => { - if (err) { return done(err); } - req.flash('info', { msg: 'Twitter account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ twitter: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - const user = new User(); - // 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`; + const user = await User.findById(req.user.id); user.twitter = profile.id; user.tokens.push({ kind: 'twitter', accessToken, tokenSecret }); - user.profile.name = profile.displayName; - user.profile.location = profile._json.location; - user.profile.picture = profile._json.profile_image_url_https; - user.save((err) => { - done(err, user); - }); - }); + 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; + await user.save(); + req.flash('info', { msg: 'Twitter account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ twitter: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const user = new User(); + // 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`; + user.twitter = profile.id; + user.tokens.push({ kind: 'twitter', accessToken, tokenSecret }); + user.profile.name = profile.displayName; + user.profile.location = profile._json.location; + user.profile.picture = profile._json.profile_image_url_https; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } })); @@ -322,63 +283,54 @@ const googleStrategyConfig = new GoogleStrategy({ clientSecret: process.env.GOOGLE_SECRET, callbackURL: '/auth/google/callback', passReqToCallback: true -}, (req, accessToken, refreshToken, params, profile, done) => { - if (req.user) { - User.findOne({ google: profile.id }, (err, existingUser) => { - if (err) { return done(err); } +}, async (req, accessToken, refreshToken, params, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ google: profile.id }); if (existingUser && (existingUser.id !== req.user.id)) { 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, (err, user) => { - if (err) { return done(err); } - user.google = profile.id; - user.tokens.push({ - kind: 'google', - accessToken, - accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), - refreshToken, - }); - 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((err) => { - req.flash('info', { msg: 'Google account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ google: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - User.findOne({ email: profile.emails[0].value }, (err, existingEmailUser) => { - if (err) { return done(err); } - if (existingEmailUser) { - req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' }); - done(err); - } else { - const user = new User(); - user.email = profile.emails[0].value; - user.google = profile.id; - user.tokens.push({ - kind: 'google', - accessToken, - accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), - refreshToken, - }); - user.profile.name = profile.displayName; - user.profile.gender = profile._json.gender; - user.profile.picture = profile._json.picture; - user.save((err) => { - done(err, user); - }); - } + const user = await User.findById(req.user.id); + user.google = profile.id; + user.tokens.push({ + kind: 'google', + accessToken, + accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), + refreshToken, }); + 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; + await user.save(); + req.flash('info', { msg: 'Google account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ google: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const existingEmailUser = await User.findOne({ email: profile.emails[0].value }); + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' }); + return done(null, existingEmailUser); + } + const user = new User(); + user.email = profile.emails[0].value; + user.google = profile.id; + user.tokens.push({ + kind: 'google', + accessToken, + accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), + refreshToken, }); + user.profile.name = profile.displayName; + user.profile.gender = profile._json.gender; + user.profile.picture = profile._json.picture; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } }); passport.use('google', googleStrategyConfig); @@ -393,52 +345,42 @@ passport.use(new LinkedInStrategy({ callbackURL: `${process.env.BASE_URL}/auth/linkedin/callback`, scope: ['r_liteprofile', 'r_emailaddress'], passReqToCallback: true -}, (req, accessToken, refreshToken, profile, done) => { - if (req.user) { - User.findOne({ linkedin: profile.id }, (err, existingUser) => { - if (err) { return done(err); } +}, async (req, accessToken, refreshToken, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ linkedin: profile.id }); 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, (err, user) => { - if (err) { return done(err); } - user.linkedin = profile.id; - user.tokens.push({ kind: 'linkedin', accessToken }); - user.profile.name = user.profile.name || profile.displayName; - user.profile.picture = user.profile.picture || profile.photos[3].value; - user.save((err) => { - if (err) { return done(err); } - req.flash('info', { msg: 'LinkedIn account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ linkedin: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - User.findOne({ email: profile.emails[0].value }, (err, existingEmailUser) => { - if (err) { return done(err); } - if (existingEmailUser) { - req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' }); - done(err); - } else { - const user = new User(); - user.linkedin = profile.id; - user.tokens.push({ kind: 'linkedin', accessToken }); - user.email = profile.emails[0].value; - user.profile.name = profile.displayName; - user.profile.picture = user.profile.picture || profile.photos[3].value; - user.save((err) => { - done(err, user); - }); - } - }); - }); + const user = await User.findById(req.user.id); + user.linkedin = profile.id; + user.tokens.push({ kind: 'linkedin', accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.picture = user.profile.picture || profile.photos[3].value; + await user.save(); + req.flash('info', { msg: 'LinkedIn account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ linkedin: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const existingEmailUser = await User.findOne({ email: profile.emails[0].value }); + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' }); + return done(null, existingEmailUser); + } + const user = new User(); + user.linkedin = profile.id; + user.tokens.push({ kind: 'linkedin', accessToken }); + user.email = profile.emails[0].value; + user.profile.name = profile.displayName; + user.profile.picture = user.profile.picture || profile.photos[3].value; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } })); @@ -450,48 +392,42 @@ passport.use(new InstagramStrategy({ clientSecret: process.env.INSTAGRAM_SECRET, callbackURL: '/auth/instagram/callback', passReqToCallback: true -}, (req, accessToken, refreshToken, profile, done) => { - if (req.user) { - User.findOne({ instagram: profile.id }, (err, existingUser) => { - if (err) { return done(err); } +}, async (req, accessToken, refreshToken, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ instagram: profile.id }); 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, (err, user) => { - if (err) { return done(err); } - user.instagram = profile.id; - user.tokens.push({ kind: 'instagram', 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((err) => { - req.flash('info', { msg: 'Instagram account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ instagram: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - const user = new User(); + const user = await User.findById(req.user.id); user.instagram = profile.id; user.tokens.push({ kind: 'instagram', 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((err) => { - done(err, user); - }); - }); + 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; + await user.save(); + req.flash('info', { msg: 'Instagram account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ instagram: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const user = new User(); + user.instagram = profile.id; + user.tokens.push({ kind: 'instagram', 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; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } })); @@ -504,63 +440,54 @@ const twitchStrategyConfig = new TwitchStrategy({ callbackURL: `${process.env.BASE_URL}/auth/twitch/callback`, scope: ['user_read', 'chat:read', 'chat:edit', 'whispers:read', 'whispers:edit', 'user:read:email'], passReqToCallback: true -}, (req, accessToken, refreshToken, params, profile, done) => { - if (req.user) { - User.findOne({ twitch: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser && (existingUser.id !== req.user.id)) { +}, async (req, accessToken, refreshToken, params, profile, done) => { + try { + if (req.user) { + const existingUser = await User.findOne({ twitch: profile.id }); + if (existingUser && existingUser.id !== req.user.id) { req.flash('errors', { msg: 'There is already a Twitch 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, (err, user) => { - if (err) { return done(err); } - user.twitch = profile.id; - user.tokens.push({ - kind: 'twitch', - accessToken, - accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), - refreshToken, - }); - user.profile.name = user.profile.name || profile.display_name; - user.profile.email = user.profile.gender || profile.email; - user.profile.picture = user.profile.picture || profile.profile_image_url; - user.save((err) => { - req.flash('info', { msg: 'Twitch account has been linked.' }); - done(err, user); - }); - }); - } - }); - } else { - User.findOne({ twitch: profile.id }, (err, existingUser) => { - if (err) { return done(err); } - if (existingUser) { return done(null, existingUser); } - User.findOne({ email: profile.email }, (err, existingEmailUser) => { - if (err) { return done(err); } - if (existingEmailUser) { - req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Twtich manually from Account Settings.' }); - done(err); - } else { - const user = new User(); - user.email = profile.email; - user.twitch = profile.id; - user.tokens.push({ - kind: 'twitch', - accessToken, - accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), - refreshToken, - }); - user.profile.name = profile.display_name; - user.profile.email = profile.email; - user.profile.picture = profile.profile_image_url; - user.save((err) => { - done(err, user); - }); - } + const user = await User.findById(req.user.id); + user.twitch = profile.id; + user.tokens.push({ + kind: 'twitch', + accessToken, + accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), + refreshToken, }); + user.profile.name = user.profile.name || profile.display_name; + user.profile.email = user.profile.gender || profile.email; + user.profile.picture = user.profile.picture || profile.profile_image_url; + await user.save(); + req.flash('info', { msg: 'Twitch account has been linked.' }); + return done(null, user); + } + const existingUser = await User.findOne({ twitch: profile.id }); + if (existingUser) { + return done(null, existingUser); + } + const existingEmailUser = await User.findOne({ email: profile.email }); + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Twitch manually from Account Settings.' }); + return done(null, existingEmailUser); + } + const user = new User(); + user.email = profile.email; + user.twitch = profile.id; + user.tokens.push({ + kind: 'twitch', + accessToken, + accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), + refreshToken, }); + user.profile.name = profile.display_name; + user.profile.email = profile.email; + user.profile.picture = profile.profile_image_url; + await user.save(); + return done(null, user); + } catch (err) { + return done(err); } }); passport.use('twitch', twitchStrategyConfig); @@ -578,14 +505,15 @@ passport.use('tumblr', new OAuthStrategy({ callbackURL: '/auth/tumblr/callback', passReqToCallback: true }, -(req, token, tokenSecret, profile, done) => { - User.findById(req.user._id, (err, user) => { - if (err) { return done(err); } +async (req, token, tokenSecret, profile, done) => { + try { + const user = await User.findById(req.user._id); user.tokens.push({ kind: 'tumblr', accessToken: token, tokenSecret }); - user.save((err) => { - done(err, user); - }); - }); + await user.save(); + return done(null, user); + } catch (err) { + return done(err); + } })); /** @@ -596,17 +524,18 @@ passport.use('foursquare', new OAuth2Strategy({ tokenURL: 'https://foursquare.com/oauth2/access_token', clientID: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET, - callbackURL: process.env.FOURSQUARE_REDIRECT_URL, + callbackURL: `${process.env.BASE_URL}/auth/foursquare/callback`, passReqToCallback: true }, -(req, accessToken, refreshToken, profile, done) => { - User.findById(req.user._id, (err, user) => { - if (err) { return done(err); } +async (req, accessToken, refreshToken, profile, done) => { + try { + const user = await User.findById(req.user._id); user.tokens.push({ kind: 'foursquare', accessToken }); - user.save((err) => { - done(err, user); - }); - }); + await user.save(); + return done(null, user); + } catch (err) { + return done(err); + } })); /** @@ -616,53 +545,49 @@ passport.use(new SteamOpenIdStrategy({ apiKey: process.env.STEAM_KEY, returnURL: `${process.env.BASE_URL}/auth/steam/callback`, profile: true, -}, (req, identifier, profile, done) => { +}, async (req, identifier, profile, done) => { const steamId = identifier.match(/\d+$/)[0]; const profileURL = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${process.env.STEAM_KEY}&steamids=${steamId}`; - - if (req.user) { - User.findOne({ steam: steamId }, (err, existingUser) => { - if (err) { return done(err); } + try { + if (req.user) { + const existingUser = await User.findOne({ steam: steamId }); if (existingUser) { req.flash('errors', { msg: 'There is already an account associated with the SteamID. Sign in with that account or delete it, then link it with your current account.' }); - done(err); - } else { - User.findById(req.user.id, (err, user) => { - if (err) { return done(err); } - user.steam = steamId; - user.tokens.push({ kind: 'steam', accessToken: steamId }); - axios.get(profileURL) - .then((res) => { - const profile = res.data.response.players[0]; - user.profile.name = user.profile.name || profile.personaname; - user.profile.picture = user.profile.picture || profile.avatarmedium; - user.save((err) => { - done(err, user); - }); - }) - .catch((err) => { - user.save((err) => { done(err, user); }); - done(err, null); - }); - }); + return done(null, existingUser); } - }); - } else { - axios.get(profileURL) - .then(({ data }) => { - const profile = data.response.players[0]; + const user = await User.findById(req.user.id); + user.steam = steamId; + user.tokens.push({ kind: 'steam', accessToken: steamId }); + try { + const res = await axios.get(profileURL); + const profileData = res.data.response.players[0]; + user.profile.name = user.profile.name || profileData.personaname; + user.profile.picture = user.profile.picture || profileData.avatarmedium; + await user.save(); + return done(null, user); + } catch (err) { + console.log(err); + await user.save(); + return done(err, user); + } + } else { + try { + const { data } = await axios.get(profileURL); + const profileData = data.response.players[0]; const user = new User(); user.steam = steamId; user.email = `${steamId}@steam.com`; // steam does not disclose emails, prevent duplicate keys user.tokens.push({ kind: 'steam', accessToken: steamId }); - user.profile.name = profile.personaname; - user.profile.picture = profile.avatarmedium; - user.save((err) => { - done(err, user); - }); - }).catch((err) => { - done(err, null); - }); + user.profile.name = profileData.personaname; + user.profile.picture = profileData.avatarmedium; + await user.save(); + return done(null, user); + } catch (err) { + return done(err, null); + } + } + } catch (err) { + return done(err); } })); @@ -674,17 +599,18 @@ passport.use('pinterest', new OAuth2Strategy({ tokenURL: 'https://api.pinterest.com/v1/oauth/token', clientID: process.env.PINTEREST_ID, clientSecret: process.env.PINTEREST_SECRET, - callbackURL: process.env.PINTEREST_REDIRECT_URL, + callbackURL: `${process.env.BASE_URL}/auth/pinterest/callback`, passReqToCallback: true }, -(req, accessToken, refreshToken, profile, done) => { - User.findById(req.user._id, (err, user) => { - if (err) { return done(err); } +async (req, accessToken, refreshToken, profile, done) => { + try { + const user = await User.findOne(req.user._id); user.tokens.push({ kind: 'pinterest', accessToken }); - user.save((err) => { - done(err, user); - }); - }); + await user.save(); + return done(null, user); + } catch (err) { + return done(err); + } })); /** @@ -696,37 +622,34 @@ const quickbooksStrategyConfig = new OAuth2Strategy({ clientID: process.env.QUICKBOOKS_CLIENT_ID, clientSecret: process.env.QUICKBOOKS_CLIENT_SECRET, callbackURL: `${process.env.BASE_URL}/auth/quickbooks/callback`, - passReqToCallback: true + passReqToCallback: true, }, -(res, accessToken, refreshToken, params, profile, done) => { - User.findById(res.user._id, (err, user) => { - if (err) { return done(err); } - user.quickbooks = res.query.realmId; - if (user.tokens.filter((vendor) => (vendor.kind === 'quickbooks'))[0]) { - user.tokens.some((tokenObject) => { - if (tokenObject.kind === 'quickbooks') { - tokenObject.accessToken = accessToken; - tokenObject.accessTokenExpires = moment().add(params.expires_in, 'seconds').format(); - tokenObject.refreshToken = refreshToken; - tokenObject.refreshTokenExpires = moment().add(params.x_refresh_token_expires_in, 'seconds').format(); - if (params.expires_in) tokenObject.accessTokenExpires = moment().add(params.expires_in, 'seconds').format(); - return true; - } - return false; - }); - user.markModified('tokens'); - user.save((err) => { done(err, user); }); +async (req, accessToken, refreshToken, params, profile, done) => { + try { + const user = await User.findById(req.user._id); + user.quickbooks = req.query.realmId; + const quickbooksToken = user.tokens.find((vendor) => vendor.kind === 'quickbooks'); + if (quickbooksToken) { + quickbooksToken.accessToken = accessToken; + quickbooksToken.accessTokenExpires = moment().add(params.expires_in, 'seconds').format(); + quickbooksToken.refreshToken = refreshToken; + quickbooksToken.refreshTokenExpires = moment().add(params.x_refresh_token_expires_in, 'seconds').format(); + if (params.expires_in) quickbooksToken.accessTokenExpires = moment().add(params.expires_in, 'seconds').format(); } else { user.tokens.push({ kind: 'quickbooks', accessToken, accessTokenExpires: moment().add(params.expires_in, 'seconds').format(), refreshToken, - refreshTokenExpires: moment().add(params.x_refresh_token_expires_in, 'seconds').format() + refreshTokenExpires: moment().add(params.x_refresh_token_expires_in, 'seconds').format(), }); - user.save((err) => { done(err, user); }); } - }); + user.markModified('tokens'); + await user.save(); + return done(null, user); + } catch (err) { + return done(err); + } }); passport.use('quickbooks', quickbooksStrategyConfig); refresh.use('quickbooks', quickbooksStrategyConfig); @@ -744,48 +667,43 @@ exports.isAuthenticated = (req, res, next) => { /** * Authorization Required middleware. */ -exports.isAuthorized = (req, res, next) => { +exports.isAuthorized = async (req, res, next) => { const provider = req.path.split('/')[2]; const token = req.user.tokens.find((token) => token.kind === provider); if (token) { - // Is there an access token expiration and access token expired? - // Yes: Is there a refresh token? - // Yes: Does it have expiration and if so is it expired? - // Yes, Quickbooks - We got nothing, redirect to res.redirect(`/auth/${provider}`); - // No, Quickbooks and Google- refresh token and save, and then go to next(); - // No: Treat it like we got nothing, redirect to res.redirect(`/auth/${provider}`); - // No: we are good, go to next(): if (token.accessTokenExpires && moment(token.accessTokenExpires).isBefore(moment().subtract(1, 'minutes'))) { if (token.refreshToken) { if (token.refreshTokenExpires && moment(token.refreshTokenExpires).isBefore(moment().subtract(1, 'minutes'))) { - res.redirect(`/auth/${provider}`); - } else { - refresh.requestNewAccessToken(`${provider}`, token.refreshToken, (err, accessToken, refreshToken, params) => { - User.findById(req.user.id, (err, user) => { - user.tokens.some((tokenObject) => { - if (tokenObject.kind === provider) { - tokenObject.accessToken = accessToken; - if (params.expires_in) tokenObject.accessTokenExpires = moment().add(params.expires_in, 'seconds').format(); - return true; - } - return false; - }); - req.user = user; - user.markModified('tokens'); - user.save((err) => { - if (err) console.log(err); - next(); - }); + return res.redirect(`/auth/${provider}`); + } + try { + const newTokens = await new Promise((resolve, reject) => { + refresh.requestNewAccessToken(`${provider}`, token.refreshToken, (err, accessToken, refreshToken, params) => { + if (err) reject(err); + resolve({ accessToken, refreshToken, params }); }); }); + + req.user.tokens.forEach((tokenObject) => { + if (tokenObject.kind === provider) { + tokenObject.accessToken = newTokens.accessToken; + if (newTokens.params.expires_in) tokenObject.accessTokenExpires = moment().add(newTokens.params.expires_in, 'seconds').format(); + } + }); + + await req.user.save(); + return next(); + } catch (err) { + console.log(err); + return next(); } } else { - res.redirect(`/auth/${provider}`); + return res.redirect(`/auth/${provider}`); } } else { - next(); + return next(); } } else { - res.redirect(`/auth/${provider}`); + return res.redirect(`/auth/${provider}`); } }; diff --git a/controllers/api.js b/controllers/api.js index de4e57a058..f20ddf8fb4 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -519,8 +519,8 @@ exports.getPayPal = (req, res, next) => { payment_method: 'paypal' }, redirect_urls: { - return_url: process.env.PAYPAL_RETURN_URL, - cancel_url: process.env.PAYPAL_CANCEL_URL + return_url: `${process.env.BASE_URL}/api/paypal/success`, + cancel_url: `${process.env.BASE_URL}/api/paypal/cancel` }, transactions: [{ description: 'Hackathon Starter', diff --git a/controllers/user.js b/controllers/user.js index be2a5743e8..220d44f588 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -83,7 +83,6 @@ exports.postLogin = (req, res, next) => { req.logIn(user, (err) => { if (err) { return next(err); } req.flash('success', { msg: 'Success! You are logged in.' }); - console.log('req.sessionStore:', req.sessionStore); res.redirect(req.session.returnTo || '/'); }); })(req, res, next); @@ -121,39 +120,36 @@ exports.getSignup = (req, res) => { * POST /signup * Create a new local account. */ -exports.postSignup = (req, res, next) => { +exports.postSignup = async (req, res, next) => { const validationErrors = []; if (!validator.isEmail(req.body.email)) validationErrors.push({ msg: 'Please enter a valid email address.' }); if (!validator.isLength(req.body.password, { min: 8 })) validationErrors.push({ msg: 'Password must be at least 8 characters long' }); if (req.body.password !== req.body.confirmPassword) validationErrors.push({ msg: 'Passwords do not match' }); - if (validationErrors.length) { req.flash('errors', validationErrors); return res.redirect('/signup'); } req.body.email = validator.normalizeEmail(req.body.email, { gmail_remove_dots: false }); - - const user = new User({ - email: req.body.email, - password: req.body.password - }); - - User.findOne({ email: req.body.email }, (err, existingUser) => { - if (err) { return next(err); } + try { + const existingUser = await User.findOne({ email: req.body.email }); if (existingUser) { req.flash('errors', { msg: 'Account with that email address already exists.' }); return res.redirect('/signup'); } - user.save((err) => { - if (err) { return next(err); } - req.logIn(user, (err) => { - if (err) { - return next(err); - } - res.redirect('/'); - }); + const user = new User({ + email: req.body.email, + password: req.body.password }); - }); + await user.save(); + req.logIn(user, (err) => { + if (err) { + return next(err); + } + res.redirect('/'); + }); + } catch (err) { + next(err); + } }; /** @@ -170,7 +166,7 @@ exports.getAccount = (req, res) => { * POST /account/profile * Update profile information. */ -exports.postUpdateProfile = (req, res, next) => { +exports.postUpdateProfile = async (req, res, next) => { const validationErrors = []; if (!validator.isEmail(req.body.email)) validationErrors.push({ msg: 'Please enter a valid email address.' }); @@ -179,34 +175,31 @@ exports.postUpdateProfile = (req, res, next) => { return res.redirect('/account'); } req.body.email = validator.normalizeEmail(req.body.email, { gmail_remove_dots: false }); - - User.findById(req.user.id, (err, user) => { - if (err) { return next(err); } + try { + const user = await User.findById(req.user.id); if (user.email !== req.body.email) user.emailVerified = false; user.email = req.body.email || ''; user.profile.name = req.body.name || ''; user.profile.gender = req.body.gender || ''; user.profile.location = req.body.location || ''; user.profile.website = req.body.website || ''; - user.save((err) => { - if (err) { - if (err.code === 11000) { - req.flash('errors', { msg: 'The email address you have entered is already associated with an account.' }); - return res.redirect('/account'); - } - return next(err); - } - req.flash('success', { msg: 'Profile information has been updated.' }); - res.redirect('/account'); - }); - }); + await user.save(); + req.flash('success', { msg: 'Profile information has been updated.' }); + res.redirect('/account'); + } catch (err) { + if (err.code === 11000) { + req.flash('errors', { msg: 'The email address you have entered is already associated with an account.' }); + return res.redirect('/account'); + } + next(err); + } }; /** * POST /account/password * Update current password. */ -exports.postUpdatePassword = (req, res, next) => { +exports.postUpdatePassword = async (req, res, next) => { const validationErrors = []; if (!validator.isLength(req.body.password, { min: 8 })) validationErrors.push({ msg: 'Password must be at least 8 characters long' }); if (req.body.password !== req.body.confirmPassword) validationErrors.push({ msg: 'Passwords do not match' }); @@ -215,44 +208,45 @@ exports.postUpdatePassword = (req, res, next) => { req.flash('errors', validationErrors); return res.redirect('/account'); } - - User.findById(req.user.id, (err, user) => { - if (err) { return next(err); } + try { + const user = await User.findById(req.user.id); user.password = req.body.password; - user.save((err) => { - if (err) { return next(err); } - req.flash('success', { msg: 'Password has been changed.' }); - res.redirect('/account'); - }); - }); + await user.save(); + req.flash('success', { msg: 'Password has been changed.' }); + res.redirect('/account'); + } catch (err) { + next(err); + } }; /** * POST /account/delete * Delete user account. */ -exports.postDeleteAccount = (req, res, next) => { - User.deleteOne({ _id: req.user.id }, (err) => { - if (err) { return next(err); } +exports.postDeleteAccount = async (req, res, next) => { + try { + await User.deleteOne({ _id: req.user.id }); req.logout((err) => { - if (err) console.log('Error : Failed to logout.', err); + if (err) console.log('Error: Failed to logout.', err); req.session.destroy((err) => { - if (err) console.log('Error : Failed to destroy the session during account deletion.', err); + if (err) console.log('Error: Failed to destroy the session during account deletion.', err); req.user = null; res.redirect('/'); }); }); - }); + } catch (err) { + next(err); + } }; /** * GET /account/unlink/:provider * Unlink OAuth provider. */ -exports.getOauthUnlink = (req, res, next) => { - const { provider } = req.params; - User.findById(req.user.id, (err, user) => { - if (err) { return next(err); } +exports.getOauthUnlink = async (req, res, next) => { + try { + const { provider } = req.params; + const user = await User.findById(req.user.id); user[provider.toLowerCase()] = undefined; const tokensWithoutProviderToUnlink = user.tokens.filter((token) => token.kind !== provider.toLowerCase()); @@ -265,17 +259,19 @@ exports.getOauthUnlink = (req, res, next) => { ) { req.flash('errors', { msg: `The ${_.startCase(_.toLower(provider))} account cannot be unlinked without another form of login enabled.` - + ' Please link another account or add an email address and password.' + + ' Please link another account or add an email address and password.' }); return res.redirect('/account'); } user.tokens = tokensWithoutProviderToUnlink; - user.save((err) => { - if (err) { return next(err); } - req.flash('info', { msg: `${_.startCase(_.toLower(provider))} account has been unlinked.` }); - res.redirect('/account'); + await user.save(); + req.flash('info', { + msg: `${_.startCase(_.toLower(provider))} account has been unlinked.`, }); - }); + res.redirect('/account'); + } catch (err) { + next(err); + } }; /** diff --git a/package-lock.json b/package-lock.json index 83ac2d33d0..1c3801ed9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "chart.js": "^4.3.0", "cheerio": "^1.0.0-rc.10", "compression": "^1.7.4", - "connect-mongo": "^4.6.0", + "connect-mongo": "^5.0.0", "dotenv": "^16.3.1", "errorhandler": "^1.5.1", "express": "^4.18.2", @@ -40,7 +40,7 @@ "lusca": "^1.7.0", "mailchecker": "^5.0.9", "moment": "^2.29.4", - "mongoose": "^6.11.2", + "mongoose": "^7.4.0", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.3", @@ -1159,9 +1159,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.0.tgz", + "integrity": "sha512-uiPeRISaglZnaZk8vwrjQZ1CxogZeY/4IYft6gBOTqu1WhVXWmCmZMWxUv2Q/pxSvPdp1JPaO62kLOcOkMqWrw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2572,9 +2572,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", - "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==" + "version": "20.4.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.4.tgz", + "integrity": "sha512-CukZhumInROvLq3+b5gLev+vgpsIqC2D0deQr/yS1WnxvmYLlJXZpaQrQiseMY+6xusl79E04UjWoqyr+t1/Ew==" }, "node_modules/@types/webidl-conversions": { "version": "7.0.0", @@ -3198,20 +3198,18 @@ } }, "node_modules/bson": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", - "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", - "dependencies": { - "buffer": "^5.6.0" - }, + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.4.0.tgz", + "integrity": "sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==", "engines": { - "node": ">=6.9.0" + "node": ">=14.20.1" } }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "funding": [ { "type": "github", @@ -3738,18 +3736,19 @@ } }, "node_modules/connect-mongo": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-4.6.0.tgz", - "integrity": "sha512-8new4Z7NLP3CGP65Aw6ls3xDBeKVvHRSh39CXuDZTQsvpeeU9oNMzfFgvqmHqZ6gWpxIl663RyoVEmCAGf1yOg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.0.0.tgz", + "integrity": "sha512-s93jiP6GkRApn5duComx6RLwtP23YrulPxShz+8peX7svd6Q+MS8nKLhKCCazbP92C13eTVaIOxgeLt0ezIiCg==", "dependencies": { "debug": "^4.3.1", "kruptein": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=12.9.0" }, "peerDependencies": { - "mongodb": "^4.1.0" + "express-session": "^1.17.1", + "mongodb": "^5.1.0" } }, "node_modules/connect-mongo/node_modules/debug": { @@ -4107,9 +4106,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.466", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.466.tgz", - "integrity": "sha512-TSkRvbXRXD8BwhcGlZXDsbI2lRoP8dvqR7LQnqQNk9KxXBc4tG8O+rTuXgTyIpEdiqSGKEBSqrxdqEntnjNncA==", + "version": "1.4.468", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.468.tgz", + "integrity": "sha512-6M1qyhaJOt7rQtNti1lBA0GwclPH+oKCmsra/hkcWs5INLxfXXD/dtdnaKUYQu/pjOBP/8Osoe4mAcNvvzoFag==", "dev": true }, "node_modules/emoji-regex": { @@ -5793,6 +5792,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -7454,20 +7454,43 @@ } }, "node_modules/mongodb": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz", - "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.7.0.tgz", + "integrity": "sha512-zm82Bq33QbqtxDf58fLWBwTjARK3NSvKYjyz997KSy6hpat0prjeX/kxjbPVyZY60XYPDNETaHkHJI2UCzSLuw==", "dependencies": { - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.5.4", + "bson": "^5.4.0", + "mongodb-connection-string-url": "^2.6.0", "socks": "^2.7.1" }, "engines": { - "node": ">=12.9.0" + "node": ">=14.20.1" }, "optionalDependencies": { - "@aws-sdk/credential-providers": "^3.186.0", "saslprep": "^1.0.3" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.201.0", + "@mongodb-js/zstd": "^1.1.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=2.3.0 <3", + "snappy": "^7.2.2" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + } } }, "node_modules/mongodb-connection-string-url": { @@ -7518,6 +7541,18 @@ "node": ">=12.22.0" } }, + "node_modules/mongodb-memory-server-core/node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dev": true, + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/mongodb-memory-server-core/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7535,6 +7570,24 @@ } } }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz", + "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==", + "dev": true, + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.5.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "saslprep": "^1.0.3" + } + }, "node_modules/mongodb-memory-server-core/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7557,20 +7610,20 @@ } }, "node_modules/mongoose": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.11.4.tgz", - "integrity": "sha512-q9NaW9/BBYZofx80SqlR7uoSR09CS3g02y+KMj1lNLUxcFFsPshupY3WWisNFauYG9gyuDF4L/RgyIK3obSghg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.4.0.tgz", + "integrity": "sha512-oHE1eqodfKzugXRlQxpo+msIea7jPcRoayDuEMr50+bYwM/juA5f+1stjkWlXcg6vo1PdJFVA6DGaKOPLuG5mA==", "dependencies": { - "bson": "^4.7.2", + "bson": "^5.4.0", "kareem": "2.5.1", - "mongodb": "4.16.0", + "mongodb": "5.7.0", "mpath": "0.9.0", - "mquery": "4.0.3", + "mquery": "5.0.0", "ms": "2.1.3", "sift": "16.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.20.1" }, "funding": { "type": "opencollective", @@ -7617,14 +7670,14 @@ } }, "node_modules/mquery": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", - "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", "dependencies": { "debug": "4.x" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/mquery/node_modules/debug": { @@ -9510,9 +9563,9 @@ } }, "node_modules/sass": { - "version": "1.64.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.0.tgz", - "integrity": "sha512-m7YtAGmQta9uANIUJwXesAJMSncqH+3INc8kdVXs6eV6GUC8Qu2IYKQSN8PRLgiQfpca697G94klm2leYMxSHw==", + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", + "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", diff --git a/package.json b/package.json index e69340bb09..cee3be5605 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "chart.js": "^4.3.0", "cheerio": "^1.0.0-rc.10", "compression": "^1.7.4", - "connect-mongo": "^4.6.0", + "connect-mongo": "^5.0.0", "dotenv": "^16.3.1", "errorhandler": "^1.5.1", "express": "^4.18.2", @@ -57,7 +57,7 @@ "lusca": "^1.7.0", "mailchecker": "^5.0.9", "moment": "^2.29.4", - "mongoose": "^6.11.2", + "mongoose": "^7.4.0", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.3",