Skip to content

Commit

Permalink
ready to use
Browse files Browse the repository at this point in the history
  • Loading branch information
ItzSamdam committed Jul 3, 2023
1 parent 80e966b commit 661c73a
Show file tree
Hide file tree
Showing 16 changed files with 161 additions and 14 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon src/server.js",
"start": "cross-env NODE_ENV=production node src/server.js",
"test": "mocha './test/**/*.test.js' --watch"
"test": "mocha './tests/**/*.specs.js' --watch"
},
"keywords": [
"nodejs",
Expand All @@ -30,12 +30,14 @@
"mailgun.js": "^9.1.1",
"md5": "^2.3.0",
"moment": "^2.29.2",
"multer": "^1.4.5-lts.1",
"mysql2": "^2.3.3",
"node-cron": "^3.0.2",
"nodemon": "^2.0.13",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"redis": "^4.6.7",
"sanitize-html": "^2.11.0",
"sequelize": "^6.32.1",
"sequelize-cli": "^6.2.0",
"socket.io": "^4.4.1",
Expand Down
6 changes: 5 additions & 1 deletion src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ app.use(passport.initialize());
passport.use('jwt', jwtStrategy);

app.get('/', async (req, res) => {
res.status(200).send('Congratulations! You\'re Live!');
const data = {
message: 'Congratulations! You\'re Live!',
uptime: 'Server Running since '+ Date()
}
res.status(200).send(data);
});
app.use('/api', routes);

Expand Down
2 changes: 2 additions & 0 deletions src/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ const dotenv = require('dotenv');
const path = require('path');
const Joi = require('joi');

//link dotenv
dotenv.config({ path: path.join(__dirname, '../../.env') });

//env validation sequence
const envValidation = Joi.object()
.keys({
NODE_ENV: Joi.string().valid('development', 'production', 'test').required(),
Expand Down
2 changes: 2 additions & 0 deletions src/config/constant.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//system constant for user
const userConstant = {
EMAIL_VERIFIED_TRUE: 1,
EMAIL_VERIFIED_FALSE: 0,
STATUS_ACTIVE: 1,
STATUS_INACTIVE: 0,
STATUS_REMOVED: 2,
};
//verification code constant
const verificationCodeConstant = {
TYPE_EMAIL_VERIFICATION: 1,
TYPE_RESET_PASSWORD: 2,
Expand Down
3 changes: 3 additions & 0 deletions src/config/database.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const config = require('./config');

module.exports = {
//dev credentials
development: {
username: config.dbUser,
password: config.dbPass,
Expand All @@ -11,6 +12,7 @@ module.exports = {
bigNumberStrings: true,
},
},
//staging credentials
test: {
username: config.dbUser,
password: config.dbPass,
Expand All @@ -21,6 +23,7 @@ module.exports = {
bigNumberStrings: true,
},
},
//live credentials
production: {
username: config.dbUser,
password: config.dbPass,
Expand Down
2 changes: 2 additions & 0 deletions src/config/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
const config = require('./config');


const enumerateErrorFormat = winston.format((info) => {
if (info.message instanceof Error) {
info.message = {
Expand Down Expand Up @@ -29,6 +30,7 @@ transport.on('rotate', (oldFilename, newFilename) => {
// call function like upload to s3 or on cloud
});

//system logger
const logger = winston.createLogger({
format: winston.format.combine(enumerateErrorFormat(), winston.format.json()),
transports: [
Expand Down
5 changes: 1 addition & 4 deletions src/config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const jwtOptions = {
passReqToCallback: true,
};

//authentication sequence
const jwtVerify = async (req, payload, done) => {
try {
if (payload.type !== tokenTypes.ACCESS) {
Expand All @@ -36,25 +37,21 @@ const jwtVerify = async (req, payload, done) => {
blacklisted: false,
});
}

if (!tokenDoc) {
return done(null, false);
}
let user = await redisService.getUser(payload.sub);
if (user) {
user = new User(user);
}

if (!user) {
console.log('User Cache Missed!');
user = await userDaom.findOneByWhere({ uuid: payload.sub });
redisService.setUser(user);
}

if (!user) {
return done(null, false);
}

done(null, user);
} catch (error) {
console.log(error);
Expand Down
1 change: 1 addition & 0 deletions src/config/tokens.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//token type
const tokenTypes = {
ACCESS: 'access',
REFRESH: 'refresh',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const UserService = require('../services/UserService');
const logger = require('../config/logger');
const { tokenTypes } = require('../config/tokens');

class AuthController {
class UserController {
constructor() {
this.userService = new UserService();
this.tokenService = new TokenService();
Expand Down Expand Up @@ -96,4 +96,4 @@ class AuthController {
};
}

module.exports = AuthController;
module.exports = UserController;
2 changes: 1 addition & 1 deletion src/database/seeders/20230602054765_create_user_seeder.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
email: 'sowadayo@example.com',
status: 1,
email_verified: 1,
password: bcrypt.hashSync('123456', 8),
password: bcrypt.hashSync('secureNode123', 8),
created_at: new Date(),
updated_at: new Date(),
},
Expand Down
2 changes: 1 addition & 1 deletion src/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const express = require('express');
const authRoute = require('./authRoute');
const authRoute = require('./userRoute');

const router = express.Router();

Expand Down
2 changes: 1 addition & 1 deletion src/routes/authRoute.js → src/routes/userRoute.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const express = require('express');
const AuthController = require('../controllers/AuthController');
const AuthController = require('../controllers/UserController');
const UserValidator = require('../validators/UserValidator');

const router = express.Router();
Expand Down
5 changes: 2 additions & 3 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const app = require('./app');
const config = require('./config/config');

console.log('Node Js Starter with Sequelize!!');
console.log('Node Js Starter with Sequelize ORM!!');
require('./cronJobs');
// eslint-disable-next-line import/order
const http = require('http');
Expand All @@ -14,6 +14,5 @@ global.io = io;
require('./config/rootSocket')(io);

server.listen(config.port, () => {
console.log('SERVER');
console.log(`Listening to port ${config.port}`);
console.log(`Server Listening on port ${config.port}`);
});
24 changes: 24 additions & 0 deletions src/utilities/mediaUpload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const multer = require('multer');
const fs = require('fs');
const storage = multer.diskStorage({
destination: function (req, file, callback) {
const destPath = req.uploadPath;
if (!fs.existsSync(destPath))
fs.mkdirSync(destPath);
callback(null, destPath);
},
filename: function (req, file, callback) {
const parts = file.originalname.split(".");
const extension = parts[parts.length - 1];
let fileName = file.fieldname + '-' + Date.now();
if (extension === 'png' || extension === 'jpeg' || extension === 'jpg')
fileName += '.' + extension;

callback(null, fileName);
}
});


const upload = multer({storage: storage});

module.exports = {upload};
8 changes: 8 additions & 0 deletions src/utilities/sanitize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const sanitizeHtml = require('sanitize-html');
exports.sanitizeInput = (dirty, options) => {
const htmlSanitizeOptions = {
allowedTags: [], allowedAttributes: []
};

return sanitizeHtml(dirty, options || htmlSanitizeOptions);
};
103 changes: 103 additions & 0 deletions tests/Authentication/AuthService.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const chai = require('chai');

const { expect } = chai;
const sinon = require('sinon');
const httpStatus = require('http-status');
const AuthService = require('../../src/services/AuthService');
const UserDaom = require('../../src/daom/UserDaom');
const models = require('../../src/models');

const User = models.user;
const bcrypt = require('bcryptjs');

let authService;
const loginData = {
email: 'example@mail.com',
password: '123123Asd',
};
const userData = {
first_name: 'Samuel',
last_name: 'Owadayo',
email: 'example@mail.com',
uuid: '4d85f12b-6e5b-468b-a971-eabe8acc9d08',
};
describe('User Login test', () => {
beforeEach(() => {
authService = new AuthService();
});
afterEach(() => {
sinon.restore();
});

it('User Login successfully', async () => {
const expectedResponse = {
statusCode: httpStatus.OK,
response: {
status: true,
code: httpStatus.OK,
message: 'Login Successful',
data: {
id: 1,
first_name: 'Samuel',
last_name: 'Owadayo',
email: 'example@mail.com',
email_verified: 1,
uuid: '4d85f12b-6e5b-468b-a971-eabe8acc9d08',
},
},
};
userData.id = 1;
userData.password = bcrypt.hashSync(loginData.password, 8);
userData.email_verified = 1;
const userModel = new User(userData);

sinon.stub(UserDaom.prototype, 'findByEmail').callsFake((email) => {
return userModel;
});
const userLogin = await authService.loginWithEmailPassword(
loginData.email,
loginData.password,
);
expect(userLogin).to.deep.include(expectedResponse);
});

it('should show INVALID EMAIL ADDRESS message', async () => {
const expectedResponse = {
statusCode: httpStatus.BAD_REQUEST,
response: {
status: false,
code: httpStatus.BAD_REQUEST,
message: 'Invalid Email Address!',
},
};

sinon.stub(UserDaom.prototype, 'findByEmail').callsFake(() => {
return null;
});
const response = await authService.loginWithEmailPassword('test@mail.com', '23232132');
expect(response).to.deep.include(expectedResponse);
});

it('Wrong Password', async () => {
const expectedResponse = {
statusCode: httpStatus.BAD_REQUEST,
response: {
status: false,
code: httpStatus.BAD_REQUEST,
message: 'Wrong Password!',
},
};
userData.id = 1;
userData.password = bcrypt.hashSync('2322342343', 8);
userData.email_verified = 1;
const userModel = new User(userData);
sinon.stub(UserDaom.prototype, 'findByEmail').callsFake((email) => {
return userModel;
});
const userLogin = await authService.loginWithEmailPassword(
loginData.email,
loginData.password,
);
expect(userLogin).to.deep.include(expectedResponse);
});
});

0 comments on commit 661c73a

Please # to comment.