2020-04-08 20:15:35 +02:00
const path = require ( 'path' ) ;
const { validationResult } = require ( 'express-validator' ) ;
const bcrypt = require ( 'bcryptjs' ) ;
const jwt = require ( 'jsonwebtoken' ) ;
const ms = require ( 'ms' ) ;
const uuid = require ( 'uuid' ) ;
const errorHandling = require ( '../assets/utils/errorHandling' ) ;
const { serverError , generalError } = require ( '../assets/config/errors' ) ;
const { JWT _SECRET , FRONT _END _HOST , EMAIL _INFO , HOST , TOKEN _LIFE } = require ( '../assets/config/config' ) ;
const transporter = require ( '../assets/config/transporter' ) ;
2020-04-22 20:44:06 +02:00
const { emailUserTemplate } = require ( '../assets/config/emails' ) ;
2020-04-08 20:15:35 +02:00
const Users = require ( '../models/users' ) ;
const Favorites = require ( '../models/favorites' ) ;
const Functions = require ( '../models/functions' ) ;
const Categories = require ( '../models/categories' ) ;
const Comments = require ( '../models/comments' ) ;
2020-04-22 22:01:18 +02:00
const Quotes = require ( '../models/quotes' ) ;
2020-04-08 20:15:35 +02:00
const deleteFilesNameStartWith = require ( '../assets/utils/deleteFilesNameStartWith' ) ;
2020-03-25 16:23:43 +01:00
2020-04-07 16:21:48 +02:00
async function handleEditUser ( res , { name , email , biography , isPublicEmail } , userId , logoName ) {
const user = await Users . findOne ( { where : { id : userId } } ) ;
user . name = name ;
2020-04-08 00:47:24 +02:00
if ( user . email !== email ) {
const tempToken = uuid . v4 ( ) ;
user . email = email ;
user . isConfirmed = false ;
user . tempToken = tempToken ;
await transporter . sendMail ( {
from : ` "FunctionProject" < ${ EMAIL _INFO . auth . user } > ` ,
to : email ,
subject : "FunctionProject - Confirmer l'email" ,
2020-04-22 20:44:06 +02:00
html : emailUserTemplate ( "Veuillez confirmer l'email" , "Oui, je confirme." , ` ${ HOST } /users/confirm-email/ ${ tempToken } ` , "Si vous avez reçu ce message par erreur, il suffit de le supprimer. Votre email ne serez pas confirmé si vous ne cliquez pas sur le lien de confirmation ci-dessus." )
2020-04-08 00:47:24 +02:00
} ) ;
}
if ( biography != undefined ) {
user . biography = biography ;
}
2020-04-07 16:21:48 +02:00
user . isPublicEmail = isPublicEmail ;
2020-04-15 13:55:07 +02:00
if ( logoName != undefined && ` /images/users/ ${ logoName } ` !== user . logo ) {
2020-04-07 16:21:48 +02:00
user . logo = ` /images/users/ ${ logoName } ` ;
}
await user . save ( ) ;
2020-04-08 00:47:24 +02:00
return res . status ( 200 ) . json ( { id : user . id , name : user . name , email : user . email , biography : user . biography , logo : user . logo , isPublicEmail : user . isPublicEmail , isAdmin : user . isAdmin , createdAt : user . createdAt } ) ;
2020-04-07 16:21:48 +02:00
}
2020-04-15 13:55:07 +02:00
exports . putUser = async ( req , res , next ) => {
2020-04-07 16:21:48 +02:00
const { name , email , biography , isPublicEmail } = req . body ;
const logo = req . files . logo ;
const errors = validationResult ( req ) ;
if ( ! errors . isEmpty ( ) ) {
return errorHandling ( next , { message : errors . array ( ) [ 0 ] . msg , statusCode : 400 } ) ;
}
if ( logo != undefined ) {
if ( ! logo || logo . truncated && (
logo . mimetype !== 'image/png' ||
logo . mimetype !== 'image/jpg' ||
logo . mimetype !== 'image/jpeg' ||
logo . mimetype !== 'image/gif'
) ) {
2020-04-08 15:26:18 +02:00
return errorHandling ( next , { message : "Le profil doit avoir une image valide (PNG, JPG, GIF) et moins de 5mo." , statusCode : 400 } ) ;
2020-04-07 16:21:48 +02:00
}
2020-04-15 13:55:07 +02:00
const splitedLogoName = logo . name . split ( '.' ) ;
if ( splitedLogoName . length !== 2 ) return errorHandling ( next , serverError ) ;
const logoName = name + req . userId + '.' + splitedLogoName [ 1 ] ;
2020-04-07 17:15:01 +02:00
// Supprime les anciens logo
try {
2020-04-15 13:55:07 +02:00
deleteFilesNameStartWith ( ` ${ name + req . userId } ` , path . join ( _ _dirname , '..' , 'assets' , 'images' , 'users' ) , async ( ) => {
logo . mv ( path . join ( _ _dirname , '..' , 'assets' , 'images' , 'users' , logoName ) , async ( error ) => {
if ( error ) return errorHandling ( next , serverError ) ;
return await handleEditUser ( res , { name , email , biography , isPublicEmail } , req . userId , logoName ) ;
} ) ;
} ) ;
2020-04-07 17:15:01 +02:00
} catch ( error ) {
console . log ( error ) ;
return errorHandling ( next , serverError ) ;
}
2020-04-07 16:21:48 +02:00
} else {
try {
2020-04-15 13:55:07 +02:00
return await handleEditUser ( res , { name , email , biography , isPublicEmail } , req . userId , null ) ;
2020-04-07 16:21:48 +02:00
} catch ( error ) {
console . log ( error ) ;
return errorHandling ( next , serverError ) ;
}
}
}
2020-03-25 18:22:03 +01:00
exports . register = async ( req , res , next ) => {
2020-03-25 16:23:43 +01:00
const { name , email , password } = req . body ;
const errors = validationResult ( req ) ;
if ( ! errors . isEmpty ( ) ) {
return errorHandling ( next , { message : errors . array ( ) [ 0 ] . msg , statusCode : 400 } ) ;
}
try {
const hashedPassword = await bcrypt . hash ( password , 12 ) ;
2020-03-31 07:48:00 +02:00
const tempToken = uuid . v4 ( ) ;
2020-03-25 16:23:43 +01:00
await Users . create ( { email , name , password : hashedPassword , tempToken } ) ;
await transporter . sendMail ( {
from : ` "FunctionProject" < ${ EMAIL _INFO . auth . user } > ` ,
to : email ,
subject : "FunctionProject - Confirmer l'inscription" ,
2020-04-22 20:44:06 +02:00
html : emailUserTemplate ( "Veuillez confirmer l'inscription" , "Oui, je m'inscris." , ` ${ HOST } /users/confirm-email/ ${ tempToken } ` , "Si vous avez reçu ce message par erreur, il suffit de le supprimer. Vous ne serez pas inscrit si vous ne cliquez pas sur le lien de confirmation ci-dessus." )
2020-03-25 16:23:43 +01:00
} ) ;
2020-03-25 22:59:08 +01:00
return res . status ( 201 ) . json ( { result : "Vous y êtes presque, veuillez vérifier vos emails pour confirmer l'inscription." } ) ;
2020-03-25 16:23:43 +01:00
} catch ( error ) {
console . log ( error ) ;
2020-04-07 16:21:48 +02:00
return errorHandling ( next , serverError ) ;
2020-03-25 16:23:43 +01:00
}
}
exports . login = async ( req , res , next ) => {
const { email , password } = req . body ;
2020-04-06 16:46:03 +02:00
const errors = validationResult ( req ) ;
if ( ! errors . isEmpty ( ) ) {
return errorHandling ( next , { message : errors . array ( ) [ 0 ] . msg , statusCode : 400 } ) ;
}
2020-03-25 16:23:43 +01:00
try {
2020-04-06 16:46:03 +02:00
const user = await Users . findOne ( { where : { email } } ) ;
2020-03-25 16:23:43 +01:00
if ( ! user ) {
return errorHandling ( next , { message : "Le mot de passe ou l'adresse email n'est pas valide." , statusCode : 400 } ) ;
}
const isEqual = await bcrypt . compare ( password , user . password ) ;
if ( ! isEqual ) {
return errorHandling ( next , { message : "Le mot de passe ou l'adresse email n'est pas valide." , statusCode : 400 } ) ;
}
2020-04-06 16:46:03 +02:00
if ( ! user . isConfirmed ) {
return errorHandling ( next , { message : "Vous devez valider votre adresse email pour votre première connexion." , statusCode : 400 } ) ;
}
2020-03-25 16:23:43 +01:00
const token = jwt . sign ( {
email : user . email , userId : user . id
2020-04-08 20:15:35 +02:00
} , JWT _SECRET , { expiresIn : TOKEN _LIFE } ) ;
return res . status ( 200 ) . json ( { token , id : user . id , name : user . name , email : user . email , biography : user . biography , logo : user . logo , isPublicEmail : user . isPublicEmail , isAdmin : user . isAdmin , createdAt : user . createdAt , expiresIn : Math . round ( ms ( TOKEN _LIFE ) / 1000 ) } ) ;
2020-03-25 16:23:43 +01:00
} catch ( error ) {
console . log ( error ) ;
2020-04-07 16:21:48 +02:00
return errorHandling ( next , serverError ) ;
2020-03-25 16:23:43 +01:00
}
}
exports . confirmEmail = async ( req , res , next ) => {
const { tempToken } = req . params ;
if ( ! tempToken ) {
return errorHandling ( next , generalError ) ;
}
try {
const user = await Users . findOne ( { where : { tempToken , isConfirmed : false } } ) ;
if ( ! user ) {
return errorHandling ( next , { message : "Le token n'est pas valide." , statusCode : 400 } ) ;
}
user . tempToken = null ;
user . isConfirmed = true ;
await user . save ( ) ;
2020-04-06 16:46:03 +02:00
return res . redirect ( ` ${ FRONT _END _HOST } /login?isConfirmed=true ` ) ;
2020-03-31 07:48:00 +02:00
} catch ( error ) {
console . log ( error ) ;
2020-04-07 16:21:48 +02:00
return errorHandling ( next , serverError ) ;
2020-03-31 07:48:00 +02:00
}
}
exports . resetPassword = async ( req , res , next ) => {
const { email } = req . body ;
const errors = validationResult ( req ) ;
if ( ! errors . isEmpty ( ) ) {
return errorHandling ( next , { message : errors . array ( ) [ 0 ] . msg , statusCode : 400 } ) ;
}
try {
const user = await Users . findOne ( { where : { email , tempToken : null } } ) ;
if ( ! user ) {
return errorHandling ( next , { message : "L'adresse email n'existe pas ou une demande est déjà en cours." , statusCode : 400 } ) ;
}
const tempToken = uuid . v4 ( ) ;
user . tempExpirationToken = Date . now ( ) + 3600000 ; // 1 heure
user . tempToken = tempToken ;
await user . save ( ) ;
await transporter . sendMail ( {
from : ` "FunctionProject" < ${ EMAIL _INFO . auth . user } > ` ,
to : email ,
subject : "FunctionProject - Réinitialisation du mot de passe" ,
2020-04-22 20:44:06 +02:00
html : emailUserTemplate ( "Veuillez confirmer la réinitialisation du mot de passe" , "Oui, je change mon mot de passe." , ` ${ FRONT _END _HOST } /newPassword?token= ${ tempToken } ` , "Si vous avez reçu ce message par erreur, il suffit de le supprimer. Votre mot de passe ne sera pas réinitialiser si vous ne cliquez pas sur le lien ci-dessus. Par ailleurs, pour la sécurité de votre compte, la réinitialisation du mot de passe est disponible pendant un délai de 1 heure, passez ce temps, la réinitialisation ne sera plus valide." )
2020-03-31 07:48:00 +02:00
} ) ;
return res . status ( 200 ) . json ( { result : "Demande de réinitialisation du mot de passe réussi, veuillez vérifier vos emails!" } ) ;
} catch ( error ) {
console . log ( error ) ;
2020-04-07 16:21:48 +02:00
return errorHandling ( next , serverError ) ;
2020-03-31 07:48:00 +02:00
}
}
exports . newPassword = async ( req , res , next ) => {
const { tempToken , password } = req . body ;
const errors = validationResult ( req ) ;
if ( ! errors . isEmpty ( ) ) {
return errorHandling ( next , { message : errors . array ( ) [ 0 ] . msg , statusCode : 400 } ) ;
}
try {
const user = await Users . findOne ( { where : { tempToken } } ) ;
if ( ! user && parseInt ( tempExpirationToken ) < Date . now ( ) ) {
return errorHandling ( next , { message : "Le token n'est pas valide." , statusCode : 400 } ) ;
}
const hashedPassword = await bcrypt . hash ( password , 12 ) ;
user . password = hashedPassword ;
user . tempToken = null ;
user . tempExpirationToken = null ;
await user . save ( ) ;
return res . status ( 200 ) . json ( { result : "Le mot de passe a bien été modifié!" } ) ;
2020-03-25 16:23:43 +01:00
} catch ( error ) {
console . log ( error ) ;
2020-04-07 16:21:48 +02:00
return errorHandling ( next , serverError ) ;
2020-03-25 16:23:43 +01:00
}
2020-04-06 23:06:21 +02:00
}
exports . getUserInfo = async ( req , res , next ) => {
const { name } = req . params ;
try {
const user = await Users . findOne ( {
2020-04-08 00:47:24 +02:00
where : { name , isConfirmed : true } ,
2020-04-06 23:06:21 +02:00
attributes : {
exclude : [ "updatedAt" , "isAdmin" , "isConfirmed" , "password" , "tempToken" , "tempExpirationToken" ]
} ,
} ) ;
if ( ! user ) {
return errorHandling ( next , { message : "L'utilisateur n'existe pas." , statusCode : 404 } ) ;
}
const favorites = await Favorites . findAll ( {
where : { userId : user . id } ,
include : [
{ model : Functions , attributes : { exclude : [ "updatedAt" , "utilizationForm" , "article" , "isOnline" ] } , include : { model : Categories , attributes : [ "name" , "color" ] } }
2020-04-22 22:01:18 +02:00
] ,
order : [ [ 'createdAt' , 'DESC' ] ] ,
limit : 5
2020-04-06 23:06:21 +02:00
} ) ;
const favoritesArray = favorites . map ( ( favorite ) => favorite . function ) ;
const comments = await Comments . findAll ( {
where : { userId : user . id } ,
include : [
{ model : Functions , attributes : { exclude : [ "updatedAt" , "utilizationForm" , "article" , "isOnline" ] } }
2020-04-07 16:21:48 +02:00
] ,
2020-04-22 22:01:18 +02:00
order : [ [ 'createdAt' , 'DESC' ] ] ,
limit : 5
2020-04-06 23:06:21 +02:00
} ) ;
const commentsArray = comments . map ( ( commentObject ) => {
return {
id : commentObject . id ,
message : commentObject . message ,
createdAt : commentObject . createdAt ,
function : commentObject . function . dataValues
} ;
} ) ;
2020-04-22 22:01:18 +02:00
const quotesArray = await Quotes . findAll ( {
where : { userId : user . id } ,
attributes : {
exclude : [ "updatedAt" , "createdAt" , "isValidated" , "userId" , "id" ]
} ,
order : [ [ 'createdAt' , 'DESC' ] ] ,
limit : 5 ,
} ) ;
2020-04-06 23:06:21 +02:00
const userObject = {
// Si Public Email
... ( user . isPublicEmail ) && { email : user . email } ,
2020-04-08 00:47:24 +02:00
isPublicEmail : user . isPublicEmail ,
2020-04-06 23:06:21 +02:00
name : user . name ,
biography : user . biography ,
logo : user . logo ,
createdAt : user . createdAt ,
favoritesArray ,
2020-04-22 22:01:18 +02:00
commentsArray ,
quotesArray
2020-04-06 23:06:21 +02:00
} ;
return res . status ( 200 ) . json ( userObject ) ;
} catch ( error ) {
console . log ( error ) ;
2020-04-07 16:21:48 +02:00
return errorHandling ( next , serverError ) ;
2020-04-06 23:06:21 +02:00
}
2020-03-25 16:23:43 +01:00
}