Skip to content

Commit

Permalink
Merge 5b0897e into 0941dce
Browse files Browse the repository at this point in the history
  • Loading branch information
eliemugenzi committed Jun 13, 2019
2 parents 0941dce + 5b0897e commit d2e7c44
Show file tree
Hide file tree
Showing 25 changed files with 731 additions and 125 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ LOCAL_DB_USER=
LOCAL_DB_PASSWORD=
LOCAL_DB_NAME=
TEST_DATABASE_URL=
AUTHOSHAVEN_USER=teslauthorshaven@gmail.com
AUTHOSHAVEN_PASS=authorshaven.005
BASE_URL=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ coverage
.node_repl_history

.env

package-lock.json
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

language: node_js
node_js:
- "stable"
Expand All @@ -12,4 +13,4 @@ before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.16.0
- export PATH="$HOME/.yarn/bin:$PATH"
after_success:
- yarn run coverage
- yarn run coverage
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
"serve": "node dist/index.js",
"start": "node dist/index.js",
"test": "nyc --reporter=html --reporter=text mocha ./test/*.js --exit --require @babel/register --require regenerator-runtime",
"coverage": "nyc report --reporter=text-lcov | coveralls"
"coverage": "nyc report --reporter=text-lcov | coveralls",
"test:local": "yarn undo && yarn migrate && yarn seed && yarn test",
"migrate": "sequelize db:migrate",
"undo": "sequelize db:migrate:undo",
"seed": "sequelize db:seed:all"
},
"author": "Andela Simulations Programme",
"license": "MIT",
"dependencies": {
"@hapi/joi": "^15.0.3",
"bcrypt": "^3.0.6",
"body-parser": "^1.18.3",
"cors": "^2.8.4",
"dotenv": "^8.0.0",
Expand All @@ -29,6 +34,7 @@
"method-override": "^2.3.10",
"methods": "^1.1.2",
"morgan": "^1.9.1",
"nodemailer": "^6.2.1",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"pg": "^7.11.0",
Expand Down
93 changes: 90 additions & 3 deletions src/api/controllers/auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,91 @@
import db from '../../sequelize/models';
import TokenHelper from '../../helpers/Token.helper';
import Mailhelper from '../../helpers/SendMail.helper';
import HashHelper from '../../helpers/hashHelper';

export default {
signup: () => {},
};
const { User } = db;

/**
* @author Elie Mugenzi
* @class AuthController
* @description this class performs the whole authentication
*/
class AuthController {
/**
*
* @param {Object} req - Request object
* @param {Object} res - Response object
* @returns {Object} - Response object
*/
static async register(req, res) {
const {
firstName, lastName, email, password, username, dob, bio, gender
} = req.body;
if (Object.keys(req.body).length === 0) {
return res.status(400).json({
status: 400,
error: 'No data sent'
});
}
const newUser = await User.create({
firstName,
lastName,
email,
password: HashHelper.hashPassword(password),
username,
dob,
bio,
gender,
verified: false
});
if (newUser) {
const token = await TokenHelper.generateToken({ user: newUser });
Mailhelper.sendMail({
to: newUser.email,
names: `${newUser.firstName} ${newUser.lastName}`,
subject: 'Welcome to Authorshaven',
message: 'Thank you for choosing Authorshaven',
token
});

res.status(201).json({
status: 201,
message: 'We have sent an email to you to verify your account',
token,
});
}
}

/**
* Verifies account
* @param {Object} req - Request
* @param {*} res - Response
* @returns {Object} - Response
*/
static async verifyAccount(req, res) {
const { token } = req.query;
try {
const user = await TokenHelper.decodeToken(token);
const verifiedUser = await User.update({
verified: true
}, {
where: {
email: user.user.email
}
});
if (verifiedUser) {
res.status(202).json({
status: 202,
message: 'Account is now verified!'
});
}
} catch (err) {
res.status(400).json({
status: 400,
error: 'Invalid Request'
});
}
}
}

export default AuthController;
6 changes: 4 additions & 2 deletions src/api/routes/authRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { Router } from 'express';
import authController from '../controllers/auth';
import validateBody from '../../middleware/validateBody';
import userValidation from '../../middleware/validUser';
import validateGender from '../../middleware/validateGender';

const authRouter = Router();
const { signup } = authController;
const { usernameExists, emailExists } = userValidation;
const { register, verifyAccount } = authController;

authRouter.post('/signup', validateBody('signup'), usernameExists, emailExists, signup);
authRouter.post('/signup', validateBody('signup'), validateGender, usernameExists, emailExists, register);
authRouter.get('/verify', verifyAccount);

export default authRouter;
23 changes: 23 additions & 0 deletions src/config/environments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@


const environments = {
development: {
dbUrl: process.env.DATABASE_URL,
},
staging: {
dbUrl: process.env.DATABASE_URL,
},
test: {
dbUrl: process.env.DATABASE_URL,
},
};

// Determine which environment we are in
const currentEnvironment = typeof (process.env.NODE_ENV) === 'string' ? process.env.NODE_ENV.toLowerCase() : '';

// Check that the current environment is one the envs defined above, if not default to development
const environment = typeof (environments[currentEnvironment]) === 'object'
? environments[currentEnvironment] : environments.development;

// Export the selected environment configuration object
export default environment;
35 changes: 35 additions & 0 deletions src/db/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Sequelize from 'sequelize';
import dbUrlParser from '../helpers/dbUrlParser';
import environment from '../config/environments';

const {
dbUser,
dbPassword,
dbName,
dbHost
} = dbUrlParser(environment.dbUrl);

const sequelize = new Sequelize(
dbName,
dbUser,
dbPassword,
{
dialect: 'postgres',
host: dbHost,
logging: false,
},
);

const models = {
// User: sequelize.import('../models/user'),
};

Object.keys(models).forEach((key) => {
if ('associate' in models[key]) {
models[key].associate(models);
}
});

export { sequelize };

export default models;
7 changes: 7 additions & 0 deletions src/helpers/Env.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import dotenv from 'dotenv';

dotenv.config();

const env = (param, value) => process.env[param] || value;

export default env;
45 changes: 45 additions & 0 deletions src/helpers/MailTemplate.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import dotenv from 'dotenv';
import env from './Env.helper';

dotenv.config();

const mailTemplate = ({
to,
token,
names
}) => {
const template = `
<div style="background:#e5eeff;width:100%;padding:20px 0;">
<div style="max-width:760px;margin:0 auto;background:#ffffff">
<div style="background:#266cef;padding:10px;color:#ffffff;text-align:center;font-size:34px">
Authors Haven - Team Tesla
</div>
<div style="padding:0;">
</div>
<div style="padding:20px;text-align:left;">
<p>
Well ${names}, congratulations for choosing AuthorsHaven.
To verify that ${to} is your email, could you please click this link below to verify your AuthorsHaven's account?
<br/>
<a href="${env('BASE_URL')}/api/auth/verify/?token=${token}">Click here to verify your account</a>
<br/>
Here there is the link below where you can visit Andela and get more information about what's Andela
</p>
<a href="https://andela.com">Visit Andela's website</a>
</div>
<br>
<div style="padding:20px;text-align:left;">
<b>Andela, Team @Tesla - Cohort 5</b>
</div>
</div>
<div style="padding:35px 10px;text-align:center;">
Copyright, 2019<br>
Andela, Team Tesla
</div>
</div>
`;
return template;
};

export default mailTemplate;
39 changes: 39 additions & 0 deletions src/helpers/SendMail.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

import mailer from 'nodemailer';
import env from './Env.helper';
import mailTemplate from './MailTemplate.helper';

const transporter = mailer.createTransport({
service: 'gmail',
auth: {
user: env('AUTHOSHAVEN_USER'),
pass: env('AUTHOSHAVEN_PASS')
}
});
/**
* @author Elie Mugenzi
* @class MailHelper
* @description A helper class for sending emails
*/
class MailHelper {
/**
* Send mail
* @param {Object} param0 - Object which contains email information
* @returns {Object} Results after sending mail
*/
static async sendMail({
to, names, subject, message, token
}) {
const msg = {
from: `Authors Haven<${env('AUTHOSHAVEN_USER')}>`,
to,
subject,
text: message,
html: mailTemplate({ to, token, names })
};
const result = await transporter.sendMail(msg);
return result;
}
}

export default MailHelper;
2 changes: 0 additions & 2 deletions src/helpers/Token.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,4 @@ class Tokenizer {
return user;
}
}


export default Tokenizer;
2 changes: 1 addition & 1 deletion src/helpers/dbUrlParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import url from 'url';

dotenv.config();

export default async (dbUrl) => {
export default (dbUrl) => {
const urlObj = url.parse(dbUrl);
const {
auth,
Expand Down
27 changes: 27 additions & 0 deletions src/helpers/hashHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import bcrypt from 'bcrypt';

/**
* @class HashHelper
*/
class HashHelper {
/**
* Hashes password
* @param {String} password - Password to hash
* @returns {String} - hashed Password
*/
static hashPassword(password) {
return bcrypt.hashSync(password, 8);
}

/**
* Compares Passwords
* @param {String} password - Password provided by a user
* @param {String} passwordToCompare - Password from Database
* @returns {Boolean} -True if they're equal, otherwise false
*/
static comparePassword(password, passwordToCompare) {
return bcrypt.compareSync(password, passwordToCompare);
}
}

export default HashHelper;
Loading

0 comments on commit d2e7c44

Please sign in to comment.