EN-60: Adding simplified Sign Up/Login/Renew/Recover process.
This commit is contained in:
@@ -31,12 +31,14 @@
|
|||||||
"express-jwt": "^8.4.1",
|
"express-jwt": "^8.4.1",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"helmet": "^7.0.0",
|
"helmet": "^7.0.0",
|
||||||
|
"jsonschema": "^1.4.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"knex": "^2.5.1",
|
"knex": "^2.5.1",
|
||||||
"mongodb-core": "^3.2.7",
|
"mongodb-core": "^3.2.7",
|
||||||
"mongoose": "^7.5.4",
|
"mongoose": "^7.5.4",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"nodemailer": "^6.9.5",
|
"nodemailer": "^6.9.5",
|
||||||
|
"nodemailer-sendgrid": "^1.0.3",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"objection": "^3.1.2",
|
"objection": "^3.1.2",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
|||||||
14
src/apps/public/account/routes.js
Normal file
14
src/apps/public/account/routes.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
'use strict';
|
||||||
|
const router = require('express').Router();
|
||||||
|
const services= require('./services.js');
|
||||||
|
|
||||||
|
router.post('/authorize', services.AuthorizeJWT);
|
||||||
|
router.get('/authorize/:session_token', services.RenewJWT);
|
||||||
|
|
||||||
|
router.post('/signup', services.TryCreateAccount);
|
||||||
|
router.patch('/signup', services.ConfirmAccount);
|
||||||
|
|
||||||
|
router.post('/recover', services.RecoverPwd);
|
||||||
|
router.patch('/recover', services.ConfirmRecoverPwd);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
263
src/apps/public/account/services.js
Normal file
263
src/apps/public/account/services.js
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
"use strict";
|
||||||
|
const jsonwebtoken = require('jsonwebtoken');
|
||||||
|
const { API_CONFIG, ROOT_PATH, LIB_PATH, HANDLERS_PATH } = process.env;
|
||||||
|
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
||||||
|
const { genKey, toSha256 } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
|
||||||
|
const { emailEvent , EMAIL_EVENTS } = require( `${ROOT_PATH}/${HANDLERS_PATH}/MailClient` );
|
||||||
|
const { create_account, already_exists, login, login_with_session_token, reset_password } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Account` );
|
||||||
|
const { Validator } = require( "jsonschema" );
|
||||||
|
|
||||||
|
const jwtSecret = apiConfig.authentication.jwtSecret;
|
||||||
|
const jwtTimeout = apiConfig.authentication.jwtTimeout;//Timeout in hours
|
||||||
|
const jwtRenewalTimeout = apiConfig.authentication.jwtRenewalTimeout;//Timeout in hours
|
||||||
|
const jwtOptions = apiConfig.authentication.jwtOptions;
|
||||||
|
const validator = new Validator();
|
||||||
|
|
||||||
|
const create_account_schema = {
|
||||||
|
type : 'object',
|
||||||
|
properties : {
|
||||||
|
email : { type : 'string' , maxLength : 256 },
|
||||||
|
password : { type : 'string', maxLength : 256},
|
||||||
|
otp : { type : 'string', maxLength : 6 },
|
||||||
|
company_type : { type : 'string', enum : ["Shipper" , "Carrier"] },
|
||||||
|
checksum : { type : 'string', maxLength : 32 }
|
||||||
|
},
|
||||||
|
required : [ 'email', 'password' ]
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirm_account_schema = {
|
||||||
|
type : 'object',
|
||||||
|
properties : create_account_schema.properties,//Same properties
|
||||||
|
required : [ 'email', 'password', 'otp', 'company_type', 'checksum' ]//Different requirements
|
||||||
|
};
|
||||||
|
|
||||||
|
const login_account_schema = {
|
||||||
|
type : 'object',
|
||||||
|
properties : create_account_schema.properties,//Same properties
|
||||||
|
required : [ 'email', 'password' ]//Different requirements
|
||||||
|
};
|
||||||
|
|
||||||
|
const password_recover_schema = create_account_schema;
|
||||||
|
const confirm_password_recover_schema = {
|
||||||
|
type : 'object',
|
||||||
|
properties : create_account_schema.properties,//Same properties
|
||||||
|
required : [ 'email', 'password', 'otp', 'checksum' ]//Different requirements
|
||||||
|
};
|
||||||
|
|
||||||
|
const AuthorizeJWT = async(req, res) => {
|
||||||
|
try{
|
||||||
|
if( validator.validate( req.body , login_account_schema ).valid ){
|
||||||
|
const { email, password } = req.body;
|
||||||
|
const user = await login( email, password );
|
||||||
|
if( !user ){
|
||||||
|
return res.status(401).send( { error : "Invalid credentials" } );
|
||||||
|
}
|
||||||
|
const current_date = new Date();
|
||||||
|
const iat = Math.floor( (current_date.getTime())/1000 );
|
||||||
|
const renewal_exp = ( iat + 3600*jwtRenewalTimeout ) * 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renew session token on every login event.
|
||||||
|
* Previous session token is lost
|
||||||
|
*/
|
||||||
|
const session_token = toSha256( `${new Date()}` );
|
||||||
|
const session_token_exp = new Date( renewal_exp );
|
||||||
|
user.session_token = session_token;
|
||||||
|
user.session_token_exp = session_token_exp;
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
iat: iat,
|
||||||
|
exp: iat + jwtTimeout * 3600,
|
||||||
|
aud: jwtOptions.audience,
|
||||||
|
iss: jwtOptions.audience,
|
||||||
|
sub: user.id,
|
||||||
|
};
|
||||||
|
const jwt = jsonwebtoken.sign( payload , jwtSecret );
|
||||||
|
return res.status(200).send( {
|
||||||
|
accessToken : jwt,
|
||||||
|
payload : payload,
|
||||||
|
session_token,
|
||||||
|
session_token_exp,
|
||||||
|
user : user
|
||||||
|
} );
|
||||||
|
}else{
|
||||||
|
return res.status(400).send( { error : "Invalid request" } );
|
||||||
|
}
|
||||||
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
|
res.status(500).send({ error : "Login: Internal error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const RenewJWT = async(req, res) => {
|
||||||
|
try{
|
||||||
|
const login_session_token = req.params.session_token;
|
||||||
|
const user = await login_with_session_token( login_session_token );
|
||||||
|
if( !user ){
|
||||||
|
return res.status(401).send( { error : "Invalid or Expired Session Token" } );
|
||||||
|
}
|
||||||
|
const current_date = new Date();
|
||||||
|
const iat = Math.floor( (current_date.getTime())/1000 );
|
||||||
|
const renewal_exp = ( iat + 3600*jwtRenewalTimeout ) * 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renew session token on every login event.
|
||||||
|
* Previous session token is lost
|
||||||
|
*/
|
||||||
|
const session_token = toSha256( `${new Date()}` );
|
||||||
|
const session_token_exp = new Date( renewal_exp );
|
||||||
|
user.session_token = session_token;
|
||||||
|
user.session_token_exp = session_token_exp;
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
iat: iat,
|
||||||
|
exp: iat + jwtTimeout * 3600,
|
||||||
|
aud: jwtOptions.audience,
|
||||||
|
iss: jwtOptions.audience,
|
||||||
|
sub: user.id,
|
||||||
|
};
|
||||||
|
const jwt = jsonwebtoken.sign( payload , jwtSecret );
|
||||||
|
return res.status(200).send( {
|
||||||
|
accessToken : jwt,
|
||||||
|
payload : payload,
|
||||||
|
session_token,
|
||||||
|
session_token_exp,
|
||||||
|
user : user
|
||||||
|
} );
|
||||||
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
|
res.status(500).send({ error : "Renew: Internal error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const TryCreateAccount = async(req, res) => {
|
||||||
|
try{
|
||||||
|
if( validator.validate( req.body , create_account_schema ).valid ){
|
||||||
|
const otp = genKey();
|
||||||
|
const { email : receiver , password } = req.body;
|
||||||
|
const email = receiver;
|
||||||
|
|
||||||
|
const it_exists = await already_exists( email );
|
||||||
|
if( it_exists ){
|
||||||
|
return res.status(400).send({ error : "Email already exists" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = { OTP : otp, user_name : email };
|
||||||
|
const checksum_entry = {
|
||||||
|
email : receiver,
|
||||||
|
password,
|
||||||
|
otp
|
||||||
|
};
|
||||||
|
const checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
|
||||||
|
await emailEvent( EMAIL_EVENTS.ACCOUNT_VERIFY , receiver , content );
|
||||||
|
console.log(
|
||||||
|
content
|
||||||
|
);
|
||||||
|
res.status(200).send( { checksum } );
|
||||||
|
}else{
|
||||||
|
res.status(400).send( { error : "Invalid request" } );
|
||||||
|
}
|
||||||
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
|
res.status(500).send({ error : "Account creation: Internal error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConfirmAccount = async(req, res) => {
|
||||||
|
try{
|
||||||
|
if( validator.validate( req.body , confirm_account_schema ).valid ){
|
||||||
|
const { email, password, otp, company_type, checksum } = req.body;
|
||||||
|
|
||||||
|
const it_exists = await already_exists( email );
|
||||||
|
if( it_exists ){
|
||||||
|
return res.status(400).send({ error : "User already registered!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const checksum_entry = {email, password, otp};
|
||||||
|
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
|
||||||
|
if( recomputed_checksum != checksum ){
|
||||||
|
return res.status(400).send({ error : "Wrong OTP" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await create_account( email, password, company_type );
|
||||||
|
|
||||||
|
const content = { user_name : email };
|
||||||
|
const receiver = email;
|
||||||
|
// await emailEvent( EMAIL_EVENTS.ACCOUNT_CONFIRMED , receiver , content );
|
||||||
|
console.log(
|
||||||
|
content
|
||||||
|
);
|
||||||
|
return res.status(200).send( { msg : "User created successfully!" } );
|
||||||
|
}else{
|
||||||
|
return res.status(400).send( { error : "Invalid request" } );
|
||||||
|
}
|
||||||
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
|
return res.status(500).send({ error : "Account creation: Internal error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const RecoverPwd = async(req, res) => {
|
||||||
|
try{
|
||||||
|
if( validator.validate( req.body , password_recover_schema ).valid ){
|
||||||
|
const otp = genKey();
|
||||||
|
const { email : receiver , password } = req.body;
|
||||||
|
const email = receiver;
|
||||||
|
|
||||||
|
const it_exists = await already_exists( email );
|
||||||
|
if( !it_exists ){
|
||||||
|
return res.status(400).send({ error : "Email is not registered!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = { OTP : otp, user_name : email };
|
||||||
|
const checksum_entry = {
|
||||||
|
email : receiver,
|
||||||
|
password,
|
||||||
|
otp
|
||||||
|
};
|
||||||
|
const checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
|
||||||
|
await emailEvent( EMAIL_EVENTS.ACCOUNT_VERIFY , receiver , content );
|
||||||
|
console.log(
|
||||||
|
content
|
||||||
|
);
|
||||||
|
res.status(200).send( { checksum } );
|
||||||
|
}else{
|
||||||
|
res.status(400).send( { error : "Invalid request" } );
|
||||||
|
}
|
||||||
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
|
res.status(500).send({ error : "Password Recover: Internal error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConfirmRecoverPwd = async(req, res) => {
|
||||||
|
try{
|
||||||
|
if( validator.validate( req.body , confirm_password_recover_schema ).valid ){
|
||||||
|
const { email, password, otp, checksum } = req.body;
|
||||||
|
|
||||||
|
const it_exists = await already_exists( email );
|
||||||
|
if( !it_exists ){
|
||||||
|
return res.status(400).send({ error : "Email is not registered!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const checksum_entry = {email, password, otp};
|
||||||
|
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
|
||||||
|
if( recomputed_checksum != checksum ){
|
||||||
|
return res.status(400).send({ error : "Wrong OTP" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await reset_password( email, password );
|
||||||
|
|
||||||
|
return res.status(200).send( { msg : "Password is reset!" } );
|
||||||
|
}else{
|
||||||
|
return res.status(400).send( { error : "Invalid request" } );
|
||||||
|
}
|
||||||
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
|
return res.status(500).send({ error : "Password Recover Confirmation: Internal error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { AuthorizeJWT, RenewJWT, TryCreateAccount, ConfirmAccount, RecoverPwd, ConfirmRecoverPwd};
|
||||||
@@ -4,6 +4,7 @@ const { ROOT_PATH , LIB_PATH } = process.env;
|
|||||||
/// Router instance
|
/// Router instance
|
||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
|
|
||||||
|
const account = require('./account/routes.js');
|
||||||
const cities = require('./cities/routes.js');
|
const cities = require('./cities/routes.js');
|
||||||
const countries = require('./countries/routes.js');
|
const countries = require('./countries/routes.js');
|
||||||
const metaData = require('./meta-data/routes.js');
|
const metaData = require('./meta-data/routes.js');
|
||||||
@@ -17,6 +18,7 @@ const publicLoadAttachments = require('./public-load-attachments/routes.js');
|
|||||||
const states = require('./states/routes.js');
|
const states = require('./states/routes.js');
|
||||||
const test = require('./test/routes.js');
|
const test = require('./test/routes.js');
|
||||||
|
|
||||||
|
router.use('/account', account);
|
||||||
router.use('/cities', cities);
|
router.use('/cities', cities);
|
||||||
router.use('/countries', countries);
|
router.use('/countries', countries);
|
||||||
router.use('/meta-data', metaData);
|
router.use('/meta-data', metaData);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"authentication": {
|
"authentication": {
|
||||||
|
"pwdSecret":"Nx2g_IWo2Zt_LS$+",
|
||||||
"jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=",
|
"jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=",
|
||||||
"jwtTimeout":720,
|
"jwtTimeout":24,
|
||||||
|
"jwtRenewalTimeout":720,
|
||||||
"tokenSecret":"9Z'jMt|(h_f(&/S+zv.K",
|
"tokenSecret":"9Z'jMt|(h_f(&/S+zv.K",
|
||||||
"jwtOptions": {
|
"jwtOptions": {
|
||||||
"header": {
|
"header": {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"authentication": {
|
"authentication": {
|
||||||
|
"pwdSecret":"Nx2g_IWo2Zt_LS$+",
|
||||||
"jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=",
|
"jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=",
|
||||||
"jwtTimeout":720,
|
"jwtTimeout":24,
|
||||||
|
"jwtRenewalTimeout":720,
|
||||||
"tokenSecret":"9Z'jMt|(h_f(&/S+zv.K",
|
"tokenSecret":"9Z'jMt|(h_f(&/S+zv.K",
|
||||||
"jwtOptions": {
|
"jwtOptions": {
|
||||||
"header": {
|
"header": {
|
||||||
|
|||||||
58
src/lib/Handlers/Account/index.js
Normal file
58
src/lib/Handlers/Account/index.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
'user strict';
|
||||||
|
const { ROOT_PATH, API_CONFIG, MODELS_PATH, LIB_PATH } = process.env;
|
||||||
|
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
||||||
|
const { toSha256 } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
|
||||||
|
const UserModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` );
|
||||||
|
|
||||||
|
const pwd_secret = apiConfig.authentication.pwdSecret;
|
||||||
|
|
||||||
|
async function create_account( email, password, company_type ){
|
||||||
|
let permissions;
|
||||||
|
if( company_type === "Shipper"){
|
||||||
|
permissions = "role_shipper";
|
||||||
|
}else{
|
||||||
|
permissions = "role_carrier";
|
||||||
|
}
|
||||||
|
let safe_password = toSha256( password + pwd_secret );
|
||||||
|
const user = new UserModel({
|
||||||
|
email,
|
||||||
|
password : safe_password,
|
||||||
|
permissions
|
||||||
|
});
|
||||||
|
await user.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reset_password( email, password ){
|
||||||
|
let safe_password = toSha256( password + pwd_secret );
|
||||||
|
const user = await UserModel.findOne({ email });
|
||||||
|
user.password = safe_password,
|
||||||
|
await user.save();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function already_exists( email ){
|
||||||
|
const user = await UserModel.findOne( { email } );
|
||||||
|
if( !user ){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login( email , password ){
|
||||||
|
let safe_password = toSha256( password + pwd_secret );
|
||||||
|
const user = await UserModel.findOne({
|
||||||
|
email , password : safe_password
|
||||||
|
},{ password : 0 });
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login_with_session_token( session_token ){
|
||||||
|
const user = await UserModel.findOne({
|
||||||
|
session_token,
|
||||||
|
session_token_exp : { $gte: new Date() }
|
||||||
|
},{ password : 0 });
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { create_account, already_exists, login, login_with_session_token, reset_password };
|
||||||
66
src/lib/Handlers/MailClient/SendGrid.handler.js
Normal file
66
src/lib/Handlers/MailClient/SendGrid.handler.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
'user strict';
|
||||||
|
const { ROOT_PATH, API_CONFIG } = process.env;
|
||||||
|
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
||||||
|
const nodemailer = require("nodemailer");
|
||||||
|
const SendGrid = require("nodemailer-sendgrid");
|
||||||
|
|
||||||
|
const SiteName = "ETA Viaporte";
|
||||||
|
|
||||||
|
const sendgridConfig = apiConfig.sendgrid;
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport(
|
||||||
|
SendGrid({
|
||||||
|
host: sendgridConfig.HOST,
|
||||||
|
apiKey: sendgridConfig.API_KEY
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
async function sendMailTemplate( templateId, receiver, subject, content ){
|
||||||
|
/**TODO: Remove in production */
|
||||||
|
const default_mail = "testing@etaviaporte.com"
|
||||||
|
if( receiver.indexOf( default_mail ) >= 0 ){
|
||||||
|
receiver = default_mail;
|
||||||
|
}
|
||||||
|
return await transporter.sendMail({
|
||||||
|
from: sendgridConfig.FROM,
|
||||||
|
to: receiver,
|
||||||
|
subject: subject,
|
||||||
|
templateId: templateId,
|
||||||
|
dynamic_template_data: content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function AccountVerifyEmail( receiver , content ){
|
||||||
|
const templateId = "d-e9b7966303694964a64b6e4954e9715d";
|
||||||
|
const subject = "[ETA] Account Verification";
|
||||||
|
const content_to_send = {
|
||||||
|
project_name: SiteName,
|
||||||
|
user_name: content.user_name,
|
||||||
|
user_email: receiver,
|
||||||
|
OTP : content.OTP
|
||||||
|
};
|
||||||
|
return await sendMailTemplate( templateId, receiver, subject, content_to_send );
|
||||||
|
}
|
||||||
|
|
||||||
|
async function AccountConfirmed( receiver, content ){
|
||||||
|
const templateId = "d-4daaab1b85d443ceba38826f606e9931";
|
||||||
|
const subject = "[ETA] Welcome to ETA";
|
||||||
|
const content_to_send = {
|
||||||
|
user_name: content.user_name,
|
||||||
|
};
|
||||||
|
return await sendMailTemplate( templateId, receiver, subject, content_to_send );
|
||||||
|
}
|
||||||
|
|
||||||
|
async function AccountPwdResetEmail( receiver, content ){
|
||||||
|
const templateId = "d-e9b7966303694964a64b6e4954e9715d";
|
||||||
|
const subject = "[ETA] Password Reset";
|
||||||
|
const content_to_send = {
|
||||||
|
project_name: SiteName,
|
||||||
|
user_name: content.user_name,
|
||||||
|
user_email: receiver,
|
||||||
|
OTP : content.OTP
|
||||||
|
};
|
||||||
|
return await sendMailTemplate( templateId, receiver, subject, content_to_send );
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { AccountVerifyEmail, AccountConfirmed, AccountPwdResetEmail };
|
||||||
43
src/lib/Handlers/MailClient/index.js
Normal file
43
src/lib/Handlers/MailClient/index.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
'user strict';
|
||||||
|
const { ROOT_PATH, HANDLERS_PATH, MODELS_PATH, API_CONFIG } = process.env;
|
||||||
|
const { AccountVerifyEmail, AccountConfirmed, AccountPwdResetEmail } = require('./SendGrid.handler');
|
||||||
|
|
||||||
|
const EMAIL_EVENTS={
|
||||||
|
ACCOUNT_VERIFY:1,
|
||||||
|
ACCOUNT_CONFIRMED:2,
|
||||||
|
ACCOUNT_PWD_RESET:3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an email according to the event.
|
||||||
|
* @param eventId : string
|
||||||
|
* @param email_content : { string receiver, {*} content }
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function emailEvent( eventId, receiver , content ){
|
||||||
|
switch( eventId ){
|
||||||
|
case EMAIL_EVENTS.ACCOUNT_VERIFY:
|
||||||
|
{
|
||||||
|
return await AccountVerifyEmail( receiver, content );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EMAIL_EVENTS.ACCOUNT_CONFIRMED:
|
||||||
|
{
|
||||||
|
return await AccountConfirmed( receiver, content );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EMAIL_EVENTS.ACCOUNT_PWD_RESET:
|
||||||
|
{
|
||||||
|
return await AccountPwdResetEmail( receiver, content );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new Error(`Email event not defined ${eventId}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return await usersModel.findById( id , { password : 0 } );
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { emailEvent , EMAIL_EVENTS };
|
||||||
@@ -1,50 +1,30 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
const { ROOT_PATH, API_CONFIG } = process.env;
|
||||||
|
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
function getPagination( query ){
|
const secret = apiConfig.authentication.jwtSecret;
|
||||||
let limit = {
|
/**
|
||||||
page : 0,
|
* Convert string to sha256 string in hex
|
||||||
elements : 10
|
* @param {*} text
|
||||||
};
|
* @returns
|
||||||
|
*/
|
||||||
if( query.page ){
|
function toSha256( text ){
|
||||||
limit.page = parseInt( query.page ) || 0;
|
return crypto.createHmac( "sha256" , "" ).update( text ).digest( 'hex' );
|
||||||
if( limit.page < 0 ){
|
|
||||||
limit.page = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( query.elements ){
|
|
||||||
limit.elements = parseInt( query.elements ) || 10;
|
|
||||||
/** Safe pagination limit */
|
|
||||||
if( limit.elements > 1000 ){
|
|
||||||
limit.elements = 1000;
|
|
||||||
}
|
|
||||||
else if( limit.elements < 0 ){
|
|
||||||
limit.elements = 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return limit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPage( page, elements, model, filter=null, projection=null){
|
/**
|
||||||
const skip = elements * page;
|
* Generate string with fixed length with random content.
|
||||||
const total = await model.count( filter );
|
* Length is limited to 64 characters.
|
||||||
const list = await model.find( filter , projection, { skip : skip , limit : elements } );
|
* @param {*} text
|
||||||
return {
|
* @returns
|
||||||
total : total,
|
*/
|
||||||
limit : elements,
|
function genKey( len = 6 , key="" ){
|
||||||
skip : skip,
|
if( len >= 64 ){
|
||||||
data : list
|
throw "invalid key len";
|
||||||
}
|
}
|
||||||
|
const complete_string = toSha256( key + new Date() + secret );
|
||||||
|
return complete_string.substr(0 , len );
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryPage(page, elements, model, filter=null, projection=null){
|
module.exports = { genKey , toSha256 };
|
||||||
const skip = elements * page;
|
|
||||||
const total = await model.count( filter );
|
|
||||||
return {
|
|
||||||
query : model.find( filter , projection, { skip : skip , limit : elements } ),
|
|
||||||
total : total,
|
|
||||||
skip : skip
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { getPagination , getPage, queryPage };
|
|
||||||
@@ -18,10 +18,10 @@ const schema = new Schema({
|
|||||||
last_name: { type: String },
|
last_name: { type: String },
|
||||||
middle_name: { type: String },
|
middle_name: { type: String },
|
||||||
email: { type: String, unique: true, lowercase: true },
|
email: { type: String, unique: true, lowercase: true },
|
||||||
password: { type: String },
|
password: { type: String , maxLength : 256 },
|
||||||
phone: { type: String },
|
phone: { type: String },
|
||||||
phone2: { type: String },
|
phone2: { type: String },
|
||||||
permissions: [{ type: String, default: 'role_admin' }],
|
permissions: [{ type: String, default: 'role_admin', enum : ['role_admin', 'role_shipper', 'role_carrier', 'role_driver' ] }],
|
||||||
gender: { type: String },
|
gender: { type: String },
|
||||||
address: { type: String },
|
address: { type: String },
|
||||||
dob: { type: String },
|
dob: { type: String },
|
||||||
@@ -58,6 +58,9 @@ const schema = new Schema({
|
|||||||
resetExpires: { type: Date }, // or a long integer
|
resetExpires: { type: Date }, // or a long integer
|
||||||
resetAttempts: { type: Number },
|
resetAttempts: { type: Number },
|
||||||
|
|
||||||
|
session_token : { type : String, maxLength : 256 },
|
||||||
|
session_token_exp : { type: Date },
|
||||||
|
|
||||||
is_hidden: { type: Boolean, default: false },
|
is_hidden: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user