feat: Adding register completion and private users endpoint
This commit is contained in:
107
README.md
107
README.md
@@ -236,4 +236,109 @@ The following list of endpoints requires a JWT.
|
|||||||
A private endpoint to test the JWT and the api response.
|
A private endpoint to test the JWT and the api response.
|
||||||
|
|
||||||
- `POST /apitest`: Return whatever is sent on the body, queries and parameters.
|
- `POST /apitest`: Return whatever is sent on the body, queries and parameters.
|
||||||
- `GET /version`: Return the API version.
|
- `GET /version`: Return the API version.
|
||||||
|
|
||||||
|
|
||||||
|
## Private endpoints
|
||||||
|
|
||||||
|
Registered resources:
|
||||||
|
|
||||||
|
### /account
|
||||||
|
|
||||||
|
Complete the register process
|
||||||
|
|
||||||
|
#### POST /account/register
|
||||||
|
|
||||||
|
Send company data to complete the register.
|
||||||
|
|
||||||
|
After this process, the logged user will receive the flag "isVerified" and the company id will be associated, at the same time, the role will be set to "owner" and the permissions will be set according to the company type: `role_shipper` or `role_carrier`.
|
||||||
|
|
||||||
|
This request can only happen once, since this process is the creation of a new company in the DB.
|
||||||
|
After setting up the company, the updates can be handled in the `/companies` endpoint.
|
||||||
|
|
||||||
|
Body content:
|
||||||
|
|
||||||
|
```{.json}
|
||||||
|
{
|
||||||
|
"company_type":"carrier|shipper",
|
||||||
|
"company_name" : "Company Name",
|
||||||
|
"company_description" : "This is a description example"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns a company object
|
||||||
|
|
||||||
|
```{.json}
|
||||||
|
{
|
||||||
|
"_id": "company_id",
|
||||||
|
"company_type":"carrier|shipper",
|
||||||
|
"company_name" : "Company Name",
|
||||||
|
"company_description" : "This is a description example"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### /branches
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /budgets
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /calendars
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /companies
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /dashboard
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /load-attachments
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /loads
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /mailer
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /news
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /orders
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /proposals
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /trackings
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /upload
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|
||||||
|
### /users
|
||||||
|
|
||||||
|
- `GET /find` : Look for a user within ETA.
|
||||||
|
- `POST /member` : Create a new user within the company (only for owner or manager users).
|
||||||
|
- `PATCH /member/:id` : Update member data (except permissions, company, job_role and password).
|
||||||
|
- `DELETE /member/:id` : Delete member but keep metadata for future references. The user will be disabled and won't be able to log in again.
|
||||||
|
- `GET /profile` : Read profile data.
|
||||||
|
- `PATCH /profile` : Update profile data (except permissions, company, job_role and password).
|
||||||
|
- `GET /:id` : Get user information with its ID.
|
||||||
|
|
||||||
|
### /vehicles
|
||||||
|
|
||||||
|
Work In Progress
|
||||||
|
|||||||
7
src/apps/private/account/routes.js
Normal file
7
src/apps/private/account/routes.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
'use strict';
|
||||||
|
const router = require('express').Router();
|
||||||
|
const services= require('./services.js');
|
||||||
|
|
||||||
|
router.post('/register', services.register);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
15
src/apps/private/account/services.js
Normal file
15
src/apps/private/account/services.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
"use strict";
|
||||||
|
const { ROOT_PATH, HANDLERS_PATH } = process.env;
|
||||||
|
const { complete_register } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Account` );
|
||||||
|
|
||||||
|
const register = async( req, res ) => {
|
||||||
|
try{
|
||||||
|
const result = await complete_register( req.context.userId , req.body );
|
||||||
|
return res.send( result );
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send({ error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { register };
|
||||||
16
src/apps/private/companies/routes.js
Normal file
16
src/apps/private/companies/routes.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'use strict';
|
||||||
|
const router = require('express').Router();
|
||||||
|
const services= require('./services.js');
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/own', services.getOwnCompany);
|
||||||
|
router.post('/own', services.postOwnCompany);
|
||||||
|
router.patch('/own', services.patchOwnCompany);
|
||||||
|
|
||||||
|
router.get('/:id', services.getCompanyById);
|
||||||
|
|
||||||
|
router.get('/shipper', services.getListShippers);
|
||||||
|
router.get('/carrier', services.getListCarriers);
|
||||||
|
router.get('/users/:companyId', services.getUserLists);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
47
src/apps/private/companies/services.js
Normal file
47
src/apps/private/companies/services.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"use strict";
|
||||||
|
const { ROOT_PATH, MODELS_PATH } = process.env;
|
||||||
|
const { getModel } = require( `${ROOT_PATH}/${MODELS_PATH}` );
|
||||||
|
const usersModel = getModel('users');
|
||||||
|
// const companiesModel = getModel('companies');
|
||||||
|
// const branchesModel = getModel('branches');
|
||||||
|
// const vehiclesModel = getModel('vehicles');
|
||||||
|
// const loadsModel = getModel('loads');
|
||||||
|
// const productCategoriesModel = getModel('product_categories');
|
||||||
|
|
||||||
|
async function getOwnCompany( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postOwnCompany( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function patchOwnCompany( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCompanyById( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getListShippers( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getListCarriers( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUserLists( req , res ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getOwnCompany,
|
||||||
|
postOwnCompany,
|
||||||
|
patchOwnCompany,
|
||||||
|
getCompanyById,
|
||||||
|
getListShippers,
|
||||||
|
getListCarriers,
|
||||||
|
getUserLists
|
||||||
|
};
|
||||||
@@ -4,20 +4,25 @@ const { ROOT_PATH , LIB_PATH } = process.env;
|
|||||||
/// Router instance
|
/// Router instance
|
||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const jwtValidator = require( `${ROOT_PATH}/${LIB_PATH}/jwtValidator.js` );
|
const jwtValidator = require( `${ROOT_PATH}/${LIB_PATH}/jwtValidator.js` );
|
||||||
|
const context = require( './lib/context' );
|
||||||
|
|
||||||
|
const account = require('./account/routes.js');
|
||||||
const loadAttachments = require('./load-attachments/routes.js');
|
const loadAttachments = require('./load-attachments/routes.js');
|
||||||
const loads = require('./loads/routes.js');
|
const loads = require('./loads/routes.js');
|
||||||
const users = require('./users/routes.js');
|
const users = require('./users/routes.js');
|
||||||
|
const companies = require('./companies/routes.js')
|
||||||
|
|
||||||
router.use( jwtValidator.middleware );
|
router.use( jwtValidator.middleware );
|
||||||
|
router.use( context.middleware );
|
||||||
|
|
||||||
|
router.use('/account', account);
|
||||||
|
router.use('/companies', companies);
|
||||||
router.use('/load-attachments', loadAttachments );
|
router.use('/load-attachments', loadAttachments );
|
||||||
router.use('/loads', loads);
|
router.use('/loads', loads);
|
||||||
router.use('/users', users);
|
router.use('/users', users);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
router.use('/orders', test);
|
router.use('/orders', test);
|
||||||
router.use('/companies', test);
|
|
||||||
router.use('/vehicles', test);
|
router.use('/vehicles', test);
|
||||||
router.use('/mailer', test);
|
router.use('/mailer', test);
|
||||||
router.use('/authmanagement', test);
|
router.use('/authmanagement', test);
|
||||||
|
|||||||
23
src/apps/private/lib/context/index.js
Normal file
23
src/apps/private/lib/context/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
'use strict';
|
||||||
|
const { ROOT_PATH, MODELS_PATH } = process.env;
|
||||||
|
const { getModel } = require( `${ROOT_PATH}/${MODELS_PATH}` );
|
||||||
|
const usersModel = getModel('users');
|
||||||
|
|
||||||
|
async function middleware( req, res, next ){
|
||||||
|
if( ! req.JWT?.isValid ){
|
||||||
|
return res.status(401).send({error:"Unauthorized",code:401});
|
||||||
|
}
|
||||||
|
const userID = req.JWT.payload.sub;
|
||||||
|
req.context = {
|
||||||
|
user : await usersModel.findById( userID , { password : 0 , session_token : 0 , session_token_exp : 0 } )
|
||||||
|
}
|
||||||
|
req.context.userId = req.context.user.id;
|
||||||
|
req.context.companyId = req.context.user.company || null;
|
||||||
|
req.context.job_role = req.context.user.job_role || null;
|
||||||
|
req.context.permissions = req.context.user.permissions || null;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
middleware
|
||||||
|
};
|
||||||
@@ -2,8 +2,15 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const services= require('./services.js');
|
const services= require('./services.js');
|
||||||
|
|
||||||
router.get('/', services.getProfileData);
|
router.get('/find', services.findList);
|
||||||
|
|
||||||
|
router.post('/member', services.postTeamMemberData);
|
||||||
|
router.patch('/member/:id', services.patchTeamMemberProfileData);
|
||||||
|
router.delete('/member/:id', services.deleteTeamMember);
|
||||||
|
|
||||||
router.get('/profile', services.getProfileData);
|
router.get('/profile', services.getProfileData);
|
||||||
router.get('/:userId', services.getProfileData);
|
router.patch('/profile', services.patchProfileData);
|
||||||
|
|
||||||
|
router.get('/:id', services.getById);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -1,20 +1,170 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
const { ROOT_PATH, HANDLERS_PATH, API_CONFIG } = process.env;
|
const { ROOT_PATH, HANDLERS_PATH } = process.env;
|
||||||
const UsersHandler = require( `${ROOT_PATH}/${HANDLERS_PATH}/Users.handler.js` );
|
const { getUserById, findUsers, patchUserData, createUserWithinCompany, deleteUserWithinCompany } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Users.handler.js` );
|
||||||
|
|
||||||
const getUsersList = async(req, res) => {
|
const findList = async(req, res) => {
|
||||||
console.log( req.params );
|
try{
|
||||||
res.send({ user : "hello world!" });
|
const {
|
||||||
|
total,
|
||||||
|
limit,
|
||||||
|
skip,
|
||||||
|
data
|
||||||
|
} = await findUsers( req.query );
|
||||||
|
return res.send({
|
||||||
|
total,
|
||||||
|
limit,
|
||||||
|
skip,
|
||||||
|
data});
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserData = async(req, res) => {
|
const getById = async(req, res) => {
|
||||||
console.log( req.params );
|
try{
|
||||||
res.send({ user : "hello world!" });
|
const id = req.params.id;
|
||||||
|
const user = await getUserById( id );
|
||||||
|
res.send({ user });
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProfileData = async(req, res) => {
|
const getProfileData = async(req, res) => {
|
||||||
const user = await UsersHandler.getUserData( req.JWT.payload.sub );
|
res.send( req.context.user );
|
||||||
res.send( user );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { getUsersList , getUserData , getProfileData};
|
const patchProfileData = async(req, res) => {
|
||||||
|
try{
|
||||||
|
if( req.body.job_role ){
|
||||||
|
/// You can't change your own role
|
||||||
|
delete req.body.job_role;
|
||||||
|
}
|
||||||
|
const user = await patchUserData( req.context.user.id , req.body );
|
||||||
|
res.send( user );
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function job_role_change_allowance( change_author_job_role , affected_job_role ){
|
||||||
|
try{
|
||||||
|
if( (change_author_job_role !== "owner") && (change_author_job_role !== "manager") ){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( affected_job_role === "owner" ){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( affected_job_role ){
|
||||||
|
case 'manager':
|
||||||
|
case 'driver':
|
||||||
|
case 'staff':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchTeamMemberProfileData = async(req, res) => {
|
||||||
|
try{
|
||||||
|
const id = req.params.id;
|
||||||
|
const companyId = req.context.companyId;
|
||||||
|
if( !companyId ){
|
||||||
|
return res.status(400).send( { error : "Not authorized to modify this user" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If a job_role change is requested, validate with rules.
|
||||||
|
if( ( req.body.job_role ) &&
|
||||||
|
( !job_role_change_allowance( req.context.job_role , req.body.job_role ) )
|
||||||
|
){
|
||||||
|
return res.status(400).send( { error : "Not authorized to upgrade the role as requested" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( req.body.job_role ) && ( req.body.job_role === "driver" ) && (req.context.permissions !== "role_carrier" ) ){
|
||||||
|
return res.status(400).send( { error : "Your company can not create drivers" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (req.context.job_role !== "owner") && (req.context.job_role !== "manager") ){
|
||||||
|
/// Only an owner or manager can modify a team member.
|
||||||
|
return res.status(400).send( { error : "Your role does not allow to modify this user" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// No one can modify an "owner".
|
||||||
|
const teamMember = await getUserById( id , { company : companyId , job_role : { $ne: "owner" } } );
|
||||||
|
if( !teamMember ){
|
||||||
|
return res.status(400).send( { error : "You can't modify users outside of your company" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply change to user.
|
||||||
|
const user_patch_result = await patchUserData( id , req.body );
|
||||||
|
return res.send( user_patch_result );
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const postTeamMemberData = async(req, res) => {
|
||||||
|
try{
|
||||||
|
const companyId = req.context.companyId;
|
||||||
|
if( !companyId ){
|
||||||
|
return res.status(400).send( { error : "Not authorized to create users" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !req.body.job_role ){
|
||||||
|
return res.status(400).send( { error : "job_role is mandatory!" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( req.body.job_role ) &&
|
||||||
|
( !job_role_change_allowance( req.context.job_role , req.body.job_role ) )
|
||||||
|
){
|
||||||
|
return res.status(400).send( { error : "Not authorized to create the role as requested" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (req.context.job_role !== "owner") && (req.context.job_role !== "manager") ){
|
||||||
|
return res.status(400).send( { error : "Not authorized to create users" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( req.body.job_role ) && ( req.body.job_role === "driver" ) && (req.context.permissions !== "role_carrier" ) ){
|
||||||
|
return res.status(400).send( { error : "Your company can not create drivers" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !req.body.email ){
|
||||||
|
return res.status(400).send( { error : "email is mandatory to create a new user" } );
|
||||||
|
}
|
||||||
|
/// Only an owner or manager can create a new user
|
||||||
|
const teamMember = await createUserWithinCompany( companyId , req.body );
|
||||||
|
return res.send( teamMember );
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTeamMember = async(req, res) => {
|
||||||
|
try{
|
||||||
|
const user_to_remove_id = req.params.id;
|
||||||
|
const manager_id = req.context.userId;
|
||||||
|
|
||||||
|
if( (req.context.job_role !== "owner") && (req.context.job_role !== "manager") ){
|
||||||
|
return res.status(400).send( { error : "Not authorized to delete this user" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
const teamMember = await deleteUserWithinCompany( manager_id, user_to_remove_id );
|
||||||
|
return res.send( teamMember );
|
||||||
|
}catch( error ){
|
||||||
|
console.error( error );
|
||||||
|
return res.status( 500 ).send( { error } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { findList , getById , getProfileData, patchProfileData, patchTeamMemberProfileData, postTeamMemberData , deleteTeamMember };
|
||||||
|
|||||||
146
src/index.js
146
src/index.js
@@ -1,73 +1,73 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const { ROOT_PATH, LIB_PATH, API_CONFIG } = process.env;
|
const { ROOT_PATH, LIB_PATH, API_CONFIG } = process.env;
|
||||||
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
||||||
const apps = require('./apps');
|
const apps = require('./apps');
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const compression = require('compression');
|
const compression = require('compression');
|
||||||
const morgan = require('morgan');
|
const morgan = require('morgan');
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const fileUpload = require('express-fileupload');
|
const fileUpload = require('express-fileupload');
|
||||||
const middlewares = require( `${ROOT_PATH}/${LIB_PATH}/Middlewares.js` );
|
const middlewares = require( `${ROOT_PATH}/${LIB_PATH}/Middlewares.js` );
|
||||||
|
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
mongoose.connect(
|
mongoose.connect(
|
||||||
apiConfig.mongodb,
|
apiConfig.mongodb,
|
||||||
{ useNewUrlParser: true }
|
{ useNewUrlParser: true }
|
||||||
).then( ( val ) => {
|
).then( ( val ) => {
|
||||||
console.log( `MongoDB Connected : ${ apiConfig.mongodb }` );
|
console.log( `MongoDB Connected : ${ apiConfig.mongodb }` );
|
||||||
});//catch throw error so service stops!
|
});//catch throw error so service stops!
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const serverPort = process.env.SERVER_PORT || 3000;
|
const serverPort = process.env.SERVER_PORT || 3000;
|
||||||
|
|
||||||
app.use( middlewares.Auth );
|
app.use( middlewares.Auth );
|
||||||
app.use(
|
app.use(
|
||||||
fileUpload({
|
fileUpload({
|
||||||
limits: { fileSize: 4 * 1024 * 1024 },
|
limits: { fileSize: 4 * 1024 * 1024 },
|
||||||
abortOnLimit: true,
|
abortOnLimit: true,
|
||||||
limitHandler: (req,res,next) => {
|
limitHandler: (req,res,next) => {
|
||||||
req.limitSize = true;
|
req.limitSize = true;
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if (req.limitSize) {
|
if (req.limitSize) {
|
||||||
res.status(413).send({message:"File size limit has been reached",status:"PAYLOAD_TOO_LARGE"});
|
res.status(413).send({message:"File size limit has been reached",status:"PAYLOAD_TOO_LARGE"});
|
||||||
}else{
|
}else{
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
|
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
|
||||||
app.use(bodyParser.json({ limit: '50mb' }));
|
app.use(bodyParser.json({ limit: '50mb' }));
|
||||||
app.use(morgan('dev'));
|
app.use(morgan('dev'));
|
||||||
app.use(helmet({
|
app.use(helmet({
|
||||||
crossOriginResourcePolicy: false
|
crossOriginResourcePolicy: false
|
||||||
}));
|
}));
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: '*',
|
origin: '*',
|
||||||
methods: [
|
methods: [
|
||||||
'GET',
|
'GET',
|
||||||
'POST',
|
'POST',
|
||||||
'PATCH',
|
'PATCH',
|
||||||
'PUT',
|
'PUT',
|
||||||
'DELETE'
|
'DELETE'
|
||||||
],
|
],
|
||||||
allowedHeaders: ['Content-Type', 'Authorization']
|
allowedHeaders: ['Content-Type', 'Authorization']
|
||||||
}));
|
}));
|
||||||
app.use( middlewares.errorJSON );
|
app.use( middlewares.errorJSON );
|
||||||
app.use( apps );
|
app.use( apps );
|
||||||
app.use( middlewares.error404 );
|
app.use( middlewares.error404 );
|
||||||
|
|
||||||
app.listen( serverPort , function(err){
|
app.listen( serverPort , function(err){
|
||||||
if( !err ){
|
if( !err ){
|
||||||
console.log('API listen on port', serverPort );
|
console.log('API listen on port', serverPort );
|
||||||
}else{
|
}else{
|
||||||
console.log( err );
|
console.log( err );
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
'user strict';
|
'user strict';
|
||||||
const { ROOT_PATH, API_CONFIG, MODELS_PATH, LIB_PATH } = process.env;
|
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 apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
|
||||||
const { toSha256 } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
|
const { toSha256 } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
|
||||||
const UserModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` );
|
const UserModel = getModel('users');
|
||||||
|
const companiesModels = getModel('companies');
|
||||||
|
|
||||||
const pwd_secret = apiConfig.authentication.pwdSecret;
|
const pwd_secret = apiConfig.authentication.pwdSecret;
|
||||||
|
|
||||||
@@ -11,6 +13,7 @@ async function create_account( email, password ){
|
|||||||
const user = new UserModel({
|
const user = new UserModel({
|
||||||
email,
|
email,
|
||||||
password : safe_password,
|
password : safe_password,
|
||||||
|
job_role : 'owner',//Always a new user created from signup is owner
|
||||||
isVerified : false//Allows old API to recover password
|
isVerified : false//Allows old API to recover password
|
||||||
});
|
});
|
||||||
await user.save();
|
await user.save();
|
||||||
@@ -58,7 +61,7 @@ async function login( email , password ){
|
|||||||
let safe_password = toSha256( password + pwd_secret );
|
let safe_password = toSha256( password + pwd_secret );
|
||||||
const user = await UserModel.findOne({
|
const user = await UserModel.findOne({
|
||||||
email , password : safe_password
|
email , password : safe_password
|
||||||
},{ password : 0 });
|
},{ password : 0 , session_token : 0 , session_token_exp : 0 });
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +69,50 @@ async function login_with_session_token( session_token ){
|
|||||||
const user = await UserModel.findOne({
|
const user = await UserModel.findOne({
|
||||||
session_token,
|
session_token,
|
||||||
session_token_exp : { $gte: new Date() }
|
session_token_exp : { $gte: new Date() }
|
||||||
},{ password : 0 });
|
},{ password : 0 , session_token : 0 , session_token_exp : 0 });
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { create_account, already_exists, verify_driver_account, login, login_with_session_token, reset_password };
|
async function complete_register( userId , data ){
|
||||||
|
let {
|
||||||
|
company_type,
|
||||||
|
company_name,
|
||||||
|
company_description,
|
||||||
|
} = 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
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( {
|
||||||
|
company_type,
|
||||||
|
company_name,
|
||||||
|
company_description,
|
||||||
|
} );
|
||||||
|
|
||||||
|
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 };
|
||||||
|
|||||||
@@ -1,9 +1,176 @@
|
|||||||
'user strict';
|
'user strict';
|
||||||
const { ROOT_PATH, HANDLERS_PATH, MODELS_PATH, API_CONFIG } = process.env;
|
const { ROOT_PATH, MODELS_PATH, HANDLERS_PATH, LIB_PATH } = process.env;
|
||||||
const usersModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` );
|
const { getModel } = require( `${ROOT_PATH}/${MODELS_PATH}` );
|
||||||
|
const { GenericHandler } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Generic.handler.js` );
|
||||||
|
const { getPagination } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
|
||||||
|
|
||||||
async function getUserData( id ){
|
const usersModel = getModel('users');
|
||||||
return await usersModel.findById( id , { password : 0 } );
|
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 } );
|
||||||
|
console.log( filter , user );
|
||||||
|
return user;
|
||||||
|
}else{
|
||||||
|
return await usersModel.findById( id , { password : 0 , session_token : 0 , session_token_exp : 0 } );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { getUserData };
|
function getAndFilterList( query ){
|
||||||
|
const filter_list = [];
|
||||||
|
const { permissions, gender, job_role, employee_id, company, branch, vehicle, active_load, categories } = query;
|
||||||
|
|
||||||
|
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 !== user.email ){
|
||||||
|
const user_already_exists = await usersModel.find({ 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 } );
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
const user = new usersModel( data );
|
||||||
|
await user.save();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteUserWithinCompany( manager_id , user_to_remove_id ){
|
||||||
|
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"; }
|
||||||
|
|
||||||
|
user.is_deleted = true;
|
||||||
|
user.email = user.id;
|
||||||
|
user.password = null;
|
||||||
|
user.deleted_at = new Date();
|
||||||
|
|
||||||
|
await user.save();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { getUserById , findUsers , patchUserData , createUserWithinCompany , deleteUserWithinCompany };
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const schema = new Schema({
|
|||||||
rfc: { type: String },
|
rfc: { type: String },
|
||||||
|
|
||||||
|
|
||||||
company_type: [{ type: String }], // Shipper , Carrier
|
company_type: { type: String, enum : [ 'Shipper', 'Carrier' ] },
|
||||||
is_broker: { type: Boolean, default: false },
|
is_broker: { type: Boolean, default: false },
|
||||||
membership: { type: String },
|
membership: { type: String },
|
||||||
membership_start_at: { type: Date },
|
membership_start_at: { type: Date },
|
||||||
@@ -43,7 +43,6 @@ const schema = new Schema({
|
|||||||
meta_data: [meta_data],
|
meta_data: [meta_data],
|
||||||
categories: [{ type: Schema.Types.ObjectId, ref: 'productcategories' }],
|
categories: [{ type: Schema.Types.ObjectId, ref: 'productcategories' }],
|
||||||
products: { type: Schema.Types.ObjectId, ref: 'products' },
|
products: { type: Schema.Types.ObjectId, ref: 'products' },
|
||||||
users: [{ type: Schema.Types.ObjectId, ref: 'users' }],
|
|
||||||
branches: [{ type: Schema.Types.ObjectId, ref: 'branches' }],
|
branches: [{ type: Schema.Types.ObjectId, ref: 'branches' }],
|
||||||
company_city: [{ type: String }],
|
company_city: [{ type: String }],
|
||||||
company_state: [{ type: String }],
|
company_state: [{ type: String }],
|
||||||
|
|||||||
73
src/lib/Models/index.js
Normal file
73
src/lib/Models/index.js
Normal 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
|
||||||
|
};
|
||||||
@@ -21,13 +21,13 @@ const schema = new Schema({
|
|||||||
password: { type: String , maxLength : 256 },
|
password: { type: String , maxLength : 256 },
|
||||||
phone: { type: String },
|
phone: { type: String },
|
||||||
phone2: { type: String },
|
phone2: { type: String },
|
||||||
permissions: [{ type: String, default: 'role_admin', enum : [ 'admin', 'role_admin', 'role_shipper', 'role_carrier', 'role_driver' ] }],
|
permissions: { type: String, default: 'role_admin', enum : [ 'admin', '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 },
|
||||||
|
|
||||||
// vehicle_status: { type: String, enum: ['Free', 'Loading', 'Moving', 'Downloading'] },
|
// vehicle_status: { type: String, enum: ['Free', 'Loading', 'Moving', 'Downloading'] },
|
||||||
job_role: { type: String }, // admin, owner, driver, staff
|
job_role: { type: String, enum : [ 'admin', 'owner', 'manager', 'driver', 'staff' ] },
|
||||||
|
|
||||||
employee_id: { type: String }, //EM-1000-1 EM-1000-2
|
employee_id: { type: String }, //EM-1000-1 EM-1000-2
|
||||||
company: { type: Schema.Types.ObjectId, ref: 'companies' },
|
company: { type: Schema.Types.ObjectId, ref: 'companies' },
|
||||||
@@ -49,19 +49,13 @@ const schema = new Schema({
|
|||||||
last_location_time: { type: Date },
|
last_location_time: { type: Date },
|
||||||
|
|
||||||
isVerified: { type: Boolean },
|
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 },
|
|
||||||
|
|
||||||
session_token : { type : String, maxLength : 256 },
|
session_token : { type : String, maxLength : 256 },
|
||||||
session_token_exp : { type: Date },
|
session_token_exp : { type: Date },
|
||||||
|
|
||||||
is_hidden: { type: Boolean, default: false },
|
is_hidden: { type: Boolean, default: false },
|
||||||
|
is_deleted: { type: Boolean, default: false },
|
||||||
|
deleted_at: { type: Date },
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model( "users", schema );
|
module.exports = mongoose.model( "users", schema );
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ function middleware( req, res, next ){
|
|||||||
req.JWT.isValid = true;
|
req.JWT.isValid = true;
|
||||||
}
|
}
|
||||||
}catch( err ){
|
}catch( err ){
|
||||||
|
console.error( err );
|
||||||
return res.status(401).send({error:"Unauthorized",code:401});
|
return res.status(401).send({error:"Unauthorized",code:401});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
|||||||
Reference in New Issue
Block a user