Skip to content

Commit

Permalink
Merge pull request #20 from andela/Ft-signup-email-168781676
Browse files Browse the repository at this point in the history
#168781676-email signup verification feature
  • Loading branch information
nakiwuge committed Oct 13, 2019
2 parents 65a76d3 + 4cef200 commit 559dbf2
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 76 deletions.
198 changes: 132 additions & 66 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"dev": "NODE_ENV=development babel-watch src/index.js",
"production": "babel src --out-dir build && node build/index.js",
"production": "NODE_ENV=production babel src --out-dir build && node build/index.js",
"lint": "eslint . --ext .js",
"db:refresh": "NODE_ENV=production sequelize db:migrate:undo:all && sequelize db:migrate && sequelize db:seed:all",
"db:seed": "NODE_ENV=production sequelize db:seed:all",
Expand Down Expand Up @@ -42,6 +42,7 @@
"@babel/register": "^7.4.4",
"@babel/runtime": "^7.6.2",
"@hapi/joi": "^16.1.7",
"@sendgrid/mail": "^6.4.0",
"babel-watch": "^7.0.0",
"bcrypt": "^3.0.6",
"chai": "^4.2.0",
Expand Down
50 changes: 45 additions & 5 deletions src/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
import sgMail from '@sendgrid/mail';
import bcrypt from 'bcrypt';
import models from '../database/models';
import responseUtil from '../utils/responseUtil';
Expand All @@ -6,28 +9,65 @@ import strings from '../utils/stringsUtil';
import hashPassword from '../utils/hashPassword';
import generateToken from '../utils/generateToken';

const { users } = models;

dotenv.config();
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

export default class UserController {
static signup({ body: { username, email, password } }, res) {
static signup(req, res) {
const { username, email, password } = req.body;
const APP_URL_BACKEND = `${req.protocol}://${req.headers.host}`;
const user = models.users.build({
username,
email,
password: hashPassword(password)
});

user.save().then(user => {
const token = generateToken(user);
const msg = {
to: email,
from: 'BarefootNomad@gmail.com',
subject: 'Account Verification',
html: `<div style="font-family:Avenir,Helvetica,sans-serif;box-sizing:border-box;padding:35px;">
<h1 style="color: #848484;">Barefoot Nomad</h1>
<p style="font-family:Avenir,Helvetica,sans-serif;box-sizing:border-box;color:#74787e;font-size:16px;line-height:1.5em;margin-top:0;text-align:left">Welcome ${username},<br> We are happy to be with you. Please verify your mail .<br> Click the button below to verify your new account.</p>
<p><a style="background-color: #3097d1; border: 2px solid #3097d1; padding: 8px; color: #fff; font-size: 16px; text-decoration: none;cursor: pointer;" href="${APP_URL_BACKEND}/api/v1/users/verify/${token}">Verify Account</a>
</a></p>
<p style="color:#74787e;font-size:16px;line-height:1.5em;margin-top:0;text-align:left">Thank you for using our system!</p>
<p style="color:#74787e;font-size:16px;line-height:1.5em;margin-top:0;">Regards,<br>Barefoot Nomad Caret Team</p>
</div>`
};
sgMail.send(msg);

return responseUtil(res, 201, strings.users.success.SIGNUP_SUCCESS, {
user_id: user.id,
username: user.username,
email: user.email,
created_at: user.createdAt.toString(),
updated_at: user.updatedAt.toString(),
token,
});
});
}

static async userVerify({ params: { token } }, res) {
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
try {
const user = await users.findOne({ where: { id: decodedToken.payload.id } });
if (!user) {
return responseUtil(res, 404, strings.users.error.USE_NOT_REGISTERED);
}
const updatedUser = await users.update(
{ isVerified: true },
{ where: { id: decodedToken.payload.id } }
);

if (updatedUser) {
return responseUtil(res, 200, strings.users.success.SUCCESS_VERIFIED, { token });
}
} catch (error) {
return responseUtil(res, 500, strings.users.error.SOMETHING_WRONG);
}
}

static async signIn(req, res) {
const { email, password } = req.body;
const user = await models.users.findOne({ where: { email } });
Expand Down
3 changes: 1 addition & 2 deletions src/database/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ module.exports = {
password: process.env.DB_PASSWORD,
database: process.env.DEVELOPMENT_DB,
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
dialect: 'postgres',
},
test: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.TEST_DB,
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
dialect: 'postgres',
logging: null,
},
Expand All @@ -23,6 +21,7 @@ module.exports = {
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.host,
use_env_variable: 'DATABASE_URL',
dialect: 'postgres',
logging: null,
}
Expand Down
23 changes: 22 additions & 1 deletion src/routes/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,30 @@ const router = express.Router();
* description: User logged in successfully!
* '400':
* description: Incorrect email or password!
*
*/
/**
* @swagger
* /users/verify/{token}:
* get:
* tags:
* - Authentication
* name: Email Verification
* summary: Email Verification
* consumes:
* - application/json
* produces:
* - application/json
* parameters:
* - name: token
* in: path
* responses:
* '200':
* description: User successfully verified!
* '404':
* description: User no registered!
*/
router.post('/register', checkSignup, verifyExist, confirmPassword, signup);
router.get('/verify/:token', UserController.userVerify);
router.post('/login', checkLogin, signIn);

export default router;
2 changes: 2 additions & 0 deletions src/tests/mockdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const testdata = {
confirmPassword: 'Pa$5W0rd',
},

verifyUser: { id: 3, isVerified: false },

missingEmail: {
username: 'caretdevs1',
password: 'Pa$5W0rd',
Expand Down
15 changes: 15 additions & 0 deletions src/tests/signupTest.spec.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import { describe, it } from 'mocha';
import strings from '../utils/stringsUtil';
import app from '../index';
import testdata from './mockdata';
import generateToken from '../utils/generateToken';


const token = generateToken(testdata.verifyUser);

chai.should();
chai.use(chaiHttp);
Expand Down Expand Up @@ -57,4 +62,14 @@ describe('Signup Test Suite', () => {
done();
});
});
it('it should verify a user', done => {
chai.request(app)
.get(`/api/v1/users/verify/${token}`)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.should.have.property('message').eql(`${strings.users.success.SUCCESS_VERIFIED}`);
done();
});
});
});
1 change: 1 addition & 0 deletions src/utils/generateToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const generateToken = user => {
{
payload: {
id: user.id,
isVerified: user.isVerified,
},
},
process.env.JWT_SECRET,
Expand Down
5 changes: 4 additions & 1 deletion src/utils/stringsUtil.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
const strings = {
users: {
success: {
SIGNUP_SUCCESS: 'User Created Successfully',
SIGNUP_SUCCESS: 'User Created Successfully, please check your email for verification',
SUCCESS_VERIFIED: 'You have been verified',
SUCCESSFUL_LOGIN: 'User logged in successfully!'
},
error: {
BAD_SIGNUP_REQUEST: 'Bad Request, Unable to sign up user',
USER_ALREADY_EXISTS: 'User with this email already exists',
USE_NOT_REGISTERED: 'user not registered',
USERNAME_ALREADY_EXISTS: 'User with this username already exists',
PASSWORD_NOT_MATCH: 'Password and Confirm Password do not match',
SOMETHING_WRONG: 'Internal server error',
BAD_LOGIN_REQUEST: 'Bad Request, Unable to login user',
LOGIN_FAILURE: 'Incorrect email or password!',
VERIFY_FIRST: 'Please verify your email first!',
Expand Down

0 comments on commit 559dbf2

Please sign in to comment.