Add email verification and notifications
This commit - [x] Fixes the flash notice color (Trivial) - [x] Adds flash message for user with no email. - [x] Adds checks to see if user's email is verified, and displays corresponding notification. - [x] Adds email templates.
This commit is contained in:
@ -4,6 +4,7 @@ import moment from 'moment';
|
||||
import dedent from 'dedent';
|
||||
import debugFactory from 'debug';
|
||||
import { isEmail } from 'validator';
|
||||
import path from 'path';
|
||||
|
||||
import { saveUser, observeMethod } from '../../server/utils/rx';
|
||||
import { blacklistedUsernames } from '../../server/utils/constants';
|
||||
@ -89,7 +90,7 @@ module.exports = function(User) {
|
||||
'You\'re email has been confirmed!'
|
||||
]
|
||||
});
|
||||
ctx.res.redirect('/email-signin');
|
||||
ctx.res.redirect('/');
|
||||
});
|
||||
|
||||
User.beforeRemote('create', function({ req, res }, _, next) {
|
||||
@ -340,7 +341,52 @@ module.exports = function(User) {
|
||||
new Error(`${email} is already associated with another account.`)
|
||||
);
|
||||
}
|
||||
return this.update$({ email }).toPromise();
|
||||
|
||||
const emailVerified = false;
|
||||
return this.update$({
|
||||
email, emailVerified
|
||||
})
|
||||
.do(() => {
|
||||
this.email = email;
|
||||
this.emailVerified = emailVerified;
|
||||
})
|
||||
.flatMap(() => {
|
||||
var mailOptions = {
|
||||
type: 'email',
|
||||
to: email,
|
||||
from: 'Team@freecodecamp.com',
|
||||
subject: 'Welcome to Free Code Camp!',
|
||||
template: path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'server',
|
||||
'views',
|
||||
'emails',
|
||||
'user-email-verify.ejs'
|
||||
)
|
||||
};
|
||||
return this.verify(mailOptions).then(
|
||||
(data) => {
|
||||
if (data) {
|
||||
return Promise.resolve(
|
||||
dedent`
|
||||
Your email has been updated successfully, Please
|
||||
follow the link we sent you, to confirm.
|
||||
`);
|
||||
}
|
||||
return Promise.reject(
|
||||
'Oops, something went wrong, please try again later'
|
||||
);
|
||||
},
|
||||
(error) => {
|
||||
debug(error);
|
||||
return Promise.reject(
|
||||
'Oops, something went wrong, please try again later'
|
||||
);
|
||||
}
|
||||
);
|
||||
}).toPromise();
|
||||
});
|
||||
};
|
||||
|
||||
@ -358,8 +404,8 @@ module.exports = function(User) {
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'status',
|
||||
type: 'object'
|
||||
arg: 'message',
|
||||
type: 'string'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
@ -486,52 +532,11 @@ module.exports = function(User) {
|
||||
}
|
||||
);
|
||||
|
||||
User.prototype.updateEmail = function updateEmail(email) {
|
||||
if (this.email && this.email === email) {
|
||||
return Promise.reject(new Error(
|
||||
`${email} is already associated with this account.`
|
||||
));
|
||||
}
|
||||
return User.doesExist(null, email)
|
||||
.then(exists => {
|
||||
if (exists) {
|
||||
return Promise.reject(
|
||||
new Error(`${email} is already associated with another account.`)
|
||||
);
|
||||
}
|
||||
return this.update$({ email }).toPromise();
|
||||
});
|
||||
};
|
||||
|
||||
User.remoteMethod(
|
||||
'updateEmail',
|
||||
{
|
||||
isStatic: false,
|
||||
description: 'updates the email of the user object',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'email',
|
||||
type: 'string',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'status',
|
||||
type: 'object'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: '/update-email',
|
||||
verb: 'POST'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
User.themes = {
|
||||
night: true,
|
||||
default: true
|
||||
};
|
||||
|
||||
User.prototype.updateTheme = function updateTheme(theme) {
|
||||
if (!this.constructor.themes[theme]) {
|
||||
const err = new Error(
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Observable } from 'rx';
|
||||
import debugFactory from 'debug';
|
||||
import { isEmail } from 'validator';
|
||||
import path from 'path';
|
||||
|
||||
const debug = debugFactory('fcc:user:remote');
|
||||
|
||||
@ -15,7 +16,6 @@ module.exports = function(app) {
|
||||
var User = app.models.User;
|
||||
var UserIdentity = app.models.UserIdentity;
|
||||
var UserCredential = app.models.UserCredential;
|
||||
var Email = app.models.Email;
|
||||
User.observe('before delete', function(ctx, next) {
|
||||
debug('removing user', ctx.where);
|
||||
var id = ctx.where && ctx.where.id ? ctx.where.id : null;
|
||||
@ -70,21 +70,18 @@ module.exports = function(app) {
|
||||
to: user.email,
|
||||
from: 'Team@freecodecamp.com',
|
||||
subject: 'Welcome to Free Code Camp!',
|
||||
redirect: '/',
|
||||
text: [
|
||||
'Greetings from San Francisco!\n\n',
|
||||
'Thank you for joining our community.\n',
|
||||
'Feel free to email us at this address if you have ',
|
||||
'any questions about Free Code Camp.\n',
|
||||
'And if you have a moment, check out our blog: ',
|
||||
'medium.freecodecamp.com.\n\n',
|
||||
'Good luck with the challenges!\n\n',
|
||||
'- the Free Code Camp Team'
|
||||
].join('')
|
||||
template: path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'views',
|
||||
'emails',
|
||||
'a-extend-user-welcome.ejs'
|
||||
),
|
||||
redirect: '/'
|
||||
};
|
||||
|
||||
debug('sending welcome email');
|
||||
return Email.send(mailOptions, function(err) {
|
||||
return user.verify(mailOptions, function(err) {
|
||||
if (err) { return next(err); }
|
||||
return req.logIn(user, function(err) {
|
||||
if (err) { return next(err); }
|
||||
|
@ -18,7 +18,8 @@ import {
|
||||
import { observeMethod } from '../utils/rx';
|
||||
|
||||
import {
|
||||
ifNoUserSend
|
||||
ifNoUserSend,
|
||||
flashIfNotVerified
|
||||
} from '../utils/middleware';
|
||||
|
||||
import getFromDisk$ from '../utils/getFromDisk$';
|
||||
@ -419,7 +420,10 @@ module.exports = function(app) {
|
||||
redirectToNextChallenge
|
||||
);
|
||||
|
||||
router.get('/challenges/:challengeName', showChallenge);
|
||||
router.get('/challenges/:challengeName',
|
||||
flashIfNotVerified,
|
||||
showChallenge
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
|
||||
|
@ -19,6 +19,8 @@ import {
|
||||
calcLongestStreak
|
||||
} from '../utils/user-stats';
|
||||
|
||||
import { flashIfNotVerified } from '../utils/middleware';
|
||||
|
||||
const debug = debugFactory('fcc:boot:user');
|
||||
const sendNonUserToMap = ifNoUserRedirectTo('/map');
|
||||
const certIds = {
|
||||
@ -183,6 +185,7 @@ module.exports = function(app) {
|
||||
router.get(
|
||||
'/settings',
|
||||
sendNonUserToMap,
|
||||
flashIfNotVerified,
|
||||
getSettings
|
||||
);
|
||||
router.get('/vote1', vote1);
|
||||
|
@ -1,3 +1,5 @@
|
||||
import dedent from 'dedent';
|
||||
|
||||
export function ifNoUserRedirectTo(url, message, type = 'errors') {
|
||||
return function(req, res, next) {
|
||||
const { path } = req;
|
||||
@ -28,3 +30,24 @@ export function ifNoUser401(req, res, next) {
|
||||
}
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
export function flashIfNotVerified(req, res, next) {
|
||||
const user = req.user;
|
||||
if (!user) {
|
||||
return next();
|
||||
}
|
||||
const email = req.user.email;
|
||||
const emailVerified = req.user.emailVerified;
|
||||
if (!email) {
|
||||
req.flash('info', { msg:
|
||||
dedent `Please update your email address when you get a moment in
|
||||
your <a href="\settings"> Settings Page.</a>`
|
||||
});
|
||||
} else if (!emailVerified) {
|
||||
req.flash('info', { msg:
|
||||
dedent `We have your email address with us, but its not yet verified.
|
||||
Please follow the link we sent you, when you get a moment.`
|
||||
});
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
@ -46,10 +46,10 @@ block content
|
||||
}
|
||||
})
|
||||
.done(data =>{
|
||||
if(data.status && data.status.count){
|
||||
$('#flash-content').html("Your email has been updated successfully!");
|
||||
if(data && data.message){
|
||||
$('#flash-content').html(data.message);
|
||||
$('#flash-board')
|
||||
.removeClass('alert-danger')
|
||||
.removeClass('alert-info')
|
||||
.addClass('alert-success')
|
||||
.fadeIn();
|
||||
}
|
||||
|
24
server/views/emails/a-extend-user-welcome.ejs
Normal file
24
server/views/emails/a-extend-user-welcome.ejs
Normal file
@ -0,0 +1,24 @@
|
||||
<h2>
|
||||
Greetings from San Francisco!
|
||||
</h2>
|
||||
<p>
|
||||
Thank you for joining our community.
|
||||
</p>
|
||||
<p>
|
||||
Please verify your email by following the link below:
|
||||
</p>
|
||||
<p>
|
||||
<a href="<%= verifyHref %>"><%= verifyHref %></a>
|
||||
</p>
|
||||
<p>
|
||||
Feel free to email us at this address if you have any questions about Free Code Camp.
|
||||
</p>
|
||||
<p>
|
||||
And if you have a moment, check out our blog: https://medium.freecodecamp.com.
|
||||
</p>
|
||||
<p>
|
||||
Good luck with the challenges!
|
||||
</p>
|
||||
<p>
|
||||
- the Free Code Camp Team.
|
||||
</p>
|
15
server/views/emails/user-email-verify.ejs
Normal file
15
server/views/emails/user-email-verify.ejs
Normal file
@ -0,0 +1,15 @@
|
||||
<h3>
|
||||
Thank you, for updating you contact details.
|
||||
</h3>
|
||||
<p>
|
||||
Please verify your email by following the link below:
|
||||
</p>
|
||||
<p>
|
||||
<a href="<%= verifyHref %>"><%= verifyHref %></a>
|
||||
</p>
|
||||
<p>
|
||||
Feel free to email us at this address if you have any questions about Free Code Camp.
|
||||
</p>
|
||||
<p>
|
||||
- the Free Code Camp Team.
|
||||
</p>
|
Reference in New Issue
Block a user