1185 lines
49 KiB
JavaScript
1185 lines
49 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 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// retourne toutes les infos utiles côté client pour un utilisateur
|
||
|
// revoir : placer dans un autre contrôler dédié à communication avec le front-end
|
||
|
exports.getConfig = (req, res, next) =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
//const configLinks = require("../config/links.js");
|
||
|
//const configIllustrations = require("../config/illustrations.js");
|
||
|
const configUsers =
|
||
|
{
|
||
|
// les routes :
|
||
|
siteUrl: config.siteUrl,
|
||
|
apiUrl: config.apiUrl,
|
||
|
// -- users
|
||
|
userRoutes: "/user",
|
||
|
subscribeRoute: "/signup",
|
||
|
getGodfatherRoute: "/getgodfatherid",
|
||
|
checkIfIsEmailfreeRoute: "/isemailfree",
|
||
|
checkSubscribeTokenRoute: "/validation/",
|
||
|
checkLoginRoute: "/checklogin/",
|
||
|
connectionRoute: "/login",
|
||
|
getLoginLinkRoute: "/getloginlink",
|
||
|
connectionWithLinkRoute: "/checkloginlink",
|
||
|
getUserInfos: "/get/",
|
||
|
createUserRoute: "/create",
|
||
|
validateUserRoute: "/validate/",
|
||
|
updateUserInfos: "/modify/",
|
||
|
searchUserRoute: "/search/",
|
||
|
getGodChilds: "/getgodchilds/",
|
||
|
checkNewLoginLinkRoute: "/confirmnewlogin/",
|
||
|
checkDeleteLinkRoute: "/confirmdelete/",
|
||
|
getPayments: "/payment/getforoneuser/",
|
||
|
unsubscribeRoute: "/subscription/stop/",
|
||
|
// -- questionnaires :
|
||
|
questionnaireRoutes: "/questionnaire",
|
||
|
getQuestionnaireRoutes: "/get",
|
||
|
previewQuestionnaireRoutes: "/preview",
|
||
|
publishedQuestionnaireRoutes: "/quiz/",
|
||
|
saveAnswersRoute: "/answer/",
|
||
|
getStatsQuestionnaires : "/stats/",
|
||
|
searchQuestionnairesRoute : "/search",
|
||
|
searchAdminQuestionnairesRoute : "/searchAdmin",
|
||
|
getListNextQuestionnaires: "/getListNextQuestionnaires/",
|
||
|
// -- tags :
|
||
|
tagsSearchRoute: "/tags/search/",
|
||
|
// -- links :
|
||
|
linksRoute: "/link/",
|
||
|
// -- illustrations :
|
||
|
illustrationsRoute: "/illustration/",
|
||
|
// -- questions & choices :
|
||
|
questionsRoute: "/question/",
|
||
|
// -- answers :
|
||
|
getQuestionnairesWithoutAnswer: "/withoutanswer/user/",
|
||
|
getPreviousAnswers: "/user/answers/",
|
||
|
getStatsAnswers : "/user/anwswers/stats/",
|
||
|
getAdminStats: "/getadminstats/",
|
||
|
// configuration des champs de saisie
|
||
|
// revoir : toutes ces données devraient venir de fichiers de configuration
|
||
|
// -- users :
|
||
|
name: { maxlength: 70, required: true },
|
||
|
email: { maxlength: 255, required: true },
|
||
|
password: { minlength: config.passwordMinLength, required: true },
|
||
|
newPassword: { minlength: config.passwordMinLength },
|
||
|
codeGodfather: { maxlength: 255 },
|
||
|
cguOk: { value: "true", required: true },
|
||
|
timeDifferenceMin:-720,
|
||
|
timeDifferenceMax:840,
|
||
|
beginCodeGodfather: config.beginCodeGodfather,
|
||
|
// -- questionnaires :
|
||
|
Questionnaire :
|
||
|
{
|
||
|
title: { maxlength: 255, required: true },
|
||
|
slug: { maxlength: 150 }, // champ requis mais calculé à partir du titre qd vide
|
||
|
introduction: { required: true }
|
||
|
},
|
||
|
searchQuestionnaires : { minlength: config.minSearchQuestionnaires, required: true },
|
||
|
// -- links :
|
||
|
Link :
|
||
|
{
|
||
|
url: { maxlength: 255, required: true },
|
||
|
anchor: { maxlength: 150, required: true }
|
||
|
},
|
||
|
nbLinksMin: 1,
|
||
|
nbLinksMax: 1,
|
||
|
// -- illustrations :
|
||
|
Illustration :
|
||
|
{
|
||
|
alt: { maxlength: 255 },
|
||
|
title: { maxlength: 255 },
|
||
|
caption: { maxlength: 255 },
|
||
|
image: { required: true, accept: "'image/jpg', 'image/jpeg', 'image/png', 'image/gif', 'image/png'" } // si possibilité de sélectionner une image déjà chargée, required != true, mais oblige à revoir la suppression des fichiers image
|
||
|
},
|
||
|
nbIllustrationsMin: 0,
|
||
|
nbIllustrationsMax: 1,
|
||
|
// -- questions & choice :
|
||
|
Question :
|
||
|
{
|
||
|
text: { maxlength: 255, required: true },
|
||
|
rank: { required: true, min:1, defaultValue:1 }
|
||
|
},
|
||
|
Choice :
|
||
|
{
|
||
|
text: { maxlength: 255, required: true }
|
||
|
},
|
||
|
nbQuestionsMin: config.nbQuestionsMin,
|
||
|
nbQuestionsMax: config.nbQuestionsMax,
|
||
|
nbChoicesMax: config.nbChoicesMax,
|
||
|
// durée des token :
|
||
|
signupValidationTimeInHours: config.tokenSignupValidationTimeInHours,
|
||
|
loginLinkTimeInHours: config.tokenLoginLinkTimeInHours,
|
||
|
connexionMinTimeInHours: config.tokenConnexionMinTimeInHours,
|
||
|
connexionMaxTimeInHours: config.tokenConnexionMaxTimeInDays,
|
||
|
loginChangingTimeInHours: config.tokenLoginChangingTimeInHours,
|
||
|
deleteUserTimeInHours: config.tokenDeleteUserTimeInHours,
|
||
|
unsubscribeLinkTimeInDays: config.tokenUnsubscribeLinkTimeInDays
|
||
|
}
|
||
|
res.status(200).json(configUsers);
|
||
|
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;
|
||
|
// l'utilisateur a répondu à un quiz avant de créer son compte. On enregistre ses 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, car normalement il sera créé lors du premier appel
|
||
|
}
|
||
|
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, 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.login = async (req, res, next) =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
const db = require("../models/index");
|
||
|
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
|
||
|
{
|
||
|
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();
|
||
|
const countLogin=await toolFile.readJSON(config.dirTmpLogin, slugify(emailSend));
|
||
|
if(countLogin && countLogin.nb >= config.maxLoginFail && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*3600*1000))
|
||
|
res.status(401).json({ errors: [txt.tooManyLoginFails.replace("MINUTES", config.loginFailTimeInMinutes)] });
|
||
|
else
|
||
|
{
|
||
|
const valid = await bcrypt.compare(req.body.password, user.password);
|
||
|
if (!valid)
|
||
|
{
|
||
|
res.status(401).json({ errors: [txt.badPassword] });
|
||
|
let newCountLogin={ nb:1, lastTime:nowTS };
|
||
|
if(countLogin.nb && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*3600*1000))
|
||
|
newCountLogin.nb=countLogin.nb+1;
|
||
|
await toolFile.createJSON(config.dirTmpLogin, slugify(emailSend), newCountLogin);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const now=new Date();
|
||
|
const timeDifference=req.body.timeDifference;// permet d'actualiser en cas de déplacements/heures d'été, etc.
|
||
|
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : user.id }, limit:1 });
|
||
|
creaUserJson(user.id);
|
||
|
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, je 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.getLoginLink = async (req, res, next) =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
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")
|
||
|
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||
|
else
|
||
|
{
|
||
|
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+config.tokenLoginLinkTimeInHours+"." });
|
||
|
}
|
||
|
}
|
||
|
next();
|
||
|
}
|
||
|
catch(e)
|
||
|
{
|
||
|
next(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.checkLoginLink = async (req, res, next) =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
const db = require("../models/index");
|
||
|
console.log(req);
|
||
|
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;
|
||
|
console.log(messageRetour);
|
||
|
}
|
||
|
}
|
||
|
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(tool.isEmpty(req.body.newGodfatherId))
|
||
|
req.body.GodfatherId=null;
|
||
|
else if(req.body.newGodfatherId && req.body.newGodfatherId!==req.params.id && await searchUserById(req.body.newGodfatherId))
|
||
|
req.body.GodfatherId=req.body.newGodfatherId;
|
||
|
else
|
||
|
messageRetour.push(txt.updatedNeedGoodGodfather);
|
||
|
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
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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");
|
||
|
const deleteFiles = await Promise.all([
|
||
|
toolFile.deleteFilesInDirectory(config.dirCacheUsers, saveFiles),
|
||
|
toolFile.deleteFilesInDirectory(config.dirCacheUsersQuestionnaires, saveFiles)
|
||
|
]);
|
||
|
res.status(200).json(deleteFiles);
|
||
|
next();
|
||
|
}
|
||
|
catch(e)
|
||
|
{
|
||
|
next(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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(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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.deleteInactiveAccounts= async(req, res, next) =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
const db = require("../models/index");
|
||
|
// compte inactif = abonnement expiré + non connecté depuis un certains nombres de jours
|
||
|
const usersInactive= await db.sequelize.query("SELECT createdAt FROM `Users` WHERE status='user' and ADDDATE(`connectedAt`, "+config.inactiveAccountTimeToDeleteInDays+") < NOW() 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() 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(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;
|