1079 lines
46 KiB
JavaScript
1079 lines
46 KiB
JavaScript
const bcrypt = require("bcrypt");
|
||
const jwt = require("jsonwebtoken");
|
||
const slugify = require('slugify');
|
||
const { Op, QueryTypes } = require("sequelize");// pour certaines requêtes sql
|
||
|
||
const config = require("../config/main.js");
|
||
const configUsers = require("../config/users.js");
|
||
const configTpl = require("../views/"+config.theme+"/config/"+config.availableLangs[0]+".js");
|
||
|
||
const tool = require("../tools/main");
|
||
const toolError = require("../tools/error");
|
||
const toolFile = require("../tools/file");
|
||
const toolMail = require("../tools/mail");
|
||
|
||
const txt = require("../lang/"+config.adminLang+"/user");
|
||
const txtGeneral = require("../lang/"+config.adminLang+"/general");
|
||
|
||
const answerCtrl = require("./answer");
|
||
const subscriptionCtrl = require("./subscription");
|
||
|
||
// Retourne certaines configurations utiles aux formulaires où vues
|
||
exports.checkEmailIsFree = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const user=await searchUserByEmail(req.body.emailTest);
|
||
const response={ };
|
||
if(user)
|
||
{
|
||
response.free=false;
|
||
if(user.Subscription)
|
||
response.validated=true;
|
||
else
|
||
response.validated=false;
|
||
}
|
||
else
|
||
response.free=true;
|
||
res.status(200).json(response);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.getGodfatherId = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const godfather=await searchIdGodfather(req.body.codeTest);
|
||
if(godfather)
|
||
res.status(200).json(godfather.id);
|
||
else
|
||
res.status(204).json({ errors: txt.godfatherNotFound });
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.signup = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
if(req.body.cguOk !== "true")
|
||
res.status(400).json({ errors: [txt.needUGCOk] });
|
||
else if(req.body.password.length < config.passwordMinLength) // si le champ est supprimé du formulaire d'inscription, générer un mot de passe ok ici
|
||
res.status(400).json({ errors: [txt.needLongPassWord.replace("MIN_LENGTH", config.passwordMinLength)] });
|
||
else
|
||
{
|
||
req.body.GodfatherId=null;
|
||
if(req.body.codeGodfather!=="")
|
||
{
|
||
const godfather=await searchIdGodfather(req.body.codeGodfather);
|
||
if(godfather)
|
||
req.body.GodfatherId=godfather.id;
|
||
}
|
||
req.body.password=await bcrypt.hash(req.body.password, config.bcryptSaltRounds);
|
||
const user=await db["User"].create({ ...req.body }, { fields: ["name", "email", "password", "newsletterOk", "GodfatherId", "timeDifference"] });
|
||
req.body.UserId=user.id;
|
||
// si l'utilisateur a répondu à un quiz avant de créer son compte, on enregistre son résultats.
|
||
if(req.body.QuestionnaireId)
|
||
{
|
||
await Promise.all([
|
||
db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] }),
|
||
db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] })
|
||
]); // pas nécessaire ici de créer le cache JSON, car il sera créé lors de la première connexion au compte.
|
||
}
|
||
await sendValidationLink(user);
|
||
res.status(201).json({ message: txt.mailValidationMessage });
|
||
next();
|
||
}
|
||
}
|
||
catch(e)
|
||
{
|
||
const returnAPI=toolError.returnSequelize(e);
|
||
if(returnAPI.messages)
|
||
{
|
||
res.status(returnAPI.status).json({ errors : returnAPI.messages });
|
||
next();
|
||
}
|
||
else
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.signupValidation = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const datas=await checkTokenUser(req.params.token);
|
||
if(datas)
|
||
{
|
||
if(datas.Subscription)
|
||
res.status(200).json({ errors: [txt.validationAlreadyMessage] });
|
||
else
|
||
{
|
||
const now=new Date();
|
||
await Promise.all([
|
||
db["Subscription"].create({ numberOfDays: config.freeAccountTimingInDays, numberOfDays: config.defaultReceiptDays, noticeOk: datas.User.newsletterOk, UserId: datas.User.id }),
|
||
db["User"].update({ connectedAt: now }, { where: { id : datas.User.id }, limit:1 })
|
||
]);
|
||
creaUserJson(datas.User.id);
|
||
const mapMail =
|
||
{
|
||
USER_NAME: datas.User.name,
|
||
NOM_SITE : config.siteName,
|
||
EMAIL : config.senderEmail,
|
||
LINK_URL : config.siteUrl+"/"+configTpl.connectionPage
|
||
};
|
||
const mailDatas =
|
||
{
|
||
mailSubject: txt.mailWelcomeSubject,
|
||
mailPreheader: txt.mailWelcomeSubject,
|
||
mailTitle: txt.mailWelcomeSubject,
|
||
mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.connectionPage,
|
||
mailHeaderLinkTxt: txt.mailWelcomeLinkTxt,
|
||
mailMainContent: tool.replaceAll(txt.mailWelcomeBodyHTML, mapMail),
|
||
linksCTA: [{ url:config.siteUrl+"/"+configTpl.connectionPage, txt:txt.mailWelcomeLinkTxt }],
|
||
mailRecipientAddress: datas.User.email
|
||
}
|
||
toolMail.sendMail(0, datas.User.email, txt.mailWelcomeSubject, tool.replaceAll(txt.mailWelcomeBodyTxt, mapMail), "", mailDatas);
|
||
if(datas.User.GodfatherId!==null)
|
||
{
|
||
const godfather=await searchUserById(datas.User.GodfatherId);
|
||
if(godfather)
|
||
{
|
||
mapMail.USER_NAME=godfather.User.name;
|
||
mapMail.EMAIL=datas.User.email;
|
||
const mailDatas2=
|
||
{
|
||
mailSubject: txt.mailThankGodfatherSubject,
|
||
mailPreheader: txt.mailThankGodfatherSubject,
|
||
mailTitle: txt.mailThankGodfatherSubject,
|
||
mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.connectionPage,
|
||
mailHeaderLinkTxt: txt.mailThankGodfatherLinkTxt,
|
||
mailMainContent: tool.replaceAll(txt.mailThankGodfatherBodyHTML, mapMail),
|
||
mailRecipientAddress: godfather.User.email
|
||
}
|
||
toolMail.sendMail(godfather.User.smtp, godfather.User.email, txt.mailThankGodfatherSubject, tool.replaceAll(txt.mailThankGodfatherBodyTxt, mapMail), "", mailDatas2);
|
||
}
|
||
}
|
||
res.status(200).json(
|
||
{
|
||
userId: datas.User.id,
|
||
token: jwt.sign({ userId: datas.User.id }, config.tokenPrivateKey, { expiresIn: config.tokenConnexionMinTimeInHours })
|
||
});
|
||
}
|
||
}
|
||
else
|
||
res.status(401).json({ errors: txt.badLinkValidationMessage });
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.checkToken = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const datas=await checkTokenUser(req.params.token);
|
||
if(datas && datas.Subscription)
|
||
{
|
||
const beginSubTS=new Date(datas.Subscription.createdAt).getTime();
|
||
const nbDaysOk=datas.Subscription.numberOfDays-Math.round((Date.now()-beginSubTS)/1000/3600/24);
|
||
res.status(200).json(
|
||
{
|
||
isValid: true,
|
||
id: datas.User.id,
|
||
name: datas.User.name,
|
||
language: datas.User.language,
|
||
timeDifference : datas.User.timeDifference,
|
||
status: datas.User.status,
|
||
nbDaysOk : nbDaysOk
|
||
});
|
||
}
|
||
else
|
||
res.status(200).json({ isValid: false });
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// Reçoit les données du formulaire de connexion avec mot de passe.
|
||
exports.login = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
// Est-ce qu'un compte existe pour l'adresse e-mail envoyée ?
|
||
const emailSend=tool.trimIfNotNull(req.body.email);
|
||
const user=await db["User"].findOne({ attributes: ["id", "password", "email", "name", "status"], where: { email: emailSend } });
|
||
if(!user)
|
||
res.status(404).json({ errors: [txt.emailNotFound] });
|
||
else
|
||
{
|
||
// Est-ce ce compte a déjà été validé par l'utilisateur ? Si non, on lui envoie un nouveau lien de validation.
|
||
const subscription=await db["Subscription"].findOne({ attributes: ["id"], where: { UserId: user.id } });
|
||
if(!subscription)
|
||
{
|
||
await sendValidationLink(user);
|
||
res.status(400).json({ errors: [txt.needValidationToLogin] });
|
||
}
|
||
else
|
||
{
|
||
const nowTS=new Date().getTime();
|
||
// L'utilisateur n'a-t-il pas testé de se connecter de trop nombreuses fois sans succès ?
|
||
const countLogin=await toolFile.readJSON(config.dirTmpLogin, slugify(emailSend));
|
||
if(countLogin && countLogin.nb >= config.maxLoginFail && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*60*1000))
|
||
res.status(401).json({ errors: [txt.tooManyLoginFails.replace("MINUTES", config.loginFailTimeInMinutes)] });
|
||
else
|
||
{
|
||
// Le mot du passe envoyé est-il cohérent avec celui de la base de données après chiffrement ?
|
||
const valid = await bcrypt.compare(req.body.password, user.password);
|
||
if (!valid)
|
||
{
|
||
res.status(401).json({ errors: [txt.badPassword] });
|
||
// On comptabilise l'erreur :
|
||
let newCountLogin={ nb:1, lastTime:nowTS };
|
||
if(countLogin.nb && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*60*1000))
|
||
newCountLogin.nb=countLogin.nb+1;
|
||
await toolFile.createJSON(config.dirTmpLogin, slugify(emailSend), newCountLogin);
|
||
}
|
||
else
|
||
{
|
||
// Si tout est ok, on enregistre la date de connexion + retourne un token de connexion.
|
||
const now=new Date();
|
||
const timeDifference=req.body.timeDifference;
|
||
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : user.id }, limit:1 });
|
||
creaUserJson(user.id);
|
||
// Connexion à rallonge uniquement possible pour utilisateur de base :
|
||
let loginTime=config.tokenConnexionMinTimeInHours;
|
||
if((req.body.keepConnected==="true") && (user.status==="user"))
|
||
loginTime=config.tokenConnexionMaxTimeInDays;
|
||
// Si des données concernant un quiz ont été transmises, on les enregistre ici :
|
||
req.body.UserId=user.id;
|
||
if(req.body.QuestionnaireId)
|
||
{
|
||
const access=await subscriptionCtrl.checkQuestionnaireAccess(user.id, req.body.QuestionnaireId);
|
||
if(!access)
|
||
await db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] });
|
||
await db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] });
|
||
answerCtrl.creaUserAnswersJson(req.body.UserId);
|
||
answerCtrl.creaUserQuestionnairesWithoutAnswerJson(req.body.UserId);
|
||
answerCtrl.creaUserStatsAnwsersJson(req.body.UserId);
|
||
}
|
||
res.status(200).json(
|
||
{
|
||
userId: user.id,
|
||
status: user.status,
|
||
connexionTime: loginTime,
|
||
token: jwt.sign({ userId: user.id }, config.tokenPrivateKey, { expiresIn: loginTime })
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// Reçoit les données du formulaire de connexion avec demande de recevoir un lien de connexion.
|
||
exports.getLoginLink = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
// Est-ce qu'un compte existe pour l'adresse e-mail envoyée ?
|
||
const emailSend=tool.trimIfNotNull(req.body.email);
|
||
const userDatas=await searchUserByEmail(emailSend);
|
||
if(!userDatas)
|
||
res.status(404).json({ errors: [txt.emailNotFound] });
|
||
else if(userDatas.User.status!=="user") // seuls les utilisateurs de base peuvent se connecter de cette façon.
|
||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
else
|
||
{
|
||
// Est-ce ce compte a déjà été validé par l'utilisateur ? Si non, on lui envoie un nouveau lien de validation.
|
||
if(!userDatas.Subscription)
|
||
{
|
||
await sendValidationLink(userDatas.User);
|
||
res.status(401).json({ errors: [txt.needValidationToLogin] });
|
||
}
|
||
else
|
||
{
|
||
const token=jwt.sign({ userId:userDatas.User.id, keepConnected:req.body.keepConnected }, config.tokenPrivateKey, { expiresIn: config.tokenLoginLinkTimeInHours });
|
||
const mapMail =
|
||
{
|
||
USER_NAME: userDatas.User.name,
|
||
LINK_URL : config.siteUrl+"/"+configTpl.loginLinkPage+token
|
||
};
|
||
const mailDatas=
|
||
{
|
||
mailSubject: txt.mailLoginLinkSubject,
|
||
mailPreheader: txt.mailLoginLinkSubject,
|
||
mailTitle: txt.mailLoginLinkSubject,
|
||
mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.loginLinkPage+token,
|
||
mailHeaderLinkTxt: txt.mailLoginLinkTxt,
|
||
mailMainContent: tool.replaceAll(txt.mailLoginLinkBodyHTML, mapMail),
|
||
linksCTA: [{ url:config.siteUrl+"/"+configTpl.loginLinkPage+token, txt:txt.mailLoginLinkTxt }],
|
||
mailRecipientAddress: userDatas.User.email
|
||
}
|
||
await toolMail.sendMail(userDatas.User.smtp, userDatas.User.email, txt.mailLoginLinkSubject, tool.replaceAll(txt.mailLoginLinkBodyTxt, mapMail), "", mailDatas);
|
||
res.status(200).json({ message: txt.mailLoginLinkMessage.replace("*TIMING*", config.tokenLoginLinkTimeInHours) });
|
||
}
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.checkLoginLink = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const userDatas= await checkTokenUser(req.body.t);
|
||
if(userDatas.User.status!=="user")
|
||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
else if(userDatas.Subscription)
|
||
{
|
||
const now=new Date();
|
||
const timeDifference=req.body.timeDifference;
|
||
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : userDatas.User.id }, limit:1 });
|
||
creaUserJson(userDatas.User.id);
|
||
let loginTime=config.tokenConnexionMinTimeInHours;
|
||
if(userDatas.decodedToken.keepConnected===true)
|
||
loginTime=config.tokenConnexionMaxTimeInDays;
|
||
// si des données concernant un quiz ont été transmises, je les enregistre ici :
|
||
if(req.body.QuestionnaireId)
|
||
{
|
||
req.body.UserId=userDatas.User.id;
|
||
const access=await subscriptionCtrl.checkQuestionnaireAccess(userDatas.User.id, req.body.QuestionnaireId);
|
||
if(!access)
|
||
await db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] });
|
||
await db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] });
|
||
answerCtrl.creaUserAnswersJson(req.body.UserId);
|
||
answerCtrl.creaUserStatsAnwsersJson(req.body.UserId);
|
||
answerCtrl.creaUserQuestionnairesWithoutAnswerJson(req.body.UserId);
|
||
}
|
||
res.status(200).json(
|
||
{
|
||
userId: userDatas.User.id,
|
||
connexionTime: loginTime,
|
||
token: jwt.sign({ userId: userDatas.User.id }, config.tokenPrivateKey, { expiresIn: loginTime })
|
||
});
|
||
}
|
||
else // ne devrait pas être possible...
|
||
{
|
||
await sendValidationLink(userDatas.User);
|
||
res.status(401).json({ errors: [txt.needValidationToLogin] });
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.modify = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
if(req.body.newPassword && req.body.newPassword.length < config.passwordMinLength)
|
||
res.status(400).json({ errors: txt.needLongPassWord.replace("MIN_LENGTH", config.passwordMinLength) });
|
||
else
|
||
{
|
||
if(req.connectedUser.User.status==="user")
|
||
{
|
||
await Promise.all([
|
||
db["User"].update({ ...req.body }, { where: { id : req.connectedUser.User.id } , fields: ["name", "language", "newsletterOk", "timeDifference"], limit:1 }),
|
||
db["Subscription"].update({ ...req.body }, { where: { UserId : req.connectedUser.User.id }, fields: ["receiptDays", "noticeOk"], limit:1 })
|
||
]);
|
||
creaUserJson(req.connectedUser.User.id);
|
||
const messageRetour=[txt.updatedOkMessage];
|
||
if(req.body.newPassword || (req.body.email != req.connectedUser.User.email))
|
||
{
|
||
const newLogin={ userId: req.connectedUser.User.id };
|
||
req.body.email=tool.trimIfNotNull(req.body.email);
|
||
if(req.body.email != req.connectedUser.User.email)
|
||
{
|
||
if (! /[A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+\.[A-Za-z]{2,6}/.test(req.body.email))
|
||
messageRetour.push(txt.updatedNeedGoodEmail);
|
||
else
|
||
{
|
||
const emailFree=await searchUserByEmail(req.body.email);
|
||
if(emailFree!==false)
|
||
messageRetour.push(txt.updatedNeedUniqueEmail.replace("NEW_EMAIL", req.body.email));
|
||
else
|
||
newLogin.email=req.body.email;
|
||
}
|
||
}
|
||
if(req.body.newPassword)
|
||
newLogin.password=await bcrypt.hash(req.body.newPassword, config.bcryptSaltRounds);
|
||
if(newLogin.email || newLogin.password)
|
||
{
|
||
const token=jwt.sign(newLogin, config.tokenPrivateKey, { expiresIn: config.tokenLoginChangingTimeInHours });
|
||
const mapMail =
|
||
{
|
||
USER_NAME: req.body.name,
|
||
LINK_URL : config.siteUrl+"/"+configTpl.newLoginLinkPage+token
|
||
};
|
||
const mailDatas=
|
||
{
|
||
mailSubject: txt.mailUpdateLoginSubject,
|
||
mailPreheader: txt.mailUpdateLoginSubject,
|
||
mailTitle: txt.mailUpdateLoginSubject,
|
||
mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.newLoginLinkPage+token,
|
||
mailHeaderLinkTxt: txt.mailUpdateLoginLinkTxt,
|
||
mailMainContent: tool.replaceAll(txt.mailUpdateLoginBodyHTML, mapMail),
|
||
linksCTA: [{ url:config.siteUrl+"/"+configTpl.newLoginLinkPage+token, txt:txt.mailUpdateLoginLinkTxt }],
|
||
mailRecipientAddress: req.body.email
|
||
}
|
||
await toolMail.sendMail(req.connectedUser.User.smtp, req.body.email, txt.mailUpdateLoginSubject, tool.replaceAll(txt.mailUpdateLoginBodyTxt, mapMail), "", mailDatas);
|
||
messageRetour.push(txt.mailUpdateLoginLinkMessage.replace("NEW_EMAIL", req.body.email));
|
||
}
|
||
}
|
||
res.status(200).json({ message: messageRetour });
|
||
}
|
||
else
|
||
{
|
||
let userToUpdating=await searchUserById(req.params.id);
|
||
if(!userToUpdating || !userToUpdating.Subscription)
|
||
res.status(400).json({ errors: [txt.updatedNeedValidatedUser] });
|
||
else
|
||
{
|
||
const messageRetour=[txtGeneral.updateOkMessage];
|
||
if(req.body.newPassword)
|
||
req.body.password=await bcrypt.hash(req.body.newPassword, config.bcryptSaltRounds);
|
||
if(req.body.newGodfatherId!=undefined)
|
||
{
|
||
const godFather=searchIdGodfather(req.body.newGodfatherId);
|
||
if(empty(godFather))
|
||
messageRetour.push(txt.updatedNeedGoodGodfather);
|
||
else
|
||
req.body.GodfatherId=godFather.id;
|
||
}
|
||
switch(req.connectedUser.User.status)
|
||
{
|
||
case "admin":
|
||
await Promise.all([
|
||
db["User"].update({ ...req.body }, { where: { id : req.params.id }, limit:1 }),
|
||
db["Subscription"].update({ ...req.body }, { where: { UserId : req.params.id }, limit:1 })
|
||
]);
|
||
break;
|
||
case "manager":
|
||
await Promise.all([
|
||
db["User"].update({ ...req.body }, { where: { id : req.params.id }, fields: ["name", "email", "password", "language", "adminComments", "smtp", "newsletterOk", "GodfatherId"], limit:1 }),
|
||
db["Subscription"].update({ ...req.body }, { where: { UserId : req.params.id }, fields: ["numberOfDays", "receiptDays", "noticeOk"], limit:1 })
|
||
]);
|
||
break;
|
||
case "creator":
|
||
await Promise.all([
|
||
db["User"].update({ ...req.body }, { where: { id : req.params.id } , fields: ["name", "email", "password", "language", "adminComments"], limit:1 }),
|
||
db["Subscription"].update({ ...req.body }, { where: { UserId : req.params.id }, fields: ["numberOfDays", "receiptDays"], limit:1 })
|
||
]);
|
||
break;
|
||
default:
|
||
throw { message: txtGeneral.serverError };
|
||
}
|
||
await creaUserJson(req.params.id);// besoin d'attendre car utiliser pour rafraîchir l'affichage
|
||
res.status(200).json({ message: messageRetour });
|
||
}
|
||
}
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
const returnAPI=toolError.returnSequelize(e);
|
||
if(returnAPI.messages)
|
||
{
|
||
res.status(returnAPI.status).json({ errors : returnAPI.messages });
|
||
next();
|
||
}
|
||
else
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.checkNewLoginLink = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const userDatas=await checkTokenUser(req.params.token);
|
||
if(userDatas)
|
||
{
|
||
if(userDatas.User.id!==req.connectedUser.User.id)
|
||
res.status(401).json({ errors: [txtGeneral.notAllowed] });
|
||
else
|
||
{
|
||
await db["User"].update({ ...userDatas.decodedToken }, { where: { id : userDatas.User.id } , fields: ["email", "password"], limit:1 });
|
||
creaUserJson(userDatas.User.id);
|
||
res.status(200).json({ message: txt.mailUpdateLoginOkMessage });
|
||
}
|
||
}
|
||
else
|
||
res.status(404).json({ errors: [txtGeneral.notAllowed] });
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.create = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const connectedUser=req.connectedUser;
|
||
if(connectedUser.User.status==="user")
|
||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
else
|
||
{
|
||
if(!tool.isEmpty(req.body.GodfatherId) && ! await searchUserById(req.body.GodfatherId))
|
||
res.status(400).json({ errors: [txt.updatedFailedGodfatherNotFound] });
|
||
else
|
||
{
|
||
req.body.password=await bcrypt.hash(req.body.password, config.bcryptSaltRounds);
|
||
if(req.body.GodfatherId==="")
|
||
req.body.GodfatherId=null;
|
||
let user;
|
||
switch(connectedUser.User.status)
|
||
{
|
||
case "admin":
|
||
user=await db["User"].create({ ...req.body });
|
||
req.body.UserId=user.id;
|
||
await db["Subscription"].create({ ...req.body });
|
||
break;
|
||
case "manager":
|
||
user=await db["User"].create({ ...req.body }, { fields: ["name", "email", "password", "language", "adminComments", "smtp", "newsletterOk", "GodfatherId"] });
|
||
req.body.UserId=user.id;
|
||
await db["Subscription"].create({ ...req.body }, { fields: ["numberOfDays", "receiptDays", "noticeOk", "UserId"] });
|
||
break;
|
||
case "creator":
|
||
user=await db["User"].create({ ...req.body }, { fields: ["name", "email", "password", "language", "adminComments"] });
|
||
req.body.UserId=user.id;
|
||
await db["Subscription"].create({ ...req.body }, { fields: ["numberOfDays", "receiptDays", "UserId"] });
|
||
break;
|
||
default:
|
||
throw { message: [txtGeneral.serverError] };
|
||
}
|
||
creaUserJson(req.body.UserId);
|
||
res.status(201).json({ message: [txt.creationOkMessage], id:req.body.UserId });
|
||
}
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
const returnAPI=toolError.returnSequelize(e);
|
||
if(returnAPI.messages)
|
||
{
|
||
res.status(returnAPI.status).json({ errors : [returnAPI.messages] });
|
||
next();
|
||
}
|
||
else
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.validate = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const connectedUser=req.connectedUser;
|
||
if(["admin", "manager"].indexOf(connectedUser.User.status) === -1)
|
||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
else
|
||
{
|
||
const userToValidating=await searchUserById(req.params.id);
|
||
if(!userToValidating)
|
||
res.status(404).json({ errors: [txt.notFound] });
|
||
else if(userToValidating.Subscription)
|
||
res.status(400).json({ errors: [txt.validationAlreadyMessageAdmin] });
|
||
else
|
||
{
|
||
await db["Subscription"].create({ numberOfDays: config.freeAccountTimingInDays, UserId: req.params.id });
|
||
creaUserJson(req.params.id);
|
||
res.status(200).json({ message: [txt.validationMessageAdmin] });
|
||
}
|
||
}
|
||
}
|
||
catch(e)
|
||
{
|
||
const returnAPI=toolError.returnSequelize(e);
|
||
if(returnAPI.messages)
|
||
{
|
||
res.status(returnAPI.status).json({ errors : returnAPI.messages });
|
||
next();
|
||
}
|
||
else
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.delete = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const connectedUser=req.connectedUser;
|
||
if(connectedUser.User.status==="user")
|
||
{
|
||
const userDatas=await searchUserById(connectedUser.User.id);
|
||
const token=jwt.sign({ userId: connectedUser.User.id }, config.tokenPrivateKey, { expiresIn: config.tokenDeleteUserTimeInHours });
|
||
const mapMail =
|
||
{
|
||
USER_NAME: userDatas.User.name,
|
||
LINK_URL : config.siteUrl+"/"+configTpl.deleteLinkPage+token
|
||
};
|
||
const mailDatas=
|
||
{
|
||
mailSubject: txt.mailDeleteSubject,
|
||
mailPreheader: txt.mailDeleteSubject,
|
||
mailTitle: txt.mailDeleteSubject,
|
||
mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.deleteLinkPage+token,
|
||
mailHeaderLinkTxt: txt.mailDeleteLinkTxt,
|
||
mailMainContent: tool.replaceAll(txt.mailDeleteBodyHTML, mapMail),
|
||
linksCTA: [{ url:config.siteUrl+"/"+configTpl.deleteLinkPage+token, txt:txt.mailDeleteLinkTxt }],
|
||
mailRecipientAddress: connectedUser.User.email
|
||
}
|
||
await toolMail.sendMail(connectedUser.User.smtp, connectedUser.User.email, txt.mailDeleteSubject, tool.replaceAll(txt.mailDeleteBodyTxt, mapMail), "", mailDatas);
|
||
res.status(200).json({ message: txt.mailDeleteLinkMessage });
|
||
}
|
||
else if (["admin","manager","creator"].indexOf(connectedUser.User.status) !== -1)
|
||
{
|
||
const userDeleted=await searchUserById(req.params.id);
|
||
if(!userDeleted)
|
||
throw { message: txt.notFound };
|
||
const nb=await db["User"].destroy({ where: { id : req.params.id }, limit:1 });
|
||
if(nb===1)
|
||
{
|
||
await toolFile.deleteJSON(config.dirCacheUsers, req.params.id);
|
||
toolFile.deleteJSON(config.dirCacheUsersQuestionnaires+"/without", req.params.id);
|
||
toolFile.deleteJSON(config.dirCacheUsersQuestionnaires+"/answers", req.params.id);
|
||
toolFile.deleteJSON(config.dirCacheUsersQuestionnaires+"/answers", "stats"+req.params.id);
|
||
let now=new Date(), wasValided=false, wasPremium=false;
|
||
if(userDeleted.Subscription)
|
||
{
|
||
wasValided=true;
|
||
if(userDeleted.Subscription.numberOfDay > config.freeAccountTimingInDays)
|
||
wasPremium=true;
|
||
}
|
||
db["UserDeleted"].create({ createdAt: userDeleted.User.createdAt, deletedAt: now, wasValided: wasValided, wasPremium: wasPremium });
|
||
res.status(200).json({ message: [txt.deleteOkMessage] });
|
||
}
|
||
else
|
||
{
|
||
res.status(400).json({ errors: txtGeneral.serverError });
|
||
throw { message: txt.deleteFailMessage+req.params.id };
|
||
}
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.checkDeleteLink = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const connectedUser=req.connectedUser;
|
||
const userDatas=await checkTokenUser(req.params.token);
|
||
if(userDatas)
|
||
{
|
||
if(connectedUser.User.id!=userDatas.User.id)
|
||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
else
|
||
{
|
||
const nb=await db["User"].destroy({ where: { id : connectedUser.User.id }, limit:1 });
|
||
if(nb===1)
|
||
{
|
||
await toolFile.deleteJSON(config.dirCacheUsers, connectedUser.User.id);
|
||
res.status(200).json({ message: txt.mailDeleteLinkOkMessage });
|
||
toolFile.deleteJSON(config.dirCacheUsersQuestionnaires+"/without", connectedUser.User.id);
|
||
toolFile.deleteJSON(config.dirCacheUsersQuestionnaires+"/answers", connectedUser.User.id);
|
||
toolFile.deleteJSON(config.dirCacheUsersQuestionnaires+"/answers", "stats"+connectedUser.User.id);
|
||
const now=new Date(); wasPremium=false;
|
||
if(userDatas.Subscription.numberOfDay > config.freeAccountTimingInDays)
|
||
wasPremium=true;
|
||
db["UserDeleted"].create({ createdAt: userDatas.User.createdAt, deletedAt: now, wasValided:true, wasPremium: wasPremium });
|
||
}
|
||
else
|
||
res.status(400).json({ errors: [txt.mailDeleteLinkAlreadyMessage] });
|
||
}
|
||
}
|
||
else
|
||
res.status(400).json({ errors: [txt.mailDeleteLinkFailMessage] });
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.getOneUserById = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const connectedUser=req.connectedUser;
|
||
if(connectedUser.User.status==="user")
|
||
{
|
||
if(connectedUser.User.id!=req.params.id)
|
||
res.status(403).json({ errors: txtGeneral.notAllowed });
|
||
else
|
||
{
|
||
const datas=await searchUserById(connectedUser.User.id);
|
||
if(datas)
|
||
res.status(200).json(datas);
|
||
else
|
||
res.status(404).json({ message:txt.notFound });
|
||
}
|
||
}
|
||
else if (["admin","manager","creator"].indexOf(connectedUser.User.status) !== -1)
|
||
{// dans le cas des "creator" il faudra peut-être limité l'accès à "ses" utilisateurs ?
|
||
const datas=await searchUserById(req.params.id);
|
||
if(datas)
|
||
res.status(200).json(datas);
|
||
else
|
||
res.status(404).json({ message:txt.notFound });
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.getOneUserGodChilds = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const connectedUser=req.connectedUser;
|
||
let godchilds;
|
||
if(["admin","manager","creator"].indexOf(connectedUser.User.status) === -1)
|
||
godchilds=await db["User"].findAll({ where: { GodfatherId: connectedUser.User.id }, attributes: ["id", "name", "email"] });
|
||
else
|
||
godchilds=await db["User"].findAll({ where: { GodfatherId: req.params.id }, attributes: ["id", "name", "email"] });
|
||
res.status(200).json(godchilds);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
exports.searchUsers = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const connectedUser=req.connectedUser;
|
||
if(["admin", "manager", "creator"].indexOf(connectedUser.User.status) === -1)
|
||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
else
|
||
{
|
||
const db = require("../models/index");
|
||
const Users=await db.sequelize.query("SELECT `id`,`name`,`email` FROM `Users` WHERE `name` LIKE :search OR `adminComments` LIKE :search OR `email` LIKE :search OR `id` = :id", { replacements: { search: "%"+req.body.search+"%", id: req.body.search }, type: QueryTypes.SELECT });
|
||
res.status(200).json(Users);
|
||
}
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// Retourne des statistiques concernant les utilisateurs
|
||
// Utile tableau de bord gestionnaires
|
||
exports.getStats= async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
let stats=await getStatsUsers();
|
||
stats.Answers=await answerCtrl.getStatsAnswers();
|
||
stats.Subscriptions=await subscriptionCtrl.getStatsSubscriptions();
|
||
res.status(200).json(stats);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// CRONS
|
||
// Supprime les fichiers stockant les erreurs de connexion ayant expirés
|
||
exports.deleteLoginFail = async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const fileExpiration=new Date().getTime()-config.loginFailTimeInMinutes*60*1000;
|
||
const deleteFiles = await toolFile.deleteOldFilesInDirectory(config.dirTmpLogin, fileExpiration);
|
||
res.status(200).json(deleteFiles);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// Suppression des fichiers pour des utilisateurs n'existant plus
|
||
exports.deleteJsonFiles= async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const users=await db["User"].findAll({ attributes: ["id"] });
|
||
let saveFiles=[];
|
||
for(let i in users)
|
||
{
|
||
saveFiles.push(users[i].id+".json");
|
||
saveFiles.push("stats"+users[i].id+".json");
|
||
}
|
||
const deleteFiles = await Promise.all([
|
||
toolFile.deleteFilesInDirectory(configUsers.dirCacheUsers, saveFiles),
|
||
toolFile.deleteFilesInDirectory(configUsers.dirCacheUsersWithoutAnswers, saveFiles),
|
||
toolFile.deleteFilesInDirectory(configUsers.dirCacheUsersAnswers, saveFiles)
|
||
]);
|
||
res.status(200).json(deleteFiles);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// Suppression des comptes n'ayant pas été validé passé un certain délai
|
||
exports.deleteUnvalided= async (req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const nowTS=Date.now();
|
||
const timeExpiration=nowTS-parseInt(config.tokenSignupValidationTimeInHours,10)*3600*1000;
|
||
const userUnvalided= await db.sequelize.query("SELECT createdAt FROM `Users` WHERE UNIX_TIMESTAMP(createdAt) < "+timeExpiration+" AND `id` NOT IN (SELECT `UserId` FROM `Subscriptions`)", { type: QueryTypes.SELECT });
|
||
if(userUnvalided.length!==0)
|
||
{
|
||
const [results, metadata] = await db.sequelize.query("DELETE FROM `Users` WHERE UNIX_TIMESTAMP(createdAt) < "+timeExpiration+" AND `id` NOT IN (SELECT `UserId` FROM `Subscriptions`)");
|
||
const now=new Date();
|
||
for(let i in userUnvalided)
|
||
await db["UserDeleted"].create({ createdAt: userUnvalided[i].createdAt, deletedAt: now, wasValided: false });
|
||
res.message=metadata.affectedRows+txt.cronDeleteUnvalidedUsersMessage;
|
||
}
|
||
res.status(200).json(true);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// Suppression des comptes dont l'abonnement a expiré et ne s'étant pas connecté depuis à certains temps
|
||
exports.deleteInactiveAccounts= async(req, res, next) =>
|
||
{
|
||
try
|
||
{
|
||
const db = require("../models/index");
|
||
const usersInactive= await db.sequelize.query("SELECT createdAt FROM `Users` WHERE status='user' AND (ADDDATE(`connectedAt`, "+config.inactiveAccountTimeToDeleteInDays+") < NOW() OR `connectedAt` IS NULL) AND `id` IN (SELECT `UserId` FROM `Subscriptions` WHERE ADDDATE(`createdAt`, `numberOfDays`)< NOW())", { type: QueryTypes.SELECT });
|
||
if(usersInactive.length!==0)
|
||
{
|
||
const [results, metadata] = await db.sequelize.query("DELETE FROM `Users` WHERE (ADDDATE(`connectedAt`, "+config.inactiveAccountTimeToDeleteInDays+") < NOW() OR `connectedAt` IS NULL) AND status='user' AND `id` IN (SELECT `UserId` FROM `Subscriptions` WHERE ADDDATE(`createdAt`, `numberOfDays`)< NOW())");
|
||
if(metadata.affectedRows!==0)
|
||
res.message=metadata.affectedRows+txt.deleteInactiveUsersMessage;
|
||
const now=new Date();
|
||
for(let i in usersInactive)
|
||
await db["UserDeleted"].create({ createdAt: usersInactive[i].createdAt, deletedAt: now, wasValided: true });
|
||
}
|
||
res.status(200).json(true);
|
||
next();
|
||
}
|
||
catch(e)
|
||
{
|
||
next(e);
|
||
}
|
||
}
|
||
|
||
// FONCTIONS UTILITAIRES
|
||
|
||
// Création du fichier des données de l'utilisateur + son abonnement et ses pauses
|
||
const creaUserJson = async (id) =>
|
||
{
|
||
id=tool.trimIfNotNull(id);
|
||
if(id===null)
|
||
return false;
|
||
const db = require("../models/index");
|
||
const User=await db["User"].findByPk(id, { attributes: { exclude: ['password'] } });
|
||
if(User)
|
||
{
|
||
let datas={ User };
|
||
const Subscription=await db["Subscription"].findOne({ where: { UserId: id } });
|
||
if(Subscription) // pas de cache pour utilisateurs non validés
|
||
{
|
||
datas.Subscription=Subscription;
|
||
const Pauses=await db["Pause"].findAll({ where: { SubscriptionId: Subscription.id } });
|
||
if(Pauses)
|
||
datas.Pauses=Pauses;
|
||
await toolFile.createJSON(config.dirCacheUsers, id, datas);
|
||
}
|
||
return datas;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
exports.creaUserJson = creaUserJson;
|
||
|
||
const searchUserById = async (id) =>
|
||
{
|
||
id=tool.trimIfNotNull(id);
|
||
if(id===null)
|
||
return false;
|
||
const user=await toolFile.readJSON(config.dirCacheUsers, id);
|
||
if(user)
|
||
return user;
|
||
else
|
||
return await creaUserJson(id);
|
||
}
|
||
exports.searchUserById = searchUserById;
|
||
|
||
const searchUserByEmail = async (email) =>
|
||
{
|
||
const db = require("../models/index");
|
||
const emailSend=tool.trimIfNotNull(email);
|
||
const userEmail=await db["User"].findOne({ attributes: ["id"], where: { email : emailSend } });
|
||
if(userEmail)
|
||
return await searchUserById(userEmail.id);// permet de récupérer les autres infos (Subscription, etc.)
|
||
else
|
||
return false;
|
||
}
|
||
|
||
// Recherche le parrain dont le "code" fourni peut être l'identifiant préfixé ou bien l'adresse e-mail
|
||
const searchIdGodfather = async (code) =>
|
||
{
|
||
let godfatherInfos="";
|
||
code=tool.trimIfNotNull(code);
|
||
if(code===null)
|
||
return false;
|
||
else if(code.indexOf("@")!==-1) // tester en premier, car une adresse peut débuter par le préfixe de l'appli...
|
||
godfatherInfos=await searchUserByEmail(code);
|
||
else if (code.startsWith(config.beginCodeGodfather))
|
||
godfatherInfos=await searchUserById(code.substring(config.beginCodeGodfather.length));
|
||
if(godfatherInfos.Subscription && godfatherInfos.User.status==="user") // le parrain doit avoir validé son compte et doit être un simple "user"
|
||
return godfatherInfos.User;
|
||
else
|
||
return null;
|
||
}
|
||
|
||
// Envoi le lien permettant au nouvel utilisateur de valider son compte
|
||
const sendValidationLink = async (user) =>
|
||
{
|
||
const token=jwt.sign({ userId: user.id }, config.tokenPrivateKey, { expiresIn: config.tokenSignupValidationTimeInHours });
|
||
const mapMail =
|
||
{
|
||
USER_NAME: user.name,
|
||
LINK_URL: config.siteUrl+"/"+configTpl.validationLinkPage+token,
|
||
};
|
||
const mailDatas=
|
||
{
|
||
mailSubject: txt.mailValidationLinkSubject,
|
||
mailPreheader: txt.mailValidationLinkSubject,
|
||
mailTitle: txt.mailValidationLinkSubject,
|
||
mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.validationLinkPage+token,
|
||
mailHeaderLinkTxt: txt.mailValidationLinkTxt,
|
||
mailMainContent: tool.replaceAll(txt.mailValidationLinkSBodyHTML, mapMail),
|
||
linksCTA: [{ url:config.siteUrl+"/"+configTpl.validationLinkPage+token, txt:txt.mailValidationLinkTxt }],
|
||
mailRecipientAddress: user.email
|
||
}
|
||
await toolMail.sendMail(0, user.email, txt.mailValidationLinkSubject, tool.replaceAll(txt.mailValidationLinkSBodyTxt, mapMail), "", mailDatas);
|
||
return true;
|
||
}
|
||
|
||
// Permet de vérifier un token de connexion : est-il valide et est-ce qu'il correspond à un utilisateur ?
|
||
// Si oui, on peut aussi enregistrer une nouvelle connexion pour l'utilisateur (une fois / jour)
|
||
const checkTokenUser = async (token) =>
|
||
{
|
||
const db = require("../models/index");
|
||
const decodedToken=jwt.verify(token, config.tokenPrivateKey);
|
||
const userId=decodedToken.userId;
|
||
const datas=await searchUserById(userId);
|
||
if(datas.User)
|
||
{
|
||
const now=new Date();
|
||
const nowTS=Date.now()
|
||
const lastConnection = Date.parse(datas.User.connectedAt);
|
||
if(lastConnection+24*3600*1000 < nowTS) // une fois par jour suffit.
|
||
{
|
||
await db["User"].update({ connectedAt: now }, { where: { id : userId }, limit:1 });
|
||
creaUserJson(userId);
|
||
}
|
||
datas.decodedToken=decodedToken;
|
||
return datas;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
exports.checkTokenUser = checkTokenUser;
|
||
|
||
// Stats sur les créations de compte, suppression.. durant les dernières 24H ou depuis le début
|
||
const getStatsUsers = async () =>
|
||
{
|
||
const db = require("../models/index");
|
||
const getNewUsers24H = await db.sequelize.query("SELECT `id` FROM `Users` WHERE `createdAt` > ADDDATE(NOW(), -1)", { type: QueryTypes.SELECT });
|
||
const getNewUsersTot = await db.sequelize.query("SELECT `id` FROM `Users`", { type: QueryTypes.SELECT });
|
||
const getDeletedUsers24H = await db.sequelize.query("SELECT `id` FROM `UserDeleteds` WHERE `deletedAt` > ADDDATE(NOW(), -1)", { type: QueryTypes.SELECT });
|
||
const getDeletedUsersTot = await db.sequelize.query("SELECT `id` FROM `UserDeleteds`", { type: QueryTypes.SELECT });
|
||
const getDeletedUsersWasValided = await db.sequelize.query("SELECT `id` FROM `UserDeleteds` WHERE `wasValided`= 1", { type: QueryTypes.SELECT });
|
||
const getDeletedUsersTotWasPremium = await db.sequelize.query("SELECT `id` FROM `UserDeleteds` WHERE `wasPremium`= 1", { type: QueryTypes.SELECT });
|
||
if(getNewUsers24H && getNewUsersTot && getDeletedUsers24H && getDeletedUsersWasValided && getDeletedUsersTotWasPremium && getDeletedUsersTot )
|
||
{
|
||
const stats =
|
||
{
|
||
nbNewUsers24H : getNewUsers24H.length,
|
||
nbNewUsersTot : getNewUsersTot.length,
|
||
nbDeletedUsers24H : getDeletedUsers24H.length,
|
||
nbDeletedUsersTot : getDeletedUsersTot.length,
|
||
nbDeletedUsersWasValided : getDeletedUsersWasValided.length,
|
||
nbDeletedUsersTotWasPremium : getDeletedUsersTotWasPremium.length
|
||
}
|
||
return stats;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
exports.getStatsUsers = getStatsUsers;
|