Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add validation for request input (inc. email) and dockerized file #4

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM node:latest
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
EXPOSE 8099
CMD ["npm", "start"]
160 changes: 111 additions & 49 deletions api/controller/mailingController.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,99 @@
const debug = require('debug')('app:mailingController');
const nodemailer = require('nodemailer');
const { config } = require('dotenv');

config();

function mailingController() {
function sendMail(req, res) {
(async function mail() {
try {
let { recipients, subject, body, cc, bcc } = req.body
debug(recipients, subject, body)
if (!recipients || !subject || !body) {
res.status(400).send({
status: false,
message: 'These fields are required'
})
return
}
let { recipients, subject, body, cc, bcc } = req.body;
debug(recipients, subject, body);

// request body paarameters now being validated in validation middleware
// if (!recipients || !subject || !body) {
// res.status(400).send({
// status: false,
// message: 'These fields are required',
// });
// return;
// }

// let mailOptions = {
// from: 'Team Fierce Mailing API <[email protected]>',
// to: recipients,
// cc: [],
// bcc: [],
// subject: subject,
// text: body,
// };

let mailOptions = {
from: 'Team Fierce Mailing API <[email protected]>',
from: `Team Fierce Mailing API ${process.env.USER}`,
to: recipients,
cc: [],
bcc: [],
subject: subject,
cc,
bcc,
subject,
text: body,
};

// let transporter = nodemailer.createTransport({
// service: 'gmail',
// auth: {
// user: process.env.USER,
// pass: process.env.PASSWORD,
// },
// });

let transporter = nodemailer.createTransport({
service: 'gmail',
host: 'smtp.gmail.com',
port: 587,
secure: false,
requireTLS: true,
auth: {
user: process.env.USER,
pass: process.env.PASSWORD
}
pass: process.env.PASSWORD,
},
});

transporter.sendMail(mailOptions, function (err, info) {
if (err) debug(err);
debug(`Email sent: ${info.response}`);
res.status(200).json({ status: 'success', data: {message: 'mail sent successfully'} });
})

transporter.sendMail(mailOptions, function(err, info) {
if (err) {
debug(err);
res.status(500).json({
status: 'error',
error: {
message: err.message
}
})
}else {
debug(`Email sent: ${info.response}`);
res.status(200).json({
status: 'success',
data: { message: 'mail sent successfully' },
});
}
});
} catch (err) {
debug(err.stack)
debug(err.stack);
}
}());
})();
}

function sendMailWithTemplate(req, res) {
(async function mail() {
try {
let { recipients, subject, body, cc, bcc } = req.body
debug(recipients, subject, body)
if (!recipients || !subject || !body) {
res.status(400).send({
status: false,
message: 'These fields are required'
})
return
}
let { recipients, subject, body, cc, bcc } = req.body;
debug(recipients, subject, body);

// request body paarameters now being validated in validation middleware
// if (!recipients || !subject || !body) {
// res.status(400).send({
// status: false,
// message: 'These fields are required'
// })
// return
// }
// if (recipients.match(mailFormat)) {
// res.json({msg: true})
// res.status(400).send({
Expand All @@ -65,38 +103,62 @@ function mailingController() {
// return
// }

// let mailOptions = {
// from: process.env.USER,
// to: recipients,
// bcc: [],
// subject: subject,
// html: body,
// };

let mailOptions = {
from: 'Team Fierce Mailing API <[email protected]>',
from: `Team Fierce Mailing API ${process.env.USER}`,
to: recipients,
bcc: [],
subject: subject,
html: body
}
cc,
bcc,
subject,
text: body,
};

// let transporter = nodemailer.createTransport({
// service: 'gmail',
// auth: {
// user: process.env.USER,
// pass: process.env.PASSWORD,
// },
// });

let transporter = nodemailer.createTransport({
service: 'gmail',
host: 'smtp.gmail.com',
port: 587,
secure: false,
requireTLS: true,
auth: {
user: process.env.USER,
pass: process.env.PASSWORD
}
pass: process.env.PASSWORD,
},
});

transporter.sendMail(mailOptions, function (err, info) {
transporter.sendMail(mailOptions, function(err, info) {
if (err) debug(err);
debug(`Email sent: ${info.response}`);
res.status(200).json({ status: 'success', data: {message: 'mail sent successfully'} });
})

res
.status(200)
.json({
status: 'success',
data: { message: 'mail sent successfully' },
});
});
} catch (err) {
debug(err.stack)
debug(err.stack);
}
}());
})();
}

return {
sendMail,
sendMailWithTemplate
sendMailWithTemplate,
};
}

module.exports = mailingController
module.exports = mailingController;
15 changes: 15 additions & 0 deletions api/helpers/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = class ApplicationError extends Error {
/**
* @description initializes the error class
*
* @param {number} statusCode status code of the request
* @param {string} message error message
* @param {array} errors an array containing errors
*/
constructor(statusCode, message = 'an error occurred', errors) {
super(message);
this.statusCode = statusCode || 500;
this.message = message;
this.errors = errors;
}
};
44 changes: 44 additions & 0 deletions api/middleware/validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { matchedData, validationResult } = require('express-validator');
const ApplicationError = require('../helpers/errors');

/**
* @description express-validator schema validator
*
* @param {Array} schema
* @param {Number} status - http statusCode
*
* @returns {Array} array of validation results and middleware
*/
module.exports = (schemas, status = 400) => {
const validationCheck = async (request, response, next) => {
const errors = validationResult(request);
request = { ...request, ...matchedData(request) };

if (!errors.isEmpty()) {
const mappedErrors = Object.entries(errors.mapped()).reduce(
(accumulator, [key, value]) => {
accumulator[key] = value.msg;
return accumulator;
},
{}
);

const validationErrors = new ApplicationError(
status,
'validation error',
mappedErrors
);

return response.status(400).json({
status: 'error',
error: {
validationErrors,
},
});
}

return next();
};

return [...(schemas.length && [schemas]), validationCheck];
};
14 changes: 9 additions & 5 deletions api/routes/mailingRoutes.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
const express = require('express');
const mailingRouter = express.Router();
const mailingController = require('../controller/mailingController')
const mailingController = require('../controller/mailingController');
const { sendMailSchema } = require('../validation/mail.validation');
const validator = require('../middleware/validator');

function router() {
const { sendMail, sendMailWithTemplate } = mailingController()
const { sendMail, sendMailWithTemplate } = mailingController();

mailingRouter.route('/sendmail').post(sendMail)
mailingRouter.route('/sendmailwithtemplate').post(sendMailWithTemplate)
mailingRouter.route('/sendmail').post(validator(sendMailSchema), sendMail);
mailingRouter
.route('/sendmailwithtemplate')
.post(validator(sendMailSchema), sendMailWithTemplate);

return mailingRouter
return mailingRouter;
}

module.exports = router;
42 changes: 42 additions & 0 deletions api/validation/mail.validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const { check } = require('express-validator');

const requiredString = 'This field is required';
const emailString = 'Value is not a valid email';

module.exports = {
sendMailSchema: [
check('recipients')
.trim()
.not()
.isEmpty()
.withMessage(requiredString)
.isEmail()
.withMessage(emailString),

check('subject')
.trim()
.not()
.isEmpty()
.withMessage(requiredString)
.isString(),

check('body')
.trim()
.not()
.isEmpty()
.withMessage(requiredString)
.isString(),

check('cc')
.optional()
.trim()
.isEmail()
.withMessage(emailString),

check('bcc')
.optional()
.trim()
.isEmail()
.withMessage(emailString),
],
};
5 changes: 4 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const express = require('express')
const morgan = require('morgan'); //logger
const bodyParser = require('body-parser')
const swaggerUI = require('swagger-ui-express');
const docs = require('./docs/email-api.json')
require('dotenv').config()

const app = express();
Expand All @@ -13,12 +15,13 @@ app.use(morgan('tiny'))
const mailingRouter = require('./api/routes/mailingRoutes')()

app.use('/api/v1', mailingRouter);
app.use('/docs', swaggerUI.serve, swaggerUI.setup(docs));

app.get('/', (req, res) => {
res.send('home')
});

port = 4000
port = 8099;
app.listen(port, function () {
console.log(`Listening on port ${port}...`)
})
Expand Down
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3'
services:
app:
container_name: team-fierce
restart: always
build: .
ports:
- '8099:8099'
Loading