feat: Split v1 and v2 apis

This commit is contained in:
Josepablo C
2024-08-05 15:33:23 -06:00
parent 49ee7d7b5a
commit c3f0b08cb7
149 changed files with 284 additions and 33 deletions

View File

@@ -0,0 +1,123 @@
'user strict';
const { ROOT_PATH, API_CONFIG, MODELS_PATH, LIB_PATH } = process.env;
const { getModel } = require( `${ROOT_PATH}/${MODELS_PATH}` );
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const { toSha256 } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const UserModel = getModel('users');
const companiesModels = getModel('companies');
const pwd_secret = apiConfig.authentication.pwdSecret;
async function create_account( email, password ){
let safe_password = toSha256( password + pwd_secret );
const user = new UserModel({
email,
password : safe_password,
job_role : 'owner',//Always a new user created from signup is owner
isVerified : true,
has_password : true
});
await user.save();
// Create user code
const id = "" + user._id;
const employee_id = "E-" + id.substring( 0 , 6 );
await UserModel.findByIdAndUpdate( id , {
employee_id
});
}
async function reset_password( email, password ){
let safe_password = toSha256( password + pwd_secret );
const user = await UserModel.findOne({ email });
user.password = safe_password;
user.isVerified = true;
user.has_password = true;
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 verify_driver_account( email ){
const user = await UserModel.findOne( { email } );
const retVal = {
has_account:false,
isVerified:false,
has_password:false
};
if( !user ){
retVal.has_account = false;
retVal.isVerified = false;
retVal.has_password = false;
}else{
retVal.has_account = true;
retVal.isVerified = user.isVerified;
retVal.has_password = ( !user.password )? false : true;
}
return retVal;
}
async function login( email , password ){
let safe_password = toSha256( password + pwd_secret );
const user = await UserModel.findOne({
email , password : safe_password
},{ password : 0 , session_token : 0 , session_token_exp : 0 }).populate('company');
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 , session_token : 0 , session_token_exp : 0 }).populate('company');
return user;
}
async function complete_register( userId , data ){
let {
company_type
} = data;
let permissions;
if( company_type.toLowerCase() === "shipper" ){
company_type = "Shipper";
permissions = "role_shipper";
}else if( company_type.toLowerCase() === "carrier" ){
company_type = "Carrier";
permissions = "role_carrier";
}else{
throw "Invalid company type";
}
data.company_type = company_type;
const user = await UserModel.findById( userId , { password : 0 , session_token : 0 , session_token_exp : 0 } );
if( user.company ){
throw "User already register";
}
const company = new companiesModels( data );
await company.save();
user.company = company;
user.job_role = "owner";
user.permissions = permissions;
user.isVerified = true;
await user.save();
return company;
}
module.exports = { create_account, already_exists, verify_driver_account, login, login_with_session_token, reset_password, complete_register };

View File

@@ -0,0 +1,111 @@
"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=null, populate_list=null, populate_select=null ) {
this.Model = Model;
this.search_param = search_param || null;
this.populate_list = populate_list || [];
this.populate_select = populate_select || null;
}
async populateQuery( query ){
if( this.populate_list.length > 0 ){
query.populate( this.populate_list );
}
else
if( this.populate_select != null ){
for( const [key,value] of Object.entries(this.populate_select) ){
query.populate( key , value );
}
}
return await query.exec();
}
async getList( page, elements, filter=null, projection=null, sort=null ){
const { query , total, skip } = await getPageQuery( page , elements, this.Model, filter, projection );
if( sort ){
query.sort( sort );
}
const list = await this.populateQuery( query );
return {
total : total,
limit : elements,
skip : skip,
data : list
};
}
/**
* Not populated query.
*
* Typical usage:
* @code{js}
* const queryObj = await generic.getListQuery(page, elements, filter, projection );
* queryObj.query.SOME_ACTIONS();
* const data = await generic.populateQuery( queryObj.query );
* @endcode
* @param {*} page
* @param {*} elements
* @param {*} filter
* @param {*} projection
* @returns { total , limit, skip, query}
*/
async getListQuery( page, elements , filter=null, projection=null, sort=null ){
const { query , total, skip } = await getPageQuery( page , elements, this.Model, filter, projection );
if( sort ){
query.sort( sort );
}
return {
total : total,
limit : elements,
skip : skip,
query : query
};
}
async findList( find_string, page, elements, projection=null ){
if( !this.search_param ){
throw new Error( "No search parameter setted up" );
}
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.populateQuery( query );
}
};
module.exports = { getPage, getPageQuery, GenericHandler };

View File

@@ -0,0 +1,85 @@
'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_list = [
{pattern:"testing@etaviaporte.com",redirect:"testing@etaviaporte.com"},
{pattern:"alex@etaviaporte.com",redirect:"alexandro_uribe@outlook.com"},
{pattern:"pablo@etaviaporte.com",redirect:"josepablo134@gmail.com"}
];
for( let i=0; i< default_mail_list.length; i++ ){
if( receiver.indexOf( default_mail_list[i].pattern ) >= 0 ){
receiver = default_mail_list[i].redirect;
break;/** Set only the first match */
}
}
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 );
}
async function ContactEmail( receiver, content ){
const templateId = "d-1090dda1091442f3a75ee8ab39ad0f10";
const subject = "[ETA] Contact Email";
const content_to_send = {
project_name: SiteName,
user_name: content.name,
user_email: receiver
};
return await sendMailTemplate( templateId, receiver, subject, content_to_send );
}
//ContactEmail( "josepablo134@gmail.com", { email : "josepablo134@gmail.com", name:"Josepablo C.", message: "This is an example" } ).then().catch();
module.exports = { AccountVerifyEmail, AccountConfirmed, AccountPwdResetEmail, ContactEmail };

View File

@@ -0,0 +1,24 @@
'user strict';
const { ROOT_PATH, API_CONFIG } = process.env;
const nodemailer = require("nodemailer");
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const transporter = nodemailer.createTransport(
apiConfig.email_standalone
);
async function StandAloneContactEmail( content ){
const default_from = apiConfig.email_standalone.auth.user;
const receiver = "support@etaviaporte.com";
const {name, email, message } = content;
return await transporter.sendMail({
from: `${name} <${default_from}>`,
to: receiver,
subject: "Contact Email From Landing Page",
text: `\n\n The following is an email from : ${email}\n\n\n` + message
});
}
//StandAloneContactEmail( { email : "josepablo134@gmail.com", name:"Josepablo C.", message: "This is an example" } ).then().catch();
module.exports = { StandAloneContactEmail };

View File

@@ -0,0 +1,50 @@
'user strict';
const { ROOT_PATH, HANDLERS_PATH, MODELS_PATH, API_CONFIG } = process.env;
const { StandAloneContactEmail } = require('./StandAlone.handler');
const { AccountVerifyEmail, AccountConfirmed, AccountPwdResetEmail, ContactEmail } = require('./SendGrid.handler');
const EMAIL_EVENTS={
ACCOUNT_VERIFY:1,
ACCOUNT_CONFIRMED:2,
ACCOUNT_PWD_RESET:3,
CONTACT_EMAIL:4,
}
/**
* 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;
case EMAIL_EVENTS.CONTACT_EMAIL:
{
await StandAloneContactEmail( content );
return await ContactEmail( receiver, content );
}
break;
default:
{
throw new Error(`Email event not defined ${eventId}`);
}
break;
}
}
module.exports = { emailEvent , EMAIL_EVENTS };

View File

@@ -0,0 +1,52 @@
'user strict';
const { getModel } = require( '../Models' );
const vehiclesModel = require('../Models/vehicles.model');
const proposalsModel = getModel('proposals');
const loadsModel = getModel('loads');
const usersModel = getModel('users');
const companiesModel = getModel('companies');
/**
* When the proposal is accepted then the load should be updated to have the
* @param {*} id
* @param {*} newProposalData
* @returns
*/
async function onPatchEvent( id , newProposalData ){
const proposal = await proposalsModel.findById( id );
if( !newProposalData.is_accepted ){
/// Update Proposal:
/// Remove shipper
await proposalsModel.findByIdAndUpdate( id , {
shipper : null
} );
/// Update Load:
/// Remove carrier, driver and vehicle
await loadsModel.findByIdAndUpdate( proposal.load, {
carrier : null,
driver : null,
vehicle : null,
} );
}else{
const shipper_user = await usersModel.findById( proposal.accepted_by );
const shipper = await companiesModel.findById( shipper_user.company );
const vehicle = await vehiclesModel.findById( proposal.vehicle );
/// Update Proposal:
/// Adding shipper to proposal
await proposalsModel.findByIdAndUpdate( id , {
shipper : shipper.id
} );
/// Update Load:
/// Add carrier, driver and vehicle
await loadsModel.findByIdAndUpdate( proposal.load, {
carrier : proposal.carrier,
driver : vehicle.driver,
vehicle : proposal.vehicle,
} );
}
}
module.exports = { onPatchEvent };

View File

@@ -0,0 +1,187 @@
'user strict';
const { ROOT_PATH, HANDLERS_PATH, LIB_PATH } = process.env;
const { getModel } = require( '../Models' );
const { GenericHandler } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Generic.handler.js` );
const { getPagination } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const usersModel = getModel('users');
const companiesModel = getModel('companies');
const populate_list = ['company','branch','vehicle','active_load','categories'];
const generic = new GenericHandler( usersModel, "first_name", populate_list );
async function getUserById( id , filter ){
if( filter ){
filter._id = id;
const user = await usersModel.findOne( filter , { password : 0 , session_token : 0 , session_token_exp : 0 } );
return user;
}else{
return await usersModel.findById( id , { password : 0 , session_token : 0 , session_token_exp : 0 } ).populate('company');
}
}
function getAndFilterList( query ){
const filter_list = [];
const { email, permissions, gender, job_role, employee_id, company, branch, vehicle, active_load, categories } = query;
if( email ){ filter_list.push( { email } ); }
if( permissions ){ filter_list.push( { permissions } ); }
if( gender ){ filter_list.push( { gender } ); }
if( job_role ){ filter_list.push( { job_role } ); }
if( employee_id ){ filter_list.push( { employee_id } ); }
if( company ){ filter_list.push( { company } ); }
if( branch ){ filter_list.push( { branch } ); }
if( vehicle ){ filter_list.push( { vehicle } ); }
if( active_load ){ filter_list.push( { active_load } ); }
if( categories ){ filter_list.push( { categories } ); }
if( filter_list.length == 0 ){
return null;
}
return filter_list;
}
async function findUsers( query ){
const filter = { "is_hidden" : false , "is_deleted" : false };
const { page, elements } = getPagination( query );
const andFilterList = getAndFilterList( query );
if( andFilterList ){
filter.$and = andFilterList;
}
let search_param;
let search_value;
if( query.first_name ){
search_param = "first_name";
search_value = query.first_name;
}
else if( query.last_name ){
search_param = "last_name";
search_value = query.last_name;
}
else if( query.middle_name ){
search_param = "middle_name";
search_value = query.middle_name;
}
else if( query.email ){
search_param = "email";
search_value = query.email;
}
else if( query.phone ){
search_param = "phone";
search_value = query.phone;
}
else if( query.phone2 ){
search_param = "phone2";
search_value = query.phone2;
}
if( search_param ){
const re = new RegExp( search_value );
filter[ search_param ] = { $regex: re, $options: 'i' };
}
const queryVal = await generic.getList(page , elements, filter, { password : 0 , session_token : 0 , session_token_exp : 0 } );
return {
total : queryVal.total,
limit : queryVal.limit,
skip : queryVal.skip,
data : queryVal.data
};
}
function clean_user_data( data , company ){
/// Avoid modifying sensitive fields.
if( data.password ){ delete data.password; }
if( data.company ){ delete data.company; }
if( data.job_role ){
/// System can only create manager,driver or staff.
if( (data.job_role !== "manager") && (data.job_role !== "driver") && (data.job_role !== "staff") ){
data.job_role = "staff";
}
}
if( data.permissions ){ delete data.permissions; }
if( company ){
if( company.company_type === 'Shipper' ){
data.permissions = "role_shipper";
}else
if( company.company_type === 'Carrier' ){
data.permissions = "role_carrier";
}
}
if( data.session_token ){ delete data.session_token; }
if( data.session_token_exp ){ delete data.session_token_exp; }
if( data.is_deleted ){ delete data.is_deleted; }
return data;
}
async function patchUserData( id , data ){
/// Avoid modifying sensitive fields.
data = clean_user_data( data , null );
const user = await usersModel.findById( id , { password : 0 , session_token : 0 , session_token_exp : 0 } );
if( (data.email) && (data.email !== user.email) ){
const user_already_exists = await usersModel.findOne({ email : data.email });
if( user_already_exists ){
throw "email already exists, please choose other";
}
/// Changing the email requires a password recovery in order to verify the email!!
data.password = "reset your password please";
}else{
delete data.email;
}
await usersModel.findByIdAndUpdate( id , data );
return await usersModel.findById( id , { password : 0 , session_token : 0 , session_token_exp : 0 } ).populate('company');
}
async function createUserWithinCompany( companyId , data ){
const company = await companiesModel.findById( companyId );
/// Avoid modifying sensitive fields.
data = clean_user_data( data , company );
data.company = companyId;
if( data.email ){
const user_already_exists = await usersModel.findOne({ email : data.email });
if( user_already_exists ){
throw "email already exists";
}
}else{
throw "email is required";
}
data.isVerified = false;
const user = new usersModel( data );
await user.save();
// Create user code
const id = "" + user._id;
const employee_id = "E-" + id.substring( 0 , 6 );
await usersModel.findByIdAndUpdate( id , {
employee_id
});
user.employee_id = employee_id;
return user;
}
async function deleteUserWithinCompany( manager_id , user_to_remove_id ){
if( manager_id === user_to_remove_id ){ throw "Manager can not remove it self"; }
const manager = await usersModel.findById( manager_id ).populate( "company" );
const company = manager.company;
if( !manager ){ throw "Invalid manager or owner"; }
if( !company ){ throw "Invalid company"; }
const user = await usersModel.findOne( {
_id : user_to_remove_id ,
company : manager.company.id
} );
if( !user ){ throw "User is invalid"; }
await usersModel.findOneAndDelete( {
_id : user_to_remove_id ,
company : manager.company.id
} );
return user;
}
module.exports = { getUserById , findUsers , patchUserData , createUserWithinCompany , deleteUserWithinCompany };

87
v1/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,
};

105
v1/src/lib/Misc.js Normal file
View File

@@ -0,0 +1,105 @@
"use strict";
const { ROOT_PATH, API_CONFIG } = process.env;
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const crypto = require('crypto');
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 secret = apiConfig.authentication.jwtSecret;
const tokenSecret = apiConfig.authentication.tokenSecret;
/**
* Convert string to sha256 string in hex
* @param {*} text
* @returns
*/
function toSha256( text ){
return crypto.createHmac( "sha256" , "" ).update( text ).digest( 'hex' );
}
/**
* Generate string with fixed length with random content.
* Length is limited to 64 characters.
* @param {*} text
* @returns
*/
function genKey( len = 5 , key="" ){
if( len >= 64 ){
throw "invalid key len";
}
const shacode = toSha256( key + new Date() + tokenSecret );
const otp_hex = shacode.slice(0 , len );
const otp_dec = Number.parseInt( otp_hex , 16 );
return ""+otp_dec;
}
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
};
}
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;
}
module.exports = { genKey , toSha256, getPagination, getPage, queryPage, downloadFile};

View File

@@ -0,0 +1,16 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = new Schema({
categories: [{ type: Schema.Types.ObjectId, ref: 'productcategories' }],
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, enum : [ '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' },
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 },
createdAt: { type : Date, required : true, default : () => { return Date.now(); } }
});
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,73 @@
"use strict";
const branches = require('./branches.model.js');
const budgets = require('./budgets.model.js');
const cities = require('./cities.model.js');
const companies = require('./companies.model.js');
const countries = require('./countries.model.js');
const load_attachments = require('./load-attachments.model.js');
const loads = require('./loads.model.js');
const mailer = require('./mailer.model.js');
const memberships = require('./memberships.model.js');
const meta_data = require('./meta-data.model.js');
const meta_groups = require('./meta-groups.model.js');
const news = require('./news.model.js');
const orders = require('./orders.model.js');
const product_categories = require('./product-categories.model.js');
const products = require('./products.model.js');
const proposals = require('./proposals.model.js');
const states = require('./states.model.js');
const trackings = require('./trackings.model.js');
const users = require('./users.model.js');
const vehicles = require('./vehicles.model.js');
function getModel( name ){
switch( name ){
case 'branches':
return branches;
case 'budgets':
return budgets;
case 'cities':
return cities;
case 'companies':
return companies;
case 'countries':
return countries;
case 'load_attachments':
return load_attachments;
case 'loads':
return loads;
case 'mailer':
return mailer;
case 'memberships':
return memberships;
case 'meta_data':
return meta_data;
case 'meta_groups':
return meta_groups;
case 'news':
return news;
case 'orders':
return orders;
case 'product_categories':
return product_categories;
case 'products':
return products;
case 'proposals':
return proposals;
case 'states':
return states;
case 'trackings':
return trackings;
case 'users':
return users;
case 'vehicles':
return vehicles;
default:
return null;
}
}
module.exports = {
getModel
};

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', required: true }, //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,91 @@
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 },
createdAt: { type : Date, required : true, default : () => { return Date.now(); } }
});
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,22 @@
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' }, // who offers load
carrier: { type: Schema.Types.ObjectId, ref: 'companies' }, // who 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 },
createdAt: { type : Date, required : true, default : () => { return Date.now(); } }
});
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,62 @@
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 , maxLength : 256 },
phone: { type: String },
phone2: { type: String },
permissions: { type: String, default: 'role_admin', enum : [ 'admin', 'role_admin', 'role_shipper', 'role_carrier', 'role_driver' ] },
gender: { type: String },
address: { type: String },
dob: { type: String },
// vehicle_status: { type: String, enum: ['Free', 'Loading', 'Moving', 'Downloading'] },
job_role: { type: String, enum : [ 'admin', 'owner', 'manager', '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: 'productcategories' }],
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 },
session_token : { type : String, maxLength : 256 },
session_token_exp : { type: Date },
is_hidden: { type: Boolean, default: false },
is_deleted: { type: Boolean, default: false },
deleted_at: { type: Date },
createdAt: { type : Date, required : true, default : () => { return Date.now(); } }
});
module.exports = mongoose.model( "users", schema );

View File

@@ -0,0 +1,57 @@
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', required: true }, // 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: 'productcategories' }],
posted_by: { type: Schema.Types.ObjectId, ref: 'users', required: true }, // carrier
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 },
createdAt: { type : Date, required : true, default : () => { return Date.now(); } }
});
module.exports = mongoose.model( "vehicles", schema );

View File

@@ -0,0 +1,29 @@
'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 ){
console.error( err );
return res.status(401).send({error:"Unauthorized",code:401});
}
next();
}else{
return res.status(401).send({error:"Unauthorized",code:401});
}
}
module.exports = {
middleware
};