From 4be7a46a1035a48f513412ca33cf5e12c8b4ca07 Mon Sep 17 00:00:00 2001 From: divlo Date: Mon, 3 Aug 2020 14:14:45 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20standardJS=20all=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app.js | 13 +- api/assets/config/emails.js | 14 +- api/assets/functions/main/armstrongNumber.js | 23 +- api/assets/functions/main/calculateAge.js | 24 +- api/assets/functions/main/convertCurrency.js | 30 +- api/assets/functions/main/convertDistance.js | 39 +- api/assets/functions/main/convertEncoding.js | 47 +- .../main/convertRomanArabicNumbers.js | 37 +- .../functions/main/convertTemperature.js | 13 +- api/assets/functions/main/fibonacci.js | 19 +- api/assets/functions/main/findLongestWord.js | 4 +- api/assets/functions/main/heapAlgorithm.js | 24 +- api/assets/functions/main/isPalindrome.js | 18 +- api/assets/functions/main/linkShortener.js | 32 +- api/assets/functions/main/randomNumber.js | 14 +- api/assets/functions/main/randomQuote.js | 4 +- api/assets/functions/main/rightPrice.js | 9 +- api/assets/functions/main/sortArray.js | 25 +- api/assets/functions/main/weatherRequest.js | 49 +- .../functions/secondary/dateTimeManagement.js | 3 +- api/assets/utils/database.js | 13 +- api/assets/utils/getPagesHelper.js | 8 +- api/controllers/admin.js | 237 +++- api/controllers/categories.js | 4 +- api/controllers/comments.js | 55 +- api/controllers/favorites.js | 32 +- api/controllers/functions.js | 60 +- api/controllers/quotes.js | 15 +- api/controllers/tasks.js | 27 +- api/controllers/users.js | 264 +++- api/middlewares/isAdmin.js | 19 +- api/middlewares/isAuth.js | 15 +- api/package-lock.json | 453 ++++--- api/package.json | 6 +- api/routes/admin.js | 310 ++--- api/routes/categories.js | 2 +- api/routes/comments.js | 8 +- api/routes/favorites.js | 6 +- api/routes/functions.js | 6 +- api/routes/quotes.js | 4 +- api/routes/tasks.js | 8 +- website/components/Footer/Footer.css | 14 +- website/components/Footer/Footer.jsx | 7 +- .../FunctionAdmin/AddEditFunction.jsx | 146 ++- .../FunctionAdmin/EditArticleFunction.jsx | 24 +- .../FunctionAdmin/EditFormFunction.jsx | 174 ++- .../components/FunctionCard/FunctionCard.css | 86 +- .../components/FunctionCard/FunctionCard.jsx | 91 +- .../FunctionPage/CommentCard/CommentCard.css | 48 +- .../FunctionPage/CommentCard/CommentCard.jsx | 122 +- .../FunctionPage/FunctionArticle.jsx | 6 +- .../FunctionComments/FunctionComments.css | 8 +- .../FunctionComments/FunctionComments.jsx | 141 ++- .../FunctionPage/FunctionComponentTop.jsx | 58 +- .../components/FunctionPage/FunctionForm.jsx | 97 +- .../components/FunctionPage/FunctionPage.jsx | 20 +- .../components/FunctionPage/FunctionTabs.css | 58 +- .../components/FunctionPage/FunctionTabs.jsx | 7 +- .../FunctionPage/FunctionTabsTop.jsx | 8 +- .../FunctionsList/FunctionsList.css | 56 +- .../FunctionsList/FunctionsList.jsx | 116 +- website/components/HeadTag.jsx | 3 +- website/components/Header/Header.css | 206 ++-- website/components/Header/Header.jsx | 84 +- website/components/Header/NavigationLink.jsx | 6 +- website/components/Loader.jsx | 12 +- website/components/Modal.jsx | 6 +- website/contexts/UserContext.jsx | 24 +- website/hoc/withoutAuth.jsx | 4 +- website/hooks/useAPI.js | 4 +- website/package-lock.json | 550 ++++----- website/public/css/general.css | 302 ++--- website/public/css/grid.css | 1059 +++++++++-------- website/public/css/normalize.css | 36 +- website/public/css/nprogress.css | 94 +- website/public/css/pages/404.css | 12 +- .../public/css/pages/FunctionComponent.css | 46 +- website/public/css/pages/admin.css | 48 +- .../css/pages/functions/chronometerTimer.css | 50 +- .../public/css/pages/functions/rightPrice.css | 22 +- .../public/css/pages/functions/toDoList.css | 46 +- website/public/css/pages/index.css | 18 +- website/public/css/pages/profile.css | 38 +- website/public/css/pages/register-login.css | 26 +- website/server.js | 2 +- 85 files changed, 3595 insertions(+), 2383 deletions(-) diff --git a/api/app.js b/api/app.js index 0fb5b0a..55fae3a 100644 --- a/api/app.js +++ b/api/app.js @@ -34,7 +34,9 @@ app.use('/quotes', require('./routes/quotes')) app.use('/tasks', require('./routes/tasks')) /* Errors Handling */ -app.use((_req, _res, next) => errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" })) // 404 +app.use((_req, _res, next) => + errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" }) +) // 404 app.use((error, _req, res, _next) => { console.log(error) const { statusCode, message } = error @@ -76,8 +78,11 @@ Tasks.belongsTo(Users, { constraints: false }) /* Server */ // sequelize.sync({ force: true }) -sequelize.sync() +sequelize + .sync() .then(() => { - app.listen(PORT, () => console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`)) + app.listen(PORT, () => + console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`) + ) }) - .catch((error) => console.log(error)) + .catch(error => console.log(error)) diff --git a/api/assets/config/emails.js b/api/assets/config/emails.js index 2e899f3..399382c 100644 --- a/api/assets/config/emails.js +++ b/api/assets/config/emails.js @@ -26,16 +26,24 @@ exports.emailQuoteTemplate = (isValid, quote, frontendLink) => `

- La citation que vous avez proposée a été ${(isValid) ? 'validée' : 'supprimée'}. + La citation que vous avez proposée a été ${ + isValid + ? 'validée' + : 'supprimée' + }.

Lien vers la fonction randomQuote de FunctionProject.

- ${(!isValid) ? ` + ${ + !isValid + ? `

Si votre citation a été supprimée et vous pensez que c'est une erreur, contactez-moi à cette adresse email : contact@divlo.fr.

- ` : ''} + ` + : '' + }

La citation en question :
diff --git a/api/assets/functions/main/armstrongNumber.js b/api/assets/functions/main/armstrongNumber.js index 971a555..cab28bc 100644 --- a/api/assets/functions/main/armstrongNumber.js +++ b/api/assets/functions/main/armstrongNumber.js @@ -16,14 +16,24 @@ function armstrongNumber (number) { let resultString = '' for (let index = 0; index < numberStringLength; index++) { result = result + parseInt(numberString[index]) ** numberStringLength - resultString = resultString + ' + ' + numberString[index] + '' + numberStringLength + '' + resultString = + resultString + + ' + ' + + numberString[index] + + '' + + numberStringLength + + '' } const formattedNumber = formatNumberResult(number) - const isArmstrongNumber = (result === number) + const isArmstrongNumber = result === number return { isArmstrongNumber, - resultHTML: `

${formattedNumber} ${isArmstrongNumber ? 'est' : "n'est pas"} un nombre d'Armstrong, car ${resultString.slice(2)} = ${formatNumberResult(result)}.

` + resultHTML: `

${formattedNumber} ${ + isArmstrongNumber ? 'est' : "n'est pas" + } un nombre d'Armstrong, car ${resultString.slice( + 2 + )} = ${formatNumberResult(result)}.

` } } @@ -32,14 +42,17 @@ module.exports = ({ res, next }, argsObject) => { let { number } = argsObject // S'il n'y a pas les champs obligatoire - if (!(number)) { + if (!number) { return errorHandling(next, requiredFields) } // Si ce n'est pas un nombre number = parseInt(number) if (isNaN(number) || number <= 0) { - return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez rentré un nombre valide.', + statusCode: 400 + }) } return res.status(200).json(armstrongNumber(number)) diff --git a/api/assets/functions/main/calculateAge.js b/api/assets/functions/main/calculateAge.js index 410110d..3e612e9 100644 --- a/api/assets/functions/main/calculateAge.js +++ b/api/assets/functions/main/calculateAge.js @@ -2,7 +2,10 @@ const errorHandling = require('../../utils/errorHandling') const moment = require('moment') const { requiredFields } = require('../../config/errors') -function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYear }) { +function calculateAge ( + currentDate, + { birthDateDay, birthDateMonth, birthDateYear } +) { const day = currentDate.getDate() const month = currentDate.getMonth() const currentDateMoment = moment([currentDate.getFullYear(), month, day]) @@ -15,7 +18,7 @@ function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYea birthDateMoment.add(ageMonths, 'months') const ageDays = currentDateMoment.diff(birthDateMoment, 'days') - const isBirthday = (birthDateDay === day && birthDateMonth === month) + const isBirthday = birthDateDay === day && birthDateMonth === month return { ageYears, ageMonths, ageDays, isBirthday } } @@ -24,20 +27,27 @@ module.exports = ({ res, next }, argsObject) => { const { birthDate } = argsObject // S'il n'y a pas les champs obligatoire - if (!(birthDate)) { + if (!birthDate) { return errorHandling(next, requiredFields) } const birthDateDay = parseInt(birthDate.substring(0, 2)) - const birthDateMonth = parseInt((birthDate.substring(3, 5)) - 1) + const birthDateMonth = parseInt(birthDate.substring(3, 5) - 1) const birthDateYear = parseInt(birthDate.substring(6, 10)) // Si ce n'est pas une date valide const currentDate = new Date() const birthDateObject = new Date(birthDateYear, birthDateMonth, birthDateDay) - const result = calculateAge(currentDate, { birthDateYear, birthDateMonth, birthDateDay }) - if ((currentDate < birthDateObject) || isNaN(result.ageYears)) { - return errorHandling(next, { message: 'Veuillez rentré une date valide...', statusCode: 400 }) + const result = calculateAge(currentDate, { + birthDateYear, + birthDateMonth, + birthDateDay + }) + if (currentDate < birthDateObject || isNaN(result.ageYears)) { + return errorHandling(next, { + message: 'Veuillez rentré une date valide...', + statusCode: 400 + }) } let resultHTML diff --git a/api/assets/functions/main/convertCurrency.js b/api/assets/functions/main/convertCurrency.js index 5a9309a..45d64a4 100644 --- a/api/assets/functions/main/convertCurrency.js +++ b/api/assets/functions/main/convertCurrency.js @@ -15,23 +15,39 @@ module.exports = ({ res, next }, argsObject) => { // Si ce n'est pas un nombre number = parseFloat(number) if (isNaN(number)) { - return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez rentré un nombre valide.', + statusCode: 400 + }) } - axios.get(`https://api.exchangeratesapi.io/latest?base=${baseCurrency}`) - .then((response) => { + axios + .get(`https://api.exchangeratesapi.io/latest?base=${baseCurrency}`) + .then(response => { const rate = response.data.rates[finalCurrency] if (!rate) { - return errorHandling(next, { message: "La devise n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La devise n'existe pas.", + statusCode: 404 + }) } const result = rate * number const dateObject = new Date(response.data.date) const year = dateObject.getFullYear() - const day = ('0' + (dateObject.getDate())).slice(-2) + const day = ('0' + dateObject.getDate()).slice(-2) const month = ('0' + (dateObject.getMonth() + 1)).slice(-2) const date = `${day}/${month}/${year}` - const resultHTML = `

${formatNumberResult(number)} ${response.data.base} = ${formatNumberResult(result.toFixed(2))} ${finalCurrency}

Dernier rafraîchissement du taux d'échange : ${date}

` + const resultHTML = `

${formatNumberResult(number)} ${ + response.data.base + } = ${formatNumberResult( + result.toFixed(2) + )} ${finalCurrency}

Dernier rafraîchissement du taux d'échange : ${date}

` return res.status(200).json({ date, result, resultHTML }) }) - .catch(() => errorHandling(next, { message: "La devise n'existe pas.", statusCode: 404 })) + .catch(() => + errorHandling(next, { + message: "La devise n'existe pas.", + statusCode: 404 + }) + ) } diff --git a/api/assets/functions/main/convertDistance.js b/api/assets/functions/main/convertDistance.js index 46e00e1..b50e756 100644 --- a/api/assets/functions/main/convertDistance.js +++ b/api/assets/functions/main/convertDistance.js @@ -2,7 +2,33 @@ const errorHandling = require('../../utils/errorHandling') const { requiredFields, generalError } = require('../../config/errors') const formatNumberResult = require('../secondary/formatNumberResult') -const correspondancesDistance = ['pm', null, null, 'nm', null, null, 'µm', null, null, 'mm', 'cm', 'dm', 'm', 'dam', 'hm', 'km', null, null, 'Mm', null, null, 'Gm', null, null, 'Tm'] +const correspondancesDistance = [ + 'pm', + null, + null, + 'nm', + null, + null, + 'µm', + null, + null, + 'mm', + 'cm', + 'dm', + 'm', + 'dam', + 'hm', + 'km', + null, + null, + 'Mm', + null, + null, + 'Gm', + null, + null, + 'Tm' +] /** * @description Convertis la longueur (distance) avec les unités allant de picomètre au Téramètre. @@ -21,7 +47,11 @@ function convertDistance (firstValue, unitFirstValue, unitFinalValue) { const result = firstValue * Math.pow(10, difference) return { result, - resultHTML: `

${formatNumberResult(firstValue)} ${unitFirstValue} = ${formatNumberResult(result)} ${unitFinalValue}

` + resultHTML: `

${formatNumberResult( + firstValue + )} ${unitFirstValue} = ${formatNumberResult( + result + )} ${unitFinalValue}

` } } return false @@ -39,7 +69,10 @@ module.exports = ({ res, next }, argsObject) => { // Si ce n'est pas un nombre number = parseFloat(number) if (isNaN(number)) { - return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez rentré un nombre valide.', + statusCode: 400 + }) } const result = convertDistance(number, numberUnit, finalUnit) diff --git a/api/assets/functions/main/convertEncoding.js b/api/assets/functions/main/convertEncoding.js index 9159378..1b281f9 100644 --- a/api/assets/functions/main/convertEncoding.js +++ b/api/assets/functions/main/convertEncoding.js @@ -73,7 +73,9 @@ function binaryToHexadecimal (value) { if (isNaN(value)) { return false } else { - return parseInt(value).toString(16).toUpperCase() + return parseInt(value) + .toString(16) + .toUpperCase() } } @@ -140,10 +142,15 @@ function numberUnicodeToText (string) { function textToBinary (s) { try { s = unescape(encodeURIComponent(s)) - let chr; let i = 0; const l = s.length; let out = '' + let chr + let i = 0 + const l = s.length + let out = '' for (; i < l; i++) { chr = s.charCodeAt(i).toString(2) - while (chr.length % 8 !== 0) { chr = '0' + chr } + while (chr.length % 8 !== 0) { + chr = '0' + chr + } out += chr } return out.replace(/(\d{8})/g, '$1 ').replace(/(^\s+|\s+$)/, '') @@ -161,10 +168,13 @@ function textToBinary (s) { function binaryToText (s) { try { s = s.replace(/\s/g, '') - let i = 0; const l = s.length; let chr; let out = '' + let i = 0 + const l = s.length + let chr + let out = '' for (; i < l; i += 8) { chr = parseInt(s.substr(i, 8), 2).toString(16) - out += '%' + ((chr.length % 2 === 0) ? chr : '0' + chr) + out += '%' + (chr.length % 2 === 0 ? chr : '0' + chr) } return decodeURIComponent(out) } catch (error) { @@ -181,10 +191,13 @@ function binaryToText (s) { function textToHexadecimal (s) { try { s = unescape(encodeURIComponent(s)) - let chr; let i = 0; const l = s.length; let out = '' + let chr + let i = 0 + const l = s.length + let out = '' for (; i < l; i++) { chr = s.charCodeAt(i).toString(16) - out += (chr.length % 2 === 0) ? chr : '0' + chr + out += chr.length % 2 === 0 ? chr : '0' + chr out += ' ' } return out.toUpperCase() @@ -209,7 +222,20 @@ function hexadecimalToText (s) { } /* OUTPUTS */ -const convertEncoding = { decimalToBinary, binaryToDecimal, decimalToHexadecimal, hexadecimalToDecimal, binaryToHexadecimal, hexadecimalToBinary, textToNumberUnicode, numberUnicodeToText, textToBinary, binaryToText, textToHexadecimal, hexadecimalToText } +const convertEncoding = { + decimalToBinary, + binaryToDecimal, + decimalToHexadecimal, + hexadecimalToDecimal, + binaryToHexadecimal, + hexadecimalToBinary, + textToNumberUnicode, + numberUnicodeToText, + textToBinary, + binaryToText, + textToHexadecimal, + hexadecimalToText +} function executeFunction (option, value) { return convertEncoding[option](value) } @@ -225,7 +251,10 @@ module.exports = ({ res, next }, argsObject) => { // Si la fonction n'existe pas // eslint-disable-next-line if (!convertEncoding.hasOwnProperty(functionName)) { - return errorHandling(next, { message: "Cette conversion n'existe pas.", statusCode: 400 }) + return errorHandling(next, { + message: "Cette conversion n'existe pas.", + statusCode: 400 + }) } const result = executeFunction(functionName, value) diff --git a/api/assets/functions/main/convertRomanArabicNumbers.js b/api/assets/functions/main/convertRomanArabicNumbers.js index 8c7c86d..cd8c433 100644 --- a/api/assets/functions/main/convertRomanArabicNumbers.js +++ b/api/assets/functions/main/convertRomanArabicNumbers.js @@ -51,7 +51,7 @@ function convertArabicToRoman (nombre) { */ function convertRomanToArabic (string) { let result = 0 - correspondancesRomainArabe.forEach((correspondance) => { + correspondancesRomainArabe.forEach(correspondance => { while (string.indexOf(correspondance[1]) === 0) { // Ajout de la valeur décimale au résultat result += correspondance[0] @@ -68,7 +68,7 @@ function convertRomanToArabic (string) { /* OUTPUTS */ const convertRomanToArabicOutput = ({ res, next }, number) => { // S'il n'y a pas les champs obligatoire - if (!(number)) { + if (!number) { return errorHandling(next, requiredFields) } @@ -80,26 +80,44 @@ const convertRomanToArabicOutput = ({ res, next }, number) => { return errorHandling(next, generalError) } - return res.status(200).json({ result, resultHTML: `

${number} s'écrit ${result} en chiffres arabes.

` }) + return res + .status(200) + .json({ + result, + resultHTML: `

${number} s'écrit ${result} en chiffres arabes.

` + }) } const convertArabicToRomanOutput = ({ res, next }, number) => { // S'il n'y a pas les champs obligatoire - if (!(number)) { + if (!number) { return errorHandling(next, requiredFields) } // Si ce n'est pas un nombre number = parseInt(number) if (isNaN(number)) { - return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez rentré un nombre valide.', + statusCode: 400 + }) } const result = convertArabicToRoman(number) - return res.status(200).json({ result, resultHTML: `

${formatNumberResult(number)} s'écrit ${result} en chiffres romains.

` }) + return res + .status(200) + .json({ + result, + resultHTML: `

${formatNumberResult( + number + )} s'écrit ${result} en chiffres romains.

` + }) } -const convertRomanArabicObject = { convertRomanToArabicOutput, convertArabicToRomanOutput } +const convertRomanArabicObject = { + convertRomanToArabicOutput, + convertArabicToRomanOutput +} function executeFunction (option, value, { res, next }) { return convertRomanArabicObject[option]({ res, next }, value) } @@ -115,7 +133,10 @@ module.exports = ({ res, next }, argsObject) => { // Si la fonction n'existe pas // eslint-disable-next-line if (!convertRomanArabicObject.hasOwnProperty(functionName)) { - return errorHandling(next, { message: "Cette conversion n'existe pas.", statusCode: 400 }) + return errorHandling(next, { + message: "Cette conversion n'existe pas.", + statusCode: 400 + }) } executeFunction(functionName, value, { res, next }) diff --git a/api/assets/functions/main/convertTemperature.js b/api/assets/functions/main/convertTemperature.js index 614eaee..af1590e 100644 --- a/api/assets/functions/main/convertTemperature.js +++ b/api/assets/functions/main/convertTemperature.js @@ -12,15 +12,17 @@ const formatNumberResult = require('../secondary/formatNumberResult') function convertTemperature (degree, unit) { let temperatureValue = 0 if (unit === '°C') { - temperatureValue = (degree - 32) * 5 / 9 + temperatureValue = ((degree - 32) * 5) / 9 } else if (unit === '°F') { - temperatureValue = ((degree * 9 / 5) + 32) + temperatureValue = (degree * 9) / 5 + 32 } else { return false } return { result: temperatureValue, - resultHTML: `

${formatNumberResult(degree)} ${(unit === '°C') ? '°F' : '°C'} = ${formatNumberResult(temperatureValue)} ${unit}

` + resultHTML: `

${formatNumberResult(degree)} ${ + unit === '°C' ? '°F' : '°C' + } = ${formatNumberResult(temperatureValue)} ${unit}

` } } @@ -36,7 +38,10 @@ module.exports = ({ res, next }, argsObject) => { // Si ce n'est pas un nombre degree = parseFloat(degree) if (isNaN(degree)) { - return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez rentré un nombre valide.', + statusCode: 400 + }) } const result = convertTemperature(degree, unitToConvert) diff --git a/api/assets/functions/main/fibonacci.js b/api/assets/functions/main/fibonacci.js index 60cae59..ecdb6d0 100644 --- a/api/assets/functions/main/fibonacci.js +++ b/api/assets/functions/main/fibonacci.js @@ -20,27 +20,36 @@ module.exports = ({ res, next }, argsObject) => { let { counter } = argsObject // S'il n'y a pas les champs obligatoire - if (!(counter)) { + if (!counter) { return errorHandling(next, requiredFields) } // Si ce n'est pas un nombre counter = parseInt(counter) if (isNaN(counter)) { - return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez rentré un nombre valide.', + statusCode: 400 + }) } // Si le nombre dépasse LIMIT_COUNTER const LIMIT_COUNTER = 51 if (counter >= LIMIT_COUNTER) { - return errorHandling(next, { message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un compteur dépassant ${LIMIT_COUNTER - 1}.`, statusCode: 400 }) + return errorHandling(next, { + message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un compteur dépassant ${LIMIT_COUNTER - + 1}.`, + statusCode: 400 + }) } const result = fibonacci(counter) - const resultFormatted = result.map((number) => formatNumberResult(number)) + const resultFormatted = result.map(number => formatNumberResult(number)) return res.status(200).json({ result, resultFormatted, - resultHTML: `

Les ${counter} premiers nombres de la suite de fibonacci :
${resultFormatted.join(', ')}

` + resultHTML: `

Les ${counter} premiers nombres de la suite de fibonacci :
${resultFormatted.join( + ', ' + )}

` }) } diff --git a/api/assets/functions/main/findLongestWord.js b/api/assets/functions/main/findLongestWord.js index 085b0b9..2a9ecaa 100644 --- a/api/assets/functions/main/findLongestWord.js +++ b/api/assets/functions/main/findLongestWord.js @@ -12,7 +12,7 @@ function findLongestWord (string) { let stringLength = 0 let result = '' - arrayString.forEach((element) => { + arrayString.forEach(element => { if (element.length > stringLength) { result = element stringLength = element.length @@ -27,7 +27,7 @@ module.exports = ({ res, next }, argsObject) => { const { string } = argsObject // S'il n'y a pas les champs obligatoire - if (!(string)) { + if (!string) { return errorHandling(next, requiredFields) } diff --git a/api/assets/functions/main/heapAlgorithm.js b/api/assets/functions/main/heapAlgorithm.js index 7f150cc..f082c66 100644 --- a/api/assets/functions/main/heapAlgorithm.js +++ b/api/assets/functions/main/heapAlgorithm.js @@ -18,9 +18,14 @@ function heapAlgorithm (string) { for (let indexString = 0; indexString < string.length; indexString++) { const firstChar = string[indexString] - const charsLeft = string.substring(0, indexString) + string.substring(indexString + 1) + const charsLeft = + string.substring(0, indexString) + string.substring(indexString + 1) const innerPermutations = heapAlgorithm(charsLeft) - for (let indexPermutation = 0; indexPermutation < innerPermutations.length; indexPermutation++) { + for ( + let indexPermutation = 0; + indexPermutation < innerPermutations.length; + indexPermutation++ + ) { results.push(firstChar + innerPermutations[indexPermutation]) } } @@ -32,19 +37,26 @@ module.exports = ({ res, next }, argsObject) => { const { string } = argsObject // S'il n'y a pas les champs obligatoire - if (!(string)) { + if (!string) { return errorHandling(next, requiredFields) } // Si la chaîne de caractère dépasse LIMIT_CHARACTERS caractères const LIMIT_CHARACTERS = 7 if (string.length > LIMIT_CHARACTERS) { - return errorHandling(next, { message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un mot dépassant ${LIMIT_CHARACTERS} caractères.`, statusCode: 400 }) + return errorHandling(next, { + message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un mot dépassant ${LIMIT_CHARACTERS} caractères.`, + statusCode: 400 + }) } const result = heapAlgorithm(string) - let resultHTML = `

Il y a ${formatNumberResult(result.length)} possibilités d'anagramme pour le mot "${string}" qui contient ${string.length} caractères, la liste :

` - result.forEach((string) => { + let resultHTML = `

Il y a ${formatNumberResult( + result.length + )} possibilités d'anagramme pour le mot "${string}" qui contient ${ + string.length + } caractères, la liste :

` + result.forEach(string => { resultHTML += string + '
' }) resultHTML += '

' diff --git a/api/assets/functions/main/isPalindrome.js b/api/assets/functions/main/isPalindrome.js index 151eee3..e087543 100644 --- a/api/assets/functions/main/isPalindrome.js +++ b/api/assets/functions/main/isPalindrome.js @@ -8,7 +8,10 @@ const { requiredFields } = require('../../config/errors') * @example reverseString('Hello') → 'olleH' */ function reverseString (string) { - return string.split('').reverse().join('') + return string + .split('') + .reverse() + .join('') } /** @@ -28,12 +31,15 @@ module.exports = ({ res, next }, argsObject) => { let { string } = argsObject // S'il n'y a pas les champs obligatoire - if (!(string)) { + if (!string) { return errorHandling(next, requiredFields) } if (typeof string !== 'string') { - return errorHandling(next, { message: 'Vous devez rentré une chaîne de caractère valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Vous devez rentré une chaîne de caractère valide.', + statusCode: 400 + }) } string = string.toLowerCase() @@ -43,6 +49,10 @@ module.exports = ({ res, next }, argsObject) => { return res.status(200).json({ isPalindrome: isPalindromeResult, reverseString: reverseStringResult, - resultHTML: `

"${string}" ${(isPalindromeResult) ? 'est' : "n'est pas"} un palindrome car
"${string}" ${(isPalindromeResult) ? '===' : '!=='} "${reverseStringResult}"

` + resultHTML: `

"${string}" ${ + isPalindromeResult ? 'est' : "n'est pas" + } un palindrome car
"${string}" ${ + isPalindromeResult ? '===' : '!==' + } "${reverseStringResult}"

` }) } diff --git a/api/assets/functions/main/linkShortener.js b/api/assets/functions/main/linkShortener.js index 14a5eba..2a58d18 100644 --- a/api/assets/functions/main/linkShortener.js +++ b/api/assets/functions/main/linkShortener.js @@ -13,12 +13,19 @@ module.exports = async ({ res, next }, argsObject) => { // Si ce n'est pas une url if (!validator.isURL(url)) { - return errorHandling(next, { message: 'Veuillez entré une URL valide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Veuillez entré une URL valide.', + statusCode: 400 + }) } // Si ce n'est pas de type slug if (!validator.isSlug(shortcutName)) { - return errorHandling(next, { message: "Le nom de votre raccourci doit être de type slug (ne pas contenir d'espaces, ni de caractères spéciaux).", statusCode: 400 }) + return errorHandling(next, { + message: + "Le nom de votre raccourci doit être de type slug (ne pas contenir d'espaces, ni de caractères spéciaux).", + statusCode: 400 + }) } // Sanitize shortcutName @@ -31,20 +38,33 @@ module.exports = async ({ res, next }, argsObject) => { const urlInDatabase = await shortLinks.findOne({ where: { url } }) if (urlInDatabase) { const urlShort = `https://short-links.divlo.fr/?q=${urlInDatabase.shortcut}` - return errorHandling(next, { message: `L'url a déjà été raccourcie...

${urlShort}`, statusCode: 400 }) + return errorHandling(next, { + message: `L'url a déjà été raccourcie...

${urlShort}`, + statusCode: 400 + }) } // Si le nom du raccourci existe déjà - const shortcutInDatabase = await shortLinks.findOne({ where: { shortcut: shortcutName } }) + const shortcutInDatabase = await shortLinks.findOne({ + where: { shortcut: shortcutName } + }) if (shortcutInDatabase) { const urlShort = `https://short-links.divlo.fr/?q=${shortcutInDatabase.shortcut}` - return errorHandling(next, { message: `Le nom du raccourci a déjà été utilisé...

${urlShort}`, statusCode: 400 }) + return errorHandling(next, { + message: `Le nom du raccourci a déjà été utilisé...

${urlShort}`, + statusCode: 400 + }) } // Ajout du lien raccourci const result = await shortLinks.create({ url, shortcut: shortcutName }) const shortcutLinkResult = `https://short-links.divlo.fr/?q=${result.shortcut}` - return res.status(200).json({ resultHTML: `URL Raccourcie :

${shortcutLinkResult}`, result: shortcutLinkResult }) + return res + .status(200) + .json({ + resultHTML: `URL Raccourcie :

${shortcutLinkResult}`, + result: shortcutLinkResult + }) } catch (error) { console.log(error) return errorHandling(next, serverError) diff --git a/api/assets/functions/main/randomNumber.js b/api/assets/functions/main/randomNumber.js index d47f1a2..cf7ffa0 100644 --- a/api/assets/functions/main/randomNumber.js +++ b/api/assets/functions/main/randomNumber.js @@ -26,11 +26,21 @@ const randomNumberOutput = ({ res, next }, argsObject) => { min = parseInt(min) max = parseInt(max) if (isNaN(min) || isNaN(max)) { - return errorHandling(next, { message: 'Les paramètres min et max doivent être des nombres...', statusCode: 400 }) + return errorHandling(next, { + message: 'Les paramètres min et max doivent être des nombres...', + statusCode: 400 + }) } const result = randomNumber(min, max) - return res.status(200).json({ result, resultHTML: `

Nombre aléatoire compris entre ${min} inclus et ${max} inclus : ${formatNumberResult(result)}

` }) + return res + .status(200) + .json({ + result, + resultHTML: `

Nombre aléatoire compris entre ${min} inclus et ${max} inclus : ${formatNumberResult( + result + )}

` + }) } exports.randomNumber = randomNumber diff --git a/api/assets/functions/main/randomQuote.js b/api/assets/functions/main/randomQuote.js index f1f724a..c7802a3 100644 --- a/api/assets/functions/main/randomQuote.js +++ b/api/assets/functions/main/randomQuote.js @@ -8,9 +8,7 @@ module.exports = async ({ res, next }, _argsObject) => { try { const quote = await Quotes.findOne({ order: sequelize.random(), - include: [ - { model: Users, attributes: ['name', 'logo'] } - ], + include: [{ model: Users, attributes: ['name', 'logo'] }], attributes: { exclude: ['isValidated'] }, diff --git a/api/assets/functions/main/rightPrice.js b/api/assets/functions/main/rightPrice.js index a3e3b8a..d482fd0 100644 --- a/api/assets/functions/main/rightPrice.js +++ b/api/assets/functions/main/rightPrice.js @@ -23,15 +23,18 @@ function getRandomArrayElement (array) { async function getAmazonProductList (subject) { const url = `https://www.amazon.fr/s?k=${subject}` - const { data } = await axios.get(`http://api.scraperapi.com/?api_key=${SCRAPER_API_KEY}&url=${url}`) - const { document } = (new JSDOM(data)).window + const { data } = await axios.get( + `http://api.scraperapi.com/?api_key=${SCRAPER_API_KEY}&url=${url}` + ) + const { document } = new JSDOM(data).window const amazonProductList = document.querySelectorAll('.s-result-item') const productsList = [] for (const indexProduct in amazonProductList) { try { const elementProduct = amazonProductList[indexProduct] const productImage = elementProduct.querySelector('.s-image') - const originalPrice = elementProduct.querySelector('.a-price-whole').innerHTML + const originalPrice = elementProduct.querySelector('.a-price-whole') + .innerHTML productsList.push({ name: productImage.alt, image: productImage.src, diff --git a/api/assets/functions/main/sortArray.js b/api/assets/functions/main/sortArray.js index f2317c9..acd8a6e 100644 --- a/api/assets/functions/main/sortArray.js +++ b/api/assets/functions/main/sortArray.js @@ -29,28 +29,41 @@ module.exports = ({ res, next }, argsObject) => { const { numbersList } = argsObject // S'il n'y a pas les champs obligatoire - if (!(numbersList)) { + if (!numbersList) { return errorHandling(next, requiredFields) } - const numbersListArray = numbersList.split(',').map((number) => number.trim().replace(' ', '')).map(Number) + const numbersListArray = numbersList + .split(',') + .map(number => number.trim().replace(' ', '')) + .map(Number) // Si ce n'est pas une liste de nombres if (numbersListArray.includes(NaN)) { - return errorHandling(next, { message: 'Vous devez rentrer une liste de nombres séparée par des virgules valide.', statusCode: 400 }) + return errorHandling(next, { + message: + 'Vous devez rentrer une liste de nombres séparée par des virgules valide.', + statusCode: 400 + }) } // Si la taille du tableau dépasse LIMIT_ARRAY_LENGTH const LIMIT_ARRAY_LENGTH = 31 if (numbersListArray.length >= LIMIT_ARRAY_LENGTH) { - return errorHandling(next, { message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec une liste de nombres dépassant ${LIMIT_ARRAY_LENGTH - 1} nombres.`, statusCode: 400 }) + return errorHandling(next, { + message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec une liste de nombres dépassant ${LIMIT_ARRAY_LENGTH - + 1} nombres.`, + statusCode: 400 + }) } const result = sortArray(numbersListArray) - const resultFormatted = result.map((number) => formatNumberResult(number)) + const resultFormatted = result.map(number => formatNumberResult(number)) return res.status(200).json({ result, resultFormatted, - resultHTML: `

La liste de nombres dans l'ordre croissant :
${resultFormatted.join(', ')}

` + resultHTML: `

La liste de nombres dans l'ordre croissant :
${resultFormatted.join( + ', ' + )}

` }) } diff --git a/api/assets/functions/main/weatherRequest.js b/api/assets/functions/main/weatherRequest.js index c334b77..9f3adc9 100644 --- a/api/assets/functions/main/weatherRequest.js +++ b/api/assets/functions/main/weatherRequest.js @@ -26,21 +26,48 @@ module.exports = ({ res, next }, argsObject) => { let { cityName } = argsObject // S'il n'y a pas les champs obligatoire - if (!(cityName)) { + if (!cityName) { return errorHandling(next, requiredFields) } cityName = cityName.split(' ').join('+') // Récupère les données météo grâce à l'API : openweathermap.org. (→ avec limite de 50 requêtes par minute) - queue.request(() => { - axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${cityName}&lang=fr&units=metric&appid=${WEATHER_API_KEY}`) - .then((response) => { - const json = response.data - const showDateTimeValue = dateTimeUTC((json.timezone / 60 / 60).toString()).showDateTimeValue - const resultHTML = `

🌎 Position : ${json.name}, ${json.sys.country}
⏰ Date et heure : ${showDateTimeValue}
☁️ Météo : ${capitalize(json.weather[0].description)}
🌡️ Température : ${json.main.temp} °C
💧 Humidité : ${json.main.humidity}%

` - return res.status(200).json({ result: json, resultHTML }) - }) - .catch(() => errorHandling(next, { message: "La ville n'existe pas (dans l'API de openweathermap.org).", statusCode: 404 })) - }, 'everyone', 'weatherRequest') + queue.request( + () => { + axios + .get( + `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&lang=fr&units=metric&appid=${WEATHER_API_KEY}` + ) + .then(response => { + const json = response.data + const showDateTimeValue = dateTimeUTC( + (json.timezone / 60 / 60).toString() + ).showDateTimeValue + const resultHTML = `

🌎 Position : ${ + json.name + }, ${ + json.sys.country + }
⏰ Date et heure : ${showDateTimeValue}
☁️ Météo : ${capitalize( + json.weather[0].description + )}
🌡️ Température : ${json.main.temp} °C
💧 Humidité : ${ + json.main.humidity + }%

` + return res.status(200).json({ result: json, resultHTML }) + }) + .catch(() => + errorHandling(next, { + message: + "La ville n'existe pas (dans l'API de openweathermap.org).", + statusCode: 404 + }) + ) + }, + 'everyone', + 'weatherRequest' + ) } diff --git a/api/assets/functions/secondary/dateTimeManagement.js b/api/assets/functions/secondary/dateTimeManagement.js index 5ef5627..6d76e6f 100644 --- a/api/assets/functions/secondary/dateTimeManagement.js +++ b/api/assets/functions/secondary/dateTimeManagement.js @@ -27,7 +27,8 @@ function showDateTime (timeNow) { const hour = ('0' + timeNow.getHours()).slice(-2) const minute = ('0' + timeNow.getMinutes()).slice(-2) const second = ('0' + timeNow.getSeconds()).slice(-2) - const showDateTimeValue = day + '/' + month + '/' + year + ' - ' + hour + ':' + minute + ':' + second + const showDateTimeValue = + day + '/' + month + '/' + year + ' - ' + hour + ':' + minute + ':' + second const objectDateTime = { year: year, month: month, diff --git a/api/assets/utils/database.js b/api/assets/utils/database.js index c055dbe..2b08ea9 100644 --- a/api/assets/utils/database.js +++ b/api/assets/utils/database.js @@ -1,9 +1,14 @@ const Sequelize = require('sequelize') const { DATABASE } = require('../config/config') -const sequelize = new Sequelize(DATABASE.name, DATABASE.user, DATABASE.password, { - dialect: 'mysql', - host: DATABASE.host -}) +const sequelize = new Sequelize( + DATABASE.name, + DATABASE.user, + DATABASE.password, + { + dialect: 'mysql', + host: DATABASE.host + } +) module.exports = sequelize diff --git a/api/assets/utils/getPagesHelper.js b/api/assets/utils/getPagesHelper.js index 99d8597..1dee3cf 100644 --- a/api/assets/utils/getPagesHelper.js +++ b/api/assets/utils/getPagesHelper.js @@ -12,7 +12,11 @@ const DEFAULT_OPTIONS = { * @param {*} Model Model Sequelize * @param {Object} options Options avec clause where etc. */ -async function getPagesHelper ({ req, res, next }, Model, options = DEFAULT_OPTIONS) { +async function getPagesHelper ( + { req, res, next }, + Model, + options = DEFAULT_OPTIONS +) { const page = helperQueryNumber(req.query.page, 1) const limit = helperQueryNumber(req.query.limit, 10) const offset = (page - 1) * limit @@ -23,7 +27,7 @@ async function getPagesHelper ({ req, res, next }, Model, options = DEFAULT_OPTI ...options }) const { count, rows } = result - const hasMore = (page * limit) < count + const hasMore = page * limit < count return res.status(200).json({ totalItems: count, hasMore, rows }) } catch (error) { console.log(error) diff --git a/api/controllers/admin.js b/api/controllers/admin.js index 2484dab..58b1907 100644 --- a/api/controllers/admin.js +++ b/api/controllers/admin.js @@ -15,7 +15,12 @@ const { EMAIL_INFO, FRONT_END_HOST } = require('../assets/config/config') const transporter = require('../assets/config/transporter') const { emailQuoteTemplate } = require('../assets/config/emails') -const handleEditFunction = async (res, resultFunction, { title, slug, description, type, categorieId, isOnline }, imageName = false) => { +const handleEditFunction = async ( + res, + resultFunction, + { title, slug, description, type, categorieId, isOnline }, + imageName = false +) => { resultFunction.title = title resultFunction.slug = slug resultFunction.description = description @@ -32,23 +37,41 @@ const handleEditFunction = async (res, resultFunction, { title, slug, descriptio exports.getFunctions = async (req, res, next) => { const categoryId = helperQueryNumber(req.query.categoryId, 0) let search = req.query.search - try { search = search.toLowerCase() } catch {}; + try { + search = search.toLowerCase() + } catch {} const options = { where: { // Trie par catégorie - ...(categoryId !== 0) && { categorieId: categoryId }, + ...(categoryId !== 0 && { categorieId: categoryId }), // Recherche - ...(search != null) && { + ...(search != null && { [Sequelize.Op.or]: [ - { title: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('title')), 'LIKE', `%${search}%`) }, - { slug: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('slug')), 'LIKE', `%${search}%`) }, - { description: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('description')), 'LIKE', `%${search}%`) } + { + title: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('title')), + 'LIKE', + `%${search}%` + ) + }, + { + slug: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('slug')), + 'LIKE', + `%${search}%` + ) + }, + { + description: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('description')), + 'LIKE', + `%${search}%` + ) + } ] - } + }) }, - include: [ - { model: Categories, attributes: ['name', 'color'] } - ], + include: [{ model: Categories, attributes: ['name', 'color'] }], attributes: { exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] }, @@ -61,18 +84,21 @@ exports.getFunctionBySlug = (req, res, next) => { const { slug } = req.params Functions.findOne({ where: { slug }, - include: [ - { model: Categories, attributes: ['name', 'color'] } - ] + include: [{ model: Categories, attributes: ['name', 'color'] }] }) - .then((result) => { + .then(result => { if (!result) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } - try { result.utilizationForm = JSON.parse(result.utilizationForm) } catch {} + try { + result.utilizationForm = JSON.parse(result.utilizationForm) + } catch {} return res.status(200).json(result) }) - .catch((error) => { + .catch(error => { console.log(error) return errorHandling(next, serverError) }) @@ -83,28 +109,49 @@ exports.postFunction = (req, res, next) => { const image = req.files.image const errors = validationResult(req) if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + return errorHandling(next, { + message: errors.array()[0].msg, + statusCode: 400 + }) } - if ((!image || image.truncated) && ( - image.mimetype !== 'image/png' || - image.mimetype !== 'image/jpg' || - image.mimetype !== 'image/jpeg' - )) { - return errorHandling(next, { message: 'La fonction doit avoir une image valide.', statusCode: 400 }) + if ( + (!image || image.truncated) && + (image.mimetype !== 'image/png' || + image.mimetype !== 'image/jpg' || + image.mimetype !== 'image/jpeg') + ) { + return errorHandling(next, { + message: 'La fonction doit avoir une image valide.', + statusCode: 400 + }) } const splitedImageName = image.name.split('.') if (splitedImageName.length !== 2) return errorHandling(next, serverError) const imageName = slug + '.' + splitedImageName[1] - image.mv(path.join(__dirname, '..', 'assets', 'images', 'functions') + '/' + imageName, async (error) => { - if (error) return errorHandling(next, serverError) - try { - const result = await Functions.create({ title, slug, description, type, categorieId, image: `/images/functions/${imageName}` }) - return res.status(201).json({ message: 'La fonction a été correctement ajouté!', result }) - } catch (error) { - console.log(error) - return errorHandling(next, serverError) + image.mv( + path.join(__dirname, '..', 'assets', 'images', 'functions') + + '/' + + imageName, + async error => { + if (error) return errorHandling(next, serverError) + try { + const result = await Functions.create({ + title, + slug, + description, + type, + categorieId, + image: `/images/functions/${imageName}` + }) + return res + .status(201) + .json({ message: 'La fonction a été correctement ajouté!', result }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } - }) + ) } exports.putFunction = async (req, res, next) => { @@ -113,43 +160,74 @@ exports.putFunction = async (req, res, next) => { const image = req.files.image const errors = validationResult(req) if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + return errorHandling(next, { + message: errors.array()[0].msg, + statusCode: 400 + }) } try { // Vérifie si la fonction existe const resultFunction = await Functions.findOne({ where: { id } }) if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } // Vérifie si le slug existe déjà const FunctionSlug = await Functions.findOne({ where: { slug } }) if (!FunctionSlug && FunctionSlug.id !== resultFunction.id) { - return errorHandling(next, { message: 'Le slug existe déjà...', statusCode: 404 }) + return errorHandling(next, { + message: 'Le slug existe déjà...', + statusCode: 404 + }) } // Sauvegarde de la fonction if (image != null) { - if (image.truncated && ( - image.mimetype !== 'image/png' || - image.mimetype !== 'image/jpg' || - image.mimetype !== 'image/jpeg' - )) { - return errorHandling(next, { message: 'La fonction doit avoir une image valide.', statusCode: 400 }) + if ( + image.truncated && + (image.mimetype !== 'image/png' || + image.mimetype !== 'image/jpg' || + image.mimetype !== 'image/jpeg') + ) { + return errorHandling(next, { + message: 'La fonction doit avoir une image valide.', + statusCode: 400 + }) } const splitedImageName = image.name.split('.') if (splitedImageName.length !== 2) return errorHandling(next, serverError) const imageName = slug + '.' + splitedImageName[1] // Supprime les anciennes images - const functionPath = path.join(__dirname, '..', 'assets', 'images', 'functions') + const functionPath = path.join( + __dirname, + '..', + 'assets', + 'images', + 'functions' + ) deleteFilesNameStartWith(slug, functionPath, () => { - image.mv(path.join(functionPath, imageName), async (error) => { + image.mv(path.join(functionPath, imageName), async error => { if (error) return errorHandling(next, serverError) - return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline }, imageName) + return await handleEditFunction( + res, + resultFunction, + { title, slug, description, type, categorieId, isOnline }, + imageName + ) }) }) } else { - return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline }) + return await handleEditFunction(res, resultFunction, { + title, + slug, + description, + type, + categorieId, + isOnline + }) } } catch (error) { console.log(error) @@ -165,7 +243,10 @@ exports.putFunctionArticle = async (req, res, next) => { // Vérifie si la fonction existe const resultFunction = await Functions.findOne({ where: { id } }) if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } resultFunction.article = article const result = await resultFunction.save() @@ -184,7 +265,10 @@ exports.putFunctionForm = async (req, res, next) => { // Vérifie si la fonction existe const resultFunction = await Functions.findOne({ where: { id } }) if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } resultFunction.utilizationForm = JSON.stringify(form) const result = await resultFunction.save() @@ -200,14 +284,19 @@ exports.deleteFunction = async (req, res, next) => { try { const result = await Functions.findOne({ where: { id } }) if (!result) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } if (result.image !== '/images/functions/default.png') { const filePath = path.join(__dirname, '..', 'assets', result.image) fs.unlinkSync(filePath) // supprime le fichier } await result.destroy() - res.status(200).json({ message: 'La fonction a été correctement supprimé!' }) + res + .status(200) + .json({ message: 'La fonction a été correctement supprimé!' }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -217,11 +306,15 @@ exports.deleteFunction = async (req, res, next) => { exports.postCategory = async (req, res, next) => { const { name, color } = req.body if (!(name && color)) { - return errorHandling(next, { message: 'La catégorie doit avoir un nom et une couleur.' }) + return errorHandling(next, { + message: 'La catégorie doit avoir un nom et une couleur.' + }) } try { const result = await Categories.create({ name, color }) - return res.status(201).json({ message: 'La catégorie a bien été crée!', result }) + return res + .status(201) + .json({ message: 'La catégorie a bien été crée!', result }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -232,7 +325,9 @@ exports.putCategory = async (req, res, next) => { const { name, color } = req.body const { id } = req.params if (!(name && color && id)) { - return errorHandling(next, { message: 'La catégorie doit avoir un nom, une couleur et un id.' }) + return errorHandling(next, { + message: 'La catégorie doit avoir un nom, une couleur et un id.' + }) } try { const category = await Categories.findOne({ where: { id } }) @@ -242,7 +337,9 @@ exports.putCategory = async (req, res, next) => { category.name = name category.color = color const result = await category.save() - return res.status(200).json({ message: 'La catégorie a bien été modifiée!', result }) + return res + .status(200) + .json({ message: 'La catégorie a bien été modifiée!', result }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -257,7 +354,9 @@ exports.deleteCategory = async (req, res, next) => { return errorHandling(next, { message: "La catégorie n'existe pas." }) } await category.destroy() - return res.status(200).json({ message: 'La catégorie a bien été supprimée!' }) + return res + .status(200) + .json({ message: 'La catégorie a bien été supprimée!' }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -269,9 +368,7 @@ exports.getQuotes = async (req, res, next) => { where: { isValidated: 0 }, - include: [ - { model: Users, attributes: ['name', 'logo'] } - ], + include: [{ model: Users, attributes: ['name', 'logo'] }], order: [['createdAt', 'DESC']] } return await getPagesHelper({ req, res, next }, Quotes, options) @@ -282,19 +379,23 @@ exports.putQuote = async (req, res, next) => { const { isValid } = req.body try { if (typeof isValid !== 'boolean') { - return errorHandling(next, { message: 'isValid doit être un booléen.', statusCode: 400 }) + return errorHandling(next, { + message: 'isValid doit être un booléen.', + statusCode: 400 + }) } const quote = await Quotes.findOne({ where: { id, isValidated: 0 }, - include: [ - { model: Users, attributes: ['name', 'email'] } - ] + include: [{ model: Users, attributes: ['name', 'email'] }] }) if (!quote) { - return errorHandling(next, { message: "La citation n'existe pas (ou est déjà validé).", statusCode: 404 }) + return errorHandling(next, { + message: "La citation n'existe pas (ou est déjà validé).", + statusCode: 404 + }) } await transporter.sendMail({ @@ -307,10 +408,14 @@ exports.putQuote = async (req, res, next) => { if (isValid) { quote.isValidated = true await quote.save() - return res.status(200).json({ message: 'La citation a bien été validée!' }) + return res + .status(200) + .json({ message: 'La citation a bien été validée!' }) } else { await quote.destroy() - return res.status(200).json({ imessage: 'La citation a bien été supprimée!' }) + return res + .status(200) + .json({ imessage: 'La citation a bien été supprimée!' }) } } catch (error) { console.log(error) diff --git a/api/controllers/categories.js b/api/controllers/categories.js index 7795e04..6702551 100644 --- a/api/controllers/categories.js +++ b/api/controllers/categories.js @@ -4,10 +4,10 @@ const { serverError } = require('../assets/config/errors') exports.getCategories = (_req, res, next) => { Categories.findAll() - .then((result) => { + .then(result => { res.status(200).json(result) }) - .catch((error) => { + .catch(error => { console.log(error) return errorHandling(next, serverError) }) diff --git a/api/controllers/comments.js b/api/controllers/comments.js index 767a118..f025e08 100644 --- a/api/controllers/comments.js +++ b/api/controllers/comments.js @@ -9,9 +9,7 @@ exports.getCommentsByFunctionId = async (req, res, next) => { const { functionId } = req.params const options = { where: { functionId }, - include: [ - { model: Users, attributes: ['name', 'logo'] } - ], + include: [{ model: Users, attributes: ['name', 'logo'] }], order: [['createdAt', 'DESC']] } return await getPagesHelper({ req, res, next }, Comments, options) @@ -21,14 +19,26 @@ exports.postCommentsByFunctionId = async (req, res, next) => { const { functionId } = req.params const { message } = req.body try { - const resultFunction = await Functions.findOne({ where: { id: functionId } }) + const resultFunction = await Functions.findOne({ + where: { id: functionId } + }) if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } if (!message) { - return errorHandling(next, { message: 'Vous ne pouvez pas poster de commentaire vide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Vous ne pouvez pas poster de commentaire vide.', + statusCode: 400 + }) } - const comment = await Comments.create({ message, userId: req.userId, functionId }) + const comment = await Comments.create({ + message, + userId: req.userId, + functionId + }) return res.status(201).json(comment) } catch (error) { console.log(error) @@ -39,12 +49,19 @@ exports.postCommentsByFunctionId = async (req, res, next) => { exports.deleteCommentById = async (req, res, next) => { const { commentId } = req.params try { - const comment = await Comments.findOne({ where: { userId: req.userId, id: parseInt(commentId) } }) + const comment = await Comments.findOne({ + where: { userId: req.userId, id: parseInt(commentId) } + }) if (!comment) { - return errorHandling(next, { message: "Le commentaire n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "Le commentaire n'existe pas.", + statusCode: 404 + }) } await comment.destroy() - return res.status(200).json({ message: 'Le commentaire a bien été supprimé.' }) + return res + .status(200) + .json({ message: 'Le commentaire a bien été supprimé.' }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -55,16 +72,26 @@ exports.putCommentsById = async (req, res, next) => { const { commentId } = req.params const { message } = req.body if (!message) { - return errorHandling(next, { message: 'Vous ne pouvez pas poster de commentaire vide.', statusCode: 400 }) + return errorHandling(next, { + message: 'Vous ne pouvez pas poster de commentaire vide.', + statusCode: 400 + }) } try { - const comment = await Comments.findOne({ where: { userId: req.userId, id: parseInt(commentId) } }) + const comment = await Comments.findOne({ + where: { userId: req.userId, id: parseInt(commentId) } + }) if (!comment) { - return errorHandling(next, { message: "Le commentaire n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "Le commentaire n'existe pas.", + statusCode: 404 + }) } comment.message = message await comment.save() - return res.status(200).json({ message: 'Le commentaire a bien été modifié.' }) + return res + .status(200) + .json({ message: 'Le commentaire a bien été modifié.' }) } catch (error) { console.log(error) return errorHandling(next, serverError) diff --git a/api/controllers/favorites.js b/api/controllers/favorites.js index 0e1e652..9affaf6 100644 --- a/api/controllers/favorites.js +++ b/api/controllers/favorites.js @@ -27,9 +27,14 @@ exports.postFavoriteByFunctionId = async (req, res, next) => { const { functionId } = req.params const { userId } = req try { - const resultFunction = await Functions.findOne({ where: { id: functionId } }) + const resultFunction = await Functions.findOne({ + where: { id: functionId } + }) if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } const favorite = await Favorites.findOne({ where: { @@ -41,7 +46,10 @@ exports.postFavoriteByFunctionId = async (req, res, next) => { await Favorites.create({ userId, functionId }) return res.status(201).json({ result: 'Le favoris a bien été ajouté!' }) } - return errorHandling(next, { message: 'La fonction est déjà en favoris.', statusCode: 400 }) + return errorHandling(next, { + message: 'La fonction est déjà en favoris.', + statusCode: 400 + }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -52,9 +60,14 @@ exports.deleteFavoriteByFunctionId = async (req, res, next) => { const { functionId } = req.params const { userId } = req try { - const resultFunction = await Functions.findOne({ where: { id: functionId } }) + const resultFunction = await Functions.findOne({ + where: { id: functionId } + }) if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } const favorite = await Favorites.findOne({ where: { @@ -63,10 +76,15 @@ exports.deleteFavoriteByFunctionId = async (req, res, next) => { } }) if (!favorite) { - return errorHandling(next, { message: "Le fonction n'est pas en favoris.", statusCode: 400 }) + return errorHandling(next, { + message: "Le fonction n'est pas en favoris.", + statusCode: 400 + }) } await favorite.destroy() - return res.status(200).json({ message: 'Le fonction a bien été supprimé des favoris.' }) + return res + .status(200) + .json({ message: 'Le fonction a bien été supprimé des favoris.' }) } catch (error) { console.log(error) return errorHandling(next, serverError) diff --git a/api/controllers/functions.js b/api/controllers/functions.js index 7620b17..9c88d52 100644 --- a/api/controllers/functions.js +++ b/api/controllers/functions.js @@ -10,24 +10,42 @@ const Sequelize = require('sequelize') exports.getFunctions = async (req, res, next) => { const categoryId = helperQueryNumber(req.query.categoryId, 0) let { search } = req.query - try { search = search.toLowerCase() } catch {}; + try { + search = search.toLowerCase() + } catch {} const options = { where: { isOnline: 1, // Trie par catégorie - ...(categoryId !== 0) && { categorieId: categoryId }, + ...(categoryId !== 0 && { categorieId: categoryId }), // Recherche - ...(search != null) && { + ...(search != null && { [Sequelize.Op.or]: [ - { title: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('title')), 'LIKE', `%${search}%`) }, - { slug: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('slug')), 'LIKE', `%${search}%`) }, - { description: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('description')), 'LIKE', `%${search}%`) } + { + title: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('title')), + 'LIKE', + `%${search}%` + ) + }, + { + slug: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('slug')), + 'LIKE', + `%${search}%` + ) + }, + { + description: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('description')), + 'LIKE', + `%${search}%` + ) + } ] - } + }) }, - include: [ - { model: Categories, attributes: ['name', 'color'] } - ], + include: [{ model: Categories, attributes: ['name', 'color'] }], attributes: { exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] }, @@ -43,18 +61,21 @@ exports.getFunctionBySlug = (req, res, next) => { attributes: { exclude: ['updatedAt', 'isOnline'] }, - include: [ - { model: Categories, attributes: ['name', 'color'] } - ] + include: [{ model: Categories, attributes: ['name', 'color'] }] }) - .then((result) => { + .then(result => { if (!result) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } - try { result.utilizationForm = JSON.parse(result.utilizationForm) } catch {} + try { + result.utilizationForm = JSON.parse(result.utilizationForm) + } catch {} return res.status(200).json(result) }) - .catch((error) => { + .catch(error => { console.log(error) return errorHandling(next, serverError) }) @@ -65,5 +86,8 @@ exports.executeFunctionBySlug = (req, res, next) => { if (functionOutput !== undefined) { return functionOutput({ res, next }, req.body) } - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + return errorHandling(next, { + message: "La fonction n'existe pas.", + statusCode: 404 + }) } diff --git a/api/controllers/quotes.js b/api/controllers/quotes.js index c2f93cb..441258a 100644 --- a/api/controllers/quotes.js +++ b/api/controllers/quotes.js @@ -9,9 +9,7 @@ exports.getQuotes = async (req, res, next) => { where: { isValidated: 1 }, - include: [ - { model: Users, attributes: ['name', 'logo'] } - ], + include: [{ model: Users, attributes: ['name', 'logo'] }], attributes: { exclude: ['isValidated'] }, @@ -27,10 +25,15 @@ exports.postQuote = (req, res, next) => { return errorHandling(next, requiredFields) } Quotes.create({ quote, author, userId: req.userId }) - .then((_result) => { - return res.status(200).json({ message: "La citation a bien été ajoutée, elle est en attente de confirmation d'un administrateur." }) + .then(_result => { + return res + .status(200) + .json({ + message: + "La citation a bien été ajoutée, elle est en attente de confirmation d'un administrateur." + }) }) - .catch((error) => { + .catch(error => { console.log(error) return errorHandling(next, serverError) }) diff --git a/api/controllers/tasks.js b/api/controllers/tasks.js index 2a8dc17..0953cfb 100644 --- a/api/controllers/tasks.js +++ b/api/controllers/tasks.js @@ -36,12 +36,20 @@ exports.putTask = async (req, res, next) => { const { isCompleted } = req.body try { if (typeof isCompleted !== 'boolean') { - return errorHandling(next, { message: 'isCompleted doit être un booléen.', statusCode: 400 }) + return errorHandling(next, { + message: 'isCompleted doit être un booléen.', + statusCode: 400 + }) } - const taskResult = await Tasks.findOne({ where: { id, userId: req.userId } }) + const taskResult = await Tasks.findOne({ + where: { id, userId: req.userId } + }) if (!taskResult) { - return errorHandling(next, { message: 'La "tâche à faire" n\'existe pas.', statusCode: 404 }) + return errorHandling(next, { + message: 'La "tâche à faire" n\'existe pas.', + statusCode: 404 + }) } taskResult.isCompleted = isCompleted const taskSaved = await taskResult.save() @@ -55,12 +63,19 @@ exports.putTask = async (req, res, next) => { exports.deleteTask = async (req, res, next) => { const { id } = req.params try { - const taskResult = await Tasks.findOne({ where: { id, userId: req.userId } }) + const taskResult = await Tasks.findOne({ + where: { id, userId: req.userId } + }) if (!taskResult) { - return errorHandling(next, { message: 'La "tâche à faire" n\'existe pas.', statusCode: 404 }) + return errorHandling(next, { + message: 'La "tâche à faire" n\'existe pas.', + statusCode: 404 + }) } await taskResult.destroy() - return res.status(200).json({ message: 'La "tâche à faire" a bien été supprimée!' }) + return res + .status(200) + .json({ message: 'La "tâche à faire" a bien été supprimée!' }) } catch (error) { console.log(error) return errorHandling(next, serverError) diff --git a/api/controllers/users.js b/api/controllers/users.js index 35f7a99..df10d01 100644 --- a/api/controllers/users.js +++ b/api/controllers/users.js @@ -7,7 +7,13 @@ const uuid = require('uuid') const Sequelize = require('sequelize') 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 { + JWT_SECRET, + FRONT_END_HOST, + EMAIL_INFO, + HOST, + TOKEN_LIFE +} = require('../assets/config/config') const transporter = require('../assets/config/transporter') const { emailUserTemplate } = require('../assets/config/emails') const Users = require('../models/users') @@ -19,7 +25,12 @@ const Quotes = require('../models/quotes') const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith') const getPagesHelper = require('../assets/utils/getPagesHelper') -async function handleEditUser (res, { name, email, biography, isPublicEmail }, userId, logoName) { +async function handleEditUser ( + res, + { name, email, biography, isPublicEmail }, + userId, + logoName +) { const user = await Users.findOne({ where: { id: userId } }) user.name = name if (user.email !== email) { @@ -31,7 +42,12 @@ async function handleEditUser (res, { name, email, biography, isPublicEmail }, u from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, to: email, subject: "FunctionProject - Confirmer l'email", - 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.') + 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.' + ) }) } if (biography != null) { @@ -42,22 +58,48 @@ async function handleEditUser (res, { name, email, biography, isPublicEmail }, u user.logo = `/images/users/${logoName}` } await user.save() - 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 }) + 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 + }) } exports.getUsers = async (req, res, next) => { let { search } = req.query - try { search = search.toLowerCase() } catch {}; + try { + search = search.toLowerCase() + } catch {} const options = { where: { isConfirmed: true, // Recherche - ...(search != null) && { - name: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', `%${search}%`) - } + ...(search != null && { + name: Sequelize.where( + Sequelize.fn('LOWER', Sequelize.col('name')), + 'LIKE', + `%${search}%` + ) + }) }, attributes: { - exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken', 'isPublicEmail', 'email'] + exclude: [ + 'updatedAt', + 'isAdmin', + 'isConfirmed', + 'password', + 'tempToken', + 'tempExpirationToken', + 'isPublicEmail', + 'email' + ] }, order: [['createdAt', 'DESC']] } @@ -69,35 +111,60 @@ exports.putUser = async (req, res, next) => { const logo = req.files.logo const errors = validationResult(req) if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + return errorHandling(next, { + message: errors.array()[0].msg, + statusCode: 400 + }) } if (logo != null) { - if ((!logo || logo.truncated) && ( - logo.mimetype !== 'image/png' || - logo.mimetype !== 'image/jpg' || - logo.mimetype !== 'image/jpeg' || - logo.mimetype !== 'image/gif' - )) { - return errorHandling(next, { message: 'Le profil doit avoir une image valide (PNG, JPG, GIF) et moins de 5mo.', statusCode: 400 }) + if ( + (!logo || logo.truncated) && + (logo.mimetype !== 'image/png' || + logo.mimetype !== 'image/jpg' || + logo.mimetype !== 'image/jpeg' || + logo.mimetype !== 'image/gif') + ) { + return errorHandling(next, { + message: + 'Le profil doit avoir une image valide (PNG, JPG, GIF) et moins de 5mo.', + statusCode: 400 + }) } const splitedLogoName = logo.name.split('.') if (splitedLogoName.length !== 2) return errorHandling(next, serverError) const logoName = name + req.userId + '.' + splitedLogoName[1] // Supprime les anciens logo try { - 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) - }) - }) + 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 + ) + } + ) + } + ) } catch (error) { console.log(error) return errorHandling(next, serverError) } } else { try { - return await handleEditUser(res, { name, email, biography, isPublicEmail }, req.userId, null) + return await handleEditUser( + res, + { name, email, biography, isPublicEmail }, + req.userId, + null + ) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -109,7 +176,10 @@ exports.register = async (req, res, next) => { const { name, email, password } = req.body const errors = validationResult(req) if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + return errorHandling(next, { + message: errors.array()[0].msg, + statusCode: 400 + }) } try { const hashedPassword = await bcrypt.hash(password, 12) @@ -119,9 +189,19 @@ exports.register = async (req, res, next) => { from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, to: email, subject: "FunctionProject - Confirmer l'inscription", - 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.') + 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.' + ) }) - return res.status(201).json({ result: "Vous y êtes presque, veuillez vérifier vos emails pour confirmer l'inscription." }) + return res + .status(201) + .json({ + result: + "Vous y êtes presque, veuillez vérifier vos emails pour confirmer l'inscription." + }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -132,24 +212,55 @@ exports.login = async (req, res, next) => { const { email, password } = req.body const errors = validationResult(req) if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + return errorHandling(next, { + message: errors.array()[0].msg, + statusCode: 400 + }) } try { const user = await Users.findOne({ where: { email } }) if (!user) { - return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 400 }) + 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 }) + return errorHandling(next, { + message: "Le mot de passe ou l'adresse email n'est pas valide.", + statusCode: 400 + }) } if (!user.isConfirmed) { - return errorHandling(next, { message: 'Vous devez valider votre adresse email pour votre première connexion.', statusCode: 400 }) + return errorHandling(next, { + message: + 'Vous devez valider votre adresse email pour votre première connexion.', + statusCode: 400 + }) } - const token = jwt.sign({ - email: user.email, userId: user.id - }, 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) }) + const token = jwt.sign( + { + email: user.email, + userId: user.id + }, + 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) + }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -162,9 +273,14 @@ exports.confirmEmail = async (req, res, next) => { return errorHandling(next, generalError) } try { - const user = await Users.findOne({ where: { tempToken, isConfirmed: false } }) + const user = await Users.findOne({ + where: { tempToken, isConfirmed: false } + }) if (!user) { - return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 }) + return errorHandling(next, { + message: "Le token n'est pas valide.", + statusCode: 400 + }) } user.tempToken = null user.isConfirmed = true @@ -180,12 +296,19 @@ 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 }) + 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 }) + 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 @@ -195,9 +318,19 @@ exports.resetPassword = async (req, res, next) => { from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, to: email, subject: 'FunctionProject - Réinitialisation du mot de passe', - html: emailUserTemplate('Veuillez confirmer la réinitialisation du mot de passe', 'Oui, je change mon mot de passe.', `${FRONT_END_HOST}/users/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.') + html: emailUserTemplate( + 'Veuillez confirmer la réinitialisation du mot de passe', + 'Oui, je change mon mot de passe.', + `${FRONT_END_HOST}/users/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.' + ) }) - return res.status(200).json({ result: 'Demande de réinitialisation du mot de passe réussi, veuillez vérifier vos emails!' }) + 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) return errorHandling(next, serverError) @@ -208,19 +341,27 @@ 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 }) + return errorHandling(next, { + message: errors.array()[0].msg, + statusCode: 400 + }) } try { const user = await Users.findOne({ where: { tempToken } }) if (!user && parseInt(user.tempExpirationToken) < Date.now()) { - return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 }) + 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é!' }) + return res + .status(200) + .json({ result: 'Le mot de passe a bien été modifié!' }) } catch (error) { console.log(error) return errorHandling(next, serverError) @@ -233,30 +374,51 @@ exports.getUserInfo = async (req, res, next) => { const user = await Users.findOne({ where: { name, isConfirmed: true }, attributes: { - exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken'] + exclude: [ + 'updatedAt', + 'isAdmin', + 'isConfirmed', + 'password', + 'tempToken', + 'tempExpirationToken' + ] } }) if (!user) { - return errorHandling(next, { message: "L'utilisateur n'existe pas.", statusCode: 404 }) + 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'] } } + { + model: Functions, + attributes: { + exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] + }, + include: { model: Categories, attributes: ['name', 'color'] } + } ], order: [['createdAt', 'DESC']], limit: 5 }) - const favoritesArray = favorites.map((favorite) => favorite.function) + 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'] } } + { + model: Functions, + attributes: { + exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] + } + } ], order: [['createdAt', 'DESC']], limit: 5 }) - const commentsArray = comments.map((commentObject) => { + const commentsArray = comments.map(commentObject => { return { id: commentObject.id, message: commentObject.message, @@ -274,7 +436,7 @@ exports.getUserInfo = async (req, res, next) => { }) const userObject = { // Si Public Email - ...(user.isPublicEmail) && { email: user.email }, + ...(user.isPublicEmail && { email: user.email }), isPublicEmail: user.isPublicEmail, name: user.name, biography: user.biography, diff --git a/api/middlewares/isAdmin.js b/api/middlewares/isAdmin.js index c2f6993..9494142 100644 --- a/api/middlewares/isAdmin.js +++ b/api/middlewares/isAdmin.js @@ -4,19 +4,28 @@ const Users = require('../models/users') module.exports = (req, _res, next) => { if (!req.userId) { - return errorHandling(next, { message: "Vous n'êtes pas connecté.", statusCode: 403 }) + return errorHandling(next, { + message: "Vous n'êtes pas connecté.", + statusCode: 403 + }) } Users.findOne({ where: { id: req.userId } }) - .then((user) => { + .then(user => { if (!user) { - return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 403 }) + return errorHandling(next, { + message: "Le mot de passe ou l'adresse email n'est pas valide.", + statusCode: 403 + }) } if (!user.isAdmin) { - return errorHandling(next, { message: "Vous n'êtes pas administrateur.", statusCode: 403 }) + return errorHandling(next, { + message: "Vous n'êtes pas administrateur.", + statusCode: 403 + }) } next() }) - .catch((error) => { + .catch(error => { console.log(error) return errorHandling(next, serverError) }) diff --git a/api/middlewares/isAuth.js b/api/middlewares/isAuth.js index a3afeb7..a06638c 100644 --- a/api/middlewares/isAuth.js +++ b/api/middlewares/isAuth.js @@ -5,18 +5,27 @@ const { JWT_SECRET } = require('../assets/config/config') module.exports = (req, _res, next) => { const token = req.get('Authorization') if (!token) { - return errorHandling(next, { message: 'Vous devez être connecter pour effectuer cette opération.', statusCode: 403 }) + return errorHandling(next, { + message: 'Vous devez être connecter pour effectuer cette opération.', + statusCode: 403 + }) } let decodedToken try { decodedToken = jwt.verify(token, JWT_SECRET) } catch (error) { - return errorHandling(next, { message: 'Vous devez être connecter pour effectuer cette opération.', statusCode: 403 }) + return errorHandling(next, { + message: 'Vous devez être connecter pour effectuer cette opération.', + statusCode: 403 + }) } if (!decodedToken) { - return errorHandling(next, { message: 'Vous devez être connecter pour effectuer cette opération.', statusCode: 403 }) + return errorHandling(next, { + message: 'Vous devez être connecter pour effectuer cette opération.', + statusCode: 403 + }) } req.userId = decodedToken.userId diff --git a/api/package-lock.json b/api/package-lock.json index 2e7beff..9ef90a6 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,6 +1,6 @@ { "name": "api", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -89,9 +89,9 @@ "dev": true }, "@types/node": { - "version": "13.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz", - "integrity": "sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg==" + "version": "14.0.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", + "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==" }, "abab": { "version": "2.0.3", @@ -114,9 +114,9 @@ } }, "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" }, "acorn-globals": { "version": "6.0.0", @@ -134,14 +134,14 @@ "dev": true }, "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -279,9 +279,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" }, "axios": { "version": "0.19.2", @@ -301,7 +301,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, "requires": { "safe-buffer": "5.1.2" } @@ -345,6 +344,21 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "bowser": { @@ -739,9 +753,9 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" }, @@ -849,11 +863,6 @@ "streamsearch": "0.1.2" } }, - "dns-prefetch-control": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz", - "integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==" - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -895,8 +904,7 @@ "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", - "dev": true + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, "dottie": { "version": "2.0.2", @@ -1008,9 +1016,9 @@ "dev": true }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "requires": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -1142,6 +1150,23 @@ "requires": { "debug": "^2.6.9", "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "eslint-module-utils": { @@ -1152,6 +1177,23 @@ "requires": { "debug": "^2.6.9", "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "eslint-plugin-es": { @@ -1191,6 +1233,15 @@ "resolve": "^1.11.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "doctrine": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", @@ -1200,6 +1251,12 @@ "esutils": "^2.0.2", "isarray": "^1.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -1353,11 +1410,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "expect-ct": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", - "integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==" - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -1393,12 +1445,27 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "express-fileupload": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.6.tgz", - "integrity": "sha512-w24zPWT8DkoIxSVkbxYPo9hkTiLpCQQzNsLRTCnecBhfbYv+IkIC5uLw2MIUAxBZ+7UMmXPjGxlhzUXo4RcbZw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.9.tgz", + "integrity": "sha512-f2w0aoe7lj3NeD8a4MXmYQsqir3Z66I08l9AKq04QbFUAjeZNmPwTlR5Lx2NGwSu/PslsAjGC38MWzo5tTjoBg==", "requires": { "busboy": "^0.3.1" } @@ -1412,19 +1479,12 @@ } }, "express-validator": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz", - "integrity": "sha512-Fs+x0yDOSiUV+o5jIRloMyBxqpSzJiMM8KQW1IRVv2l49F6ATU0F9uPa+3K6vXNlLlhUjauv2FCGLFPMaNr24w==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.6.0.tgz", + "integrity": "sha512-xcephfzFbUssJph/nOSKIdx+I+8GRz5by/8rOIKL6gJikKKKJjnwYH5TG1nIDB6kEalUtZMbOFuSNOp/HHY84Q==", "requires": { "lodash": "^4.17.15", - "validator": "^12.1.0" - }, - "dependencies": { - "validator": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz", - "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ==" - } + "validator": "^13.1.1" } }, "extend": { @@ -1449,9 +1509,9 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", @@ -1507,6 +1567,21 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "find-root": { @@ -1547,23 +1622,6 @@ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - } } }, "forever-agent": { @@ -1586,11 +1644,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, - "frameguard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz", - "integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g==" - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -1724,11 +1777,11 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -1760,22 +1813,18 @@ "dev": true }, "helmet": { - "version": "3.21.3", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.3.tgz", - "integrity": "sha512-8OjGNdpG3WQhPO71fSy2fT4X3FSNutU1LDeAf+YS+Vil6r+fE7w8per5mNed6egGYbZl3QhKXgFzMYSwys+YQw==", + "version": "3.23.3", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz", + "integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==", "requires": { "depd": "2.0.0", - "dns-prefetch-control": "0.2.0", "dont-sniff-mimetype": "1.1.0", - "expect-ct": "0.2.0", "feature-policy": "0.3.0", - "frameguard": "3.1.0", "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.9.5", + "helmet-csp": "2.10.0", "hide-powered-by": "1.1.0", "hpkp": "2.0.0", "hsts": "2.2.0", - "ienoopen": "1.1.0", "nocache": "2.1.0", "referrer-policy": "1.2.0", "x-xss-protection": "1.3.0" @@ -1794,9 +1843,9 @@ "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" }, "helmet-csp": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.5.tgz", - "integrity": "sha512-w9nps5adqFQwgktVPDbXkARmZot/nr8aegzQas9AXdBSwBFBBefPpDSTV0wtgHlAUdDwY6MZo7qAl9yts3ppJg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", + "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", "requires": { "bowser": "2.9.0", "camelize": "1.0.0", @@ -1879,11 +1928,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ienoopen": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz", - "integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==" - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -2197,9 +2241,9 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", - "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz", + "integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==", "requires": { "abab": "^2.0.3", "acorn": "^7.1.1", @@ -2221,7 +2265,7 @@ "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.0.0", + "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0", @@ -2277,13 +2321,6 @@ "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^5.6.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } } }, "jsprim": { @@ -2441,12 +2478,11 @@ "dev": true }, "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^3.0.2" } }, "make-dir": { @@ -2487,16 +2523,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.43.0" + "mime-db": "1.44.0" } }, "mimic-fn": { @@ -2536,29 +2572,48 @@ } }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" }, "moment-timezone": { - "version": "0.5.28", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz", - "integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==", + "version": "0.5.31", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", + "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", "requires": { "moment": ">= 2.9.0" } }, "morgan": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", - "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", - "dev": true, + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", "requires": { - "basic-auth": "~2.0.0", + "basic-auth": "~2.0.1", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "ms": { @@ -2589,25 +2644,12 @@ }, "dependencies": { "iconv-lite": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz", - "integrity": "sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "requires": { "safer-buffer": ">= 2.1.2 < 3" } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, @@ -2617,6 +2659,22 @@ "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", "requires": { "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } } }, "natural-compare": { @@ -2642,9 +2700,9 @@ "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, "nodemailer": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.6.tgz", - "integrity": "sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA==" + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.11.tgz", + "integrity": "sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ==" }, "nodemon": { "version": "2.0.4", @@ -2793,8 +2851,7 @@ "on-headers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, "once": { "version": "1.4.0", @@ -3319,19 +3376,19 @@ } }, "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", "requires": { - "lodash": "^4.17.15" + "lodash": "^4.17.19" } }, "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", "requires": { - "request-promise-core": "1.1.3", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" }, @@ -3479,6 +3536,21 @@ "statuses": "~1.5.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -3492,9 +3564,9 @@ "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" }, "sequelize": { - "version": "5.21.5", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.5.tgz", - "integrity": "sha512-n9hR5K4uQGmBGK/Y/iqewCeSFmKVsd0TRnh0tfoLoAkmXbKC4tpeK96RhKs7d+TTMtrJlgt2TNLVBaAxEwC4iw==", + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.22.3.tgz", + "integrity": "sha512-+nxf4TzdrB+PRmoWhR05TP9ukLAurK7qtKcIFv5Vhxm5Z9v+d2PcTT6Ea3YAoIQVkZ47QlT9XWAIUevMT/3l8Q==", "requires": { "bluebird": "^3.5.0", "cls-bluebird": "^2.1.0", @@ -3521,11 +3593,6 @@ "ms": "^2.1.1" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -3644,11 +3711,6 @@ "ms": "^2.1.1" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -3768,9 +3830,9 @@ "dev": true }, "sqlstring": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", - "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz", + "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==" }, "sshpk": { "version": "1.16.1", @@ -4121,6 +4183,23 @@ "dev": true, "requires": { "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "uniq": { @@ -4193,9 +4272,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", - "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" }, "v8-compile-cache": { "version": "2.1.1", @@ -4214,9 +4293,9 @@ } }, "validator": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz", - "integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA==" + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz", + "integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw==" }, "vary": { "version": "1.1.2", @@ -4343,9 +4422,9 @@ } }, "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "x-xss-protection": { "version": "1.3.0", @@ -4375,9 +4454,9 @@ "dev": true }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } } diff --git a/api/package.json b/api/package.json index 4253794..bdcc9fe 100644 --- a/api/package.json +++ b/api/package.json @@ -25,11 +25,11 @@ "sequelize": "^5.21.5", "smart-request-balancer": "^2.1.1", "uuid": "^7.0.2", - "validator": "^13.0.0" + "validator": "^13.0.0", + "dotenv": "^8.2.0", + "morgan": "^1.9.1" }, "devDependencies": { - "dotenv": "^8.2.0", - "morgan": "^1.9.1", "nodemon": "^2.0.4", "snazzy": "^8.0.0", "standard": "^14.3.4" diff --git a/api/routes/admin.js b/api/routes/admin.js index 17cb27d..1d8d703 100644 --- a/api/routes/admin.js +++ b/api/routes/admin.js @@ -9,197 +9,215 @@ const AdminRouter = Router() AdminRouter.route('/functions') -// Récupère les fonctions + // Récupère les fonctions .get(adminController.getFunctions) -// Permet de créé une fonction - .post(fileUpload({ - useTempFiles: true, - safeFileNames: true, - preserveExtension: Number, - limits: { fileSize: 5 * 1024 * 1024 }, // 5mb, - parseNested: true - }), - [ - body('title') - .not() - .isEmpty() - .withMessage('La fonction doit avoir un titre.') - .isLength({ max: 100 }) - .withMessage('Le titre est trop long.') - .custom((title) => { - if (title === 'undefined') { - return Promise.reject(new Error('La fonction doit avoir un titre.')) - } - return true - }), - body('slug') - .not() - .isEmpty() - .withMessage('La fonction doit avoir un slug.') - .isLength({ max: 100 }) - .withMessage('Le slug est trop long.') - .custom((slug) => { - if (slug === 'undefined') { - return Promise.reject(new Error('La fonction doit avoir un slug.')) - } - return true - }) - .custom(async (slug) => { - try { - const FunctionSlug = await Functions.findOne({ where: { slug } }) - if (FunctionSlug) { - return Promise.reject(new Error('Le slug existe déjà...')) + // Permet de créé une fonction + .post( + fileUpload({ + useTempFiles: true, + safeFileNames: true, + preserveExtension: Number, + limits: { fileSize: 5 * 1024 * 1024 }, // 5mb, + parseNested: true + }), + [ + body('title') + .not() + .isEmpty() + .withMessage('La fonction doit avoir un titre.') + .isLength({ max: 100 }) + .withMessage('Le titre est trop long.') + .custom(title => { + if (title === 'undefined') { + return Promise.reject(new Error('La fonction doit avoir un titre.')) } - } catch (error) { - console.log(error) - } - return true - }), - body('description') - .not() - .isEmpty() - .withMessage('La fonction doit avoir une description.') - .isLength({ max: 255, min: 1 }) - .withMessage('La description est trop longue.') - .custom((description) => { - if (description === 'undefined') { - return Promise.reject(new Error('La fonction doit avoir une description.')) - } - return true - }), - body('categorieId') - .not() - .isEmpty() - .withMessage('La fonction doit avoir une catégorie.') - .custom(async (categorieId) => { - try { - const categorieFound = await Categories.findOne({ where: { id: parseInt(categorieId) } }) - if (!categorieFound) { - return Promise.reject(new Error("La catégorie n'existe pas!")) + return true + }), + body('slug') + .not() + .isEmpty() + .withMessage('La fonction doit avoir un slug.') + .isLength({ max: 100 }) + .withMessage('Le slug est trop long.') + .custom(slug => { + if (slug === 'undefined') { + return Promise.reject(new Error('La fonction doit avoir un slug.')) } - } catch (error) { - console.log(error) - } - return true - }), - body('type') - .custom((type) => { + return true + }) + .custom(async slug => { + try { + const FunctionSlug = await Functions.findOne({ where: { slug } }) + if (FunctionSlug) { + return Promise.reject(new Error('Le slug existe déjà...')) + } + } catch (error) { + console.log(error) + } + return true + }), + body('description') + .not() + .isEmpty() + .withMessage('La fonction doit avoir une description.') + .isLength({ max: 255, min: 1 }) + .withMessage('La description est trop longue.') + .custom(description => { + if (description === 'undefined') { + return Promise.reject( + new Error('La fonction doit avoir une description.') + ) + } + return true + }), + body('categorieId') + .not() + .isEmpty() + .withMessage('La fonction doit avoir une catégorie.') + .custom(async categorieId => { + try { + const categorieFound = await Categories.findOne({ + where: { id: parseInt(categorieId) } + }) + if (!categorieFound) { + return Promise.reject(new Error("La catégorie n'existe pas!")) + } + } catch (error) { + console.log(error) + } + return true + }), + body('type').custom(type => { if (!(type === 'article' || type === 'form' || type === 'page')) { - return Promise.reject(new Error('Le type de la fonction peut être : article, form ou page.')) + return Promise.reject( + new Error( + 'Le type de la fonction peut être : article, form ou page.' + ) + ) } return true }) - ], adminController.postFunction) + ], + adminController.postFunction + ) AdminRouter.route('/functions/:slug') -// Récupère les informations d'une fonction + // Récupère les informations d'une fonction .get(adminController.getFunctionBySlug) AdminRouter.route('/functions/:id') -// Modifie information basique d'une fonction - .put(fileUpload({ - useTempFiles: true, - safeFileNames: true, - preserveExtension: Number, - limits: { fileSize: 5 * 1024 * 1024 }, // 5mb, - parseNested: true - }), - [ - body('title') - .not() - .isEmpty() - .withMessage('La fonction doit avoir un titre.') - .isLength({ max: 100 }) - .withMessage('Le titre est trop long.') - .custom((title) => { - if (title === 'undefined') { - return Promise.reject(new Error('La fonction doit avoir un titre.')) - } - return true - }), - body('slug') - .not() - .isEmpty() - .withMessage('La fonction doit avoir un slug.') - .isLength({ max: 100 }) - .withMessage('Le slug est trop long.') - .custom((slug) => { - if (slug === 'undefined') { - return Promise.reject(new Error('La fonction doit avoir un slug.')) - } - return true - }), - body('description') - .not() - .isEmpty() - .withMessage('La fonction doit avoir une description.') - .isLength({ max: 255, min: 1 }) - .withMessage('La description est trop longue.') - .custom((description) => { - if (description === 'undefined') { - return Promise.reject(new Error('La fonction doit avoir une description.')) - } - return true - }), - body('categorieId') - .not() - .isEmpty() - .withMessage('La fonction doit avoir une catégorie.') - .custom(async (categorieId) => { - try { - const categorieFound = await Categories.findOne({ where: { id: parseInt(categorieId) } }) - if (!categorieFound) { - return Promise.reject(new Error("La catégorie n'existe pas!")) + // Modifie information basique d'une fonction + .put( + fileUpload({ + useTempFiles: true, + safeFileNames: true, + preserveExtension: Number, + limits: { fileSize: 5 * 1024 * 1024 }, // 5mb, + parseNested: true + }), + [ + body('title') + .not() + .isEmpty() + .withMessage('La fonction doit avoir un titre.') + .isLength({ max: 100 }) + .withMessage('Le titre est trop long.') + .custom(title => { + if (title === 'undefined') { + return Promise.reject(new Error('La fonction doit avoir un titre.')) } - } catch (error) { - console.log(error) - } - return true - }), - body('type') - .custom((type) => { + return true + }), + body('slug') + .not() + .isEmpty() + .withMessage('La fonction doit avoir un slug.') + .isLength({ max: 100 }) + .withMessage('Le slug est trop long.') + .custom(slug => { + if (slug === 'undefined') { + return Promise.reject(new Error('La fonction doit avoir un slug.')) + } + return true + }), + body('description') + .not() + .isEmpty() + .withMessage('La fonction doit avoir une description.') + .isLength({ max: 255, min: 1 }) + .withMessage('La description est trop longue.') + .custom(description => { + if (description === 'undefined') { + return Promise.reject( + new Error('La fonction doit avoir une description.') + ) + } + return true + }), + body('categorieId') + .not() + .isEmpty() + .withMessage('La fonction doit avoir une catégorie.') + .custom(async categorieId => { + try { + const categorieFound = await Categories.findOne({ + where: { id: parseInt(categorieId) } + }) + if (!categorieFound) { + return Promise.reject(new Error("La catégorie n'existe pas!")) + } + } catch (error) { + console.log(error) + } + return true + }), + body('type').custom(type => { if (!(type === 'article' || type === 'form' || type === 'page')) { - return Promise.reject(new Error('Le type de la fonction peut être : article, form ou page.')) + return Promise.reject( + new Error( + 'Le type de la fonction peut être : article, form ou page.' + ) + ) } return true }) - ], adminController.putFunction) + ], + adminController.putFunction + ) -// Supprime une fonction avec son id + // Supprime une fonction avec son id .delete(adminController.deleteFunction) AdminRouter.route('/functions/article/:id') - .put(adminController.putFunctionArticle) AdminRouter.route('/functions/form/:id') - .put(adminController.putFunctionForm) AdminRouter.route('/categories') -// Crée une catégorie + // Crée une catégorie .post(adminController.postCategory) AdminRouter.route('/categories/:id') -// Modifier une catégorie avec son id + // Modifier une catégorie avec son id .put(adminController.putCategory) -// Supprime une catégorie avec son id + // Supprime une catégorie avec son id .delete(adminController.deleteCategory) AdminRouter.route('/quotes') -// Récupère les citations pas encore validées + // Récupère les citations pas encore validées .get(adminController.getQuotes) AdminRouter.route('/quotes/:id') -// Valide ou supprime une citation + // Valide ou supprime une citation .put(adminController.putQuote) module.exports = AdminRouter diff --git a/api/routes/categories.js b/api/routes/categories.js index 74ad2d3..278ab55 100644 --- a/api/routes/categories.js +++ b/api/routes/categories.js @@ -5,7 +5,7 @@ const CategoriesRouter = Router() CategoriesRouter.route('/') -// Récupère les catégories + // Récupère les catégories .get(categoriesController.getCategories) module.exports = CategoriesRouter diff --git a/api/routes/comments.js b/api/routes/comments.js index d370db9..20114b7 100644 --- a/api/routes/comments.js +++ b/api/routes/comments.js @@ -6,18 +6,18 @@ const CommentsRouter = Router() CommentsRouter.route('/:commentId') -// Modifier un commentaire + // Modifier un commentaire .put(isAuth, commentsController.putCommentsById) -// Supprime un commentaire + // Supprime un commentaire .delete(isAuth, commentsController.deleteCommentById) CommentsRouter.route('/:functionId') -// Récupère les commentaires + // Récupère les commentaires .get(commentsController.getCommentsByFunctionId) -// Permet à un utilisateur de poster un commentaire sur une fonction + // Permet à un utilisateur de poster un commentaire sur une fonction .post(isAuth, commentsController.postCommentsByFunctionId) module.exports = CommentsRouter diff --git a/api/routes/favorites.js b/api/routes/favorites.js index b3c287d..e4cfbd6 100644 --- a/api/routes/favorites.js +++ b/api/routes/favorites.js @@ -6,13 +6,13 @@ const FavoritesRouter = Router() FavoritesRouter.route('/:functionId') -// Récupère si une fonction est en favoris (d'un utilisateur) + // Récupère si une fonction est en favoris (d'un utilisateur) .get(isAuth, favoritesController.getFavoriteByFunctionId) -// Permet à un utilisateur d'ajouter une fonction aux favoris + // Permet à un utilisateur d'ajouter une fonction aux favoris .post(isAuth, favoritesController.postFavoriteByFunctionId) -// Supprime une fonction des favoris d'un utilisateur + // Supprime une fonction des favoris d'un utilisateur .delete(isAuth, favoritesController.deleteFavoriteByFunctionId) module.exports = FavoritesRouter diff --git a/api/routes/functions.js b/api/routes/functions.js index c713923..a901b51 100644 --- a/api/routes/functions.js +++ b/api/routes/functions.js @@ -5,15 +5,15 @@ const FunctionsRouter = Router() FunctionsRouter.route('/') -// Récupère les fonctions + // Récupère les fonctions .get(functionsController.getFunctions) FunctionsRouter.route('/:slug') -// Récupère les informations de la fonction par son slug + // Récupère les informations de la fonction par son slug .get(functionsController.getFunctionBySlug) -// Exécute la fonction demandée en paramètre + // Exécute la fonction demandée en paramètre .post(functionsController.executeFunctionBySlug) module.exports = FunctionsRouter diff --git a/api/routes/quotes.js b/api/routes/quotes.js index 523b3c6..39ee97b 100644 --- a/api/routes/quotes.js +++ b/api/routes/quotes.js @@ -6,10 +6,10 @@ const QuotesRouter = Router() QuotesRouter.route('/') -// Récupère les citations + // Récupère les citations .get(quotesController.getQuotes) -// Proposer une citation + // Proposer une citation .post(isAuth, quotesController.postQuote) module.exports = QuotesRouter diff --git a/api/routes/tasks.js b/api/routes/tasks.js index b156fb1..7b02243 100644 --- a/api/routes/tasks.js +++ b/api/routes/tasks.js @@ -6,18 +6,18 @@ const TasksRouter = Router() TasksRouter.route('/') -// Récupère les tâches à faire d'un user + // Récupère les tâches à faire d'un user .get(isAuth, tasksController.getTasks) -// Poster une nouvelle tâche à faire + // Poster une nouvelle tâche à faire .post(isAuth, tasksController.postTask) TasksRouter.route('/:id') -// Permet de mettre une tâche à faire en isCompleted ou !isCompleted + // Permet de mettre une tâche à faire en isCompleted ou !isCompleted .put(isAuth, tasksController.putTask) -// Supprimer une tâche à faire + // Supprimer une tâche à faire .delete(isAuth, tasksController.deleteTask) module.exports = TasksRouter diff --git a/website/components/Footer/Footer.css b/website/components/Footer/Footer.css index 06a1f82..cbc781c 100644 --- a/website/components/Footer/Footer.css +++ b/website/components/Footer/Footer.css @@ -1,10 +1,10 @@ .footer { - border-top: var(--border-header-footer); - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; + border-top: var(--border-header-footer); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; } .footer-text { - line-height: 2.5; -} \ No newline at end of file + line-height: 2.5; +} diff --git a/website/components/Footer/Footer.jsx b/website/components/Footer/Footer.jsx index e844635..368a2bb 100644 --- a/website/components/Footer/Footer.jsx +++ b/website/components/Footer/Footer.jsx @@ -8,8 +8,11 @@ export default function Footer () { FunctionProject -  - Version 2.1
- Divlo | Tous droits réservés +  - Version 2.1
+ + Divlo + {' '} + | Tous droits réservés

) diff --git a/website/components/FunctionAdmin/AddEditFunction.jsx b/website/components/FunctionAdmin/AddEditFunction.jsx index 17991cd..9fba0b4 100644 --- a/website/components/FunctionAdmin/AddEditFunction.jsx +++ b/website/components/FunctionAdmin/AddEditFunction.jsx @@ -5,7 +5,7 @@ import useAPI from '../../hooks/useAPI' import api from '../../utils/api' import '../../public/css/pages/admin.css' -const AddEditFunction = (props) => { +const AddEditFunction = props => { const [, categories] = useAPI('/categories') const [inputState, setInputState] = useState(props.defaultInputState) const [message, setMessage] = useState('') @@ -22,18 +22,29 @@ const AddEditFunction = (props) => { } }, [categories]) - const apiCallFunction = (formData) => { - if (props.isEditing) return api.put(`/admin/functions/${inputState.id}`, formData, { headers: { Authorization: props.user.token } }) - return api.post('/admin/functions', formData, { headers: { Authorization: props.user.token } }) + const apiCallFunction = formData => { + if (props.isEditing) { + return api.put(`/admin/functions/${inputState.id}`, formData, { + headers: { Authorization: props.user.token } + }) + } + return api.post('/admin/functions', formData, { + headers: { Authorization: props.user.token } + }) } const handleChange = (event, isTypeCheck = false) => { const inputStateNew = { ...inputState } - inputStateNew[event.target.name] = (event.target.files != null) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value + inputStateNew[event.target.name] = + event.target.files != null + ? event.target.files[0] + : isTypeCheck + ? event.target.checked + : event.target.value setInputState(inputStateNew) } - const handleSubmit = (event) => { + const handleSubmit = event => { event.preventDefault() setIsLoading(true) const formData = new window.FormData() @@ -53,8 +64,10 @@ const AddEditFunction = (props) => { setIsLoading(false) window.location.reload(true) }) - .catch((error) => { - setMessage(`

Erreur: ${error.response.data.message}

`) + .catch(error => { + setMessage( + `

Erreur: ${error.response.data.message}

` + ) setIsLoading(false) }) } @@ -63,23 +76,61 @@ const AddEditFunction = (props) => { <>
- - + +
- - + +
- -