267 lines
8.6 KiB
JavaScript
267 lines
8.6 KiB
JavaScript
'use strict';
|
|
|
|
const User = require('../model/user'),
|
|
UserModelFactory = require('../model/user_model_factory'),
|
|
App = require('../model/application'),
|
|
cache = require('../helpers/mem_cache'),
|
|
util = require('util'),
|
|
jwt = require('jsonwebtoken'),
|
|
verifyAsync = util.promisify(jwt.verify),
|
|
moment = require('moment'),
|
|
ObjectId = require('mongodb').ObjectId,
|
|
{ Errors, UserTypes, DEFAULT_LANG } = require('../helpers/constants'),
|
|
{ AppError, AppParamError, AppAuthError } = require('../helpers/app_error'),
|
|
mailer = require('../helpers/mailer'),
|
|
env = require('../helpers/env'),
|
|
subscriptionCtl = require("./subscription");
|
|
|
|
async function ensureParentExists(user) {
|
|
if (!user || user.kind <= 1 || !user.parent || !ObjectId.isValid(user.parent)) return;
|
|
|
|
const pUser = await User.findById(ObjectId(user.parent), null, { lean: true });
|
|
if (!pUser) AppError.throw(Errors.PARENT_NOT_EXIST);
|
|
|
|
return pUser;
|
|
}
|
|
|
|
async function clearTempData(uid) {
|
|
if (ObjectId.isValid(uid))
|
|
await App.deleteMany({ byUser: ObjectId(uid), $or: [{ status: -1 }, { status: 0 }] });
|
|
}
|
|
|
|
async function createUser_post(req, res) {
|
|
const _user = req.body;
|
|
delete _user._id;
|
|
|
|
await ensureParentExists(_user);
|
|
|
|
const newUser = new User(_user);
|
|
const user = await newUser.save();
|
|
res.json(user);
|
|
}
|
|
|
|
async function getUser_get(req, res) {
|
|
const _user = await User.findById(req.params.userId, null, { lean: true }).populate({ path: 'Country', select: 'code name -_id' });
|
|
res.json(_user);
|
|
}
|
|
|
|
async function updateUser_put(req, res) {
|
|
const _user = req.body, userId = req.params.userId;
|
|
|
|
if (!_user || !ObjectId.isValid(userId)) AppParamError.throw();
|
|
delete req.body._id;
|
|
|
|
let kind = _user.kind;
|
|
if (!kind) {
|
|
const theUser = await User.findById(userId, 'kind', { lean: true });
|
|
if (!theUser) AppError.throw(Errors.USER_NOT_FOUND);
|
|
|
|
kind = theUser.kind;
|
|
}
|
|
|
|
const UserModel = UserModelFactory.create(kind);
|
|
const user = await UserModel.findOneAndUpdate({ _id: ObjectId(userId) }, _user, { new: true, lean: true });
|
|
res.json(user);
|
|
}
|
|
|
|
async function deleteUser(req, res) {
|
|
const _id = req.params.userId;
|
|
if (!_id || !ObjectId.isValid(_id)) AppParamError.throw();
|
|
|
|
const user = await User.findByIdAndRemove({ _id: ObjectId(_id) });
|
|
if (user) await user.remove();
|
|
|
|
cache.delete(_id);
|
|
res.json({ ok: true });
|
|
}
|
|
|
|
async function login_post(req, res) {
|
|
if (!req.body || !req.body.username || !req.body.password) AppAuthError.throw(Errors.WRONG_CREDENTIAL);
|
|
|
|
const _options = { username: { $regex: new RegExp(`^${req.body.username}$`, 'i') }, password: req.body.password };
|
|
const _user = await User.findOne(_options, null, { lean: true });
|
|
|
|
if (!_user) AppAuthError.throw(Errors.WRONG_CREDENTIAL);
|
|
|
|
if (_user.kind == UserTypes.DEVICE && (req.body.dev && req.body.dev == 'web')) AppAuthError.throw(Errors.INVALID_ACCOUNT);
|
|
|
|
const hasParent = Boolean(_user && _user.parent && _user.kind >= 2);
|
|
// The applicator user
|
|
let _parentUser;
|
|
if (hasParent) {
|
|
_parentUser = await User.findOne({ _id: _user.parent }, null, { lean: true });
|
|
} else if (_user.kind === UserTypes.ADMIN || _user.kind === UserTypes.APP) {
|
|
_parentUser = _user;
|
|
}
|
|
|
|
if (hasParent && !_parentUser) AppAuthError.throw(Errors.WRONG_CREDENTIAL);
|
|
|
|
await clearTempData(_user._id);
|
|
|
|
const isActive = hasParent ? (_parentUser.active && _user.active) : _user.active;
|
|
let authUser;
|
|
if (isActive) {
|
|
const premium = _parentUser.premium || 0; // To be removed soon
|
|
const token = jwt.sign({ uid: _user._id, ut: _user.kind }, env.TOKEN_SECRET, {});
|
|
// const memberInfo = {};
|
|
const memberInfo = _user.kind !== UserTypes.ADMIN ? await subscriptionCtl.resolvePaymentUser(_parentUser) : {};
|
|
|
|
let userLang = _user.lang; // #1518, Make sure only appy the language not it has not ever selected.
|
|
if (!userLang) {
|
|
const inUserLang = req.body.lang && req.body.lang.toLowerCase();
|
|
userLang = inUserLang && req.body.lang && ['en', 'pt', 'es'].includes(inUserLang) ? inUserLang : DEFAULT_LANG;
|
|
}
|
|
|
|
if (_user.kind === UserTypes.DEVICE) {
|
|
authUser = { token: token };
|
|
} else {
|
|
authUser = {
|
|
_id: _user._id, token: token, roles: [_user.kind], pui: _parentUser ? _parentUser._id : _user._id, lang: userLang, billable: _parentUser.billable, pre: premium, membership: memberInfo
|
|
};
|
|
}
|
|
|
|
// Update user last login info
|
|
await User.updateOne({ _id: _user._id }, { loggedInAt: Date.now(), lang: userLang }, { timestamps: false });
|
|
|
|
// Set the authenticated user info into the session cache
|
|
cache.set(_user._id.toHexString(), {
|
|
puid: _parentUser._id.toHexString(), billable: _parentUser.billable, premium: premium, membership: memberInfo, lang: userLang || DEFAULT_LANG, ts: moment.utc().unix(),
|
|
kind: _user.kind
|
|
});
|
|
} else {
|
|
AppAuthError.throw(Errors.ACC_INACTIVE);
|
|
}
|
|
|
|
res.json(authUser);
|
|
}
|
|
|
|
async function isUserNameExists_post(req, res) {
|
|
if (!req.body || !req.body.username) AppParamError.throw();
|
|
|
|
const user = await User.findOne({ username: ({ $regex: new RegExp(`^${req.body.username}$`, 'i') }) });
|
|
res.json(user ? 1 : 0);
|
|
}
|
|
|
|
async function search_post(req, res) {
|
|
const _options = { markedDelete: { $ne: true } };
|
|
if (req.body.byPuid && ObjectId.isValid(req.body.byPuid))
|
|
_options.parent = ObjectId(req.body.byPuid);
|
|
else
|
|
AppParamError.throw();
|
|
|
|
if (req.body.kind)
|
|
_options.kind = req.body.kind;
|
|
else
|
|
_options.kind = { $in: [UserTypes.APP_ADM, UserTypes.OFFICER, UserTypes.INSPECTOR] };
|
|
|
|
const users = await User.find(_options, '-password').lean();
|
|
res.json(users || []);
|
|
}
|
|
|
|
async function clearTempData_post(req, res) {
|
|
await clearTempData(req.uid);
|
|
|
|
if (cache.get(req.uid))
|
|
cache.delete(req.uid);
|
|
res.json({ ok: true });
|
|
}
|
|
|
|
async function setUserLanguage_post(req, res) {
|
|
const lang = req.body.lang || DEFAULT_LANG;
|
|
|
|
const user = await User.findByIdAndUpdate(ObjectId(req.uid), { $set: { lang: String(lang).trim() } }, { new: true, lean: true });
|
|
|
|
// Update user language in the cache
|
|
const cUser = cache.get(req.uid);
|
|
if (cUser && cUser.lang && cUser.lang != user.lang) {
|
|
cUser.lang = user.lang;
|
|
cache.set(req.uid, cUser);
|
|
}
|
|
res.json(user);
|
|
}
|
|
|
|
async function getUserDetail_post(req, res) {
|
|
const uname = req.body.username;
|
|
if (!req.body || !uname) return res.json(null);
|
|
|
|
const user = await User.findOne({ username: { $regex: new RegExp(`^${uname}$`, 'i') } }, { password: 0 }).populate('parent', 'username').lean();
|
|
res.json(user);
|
|
}
|
|
|
|
function getHostFromReq(req) {
|
|
let host = `https://${req.hostname}`;
|
|
if (!req.app.isProd)
|
|
host += ':4200';
|
|
return host;
|
|
}
|
|
|
|
async function mailPwdReset_post(req, res) {
|
|
let userEmail = req.body.email;
|
|
if (!userEmail)
|
|
return res.json(null);
|
|
else
|
|
userEmail = userEmail.trim();
|
|
|
|
const user = await User.findOne({ username: userEmail });
|
|
|
|
if (!user) AppError.throw(Errors.USER_NOT_FOUND);
|
|
// Create the reset token to use within the reset password email link
|
|
const payload = { id: user._id, email: userEmail };
|
|
const secret = `${user.password}-${user.createdAt.getTime()}`;
|
|
const token = jwt.sign(payload, secret, { expiresIn: env.PWD_RESET_VALID_TIME || '3h' });
|
|
|
|
await mailer.sendMail(
|
|
'reset-password',
|
|
{
|
|
locale: user.lang || DEFAULT_LANG,
|
|
host: getHostFromReq(req), id: payload.id, token: token
|
|
},
|
|
userEmail
|
|
);
|
|
|
|
res.json({ result: 1 });
|
|
}
|
|
|
|
async function getResetPwdToken_get(req, res) {
|
|
if (!req.params || !req.params.id || !req.params.token) AppParamError.throw();
|
|
|
|
const user = await User.findOne({ _id: ObjectId(req.params.id) }).lean();
|
|
if (!user) AppError.throw(Errors.USER_NOT_FOUND);
|
|
|
|
const secret = `${user.password}-${user.createdAt.getTime()}`;
|
|
const verRes = await verifyAsync(req.params.token, secret);
|
|
res.json({ id: verRes.id, token: req.params.token });
|
|
}
|
|
|
|
async function resetPassword(req, res) {
|
|
if (!req.body.id) AppParamError.throw();
|
|
|
|
const user = await User.findOne({ _id: ObjectId(req.body.id) });
|
|
if (!user) AppError.throw(Errors.USER_NOT_FOUND);
|
|
|
|
const _user = user;
|
|
const secret = `${user.get('password')}-${user.get('createdAt').getTime()}`;
|
|
|
|
await jwt.verify(req.body.token, secret);
|
|
|
|
_user.password = req.body.password;
|
|
await _user.save();
|
|
|
|
await mailer.sendMail(
|
|
'password-reset',
|
|
{
|
|
locale: _user.lang || DEFAULT_LANG,
|
|
host: getHostFromReq(req), username: _user.username
|
|
},
|
|
_user.username,
|
|
);
|
|
|
|
res.json({ ok: true });
|
|
}
|
|
|
|
module.exports = {
|
|
isUserNameExists_post, createUser_post, getUser_get, updateUser_put, deleteUser, login_post, search_post, clearTempData_post, setUserLanguage_post,
|
|
getUserDetail_post, mailPwdReset_post, getResetPwdToken_get, resetPassword,
|
|
ensureParentExists, clearTempData, getHostFromReq
|
|
}
|