feat: Improve code organization

This commit is contained in:
2023-10-09 13:08:36 -06:00
parent d01f62004f
commit 4e6dbdcedb
62 changed files with 60 additions and 57 deletions

10
src/apps/index.js Normal file
View File

@@ -0,0 +1,10 @@
const express = require('express');
const app = express();
const private = require('./private');
const public = require('./public');
app.use( public );
app.use( private );
module.exports = app;

37
src/apps/private/index.js Normal file
View File

@@ -0,0 +1,37 @@
'use strict';
const { ROOT_PATH , LIB_PATH } = process.env;
/// Router instance
const router = require('express').Router();
const jwtValidator = require( `${ROOT_PATH}/${LIB_PATH}/jwtValidator.js` );
const loadAttachments = require('./load-attachments/routes.js');
const loads = require('./loads/routes.js');
const users = require('./users/routes.js');
router.use( jwtValidator.middleware );
router.use('/load-attachments', loadAttachments );
router.use('/loads', loads);
router.use('/users', users);
/*
router.use('/orders', test);
router.use('/companies', test);
router.use('/vehicles', test);
router.use('/mailer', test);
router.use('/authmanagement', test);
router.use('/memberships', test);
router.use('/checkAccount', test);
router.use('/proposals', test);
router.use('/bootresolvers', test);
router.use('/budgets', test);
router.use('/news', test);
router.use('/branches', test);
router.use('/trackings', test);
router.use('/upload', test);
router.use('/calendars', test);
router.use('/dashboard', test);
*/
module.exports = router;

View File

@@ -0,0 +1,11 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.post('/loading/:id', services.postLoadingAttachment );
router.post('/downloading/:id', services.postDownloadingAttachment );
router.get('/load/:id', services.getLoadAttachmentList );
router.get('/:id', services.getAttachment );
router.get('/', services.getAttachmentList );
module.exports = router;

View File

@@ -0,0 +1,150 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, API_CONFIG } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const s3Client = new S3Client({
region : apiConfig.S3.region,
credentials : {
accessKeyId : apiConfig.S3.accessKeyId,
secretAccessKey : apiConfig.S3.secretAccessKey
}
});
const s3Bucket = apiConfig.S3.bucket;
const s3BucketKey = apiConfig.S3.key;
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/load-attachments.model.js` );
const UserModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` );
const LoadsModel = require( `${ROOT_PATH}/${MODELS_PATH}/loads.model.js` );
async function getAuthorizationFilter( userId ){
const user = await UserModel.findById( userId );
const companyId = user.company.toString();
return {
$or: [
{ company : companyId },
{ carrier : companyId },
]
};
}
const getAttachment = async(req, res) => {
const attachmentId = req.params.id;
const CompanyAccessFilter = await getAuthorizationFilter( req.JWT.payload.sub );
const filter = {
$and : [
{ _id : attachmentId },
CompanyAccessFilter
]
};
const retVal = await Model.findOne( filter ) || {};
res.send( retVal );
};
const getAttachmentList = async(req, res) => {
const filter = await getAuthorizationFilter( req.JWT.payload.sub );
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getLoadAttachmentList = async(req, res) => {
const loadId = req.params.id;
const CompanyAccessFilter = await getAuthorizationFilter( req.JWT.payload.sub );
console.log( loadId );
const filter = {
$and : [
{ load : loadId },
CompanyAccessFilter
]
};
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
async function getLoadById( loadId , companyId ){
const filter = {
$and : [
{ _id : loadId },
{
$or: [
{ company : companyId },
{ carrier : companyId },
]
}
]
};
return await LoadsModel.findOne( filter ) || null;
}
async function createLoadAttachment( type , userId , loadId ){
const user = await UserModel.findById( userId );
const companyId = user.company.toString();
const load = await getLoadById( loadId , companyId );
const prevAttachment = (load)? await Model.findOne({ load : loadId , type : type }) : null;
let attachment = null;
if( load && !prevAttachment ){
attachment = new Model({
type : type,
carrier : companyId,
load : loadId,
author : userId
});
await attachment.save();
}
else if( load && prevAttachment ){
prevAttachment.updatedAt = Date.now();
await prevAttachment.save();
attachment = prevAttachment;
}else{
/**
* load is not valid => I don't have access to this load!
*/
attachment = null;
}
return attachment;
}
async function uploadFile( bucket, key, file , obj_id ){
const params = {
Bucket: bucket,
Key : `${key}/${obj_id}`,
ContentType : file.mimetype,
Body : file.data
};
const s3resp = await s3Client.send( new PutObjectCommand( params ) );
return s3resp;
}
const postLoadingAttachment = async(req, res) => {
const loadId = req.params.id;
const attachment = await createLoadAttachment( "Loading", req.JWT.payload.sub , loadId );
const file = req.files.attachment;
if( attachment && file ){
const s3resp = await uploadFile( s3Bucket, s3BucketKey, file , attachment._id );
res.send( attachment );
}else if( !file ){
res.status(400).send({ error : "attachment file not found" , code: 400 });
}else{
res.status(401).send({error:"Unauthorized",code:401});
}
};
const postDownloadingAttachment = async(req, res) => {
const loadId = req.params.id;
const attachment = await createLoadAttachment( "Downloading", req.JWT.payload.sub , loadId );
const file = req.files.attachment;
if( attachment && file ){
const s3resp = await uploadFile( s3Bucket, s3BucketKey, file , attachment._id );
res.send( attachment );
}else if( !file ){
res.status(400).send({ error : "attachment file not found" , code: 400 });
}else{
res.status(401).send({error:"Unauthorized",code:401});
}
};
module.exports = { getAttachment, getAttachmentList, getLoadAttachmentList, postLoadingAttachment, postDownloadingAttachment };

View File

@@ -0,0 +1,8 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getLoadsList);
router.get('/:id', services.getLoad);
module.exports = router;

View File

@@ -0,0 +1,47 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , queryPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/loads.model.js` );
const UserModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` );
const CompaniesModel = require( `${ROOT_PATH}/${MODELS_PATH}/companies.model.js` );
async function getAuthorizationFilter( userId ){
const user = await UserModel.findById( userId );
const companyId = user.company.toString();
return {
$or: [
{ company : companyId },
{ carrier : companyId },
]
};
}
const getLoadsList = async(req, res) => {
const filter = await getAuthorizationFilter( req.JWT.payload.sub );
const { page , elements } = getPagination( req.query );
const query = await queryPage( page , elements, Model, filter );
const list = await query.query.populate('product')
.populate('company')
.populate('carrier');
res.send({
total : query.total,
limit : elements,
skip : query.skip,
data : list
});
};
const getLoad = async(req, res) => {
const loadId = req.params.id;
const CompanyAccessFilter = await getAuthorizationFilter( req.JWT.payload.sub );
const filter = {
$and : [
{ _id : loadId },
CompanyAccessFilter
]
};
const retVal = await Model.findOne( filter ).populate('product').populate('company').populate('carrier') || {};
res.send( retVal );
};
module.exports = { getLoadsList, getLoad };

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getProfileData);
router.get('/profile', services.getProfileData);
router.get('/:userId', services.getProfileData);
module.exports = router;

View File

@@ -0,0 +1,20 @@
"use strict";
const { ROOT_PATH, HANDLERS_PATH, API_CONFIG } = process.env;
const UsersHandler = require( `${ROOT_PATH}/${HANDLERS_PATH}/Users.handler.js` );
const getUsersList = async(req, res) => {
console.log( req.params );
res.send({ user : "hello world!" });
};
const getUserData = async(req, res) => {
console.log( req.params );
res.send({ user : "hello world!" });
};
const getProfileData = async(req, res) => {
const user = await UsersHandler.getUserData( req.JWT.payload.sub );
res.send( user );
};
module.exports = { getUsersList , getUserData , getProfileData};

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,33 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const { GenericHandler } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Generic.handler.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/cities.model.js` );
const generic = new GenericHandler( Model, "city_name" );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await generic.getList(page , elements);
res.send( retVal );
};
const findList = async(req, res) => {
const findString = req.query.regex || null;
const { page , elements } = getPagination( req.query );
let retVal;
if( findString ){
retVal = await generic.findList( findString, page, elements );
}else{
retVal = await generic.getList(page , elements);
}
res.send( retVal );
};
const getById = async(req, res) => {
const id=req.params.id;
const retVal = await generic.getById( id );
res.send( retVal );
};
module.exports = { getList , findList , getById };

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,28 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/countries.model.js` );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model );
res.send( retVal );
};
const findList = async(req, res) => {
let filter=null;
if( req.query.regex ){
const re = new RegExp( req.query.regex );
filter = { "country_name" : { $regex: re, $options: 'i' }};
}
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getById = async(req, res) => {
const retVal = await Model.findById( req.params.id );
res.send( retVal );
};
module.exports = { getList, findList, getById };

30
src/apps/public/index.js Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
const { ROOT_PATH , LIB_PATH } = process.env;
/// Router instance
const router = require('express').Router();
const cities = require('./cities/routes.js');
const countries = require('./countries/routes.js');
const metaData = require('./meta-data/routes.js');
const metaGroups = require('./meta-groups/routes.js');
const productCategories = require('./product-categories/routes.js');
const products = require('./products/routes.js');
const publicVehicles = require('./public-vehicles/routes.js');
const publicLoadAttachments = require('./public-load-attachments/routes.js');
const states = require('./states/routes.js');
const test = require('./test/routes.js');
router.use('/cities', cities);
router.use('/countries', countries);
router.use('/meta-data', metaData);
router.use('/meta-groups', metaGroups);
router.use('/product-categories', productCategories);
router.use('/products', products);
router.use("/public-vehicles", publicVehicles);
router.use('/public-load-attachments', publicLoadAttachments );
router.use('/states', states);
router.use("/test", test);
module.exports = router;

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,28 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/meta-data.model.js` );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model );
res.send( retVal );
};
const findList = async(req, res) => {
let filter=null;
if( req.query.regex ){
const re = new RegExp( req.query.regex );
filter = { "meta_value" : { $regex: re, $options: 'i' }};
}
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getById = async(req, res) => {
const retVal = await Model.findById( req.params.id );
res.send( retVal );
};
module.exports = { getList, findList, getById };

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,28 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/meta-groups.model.js` );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model );
res.send( retVal );
};
const findList = async(req, res) => {
let filter=null;
if( req.query.regex ){
const re = new RegExp( req.query.regex );
filter = { "group_label" : { $regex: re, $options: 'i' }};
}
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getById = async(req, res) => {
const retVal = await Model.findById( req.params.id );
res.send( retVal );
};
module.exports = { getList, findList, getById };

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,28 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/product-categories.model.js` );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model );
res.send( retVal );
};
const findList = async(req, res) => {
let filter=null;
if( req.query.regex ){
const re = new RegExp( req.query.regex );
filter = { "name" : { $regex: re, $options: 'i' }};
}
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getById = async(req, res) => {
const retVal = await Model.findById( req.params.id );
res.send( retVal );
};
module.exports = { getList, findList, getById };

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,28 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/products.model.js` );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model );
res.send( retVal );
};
const findList = async(req, res) => {
let filter=null;
if( req.query.regex ){
const re = new RegExp( req.query.regex );
filter = { "name" : { $regex: re, $options: 'i' }};
}
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getById = async(req, res) => {
const retVal = await Model.findById( req.params.id );
res.send( retVal );
};
module.exports = { getList, findList, getById };

View File

@@ -0,0 +1,7 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/download/:id', services.getAttachmentFile );
module.exports = router;

View File

@@ -0,0 +1,38 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, API_CONFIG } = process.env;
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const s3Client = new S3Client({
region : apiConfig.S3.region,
credentials : {
accessKeyId : apiConfig.S3.accessKeyId,
secretAccessKey : apiConfig.S3.secretAccessKey
}
});
const s3Bucket = apiConfig.S3.bucket;
const s3BucketKey = apiConfig.S3.key;
async function downloadFile( bucket, key, obj_id ){
const params = {
Bucket: bucket,
Key : `${key}/${obj_id}`
};
const s3resp = await s3Client.send( new GetObjectCommand( params ) );
const chunks = []
for await (const chunk of s3resp.Body) {
chunks.push(chunk)
}
const body = Buffer.concat(chunks);
s3resp.Body = body;
return s3resp;
}
const getAttachmentFile = async(req, res) => {
const attachmentId = req.params.id;
const file = await downloadFile( s3Bucket, s3BucketKey, attachmentId );
res.attachment( attachmentId );
res.setHeader('Content-Type', file.ContentType );
res.send( file.Body );
}
module.exports = { getAttachmentFile };

View File

@@ -0,0 +1,7 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getPublicVehiclesList);
module.exports = router;

View File

@@ -0,0 +1,25 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/vehicles.model.js` );
const getPublicVehiclesList = async(req, res) => {
const filter = { status : "Free" };
const select = [
"city",
"state",
"truck_type",
"tyre_type",
"destino",
"available_date",
"createdAt",
"updatedAt",
"published_date",
"status"
];
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model, filter, select );
res.send( retVal );
};
module.exports = { getPublicVehiclesList };

View File

@@ -0,0 +1,9 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.get('/', services.getList);
router.get('/find', services.findList);
router.get('/:id', services.getById);
module.exports = router;

View File

@@ -0,0 +1,28 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
const { getPagination , getPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const Model = require( `${ROOT_PATH}/${MODELS_PATH}/states.model.js` );
const getList = async(req, res) => {
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page , elements, Model );
res.send( retVal );
};
const findList = async(req, res) => {
let filter=null;
if( req.query.regex ){
const re = new RegExp( req.query.regex );
filter = { "state_name" : { $regex: re, $options: 'i' }};
}
const { page , elements } = getPagination( req.query );
const retVal = await getPage( page, elements, Model, filter );
res.send( retVal );
};
const getById = async(req, res) => {
const retVal = await Model.findById( req.params.id );
res.send( retVal );
};
module.exports = { getList, findList, getById };

View File

@@ -0,0 +1,8 @@
'use strict';
const router = require('express').Router();
const services= require('./services.js');
router.post('/apitest', services.postTest);
router.get('/version', services.getVersion);
module.exports = router;

View File

@@ -0,0 +1,20 @@
"use strict";
const { ROOT_PATH, LIB_PATH, API_CONFIG } = process.env;
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const postTest = async(req, res) => {
res.send({
msg:"Hello world!",
data:{
apiQuery:req.query,
apiParams:req.params.params,
body:req.body,
}
});
};
const getVersion = async(req, res) => {
res.send( apiConfig.version );
};
module.exports = { postTest , getVersion };

36
src/config/apiConfig.json Normal file
View File

@@ -0,0 +1,36 @@
{
"authentication": {
"jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=",
"jwtTimeout":720,
"tokenSecret":"9Z'jMt|(h_f(&/S+zv.K",
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://www.etaviaporte.com",
"issuer": "etaviaporte",
"algorithm": "HS256",
"expiresIn": "1d"
}
},
"version" : {
"version" : "1.0.14",
"name": "ETA Beta",
"date":"10/2023"
},
"S3" : {
"accessKeyId": "AKIAXTQEUF6MLCHTUIKW",
"secretAccessKey": "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",
"bucket": "enruta",
"key":"loadattachments",
"region": "us-west-1"
},
"sendgrid" : {
"HOST": "smtp.sendgrid.net",
"PORT": "465",
"username": "apikey",
"API_KEY": "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",
"FROM": "noreply@etaviaporte.com"
},
"mongodb": "mongodb+srv://enruta_admin:NeptFx4RUZG8OsfA@enruta.vwofshy.mongodb.net/enrutaviaporte?retryWrites=true&w=majority"
}

View File

@@ -0,0 +1,35 @@
{
"authentication": {
"jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=",
"jwtTimeout":720,
"tokenSecret":"9Z'jMt|(h_f(&/S+zv.K",
"jwtOptions": {
"header": {
"typ": "access"
},
"audience": "https://www.etaviaporte.com",
"issuer": "etaviaporte",
"algorithm": "HS256",
"expiresIn": "1d"
}
},
"version" : {
"version" : "1.0.14",
"name": "ETA Beta",
"date":"10/2023"
},
"S3" : {
"accessKeyId": "AKIAXTQEUF6MLCHTUIKW",
"secretAccessKey": "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",
"bucket": "enruta",
"region": "us-west-1"
},
"sendgrid" : {
"HOST": "smtp.sendgrid.net",
"PORT": "465",
"username": "apikey",
"API_KEY": "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",
"FROM": "noreply@etaviaporte.com"
},
"mongodb": "mongodb://localhost/etaviaporte?retryWrites=true&w=majority"
}

71
src/index.js Normal file
View File

@@ -0,0 +1,71 @@
'use strict';
require('dotenv').config();
const { ROOT_PATH, LIB_PATH, API_CONFIG } = process.env;
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const apps = require('./apps');
const express = require('express');
const cors = require('cors');
const compression = require('compression');
const morgan = require('morgan');
const helmet = require('helmet');
const bodyParser = require('body-parser');
const fileUpload = require('express-fileupload');
const middlewares = require( `${ROOT_PATH}/${LIB_PATH}/Middlewares.js` );
const mongoose = require('mongoose');
mongoose.connect(
apiConfig.mongodb,
{ useNewUrlParser: true }
).then( ( val ) => {
console.log( `MongoDB Connected : ${ apiConfig.mongodb }` );
});//catch throw error so service stops!
const app = express();
const serverPort = process.env.SERVER_PORT || 3000;
app.use( middlewares.Auth );
app.use(
fileUpload({
limits: { fileSize: 4 * 1024 * 1024 },
abortOnLimit: true,
limitHandler: (req,res,next) => {
req.limitSize = true;
},
})
);
app.use((req, res, next) => {
if (req.limitSize) {
res.status(413).send({message:"File size limit has been reached",status:"PAYLOAD_TOO_LARGE"});
}else{
next()
}
});
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(morgan('dev'));
app.use(helmet());
app.use(compression());
app.use(cors({
origin: '*',
methods: [
'GET',
'POST',
'PATCH',
'PUT',
'DELETE'
],
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.use( middlewares.errorJSON );
app.use( apps );
app.use( middlewares.error404 );
app.listen( serverPort , function(err){
if( !err ){
console.log('API listen on port', serverPort );
}else{
console.log( err );
}
});

View File

@@ -0,0 +1,59 @@
"use strict";
const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env;
async function getPage( page, elements, Model, filter=null, projection=null){
const skip = elements * page;
const total = await Model.count( filter );
const list = await Model.find( filter , projection, { skip : skip , limit : elements } );
return {
total : total,
limit : elements,
skip : skip,
data : list
}
}
async function getPageQuery(page, elements, Model, filter=null, projection=null){
const skip = elements * page;
const total = await Model.count( filter );
return {
query : Model.find( filter , projection, { skip : skip , limit : elements } ),
total : total,
skip : skip
};
}
class GenericHandler{
constructor( Model, search_param, populate_list=null ) {
this.Model = Model;
this.search_param = search_param;
this.populate_list = populate_list || [];
}
async populateQuey( query ){
if( this.populate_list.length > 0 ){
query.populate( this.populate_list );
}
return await query.exec();
}
async getList( page, elements, filter=null, projection=null ){
const { query } = await getPageQuery( page , elements, this.Model, filter, projection );
return await this.populateQuey( query );
}
async findList( find_string, page, elements, projection=null ){
const search_param = this.search_param;
const re = new RegExp( find_string );
const filter = {};
filter[ search_param ] = { $regex: re, $options: 'i' };
return await this.getList( page, elements, filter, projection );
}
async getById( id, projection=null ){
const query = Model.findById( id, projection );
return await this.populateQuey( query );
}
};
module.exports = { getPage, getPageQuery, GenericHandler };

View File

@@ -0,0 +1,9 @@
'user strict';
const { ROOT_PATH, HANDLERS_PATH, MODELS_PATH, API_CONFIG } = process.env;
const usersModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` );
async function getUserData( id ){
return await usersModel.findById( id , { password : 0 } );
}
module.exports = { getUserData };

87
src/lib/Middlewares.js Normal file
View File

@@ -0,0 +1,87 @@
'use strict';
/**
* HASH
*****************************************************
* DEPENDENCIES
*****************************************************
* Based on Express Framework
* System
*****************************************************
* PUBLIC METHODS
*****************************************************
* Auth( req, res, next)
* Extract JWT or BasicAuth data
* errorJSON( error , request , response , next )
* Generate error response on bad JSON format
* error404( request , response , next )
* Generate error 404 response
* apiKey( request , response , next )
* Generate error on invalid apikey
**/
/// Extract JWT or BasicAuth
function Auth( req, res , next ){
///
/// Try to extract the authorization data from headers
///
let auth;
if( req.headers.hasOwnProperty( "authorization" ) ){
auth = req.headers.authorization;
auth = auth.split(" ")[1];
if( !auth ){ console.log( "NO HEADER AUTH available" ); return next(); }
//console.log( auth );
/// Try BasicAuth {
try{
let ba = Buffer.from( auth , 'base64' ).toString()
//const [user,pass] = ba.split(':');
ba = ba.split(':');
if( ba.length == 2 ){
req.basicAuth = { user : ba[0] , password : ba[1] };
}
}catch(error){
console.log("MIDDLEWARE_AUTH_ERR_BA",error);
}
/// Try BasicAuth }
}else if( req.query.access_token ){
auth = req.query.access_token;
if( !auth ){ console.log( "NO QUERY AUTH available" ); return next(); }
}
if( auth ){
/// Try JWT {
try{
let jwt = auth.split(".");
if( jwt.length == 3 ){
req.JWT = {};
req.JWT.raw = auth;
}
}catch( error ){
console.log("MIDDLEWARE_AUTH_ERR_JWT",error);
}
/// Try JWT }
}
next();
}
function errorJSON( error , request , response , next ){
console.log(error);
if( error !== null ){
/// For body-parser errors
if( error instanceof SyntaxError && error.status === 400 && 'body' in error ){
return response.status(400).json({ error : 'Invalid json' , code : 400 });
}
/// For any error
return response.status(500).send( { error: "Internal server error" , code : 500 } );
}else{
return next();
}
}
function error404( request , response , next ){
return response.status(404).send( { error : "Page not found", code : 404 } );
}
module.exports = {
Auth,
errorJSON,
error404,
};

50
src/lib/Misc.js Normal file
View File

@@ -0,0 +1,50 @@
"use strict";
function getPagination( query ){
let limit = {
page : 0,
elements : 10
};
if( query.page ){
limit.page = parseInt( query.page ) || 0;
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;
const total = await model.count( filter );
const list = await model.find( filter , projection, { skip : skip , limit : elements } );
return {
total : total,
limit : elements,
skip : skip,
data : list
}
}
async function queryPage(page, elements, model, filter=null, projection=null){
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 };

View File

@@ -0,0 +1,16 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
categories: [{ type: Schema.Types.ObjectId, ref: 'product-categories' }],
company: { type: Schema.Types.ObjectId, ref: 'companies' },
branch_name: { type: String },
phone: { type: String },
city: { type: String },
state: { type: String },
truck_type: [{ type: String }],
description:{type: String},
address : { type: String }
});
module.exports = mongoose.model( "branches", schema );

View File

@@ -0,0 +1,33 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
company: { type: Schema.Types.ObjectId, ref: 'companies', required: true},
client: { type: String, required: true },
material: { type: String },
origin: { type: String },
destination: { type: String },
truck_type: { type: String },
num_tons: { type: Number },
price_per_ton: { type: Number },
tonnage: { type: String },
pickup_distance: { type: Number },
delivery_distance: { type: Number },
warehouse_distance: { type: Number },
total_km_travel: { type: Number },
cost_per_liter: { type: Number },
fuel_price_per_liter: { type: Number },
other_fuel_expenses: { type: Number },
total_fuel_consumed: { type: Number },
total_cost_fuel: { type: Number },
driver_salary: { type: Number },
accomadation_allowance: { type: Number },
other_administrative_expenses: { type: Number },
total_before_tax: { type: Number },
total_utility_per_km: { type: Number },
total_profit: { type: Number },
profit_percentage: { type: Number },
total_administrative_expenses: { type: Number }
});
module.exports = mongoose.model( "budgets", schema );

View File

@@ -0,0 +1,11 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
country_name: { type: String },
country_code: { type: String },
state_name: { type: String },
city_name: { type: String, required: true }
});
module.exports = mongoose.model( "cities", schema );

View File

@@ -0,0 +1,65 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const meta_data = new Schema({
meta_group: { type: String },
meta_key: { type: String },
meta_value: { type: String },
});
const company_contacts = new Schema({
meta_key: { type: String },
meta_value: { type: String },
});
const address = new Schema({
street_address1: { type: String },
street_address2: { type: String },
city: { type: String },
state: { type: String },
country: { type: String },
zipcode: { type: String },
landmark: { type: String },
lat: { type: String },
lng: { type: String },
});
const schema = new Schema({
company_serial_number: { type: String },
company_code: { type: String },
is_company: { type: String }, //1000
company_name: { type: String, required: true },
company_legal_name: { type: String },
company_description: { type: String },
rfc: { type: String },
company_type: [{ type: String }], // SHIPPER , CARRIER
is_broker: { type: Boolean, default: false },
membership: { type: String },
membership_start_at: { type: Date },
meta_data: [meta_data],
categories: [{ type: Schema.Types.ObjectId, ref: 'productcategories' }],
products: { type: Schema.Types.ObjectId, ref: 'products' },
users: [{ type: Schema.Types.ObjectId, ref: 'users' }],
branches: [{ type: Schema.Types.ObjectId, ref: 'branches' }],
company_city: [{ type: String }],
company_state: [{ type: String }],
truck_type: [{ type: String }],
street_address1: { type: String },
street_address2: { type: String },
city: { type: String },
state: { type: String },
country: { type: String },
zipcode: { type: String },
landmark: { type: String },
lat: { type: String },
lng: { type: String },
is_hidden: { type: Boolean, default: false },
});
module.exports = mongoose.model( "companies", schema );

View File

@@ -0,0 +1,10 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
country_name: { type: String, required: true },
country_code: { type: String, required: true },
phone_code: { type: String, required: true }
});
module.exports = mongoose.model( "countries", schema );

View File

@@ -0,0 +1,19 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
status: { type: String, default: 'Draft', enum: ['Draft', 'Done'] },/*Once in Done state, no changes are allowed.*/
updatedAt: {
type: Date,
default : () => Date.now()
},
type: { type: String, enum: ['Loading', 'Downloading'], required : true },
company: { type: Schema.Types.ObjectId, ref: 'companies' }, //shipper
carrier: { type: Schema.Types.ObjectId, ref: 'companies', required: true }, // carrier
load: { type: Schema.Types.ObjectId, ref: 'loads', required: true },
author: { type: Schema.Types.ObjectId, ref: 'users', required: true },
doneAt: { type: Date }
});
module.exports = mongoose.model( "loadattachments", schema );

View File

@@ -0,0 +1,90 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const address = new Schema({
company_name: { type: String },
street_address1: { type: String },
street_address2: { type: String },
city: { type: String },
state: { type: String },
country: { type: String },
zipcode: { type: String },
landmark: { type: String },
lat: { type: String },
lng: { type: String },
});
const pointSchema = new Schema({
type: {
type: String,
enum: ['Point'],
required: true
},
coordinates: {
type: [Number],
required: true
}
});
const schema = new Schema({
shipment_code: { type: String },
company: { type: Schema.Types.ObjectId, ref: 'companies', required: true }, //shipper
carrier: { type: Schema.Types.ObjectId, ref: 'companies' }, // carrier
vehicle: { type: Schema.Types.ObjectId, ref: 'vehicles' },
driver: { type: Schema.Types.ObjectId, ref: 'users' },
posted_by: { type: Schema.Types.ObjectId, ref: 'users' }, // shipper
posted_by_name: { type: String }, // search purpose
bidder: { type: Schema.Types.ObjectId, ref: 'users' },
origin: address,
origin_geo: {
type: pointSchema,
},
destination: address,
destination_geo: {
type: pointSchema,
},
categories: [{ type: Schema.Types.ObjectId, ref: 'productcategories' }],
product: { type: Schema.Types.ObjectId, ref: 'products' },
truck_type: { type: String },
tyre_type: { type: String },
weight: { type: Number },
estimated_cost: { type: Number },
distance: { type: Number },
actual_cost: { type: Number },
// 1. Posted Shipments (Posted)
// 2. Shipments Loading (Loading)
// 3. Enroute Shipments (In Transit)
// 4. Shipments in Unloading (Unloading)
// 5. Shipments pending finding truck
status: { type: String, default: 'Draft', enum: ['Draft', 'Published', 'Completed', 'Closed'] },
load_status: { type: String, enum: ['Published', 'Loading', 'Transit', 'Downloading', 'Delivered'] },
contract_start_date: { type: Date },
contract_end_date: { type: Date },
est_loading_date: { type: Date },
est_unloading_date: { type: Date },
published_date: { type: Date },
loaded_date: { type: Date },
transit_date: { type: Date },
delivered_date: { type: Date },
load_status_updated: { type: Date, default: () => Date.now() },
notes: { type: String },
payment_term: { type: String },
terms_and_conditions: { type: String },
});
module.exports = mongoose.model( "loads", schema );

View File

@@ -0,0 +1,8 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
text: { type: String, required: true }
});
module.exports = mongoose.model( "mailer", schema );

View File

@@ -0,0 +1,10 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
name: { type: String, required: true }, // free, pro
price: { type: Number },
validity : { type: Number }, // number 0f days
});
module.exports = mongoose.model( "memberships", schema );

View File

@@ -0,0 +1,10 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
meta_group: { type: Schema.Types.ObjectId, ref: 'metagroups'},// settings, terms, collaborator
meta_key: { type: String, required: true }, // collaborator
meta_value: { type: String }
});
module.exports = mongoose.model( "metadatas", schema );

View File

@@ -0,0 +1,11 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
group_label: { type: String, required: true },
group_key: { type: String, required: true },
group_field_type: { type: String,default:'text' }, // text, textarea, html, select
group_options: [{ type: String }]
});
module.exports = mongoose.model( "metagroups", schema );

View File

@@ -0,0 +1,26 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const attachFile = new Schema({
file:{ type: String },
originalname:{ type: String },
mimetype:{ type: String },
focus_point:[{
thumbnail:{ type: String, default:"main" },
x:{ type: Number },
y:{ type: Number },
w:{ type: Number },
h:{ type: Number },
s:{ type: Number }
}]
});
const schema = new Schema({
title : { type: String, required: true },
description : { type: String, required: true },
link_text : { type: String, required: true },
link_url : { type: String, required: true },
news_image: attachFile,
});
module.exports = mongoose.model( "news", schema );

View File

@@ -0,0 +1,11 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
load: { type: Schema.Types.ObjectId, ref: 'loads' },
shipper: { type: Schema.Types.ObjectId, ref: 'companies' }, // how offers load
carrier: { type: Schema.Types.ObjectId, ref: 'companies' }, // how transport the load
vehicle: { type: Schema.Types.ObjectId, ref: 'vehicles' },
});
module.exports = mongoose.model( "orders", schema );

View File

@@ -0,0 +1,8 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
name: { type: String, required: true } // fruit, boxes, wood
});
module.exports = mongoose.model( "productcategories", schema );

View File

@@ -0,0 +1,9 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
name: { type: String, required: true },
segment: { type: String},
});
module.exports = mongoose.model( "products", schema );

View File

@@ -0,0 +1,21 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
load: { type: Schema.Types.ObjectId, ref: 'loads' },
shipper: { type: Schema.Types.ObjectId, ref: 'companies' }, // how offers load
carrier: { type: Schema.Types.ObjectId, ref: 'companies' }, // how transport the load
vehicle: { type: Schema.Types.ObjectId, ref: 'vehicles' },
bidder: { type: Schema.Types.ObjectId, ref: 'users' },
comment: { type: String },
is_withdrawn: { type: Boolean, default: false },
accepted_by: { type: Schema.Types.ObjectId, ref: 'users' },
accepted_date: { type: Date },
is_accepted: { type: Boolean, default: false },
});
module.exports = mongoose.model( "proposals", schema );

View File

@@ -0,0 +1,12 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
country_name: { type: String},
country_code: { type: String },
state_name: { type: String, required: true },
state_code: { type: String }
});
module.exports = mongoose.model( "states", schema );

View File

@@ -0,0 +1,12 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
lat: { type: String },
lng: { type: String },
user: { type: Schema.Types.ObjectId, ref: 'users' },
load: { type: Schema.Types.ObjectId, ref: 'loads' },
vehicle: { type: Schema.Types.ObjectId, ref: 'vehicles' },
});
module.exports = mongoose.model( "trackings", schema );

View File

@@ -0,0 +1,64 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const pointSchema = new Schema({
type: {
type: String,
enum: ['Point'],
required: true
},
coordinates: {
type: [Number],
required: true
}
});
const schema = new Schema({
first_name: { type: String },
last_name: { type: String },
middle_name: { type: String },
email: { type: String, unique: true, lowercase: true },
password: { type: String },
phone: { type: String },
phone2: { type: String },
permissions: [{ type: String, default: 'role_admin' }],
gender: { type: String },
address: { type: String },
dob: { type: String },
// vehicle_status: { type: String, enum: ['Free', 'Loading', 'Moving', 'Downloading'] },
job_role: { type: String }, // admin, owner, driver, staff
employee_id: { type: String }, //EM-1000-1 EM-1000-2
company: { type: Schema.Types.ObjectId, ref: 'companies' },
branch: { type: Schema.Types.ObjectId, ref: 'branches' },
vehicle: { type: Schema.Types.ObjectId, ref: 'vehicles' },
active_load: { type: Schema.Types.ObjectId, ref: 'loads' },
categories: [{ type: Schema.Types.ObjectId, ref: 'product-categories' }],
user_city: [{ type: String }],
user_state: [{ type: String }],
user_description: { type: String },
truck_type: [{ type: String }],
last_location_lat: { type: String },
last_location_lng: { type: String },
last_location_geo: { type: pointSchema },
last_location_time: { type: Date },
isVerified: { type: Boolean },
verifyToken: { type: String },
verifyShortToken: { type: String },
verifyExpires: { type: Date }, // or a long integer
verifyChanges: { type: Object }, // an object (key-value map), e.g. { field: "value" }
resetToken: { type: String },
resetShortToken: { type: String },
resetExpires: { type: Date }, // or a long integer
resetAttempts: { type: Number },
is_hidden: { type: Boolean, default: false },
});
module.exports = mongoose.model( "users", schema );

View File

@@ -0,0 +1,55 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const pointSchema = new Schema({
type: {
type: String,
enum: ['Point'],
required: true
},
coordinates: {
type: [Number],
required: true
}
});
const schema = new Schema({
company: { type: Schema.Types.ObjectId, ref: 'companies' }, // carrier
vehicle_code: { type: String },
vehicle_name: { type: String },
vehicle_number: { type: String }, //camion001 002 etc
circulation_serial_number: { type: String },
trailer_plate_1: { type: String },
trailer_plate_2: { type: String },
truck_type: { type: String },
tyre_type: { type: String },
city: { type: String },
state: { type: String },
background_tracking: { type: Boolean, default: false },
status: { type: String, enum: ['Free', 'Loading', 'Transit', 'Downloading'] },
categories: [{ type: Schema.Types.ObjectId, ref: 'product-categories' }],
published_date: { type: Date },
available_date: { type: String },
is_available: { type: Boolean, default: false },
active_load: { type: Schema.Types.ObjectId, ref: 'loads' },
load_shipper: { type: Schema.Types.ObjectId, ref: 'companies' },
available_in: [{ type: String }],
destino: { type: String },
driver: { type: Schema.Types.ObjectId, ref: 'users' },
notes: { type: String },
last_location_lat: { type: String },
last_location_lng: { type: String },
last_location_geo: { type: pointSchema },
last_location_time: { type: Date },
});
module.exports = mongoose.model( "vehicles", schema );

28
src/lib/jwtValidator.js Normal file
View File

@@ -0,0 +1,28 @@
'user strict';
const { ROOT_PATH, API_CONFIG } = process.env;
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const jwt = require('jsonwebtoken');
const jwtSecret = apiConfig.authentication.jwtSecret;
function middleware( req, res, next ){
if( req.JWT ){
req.JWT.isValid = false;
try{
req.JWT.payload = jwt.verify( req.JWT.raw, jwtSecret );
if( !req.JWT.payload ){
return res.status(401).send({error:"Unauthorized",code:401});
}else{
req.JWT.isValid = true;
}
}catch( err ){
return res.status(401).send({error:"Unauthorized",code:401});
}
next();
}else{
return res.status(401).send({error:"Unauthorized",code:401});
}
}
module.exports = {
middleware
};