2020-08-07 12:23:59 +02:00
// Contrôleurs gérant l'abonnement de l'utilisateur mais aussi les questionnaires auxquels il a accès
const { Op , QueryTypes } = require ( "sequelize" ) ; // pour certaines requêtes sql
const jwt = require ( "jsonwebtoken" ) ;
const config = require ( "../config/main" ) ;
const configQuestionnaires = require ( "../config/questionnaires" ) ;
const configTpl = require ( "../views/" + config . theme + "/config/" + config . availableLangs [ 0 ] + ".js" ) ;
const tool = require ( "../tools/main" ) ;
const toolFile = require ( "../tools/file" ) ;
const toolMail = require ( "../tools/mail" ) ;
const txt = require ( "../lang/" + config . adminLang + "/subscription" ) ;
const txtQuestionnaire = require ( "../lang/" + config . adminLang + "/questionnaire" ) ;
const txtQuestionnaireAccess = require ( "../lang/" + config . adminLang + "/questionnaireaccess" ) ;
const txtGeneral = require ( "../lang/" + config . adminLang + "/general" ) ;
const answerCtrl = require ( "./answer" ) ;
const questionnaireCtrl = require ( "./questionnaire" ) ;
const userCtrl = require ( "./user" ) ;
// Clic sur lien de désabonnement aux alertes email
exports . unsubscribeLink = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
const userDatas = await userCtrl . checkTokenUser ( req . params . token ) ;
await db [ "User" ] . update ( { newsletterOk : false } , { where : { id : userDatas . User . id } , limit : 1 } ) ;
await db [ "Subscription" ] . update ( { noticeOk : false } , { where : { UserId : userDatas . User . id } , limit : 1 } ) ;
userCtrl . creaUserJson ( userDatas . User . id ) ;
res . status ( 200 ) . json ( { message : txt . unsubscriptionOk } ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// CRONS
2020-09-10 10:54:50 +02:00
// Envoi des notifications aux abonnés arrivés à quelques jours de la fin de leur période d'abonnement gratuit.
2020-08-07 12:23:59 +02:00
exports . notifyExpirationFreeAccount = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
const dateExpirationMin = new Date ( new Date ( ) . getTime ( ) - ( config . freeAccountTimingInDays - config . freeAccountExpirationNotificationInDays + 1 ) * 24 * 3600 * 1000 ) ;
const dateExpirationMax = new Date ( new Date ( ) . getTime ( ) - ( config . freeAccountTimingInDays - config . freeAccountExpirationNotificationInDays ) * 24 * 3600 * 1000 ) ;
const users = await db [ "Subscription" ] . findAll (
{
where :
{
[ Op . and ] :
[
{ numberOfDays : config . freeAccountTimingInDays } ,
{ createdAt : { [ Op . gte ] : dateExpirationMin } } ,
{ createdAt : { [ Op . lt ] : dateExpirationMax } }
]
} ,
attributes : [ "UserId" ]
} ) ;
const sendNotification = async ( user ) =>
{
let userInfos = await userCtrl . searchUserById ( user . UserId ) ;
if ( userInfos && userInfos . User . status == "user" )
{
const mapMail =
{
SITE _NAME : config . siteName ,
USER _NAME : userInfos . User . name ,
2020-08-14 17:24:54 +02:00
LINK _URL : config . siteUrl + "/" + configTpl . updateAccountPage + "#subscribe"
2020-08-07 12:23:59 +02:00
} ;
const mailDatas =
{
mailSubject : txt . mailEndFreeTimeSubject ,
mailPreheader : txt . mailEndFreeTimeSubject ,
mailTitle : txt . mailEndFreeTimeSubject ,
2020-08-14 17:24:54 +02:00
mailHeaderLinkUrl : config . siteUrl + "/" + configTpl . updateAccountPage + "#subscribe" ,
2020-08-07 12:23:59 +02:00
mailHeaderLinkTxt : txt . mailEndFreeTimeLinkTxt ,
mailMainContent : tool . replaceAll ( txt . mailEndFreeTimeBodyHTML , mapMail ) ,
2020-08-14 17:24:54 +02:00
linksCTA : [ { url : config . siteUrl + "/" + configTpl . updateAccountPage + "#subscribe" , txt : txt . mailEndFreeTimeLinkTxt } ] ,
2020-08-07 12:23:59 +02:00
mailRecipientAddress : userInfos . User . email
}
await toolMail . sendMail ( userInfos . User . smtp , userInfos . User . email , txt . mailEndFreeTimeSubject , tool . replaceAll ( txt . mailEndFreeTimeBodyTxt , mapMail ) , "" , mailDatas ) ;
}
}
for ( let i in users )
sendNotification ( users [ i ] ) ;
if ( res . message )
res . message += "\n" + users . length + txt . mailEndFreeTimeMessage ;
else
res . message = users . length + txt . mailEndFreeTimeMessage ;
res . status ( 200 ) . json ( true ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-09-10 10:54:50 +02:00
// Envoi des notifications aux abonnés arrivés à quelques jours de la fin de leur période d'abonnement prémium.
2020-08-07 12:23:59 +02:00
exports . notifyExpirationAccount = async ( req , res , next ) =>
2020-09-10 10:54:50 +02:00
{
2020-08-07 12:23:59 +02:00
try
{
const db = require ( "../models/index" ) ;
const getUsers = async ( nbDays ) =>
{
const dateExpirationMin = new Date ( new Date ( ) . getTime ( ) + nbDays * 24 * 3600 * 1000 ) ;
const dateExpirationMax = new Date ( new Date ( ) . getTime ( ) + ( nbDays + 1 ) * 24 * 3600 * 1000 ) ;
const users = await db [ "Subscription" ] . findAll (
{
where :
{
[ Op . and ] :
[
{ numberOfDays : { [ Op . gt ] : config . freeAccountTimingInDays } } ,
db . sequelize . where ( db . sequelize . fn ( 'ADDDATE' , db . sequelize . col ( 'createdAt' ) , db . sequelize . literal ( 'INTERVAL `numberOfDays` DAY' ) ) , { [ Op . gte ] : dateExpirationMin } ) ,
db . sequelize . where ( db . sequelize . fn ( 'ADDDATE' , db . sequelize . col ( 'createdAt' ) , db . sequelize . literal ( 'INTERVAL `numberOfDays` DAY' ) ) , { [ Op . lt ] : dateExpirationMax } )
]
} ,
attributes : [ "UserId" ] ,
} ) ;
return users ;
}
2020-09-10 10:54:50 +02:00
const sendNotification = async ( user , mail ) =>
2020-08-07 12:23:59 +02:00
{
2020-09-15 11:34:10 +02:00
let userInfos = await userCtrl . searchUserById ( user . UserId ) ;
2020-08-07 12:23:59 +02:00
if ( userInfos && userInfos . User . status == "user" )
2020-09-10 10:54:50 +02:00
{
2020-08-07 12:23:59 +02:00
const mapMail =
{
SITE _NAME : config . siteName ,
USER _NAME : userInfos . User . name ,
2020-09-10 10:54:50 +02:00
LINK _URL : config . siteUrl + "/" + configTpl . updateAccountPage + "#subscribe"
2020-08-07 12:23:59 +02:00
} ;
2020-09-23 12:50:21 +02:00
let mailSubject ;
if ( mail !== "first" )
mailSubject = txt . mailExpirationRelaunchTxt + txt . mailExpirationSubject ;
else
mailSubject = txt . mailExpirationSubject ;
const mailDatas =
2020-08-07 12:23:59 +02:00
{
2020-09-23 12:50:21 +02:00
mailSubject : mailSubject ,
mailPreheader : mailSubject ,
mailTitle : mailSubject ,
2020-09-10 10:54:50 +02:00
mailHeaderLinkUrl : config . siteUrl + "/" + configTpl . updateAccountPage + "#subscribe" ,
2020-08-07 12:23:59 +02:00
mailHeaderLinkTxt : txt . mailExpirationLinkTxt ,
mailMainContent : tool . replaceAll ( txt . mailExpirationBodyHTML , mapMail ) ,
2020-09-10 10:54:50 +02:00
linksCTA : [ { url : config . siteUrl + "/" + configTpl . updateAccountPage + "#subscribe" , txt : txt . mailExpirationLinkTxt } ] ,
2020-08-07 12:23:59 +02:00
mailRecipientAddress : userInfos . User . email
}
await toolMail . sendMail ( userInfos . User . smtp , userInfos . User . email , txt . mailExpirationSubject , tool . replaceAll ( txt . mailExpirationBodyTxt , mapMail ) , "" , mailDatas ) ;
}
}
// première salve :
let users1 = await getUsers ( config . accountExpirationFirstNotificationInDays ) ;
2020-09-10 10:54:50 +02:00
for ( let i in users1 )
2020-08-07 12:23:59 +02:00
sendNotification ( users1 [ i ] , "first" ) ;
2020-09-23 12:50:21 +02:00
// relance :
2020-08-07 12:23:59 +02:00
let users2 = await getUsers ( config . accountExpirationSecondNotificationInDays ) ;
2020-09-10 10:54:50 +02:00
for ( let i in users2 )
2020-08-07 12:23:59 +02:00
sendNotification ( users2 [ i ] , "second" ) ;
if ( res . message )
res . message += "\n" + tool . replaceAll ( txt . mailExpirationMessage , { FIRST : users1 . length , SECOND : users2 . length } ) ;
else
res . message = tool . replaceAll ( txt . mailExpirationMessage , { FIRST : users1 . length , SECOND : users2 . length } ) ;
res . status ( 200 ) . json ( true ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// CRON donnant accès à l'utilisateur à nouveau quiz suivant son choix
2020-08-26 12:27:11 +02:00
// si il n'a plus de nouveaux quizs et que l'utilisateur souhaite recevoir un email, on lui passe un quiz au hasard à retester.
2020-08-07 12:23:59 +02:00
exports . addNewQuestionnaireUsers = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
// Utilisateurs dont l'abonnement est toujours actif et souhaitant recevoir un nouveau quiz le jour de l'appel de cette méthode.
2020-08-26 12:27:11 +02:00
// Le tout en heure locale et en ignorant ceux qui ont déjà été traités ce jour.
2020-08-30 16:26:15 +02:00
const subscriptionsOk = await db . sequelize . query ( "SELECT `Subscriptions`.`id` as SubscriptionId, `Subscriptions`.`lastProcessingAt`, `UserId`, `name`, `email`, `smtp`, `language`, `noticeOk`, `receiptDays`, ADDDATE(UTC_TIMESTAMP, INTERVAL `timeDifference` MINUTE) AS localDate FROM `Subscriptions` INNER JOIN `Users` ON `Subscriptions`.`UserId`=`Users`.`id` WHERE `status`='user' AND ADDDATE(`Subscriptions`.`createdAt`, `numberOfDays`) > UTC_TIMESTAMP HAVING HOUR(localDate) > " + config . hourGiveNewQuestionnaireBegin + " AND HOUR(localDate) < " + config . hourGiveNewQuestionnaireEnd + " AND LOCATE(DAYOFWEEK(localDate),receiptDays)!=0 AND SubscriptionId NOT IN (SELECT DISTINCT `SubscriptionId` FROM `Pauses` WHERE ADDDATE(`startingAt`, INTERVAL `timeDifference` MINUTE) <= localDate AND ADDDATE(`endingAT`, INTERVAL `timeDifference` MINUTE) > localDate) AND DATEDIFF(NOW(),`Subscriptions`.`lastProcessingAt`) >= 1 LIMIT " + config . numberNewQuestionnaireAtSameTime , { type : QueryTypes . SELECT } ) ;
2020-08-07 12:23:59 +02:00
if ( subscriptionsOk . length === 0 )
res . status ( 200 ) . json ( { message : txt . allSubscriptionProcessed } ) ;
else
{
2020-08-26 12:27:11 +02:00
let newQuestionnaire , access , questionnaire , token , now = new Date ( ) ;
2020-08-07 12:23:59 +02:00
for ( let i in subscriptionsOk )
{
newQuestionnaire = await db . sequelize . query ( "SELECT `Questionnaires`.`id`, `title`, `slug`, `introduction`, `url`, `anchor`,`estimatedTime` FROM `Questionnaires` INNER JOIN `Links` ON `Links`.`QuestionnaireId`=`Questionnaires`.`id` WHERE `isPublished`=1 AND `language`='" + subscriptionsOk [ i ] . language + "' AND `Questionnaires`.`id` NOT IN (SELECT `QuestionnaireId` FROM `QuestionnaireAccesses` WHERE `UserId`=" + subscriptionsOk [ i ] . UserId + ") ORDER BY `id` LIMIT 1" , { type : QueryTypes . SELECT } ) ;
if ( newQuestionnaire . length !== 0 )
{
access = await db [ "QuestionnaireAccess" ] . create ( { QuestionnaireId : newQuestionnaire [ 0 ] . id , UserId : subscriptionsOk [ i ] . UserId , selfCreatedOk : false } ) ;
if ( access )
{
answerCtrl . creaUserQuestionnairesWithoutAnswerJson ( subscriptionsOk [ i ] . UserId ) ;
if ( subscriptionsOk [ i ] . noticeOk )
{
token = jwt . sign ( { userId : subscriptionsOk [ i ] . UserId } , config . tokenPrivateKey , { expiresIn : config . tokenUnsubscribeLinkTimeInDays } ) ;
const mapMail =
{
USER _NAME : subscriptionsOk [ i ] . name ,
2020-08-26 12:27:11 +02:00
QUESTIONNAIRE _URL : config . siteUrl + "/" + configQuestionnaires . dirWebQuestionnaire + "/" + newQuestionnaire [ 0 ] . slug + ".html" ,
2020-08-07 12:23:59 +02:00
UNSUBSCRIBE _URL : config . siteUrl + "/" + configTpl . stopMailPage + token
} ;
const mailDatas =
{
mailSubject : newQuestionnaire [ 0 ] . title ,
mailPreheader : newQuestionnaire [ 0 ] . title ,
mailTitle : newQuestionnaire [ 0 ] . title ,
mailHeaderLinkUrl : config . siteUrl + "/" + configTpl . stopMailPage + token ,
mailHeaderLinkTxt : txt . mailStopMailLinkTxt ,
mailMainContent : newQuestionnaire [ 0 ] . introduction + "<p><b>" + txtQuestionnaire . estimatedTime + "</b> " + txtQuestionnaire . estimatedTimeOption [ newQuestionnaire [ 0 ] . estimatedTime ] + ".</p>" ,
linksCTA : [ { url : newQuestionnaire [ 0 ] . url , txt : newQuestionnaire [ 0 ] . anchor } , { url : config . siteUrl + "/" + configQuestionnaires . dirWebQuestionnaire + "/" + newQuestionnaire [ 0 ] . slug + ".html#questionnaire" , txt : txtQuestionnaire . btnShowQuestionnaire } ] ,
mailRecipientAddress : subscriptionsOk [ i ] . email
}
toolMail . sendMail ( subscriptionsOk [ i ] . smtp , subscriptionsOk [ i ] . email , newQuestionnaire [ 0 ] . title , tool . replaceAll ( txt . mailNewQuestionnaireBodyTxt , mapMail ) , "" , mailDatas ) ;
}
}
2020-08-26 12:27:11 +02:00
db [ "Subscription" ] . update ( { lastProcessingAt : now } , { where : { id : subscriptionsOk [ i ] . SubscriptionId } , limit : 1 } ) ;
2020-08-07 12:23:59 +02:00
}
else
{
2020-08-26 12:27:11 +02:00
// L'utilisateur a déjà accès à tous les quizs enregistés.
// Si il est abonné par email, on en tire un au hasard pour le lui envoyer, sinon on ne fait rien
if ( subscriptionsOk [ i ] . noticeOk )
{
getRandomQuestionnaire = await db . sequelize . query ( "SELECT `Questionnaires`.`id`, `title`, `slug`, `introduction`, `url`, `anchor`, `estimatedTime` FROM `Questionnaires` INNER JOIN `Links` ON `Links`.`QuestionnaireId`=`Questionnaires`.`id` WHERE `isPublished`=1 AND `language`='" + subscriptionsOk [ i ] . language + "' ORDER BY RAND() LIMIT 1" , { type : QueryTypes . SELECT } ) ;
token = jwt . sign ( { userId : subscriptionsOk [ i ] . UserId } , config . tokenPrivateKey , { expiresIn : config . tokenUnsubscribeLinkTimeInDays } ) ;
const mapMail =
{
USER _NAME : subscriptionsOk [ i ] . name ,
QUESTIONNAIRE _URL : config . siteUrl + "/" + configQuestionnaires . dirWebQuestionnaire + "/" + getRandomQuestionnaire [ 0 ] . slug + ".html" ,
UNSUBSCRIBE _URL : config . siteUrl + "/" + configTpl . stopMailPage + token
} ;
const mailDatas =
{
mailSubject : getRandomQuestionnaire [ 0 ] . title ,
mailPreheader : getRandomQuestionnaire [ 0 ] . title ,
mailTitle : getRandomQuestionnaire [ 0 ] . title ,
mailHeaderLinkUrl : config . siteUrl + "/" + configTpl . stopMailPage + token ,
mailHeaderLinkTxt : txt . mailStopMailLinkTxt ,
mailMainContent : "<h4>" + txtQuestionnaireAccess . questionnaireRetryInfo + "</h4>" + getRandomQuestionnaire [ 0 ] . introduction + "<p><b>" + txtQuestionnaire . estimatedTime + "</b> " + txtQuestionnaire . estimatedTimeOption [ getRandomQuestionnaire [ 0 ] . estimatedTime ] + ".</p>" ,
linksCTA : [ { url : getRandomQuestionnaire [ 0 ] . url , txt : getRandomQuestionnaire [ 0 ] . anchor } , { url : config . siteUrl + "/" + configQuestionnaires . dirWebQuestionnaire + "/" + getRandomQuestionnaire [ 0 ] . slug + ".html#questionnaire" , txt : txtQuestionnaire . btnShowQuestionnaire } ] ,
mailRecipientAddress : subscriptionsOk [ i ] . email
}
toolMail . sendMail ( subscriptionsOk [ i ] . smtp , subscriptionsOk [ i ] . email , getRandomQuestionnaire [ 0 ] . title , tool . replaceAll ( txtQuestionnaireAccess . questionnaireRetryInfoTxt , mapMail ) , "" , mailDatas ) ;
db [ "Subscription" ] . update ( { lastProcessingAt : now } , { where : { id : subscriptionsOk [ i ] . SubscriptionId } , limit : 1 } ) ;
}
2020-08-07 12:23:59 +02:00
}
}
res . status ( 200 ) . json ( subscriptionsOk ) ;
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// FONCTIONS UTILITAIRES
// Retourne un booléen suivant si l'utilisateur a accès ou non à un questionnaire
const checkQuestionnaireAccess = async ( UserId , QuestionnaireId ) =>
{
const db = require ( "../models/index" ) ;
const checkQuestionnaireAccess = await db [ "QuestionnaireAccess" ] . findOne ( { where : { UserId : UserId , QuestionnaireId : QuestionnaireId } , attributes : [ "createdAt" ] } ) ;
if ( checkQuestionnaireAccess )
return true ;
else
return false ;
}
exports . checkQuestionnaireAccess = checkQuestionnaireAccess ;
// Combien d'abonnements ont été enregistrés ces dernières 24 H ? depuis le début ? combien sont en premium ?
// Revoir : chercher les dates de paiement WP pour avoir stats 24 H ?
const getStatsSubscriptions = async ( ) =>
{
const db = require ( "../models/index" ) ;
const getSubscriptions24H = await db . sequelize . query ( "SELECT `id` FROM `Subscriptions` WHERE `createdAt` > ADDDATE(NOW(), -1)" , { type : QueryTypes . SELECT } ) ;
const getSubscriptionsTot = await db . sequelize . query ( "SELECT `id` FROM `Subscriptions`" , { type : QueryTypes . SELECT } ) ;
const getSubscriptionsPremium = await db . sequelize . query ( "SELECT `id` FROM `Subscriptions` WHERE `numberOfDays` > :nb" , { replacements : { nb : config . freeAccountTimingInDays } , type : QueryTypes . SELECT } ) ;
if ( getSubscriptions24H && getSubscriptionsTot && getSubscriptionsPremium )
{
const stats =
{
nbSubscriptions24H : getSubscriptions24H . length ,
nbSubscriptionsTot : getSubscriptionsTot . length ,
nbSubscriptionsPremium : getSubscriptionsPremium . length
}
return stats ;
}
else
return false ;
}
2020-08-26 12:27:11 +02:00
exports . getStatsSubscriptions = getStatsSubscriptions ;