@tiledesk/tiledesk-server
Version:
The Tiledesk server module
613 lines (477 loc) • 25.8 kB
JavaScript
var express = require('express');
var router = express.Router({mergeParams: true});
var Project_user = require("../models/project_user");
var mongoose = require('mongoose');
var User = require("../models/user");
var emailService = require("../services/emailService");
var Project = require("../models/project");
var pendinginvitation = require("../services/pendingInvitationService");
const authEvent = require('../event/authEvent');
var winston = require('../config/winston');
var RoleConstants = require("../models/roleConstants");
var ProjectUserUtil = require("../utils/project_userUtil");
const uuidv4 = require('uuid/v4');
var passport = require('passport');
require('../middleware/passport')(passport);
var validtoken = require('../middleware/valid-token')
var roleChecker = require('../middleware/has-role');
const puEvent = require('../event/projectUserEvent');
router.post('/invite', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('admin')], function (req, res) {
winston.debug('Invite ProjectUser body ', req.body);
let id_project = req.projectid;
let email = req.body.email;
if (email) {
email = email.toLowerCase();
}
winston.debug('Invite ProjectUser with email ' + email + ' on project ' + req.projectid);
User.findOne({ email: email, status: 100 }, (err, user) => {
if (err) {
winston.error("Error in searching for a possible existing project user with email " + email);
return res.status(500).send({ success: false, error: "An error occurred during the invite process" });
}
if (!user) {
// User not registered on Tiledesk Platform -> Save email and project_id in pending invitation
// TODO req.user.firstname is null for bot visitor
return pendinginvitation.saveInPendingInvitation(req.projectid, req.project.name, email, req.body.role, req.user._id, req.user.firstname, req.user.lastname).then(function (savedPendingInvitation) {
var eventData = { req: req, savedPendingInvitation: savedPendingInvitation };
winston.debug("eventData", eventData);
authEvent.emit('project_user.invite.pending', eventData);
return res.json({ msg: "User not found, save invite in pending ", pendingInvitation: savedPendingInvitation });
}).catch(function (err) {
return res.send(err);
});
} else if (req.user.id == user._id) {
// If the current user id is = to the id of found user return an error. To a user is not allowed to invite oneself.
winston.debug('Invite User: found user ' + user._id + ' is equal to current user ' + req.user.id + '. Is not allowed to invite oneself.')
return res.status(403).send({ success: false, msg: 'Forbidden. It is not allowed to invite oneself', code: 4000 });
} else {
/**
* *** IT IS NOT ALLOWED TO INVITE A USER WHO IS ALREADY A MEMBER OF THE PROJECT ***
* FIND THE PROJECT USERS FOR THE PROJECT ID PASSED BY THE CLIENT IN THE BODY OF THE REQUEST
* IF THE ID OF THE USER FOUND FOR THE EMAIL (PASSED IN THE BODY OF THE REQUEST - see above)
* MATCHES ONE OF THE USER ID CONTENTS IN THE PROJECTS USER OBJECT STOP THE WORKFLOW AND RETURN AN ERROR */
// rolequery
// var role = [RoleConstants.OWNER, RoleConstants.ADMIN, RoleConstants.SUPERVISOR, RoleConstants.AGENT];
// winston.debug("role", role);
// winston.debug("PROJECT USER ROUTES - req projectid", req.projectid);
return Project_user.findOne({ id_project: req.projectid, id_user: user._id, roleType: RoleConstants.TYPE_AGENTS, status: "active"}, function (err, puser) {
// return Project_user.find({ id_project: req.projectid, role: { $in : role }, status: "active"}, function (err, projectuser) {
//puser = projectuser
winston.debug('PRJCT-USERS FOUND (FILTERED FOR THE PROJECT ID) ', puser )
if (err) {
winston.error("Error inviting an already existing user: ", err);
return res.status(500).send({ success: false, msg: "An error occurred on inviting user " + email + " on project " + id_project })
}
// if (!roles.includes(req.body.role)) {
// return res.status(400).send({ success: false, msg: 'Invalid role specified: ' + req.body.role });
// }
let user_available = typeof req.body.user_available === 'boolean' ? req.body.user_available : true
if (puser) {
if (puser.trashed !== true) {
winston.warn("Trying to invite an already project member user")
return res.status(403).send({ success: false, msg: 'Forbidden. User is already a member', code: 4001 });
}
winston.debug('NO ERROR, SO CREATE AND SAVE A NEW PROJECT USER ')
var newProject_user = new Project_user({
// _id: new mongoose.Types.ObjectId(),
id_project: req.projectid,
id_user: user._id,
role: req.body.role,
roleType : RoleConstants.TYPE_AGENTS,
user_available: user_available,
createdBy: req.user.id,
updatedBy: req.user.id
});
return newProject_user.save(function (err, savedProject_user) {
if (err) {
winston.error("Error update existing project user before inviting it ", err)
return res.status(500).send({ success: false, msg: "An error occurred on inviting user " + email + " on project " + id_project })
}
emailService.sendYouHaveBeenInvited(email, req.user.firstname, req.user.lastname, req.project.name, id_project, user.firstname, user.lastname, req.body.role)
updatedPuser.populate({path:'id_user', select:{'firstname':1, 'lastname':1}},function (err, updatedPuserPopulated){
var pu = updatedPuserPopulated.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(savedProject_userPopulated, req.project.settings && req.project.settings.max_agent_assigned_chat);
var eventData = {req:req, updatedPuserPopulated: pu};
winston.debug("eventData",eventData);
authEvent.emit('project_user.invite', eventData);
});
return res.status(200).send(updatedPuser);
})
} else {
let newProject_user = new Project_user({
id_project: id_project,
id_user: user._id,
role: req.body.role,
roleType : RoleConstants.TYPE_AGENTS,
user_available: user_available,
createdBy: req.user.id,
updatedBy: req.user.id
})
newProject_user.save((err, savedProject_user) => {
if (err) {
winston.error("Error saving new project user: ", err)
return res.status(500).send({ success: false, msg: "An error occurred on inviting user " + email + " on project " + id_project })
}
emailService.sendYouHaveBeenInvited(email, req.user.firstname, req.user.lastname, req.project.name, id_project, user.firstname, user.lastname, req.body.role)
savedProject_user.populate({path:'id_user', select:{'firstname':1, 'lastname':1}},function (err, savedProject_userPopulated){
var pu = savedProject_userPopulated.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(savedProject_userPopulated, req.project.settings && req.project.settings.max_agent_assigned_chat);
var eventData = {req:req, savedProject_userPopulated: pu};
winston.debug("eventData",eventData);
authEvent.emit('project_user.invite', eventData);
});
return res.status(200).send(savedProject_user);
})
}
})
}
});
});
router.post('/', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], function (req, res) {
var newProject_user = new Project_user({
id_project: req.projectid, //il fullname????
uuid_user: uuidv4(),
// role: RoleConstants.USER,
// - Create project_user endpoint by agent (Ticketing) now is with Guest Role
role: RoleConstants.GUEST,
roleType : RoleConstants.TYPE_USERS,
user_available: false,
tags: req.body.tags,
createdBy: req.user.id,
updatedBy: req.user.id
});
return newProject_user.save(function (err, savedProject_user) {
if (err) {
winston.error('--- > ERROR ', err)
return res.status(500).send({ success: false, msg: 'Error saving object.' });
}
return res.json(savedProject_user);
});
})
router.put('/', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], function (req, res) {
winston.debug("projectuser patch", req.body);
var update = {};
if (req.body.user_available!=undefined) {
update.user_available = req.body.user_available;
}
if (req.body.profileStatus!=undefined) {
update.profileStatus = req.body.profileStatus;
}
if (req.body.max_assigned_chat!=undefined) {
update.max_assigned_chat = req.body.max_assigned_chat;
}
if (req.body.number_assigned_requests!=undefined) {
update.number_assigned_requests = req.body.number_assigned_requests;
}
if (req.body.attributes!=undefined) {
update.attributes = req.body.attributes;
}
if (req.body["settings.email.notification.conversation.assigned.toyou"]!=undefined) {
update["settings.email.notification.conversation.assigned.toyou"] = req.body["settings.email.notification.conversation.assigned.toyou"];
}
if (req.body["settings.email.notification.conversation.pooled"]!=undefined) {
update["settings.email.notification.conversation.pooled"] = req.body["settings.email.notification.conversation.pooled"];
}
if (req.body.tags!=undefined) {
update.tags = req.body.tags;
}
Project_user.findByIdAndUpdate(req.projectuser.id, update, { new: true, upsert: true }, function (err, updatedProject_user) {
if (err) {
winston.error("Error gettting project_user for update", err);
return res.status(500).send({ success: false, msg: 'Error updating object.' });
}
updatedProject_user.populate({ path:'id_user', select: { 'firstname': 1, 'lastname': 1 }}, function (err, updatedProject_userPopulated) {
var pu = updatedProject_userPopulated.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(updatedProject_userPopulated, req.project.settings && req.project.settings.max_agent_assigned_chat);
authEvent.emit('project_user.update', {updatedProject_userPopulated:pu, req: req});
});
res.json(updatedProject_user);
});
});
router.put('/:project_userid', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('admin', ['subscription'])], function (req, res) {
winston.debug("project_userid update", req.body);
var update = {};
if (req.body.role!=undefined) {
update.role = req.body.role;
}
if (req.body.user_available!=undefined) {
update.user_available = req.body.user_available;
}
if (req.body.profileStatus!=undefined) {
update.profileStatus = req.body.profileStatus;
}
if (req.body.max_assigned_chat!=undefined) {
update.max_assigned_chat = req.body.max_assigned_chat;
}
if (req.body.number_assigned_requests!=undefined) {
update.number_assigned_requests = req.body.number_assigned_requests;
}
if (req.body.attributes!=undefined) {
update.attributes = req.body.attributes;
}
const allowedStatuses = ['active', 'disabled'];
if (req.body.status !== undefined && allowedStatuses.includes(req.body.status)) {
update.status = req.body.status;
}
if (req.body["settings.email.notification.conversation.assigned.toyou"]!=undefined) {
update["settings.email.notification.conversation.assigned.toyou"] = req.body["settings.email.notification.conversation.assigned.toyou"];
}
if (req.body["settings.email.notification.conversation.pooled"]!=undefined) {
update["settings.email.notification.conversation.pooled"] = req.body["settings.email.notification.conversation.pooled"];
}
if (req.body.tags!=undefined) {
update.tags = req.body.tags;
}
winston.debug("project_userid update", update);
Project_user.findByIdAndUpdate(req.params.project_userid, update, { new: true, upsert: true }, function (err, updatedProject_user) {
if (err) {
winston.error("Error gettting project_user for update", err);
return res.status(500).send({ success: false, msg: 'Error updating object.' });
}
updatedProject_user.populate({path:'id_user', select:{'firstname':1, 'lastname':1}},function (err, updatedProject_userPopulated){
if (err) {
winston.error("Error gettting updatedProject_userPopulated for update", err);
}
var pu = updatedProject_userPopulated.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(updatedProject_user, req.project.settings && req.project.settings.max_agent_assigned_chat);
authEvent.emit('project_user.update', {updatedProject_userPopulated:pu, req: req});
});
res.json(updatedProject_user);
});
});
// TODO fai servizio di patch degli attributi come request
// TODO blocca cancellazione owner?
router.delete('/:project_userid', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('admin')], function (req, res) {
const { hard, soft } = req.query;
const pu_id = req.params.project_userid;
winston.debug(req.body);
if (soft === "true") {
// Soft Delete
Project_user.findByIdAndUpdate(pu_id, { trashed: true }, { new: true }, (err, project_user) => {
if (err) {
winston.error("Error gettting project_user for soft delete", err);
return res.status(500).send({ success: false, msg: 'Error deleting Project User with id ' + pu_id });
}
winston.debug("Soft deleted project_user", project_user);
if (!project_user) {
winston.warn("Project user not found for soft delete with id " + pu_id);
return res.status(404).send({ success: false, error: 'Project user not found with id ' + pu_id });
}
puEvent.emit('project_user.deleted', project_user);
// Event 'project_user.delete' not working - Check it and improve it to manage soft/hard delete
return res.status(200).send(project_user);
})
}
else if (hard === "true") {
// Hard Delete
Project_user.findByIdAndRemove(pu_id, { new: false }, (err, project_user) => {
if (err) {
winston.error("Error gettting project_user for hard delete", err);
return res.status(500).send({ success: false, msg: 'Error deleting Project user with id ' + pu_id });
}
if (!project_user) {
winston.warn("Project user not found for soft delete with id " + pu_id);
return res.status(404).send({ success: false, error: 'Project user not found with id ' + pu_id });
}
winston.debug("Hard deleted project_user", project_user);
if (project_user) {
project_user.populate({ path: 'id_user', select: { 'firstname': 1, 'lastname': 1 } }, function (err, project_userPopulated) {
authEvent.emit('project_user.delete', { req: req, project_userPopulated: project_userPopulated });
});
}
puEvent.emit('project_user.deleted', project_user);
return res.status(200).send(project_user);
});
}
else {
// Disable
Project_user.findByIdAndUpdate(pu_id, { status: "disabled", user_available: false }, { new: true }, (err, project_user) => {
if (err) {
winston.error("Error gettting project_user for disable user", err);
return res.status(500).send({ success: false, msg: 'Error disabling Project User with id ' + pu_id });
}
if (!project_user) {
winston.warn("Project user not found for soft delete with id " + pu_id);
return res.status(404).send({ success: false, error: 'Project user not found with id ' + pu_id });
}
winston.debug("Disabled project_user", project_user);
// Event 'project_user.delete' not working - Check it and improve it to manage disable project user
return res.status(200).send(project_user);
});
}
});
// Restore a soft-deleted (trashed) project user. Fails if not found or not trashed.
router.put('/:project_userid/restore', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('admin')], function (req, res) {
const pu_id = req.params.project_userid;
Project_user.findOne({ _id: pu_id, id_project: req.projectid }, function (err, project_user) {
if (err) {
winston.error("Error finding project_user for restore", err);
return res.status(500).send({ success: false, msg: 'Error restoring Project User with id ' + pu_id });
}
if (!project_user) {
winston.warn("Project user not found for restore with id " + pu_id);
return res.status(404).send({ success: false, error: 'Project user not found with id ' + pu_id });
}
if (project_user.trashed !== true) {
winston.warn("Project user is not trashed, cannot restore id " + pu_id);
return res.status(400).send({ success: false, error: 'Project user is not trashed, cannot restore' });
}
Project_user.findByIdAndUpdate(pu_id, { trashed: false, status: 'active' }, { new: true }, function (err, updatedProject_user) {
if (err) {
winston.error("Error restoring project_user", err);
return res.status(500).send({ success: false, msg: 'Error restoring Project User with id ' + pu_id });
}
winston.debug("Restored project_user", updatedProject_user);
updatedProject_user.populate({ path: 'id_user', select: { 'firstname': 1, 'lastname': 1 } }, function (err, updatedProject_userPopulated) {
if (err) {
winston.error("Error populating restored project_user", err);
} else {
var pu = updatedProject_userPopulated.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(updatedProject_user, req.project && req.project.settings && req.project.settings.max_agent_assigned_chat);
authEvent.emit('project_user.update', { updatedProject_userPopulated: pu, req: req });
}
});
return res.status(200).send(updatedProject_user);
});
});
});
router.get('/me', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['subscription'])], function (req, res, next) {
if (!req.project) {
return res.status(404).send({ success: false, msg: 'Project not found.' });
}
var project_user = req.projectuser;
var pu = project_user.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(project_user, req.project.settings && req.project.settings.max_agent_assigned_chat);
res.json([pu]);
});
router.get('/:project_userid', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['subscription'])], function (req, res) {
// router.get('/details/:project_userid', function (req, res) {
// winston.debug("PROJECT USER ROUTES - req projectid", req.projectid);
Project_user.findOne({ _id: req.params.project_userid, id_project: req.projectid}).
populate('id_user'). //qui cache importante ma populatevirtual
exec(function (err, project_user) {
if (err) {
winston.error("Error gettting project_user for get", err);
return res.status(500).send({ success: false, msg: 'Error getting object.' });
}
if (!project_user) {
return res.status(404).send({ success: false, msg: 'Object not found.' });
}
// res.json(project_user);
var pu = project_user.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(project_user, req.project.settings && req.project.settings.max_agent_assigned_chat);
res.json(pu);
});
});
router.get('/users/search', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('user', ['subscription'])], async (req, res, next) => { //changed for smtp
// router.get('/users/search', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['subscription'])], async (req, res, next) => {
winston.debug("--> users search ");
if (!req.project) {
return res.status(404).send({ success: false, msg: 'Project not found.' });
}
let query = {email: req.query.email};
winston.debug('query: ', query);
let user = await User.findOne(query).exec();
winston.debug('user: ', user);
if (!user) {
return res.status(404).send({ success: false, msg: 'Object not found.' });
}
let project_user = await Project_user.findOne({id_user: user._id, id_project: req.projectid}).exec();
winston.debug('project_user: ', project_user);
if (!project_user) {
return res.status(403).json({msg: "Unauthorized. This is not a your teammate." });
}
return res.json({_id: user._id});
});
/**
* GET PROJECT-USER BY PROJECT ID AND CURRENT USER ID
// */
router.get('/users/:user_id', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['subscription'])], function (req, res, next) {
winston.debug("--> users USER ID ", req.params.user_id);
if (!req.project) {
return res.status(404).send({ success: false, msg: 'Project not found.' });
}
var isObjectId = mongoose.Types.ObjectId.isValid(req.params.user_id);
winston.debug("isObjectId:"+ isObjectId);
var queryProjectUser ={ id_project: req.projectid};
if (isObjectId) {
queryProjectUser.id_user = req.params.user_id
}else {
queryProjectUser.uuid_user = req.params.user_id
}
var q1 = Project_user.findOne(queryProjectUser);
if (isObjectId) {
q1.populate('id_user'); //qui cache importante ma populatevirtual
}
// Project_user.findOne({ id_user: req.params.user_id, id_project: req.projectid }).
q1.exec(function (err, project_user) {
if (err) {
winston.error("Error gettting project_user for get users", err);
return res.status(500).send({ success: false, msg: 'Error getting object.' });
}
if (!project_user) {
return res.status(404).send({ success: false, msg: 'Object not found.' });
}
// res.json(project_user);
var pu = project_user.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(project_user, req.project.settings && req.project.settings.max_agent_assigned_chat);
res.json([pu]);
});
});
// TODO if project is deleted
// 2020-03-31T17:25:45.939421+00:00 app[web.1]:
// 2020-03-31T17:25:45.998260+00:00 app[web.1]: error: uncaughtException: Cannot read property 'settings' of undefined
// 2020-03-31T17:25:45.998262+00:00 app[web.1]: TypeError: Cannot read property 'settings' of undefined
// 2020-03-31T17:25:45.998262+00:00 app[web.1]: at /app/routes/project_user.js:372:68
// 2020-03-31T17:25:45.998263+00:00 app[web.1]: at /app/node_modules/mongoose/lib/model.js:4779:16
// 2020-03-31T17:25:45.998263+00:00 app[web.1]: at /app/node_modules/mongoose/lib/utils.js:276:16
// 2020-03-31T17:25:45.998263+00:00 app[web.1]: at /app/node_modules/mongoose/lib/model.js:4798:21
// 2020-03-31T17:25:45.998264+00:00 app[web.1]: at _hooks.execPost (/app/node_modules/mongoose/lib/query.js:4364:11)
// 2020-03-31T17:25:45.998264+00:00 app[web.1]: at /app/node_modules/kareem/index.js:135:16
// 2020-03-31T17:25:45.998269+00:00 app[web.1]: at processTicksAndRejections (internal/process/next_tick.js:74:9) {"error":{},"stack":"TypeError: Cannot read property 'settings' of undefined\n at /app/routes/project_user.js:372
//
/**
* RETURN THE PROJECT-USERS OBJECTS FILTERD BY PROJECT-ID AND WITH NESTED THE USER OBJECT
* WF: 1. GET PROJECT-USER by the passed project ID
* 2. POPULATE THE user_id OF THE PROJECT-USER object WITH THE USER OBJECT
*/
router.get('/', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['bot', 'subscription'])], function (req, res) {
// rolequery
// var role = [RoleConstants.OWNER, RoleConstants.ADMIN, RoleConstants.SUPERVISOR, RoleConstants.AGENT];
var query;
if (req.query.role) {
role = req.query.role;
winston.debug("role", role);
query = {id_project: req.projectid, role: { $in : role } };
} else {
query = {id_project: req.projectid, roleType: RoleConstants.TYPE_AGENTS };
}
if (!req.query.trashed || req.query.trashed === 'false' || req.query.trashed === false) {
query.trashed = { $ne: true };
}
// var query = {id_project: req.projectid, role: { $in : role } };
if (req.query.presencestatus) {
query["presence.status"] = req.query.presencestatus;
}
winston.debug("query", query);
if (req.query.status) {
query["status"] = req.query.status;
}
Project_user.find(query).
populate('id_user').
// lean().
exec(function (err, project_users) {
if (err) {
winston.error("Error gettting project_user for get users", err);
return res.status(500).send({ success: false, msg: 'Error getting object.' });
}
var ret = [];
project_users.forEach(function(project_user) {
var pu = project_user.toJSON();
pu.isBusy = ProjectUserUtil.isBusy(project_user, req.project && req.project.settings && req.project.settings.max_agent_assigned_chat);
ret.push(pu);
});
res.json(ret);
});
});
module.exports = router;