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:
Mrugesh Mohapatra
2016-05-07 17:46:39 +05:30
parent bbacbd3d81
commit ff4dfb09da
8 changed files with 135 additions and 64 deletions

View File

@ -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(

View File

@ -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); }

View File

@ -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);

View File

@ -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);

View File

@ -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();
}

View File

@ -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();
}

View 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>

View 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>