diff --git a/README.md b/README.md index e7eea7a..06c9b07 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,9 @@

+ Gitmoji + JavaScript Style Guide Licence MIT - Repo Size - Commit Activity - Contributors Stars

FunctionProject @@ -76,6 +75,8 @@ npm run dev # API lancé sur http://localhost:8080 Enjoy! 😃 +[![JavaScript Style Guide](https://cdn.rawgit.com/standard/standard/master/badge.svg)](https://github.com/standard/standard) + ## 📄 Licence Ce projet est sous licence MIT - voir le fichier [LICENSE](./LICENSE) pour plus de détails. \ No newline at end of file diff --git a/api/.gitignore b/api/.gitignore index ea03c17..68faeba 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -18,6 +18,7 @@ .env.development.local .env.test.local .env.production.local +.env.production /temp /assets/images/ diff --git a/api/app.js b/api/app.js index 68baaca..0fb5b0a 100644 --- a/api/app.js +++ b/api/app.js @@ -1,83 +1,83 @@ /* Modules */ -require('dotenv').config(); -const path = require('path'); -const express = require('express'); -const helmet = require('helmet'); -const cors = require('cors'); -const morgan = require('morgan'); -const redirectToHTTPS = require('express-http-to-https').redirectToHTTPS; +require('dotenv').config() +const path = require('path') +const express = require('express') +const helmet = require('helmet') +const cors = require('cors') +const morgan = require('morgan') +const redirectToHTTPS = require('express-http-to-https').redirectToHTTPS /* Files Imports & Variables */ -const sequelize = require('./assets/utils/database'); -const { PORT } = require('./assets/config/config'); -const errorHandling = require('./assets/utils/errorHandling'); -const isAuth = require('./middlewares/isAuth'); -const isAdmin = require('./middlewares/isAdmin'); -const app = express(); +const sequelize = require('./assets/utils/database') +const { PORT } = require('./assets/config/config') +const errorHandling = require('./assets/utils/errorHandling') +const isAuth = require('./middlewares/isAuth') +const isAdmin = require('./middlewares/isAdmin') +const app = express() /* Middlewares */ -app.use(helmet()); -app.use(cors()); -app.use(morgan('dev')); -app.use(express.json()); -app.use(redirectToHTTPS([/localhost:(\d{4})/])); +app.use(helmet()) +app.use(cors()) +app.use(morgan('dev')) +app.use(express.json()) +app.use(redirectToHTTPS([/localhost:(\d{4})/])) -/* Routes */ -app.use('/images', express.static(path.join(__dirname, "assets", "images"))); -app.use('/functions', require('./routes/functions')); -app.use('/categories', require('./routes/categories')); -app.use('/users', require('./routes/users')); -app.use('/admin', isAuth, isAdmin, require('./routes/admin')); -app.use('/favorites', require('./routes/favorites')); -app.use('/comments', require('./routes/comments')); -app.use('/quotes', require('./routes/quotes')); -app.use('/tasks', require('./routes/tasks')); +/* Routes */ +app.use('/images', express.static(path.join(__dirname, 'assets', 'images'))) +app.use('/functions', require('./routes/functions')) +app.use('/categories', require('./routes/categories')) +app.use('/users', require('./routes/users')) +app.use('/admin', isAuth, isAdmin, require('./routes/admin')) +app.use('/favorites', require('./routes/favorites')) +app.use('/comments', require('./routes/comments')) +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; - return res.status(statusCode || 500).json({ message }); -}); + console.log(error) + const { statusCode, message } = error + return res.status(statusCode || 500).json({ message }) +}) /* Database Relations */ -const Functions = require('./models/functions'); -const Categories = require('./models/categories'); -const Users = require('./models/users'); -const Favorites = require('./models/favorites'); -const Comments = require('./models/comments'); -const Quotes = require('./models/quotes'); -const Tasks = require('./models/tasks'); +const Functions = require('./models/functions') +const Categories = require('./models/categories') +const Users = require('./models/users') +const Favorites = require('./models/favorites') +const Comments = require('./models/comments') +const Quotes = require('./models/quotes') +const Tasks = require('./models/tasks') // A function has a category -Categories.hasOne(Functions, { constraints: true, onDelete: 'CASCADE'}); -Functions.belongsTo(Categories); +Categories.hasOne(Functions, { constraints: true, onDelete: 'CASCADE' }) +Functions.belongsTo(Categories) // Users can have favorites functions -Users.hasMany(Favorites); -Favorites.belongsTo(Users, { constraints: false }); -Functions.hasMany(Favorites); -Favorites.belongsTo(Functions, { constraints: false }); +Users.hasMany(Favorites) +Favorites.belongsTo(Users, { constraints: false }) +Functions.hasMany(Favorites) +Favorites.belongsTo(Functions, { constraints: false }) // Users can post comments on functions -Users.hasMany(Comments); -Comments.belongsTo(Users, { constraints: false }); -Functions.hasMany(Comments); -Comments.belongsTo(Functions, { constraints: false }); +Users.hasMany(Comments) +Comments.belongsTo(Users, { constraints: false }) +Functions.hasMany(Comments) +Comments.belongsTo(Functions, { constraints: false }) // Users can suggest new quotes -Users.hasMany(Quotes); -Quotes.belongsTo(Users, { constraints: false }); +Users.hasMany(Quotes) +Quotes.belongsTo(Users, { constraints: false }) // Users can have tasks -Users.hasMany(Tasks); -Tasks.belongsTo(Users, { constraints: false }); +Users.hasMany(Tasks) +Tasks.belongsTo(Users, { constraints: false }) /* Server */ // sequelize.sync({ force: true }) sequelize.sync() - .then(() => { - app.listen(PORT, () => console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`)); - }) - .catch((error) => console.log(error)); \ No newline at end of file + .then(() => { + app.listen(PORT, () => console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`)) + }) + .catch((error) => console.log(error)) diff --git a/api/assets/config/config.js b/api/assets/config/config.js index 85908ad..185434b 100644 --- a/api/assets/config/config.js +++ b/api/assets/config/config.js @@ -1,26 +1,26 @@ const config = { - PORT: process.env.PORT || 8080, - HOST: process.env.HOST, - FRONT_END_HOST: process.env.FRONT_END_HOST, - WEATHER_API_KEY: process.env.OpenWeatherMap_API_KEY, - SCRAPER_API_KEY: process.env.Scraper_API_KEY, - DATABASE: { - host: process.env.DB_HOST, - name: process.env.DB_NAME, - user: process.env.DB_USER, - password: process.env.DB_PASS - }, - JWT_SECRET: process.env.JWT_SECRET, - EMAIL_INFO: { - host: process.env.EMAIL_HOST, - port: 465, - secure: true, // true for 465, false for other ports - auth: { - user: process.env.EMAIL_USER, - pass: process.env.EMAIL_PASSWORD - } - }, - TOKEN_LIFE: '1 week' -}; + PORT: process.env.PORT || 8080, + HOST: process.env.HOST, + FRONT_END_HOST: process.env.FRONT_END_HOST, + WEATHER_API_KEY: process.env.OpenWeatherMap_API_KEY, + SCRAPER_API_KEY: process.env.Scraper_API_KEY, + DATABASE: { + host: process.env.DB_HOST, + name: process.env.DB_NAME, + user: process.env.DB_USER, + password: process.env.DB_PASS + }, + JWT_SECRET: process.env.JWT_SECRET, + EMAIL_INFO: { + host: process.env.EMAIL_HOST, + port: 465, + secure: true, // true for 465, false for other ports + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASSWORD + } + }, + TOKEN_LIFE: '1 week' +} -module.exports = config; \ No newline at end of file +module.exports = config diff --git a/api/assets/config/emails.js b/api/assets/config/emails.js index 2d277d2..2e899f3 100644 --- a/api/assets/config/emails.js +++ b/api/assets/config/emails.js @@ -26,7 +26,7 @@ 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. @@ -35,7 +35,7 @@ exports.emailQuoteTemplate = (isValid, quote, frontendLink) => `

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 :
@@ -57,7 +57,7 @@ exports.emailQuoteTemplate = (isValid, quote, frontendLink) => ` -`; +` exports.emailUserTemplate = (subtitle, buttonText, url, footerText) => `

@@ -107,4 +107,4 @@ exports.emailUserTemplate = (subtitle, buttonText, url, footerText) => `
-`; \ No newline at end of file +` diff --git a/api/assets/config/errors.js b/api/assets/config/errors.js index b2514e6..3c62e8f 100644 --- a/api/assets/config/errors.js +++ b/api/assets/config/errors.js @@ -1,18 +1,18 @@ const errors = { - generalError: { - message: "Vous n'avez pas rentré de valeur valide.", - statusCode: 400 - }, + generalError: { + message: "Vous n'avez pas rentré de valeur valide.", + statusCode: 400 + }, - serverError: { - message: "Le serveur n'a pas pu traiter votre requête.", - statusCode: 500 - }, + serverError: { + message: "Le serveur n'a pas pu traiter votre requête.", + statusCode: 500 + }, - requiredFields: { - message: "Vous devez remplir tous les champs...", - statusCode: 400 - } -}; + requiredFields: { + message: 'Vous devez remplir tous les champs...', + statusCode: 400 + } +} -module.exports = errors; \ No newline at end of file +module.exports = errors diff --git a/api/assets/config/transporter.js b/api/assets/config/transporter.js index 6f32737..09ef01f 100644 --- a/api/assets/config/transporter.js +++ b/api/assets/config/transporter.js @@ -1,6 +1,6 @@ -const nodemailer = require('nodemailer'); -const { EMAIL_INFO } = require('./config'); +const nodemailer = require('nodemailer') +const { EMAIL_INFO } = require('./config') -const transporter = nodemailer.createTransport(EMAIL_INFO); +const transporter = nodemailer.createTransport(EMAIL_INFO) -module.exports = transporter; \ No newline at end of file +module.exports = transporter diff --git a/api/assets/functions/functionObject.js b/api/assets/functions/functionObject.js index 0ae03e2..69a4bef 100644 --- a/api/assets/functions/functionObject.js +++ b/api/assets/functions/functionObject.js @@ -1,44 +1,44 @@ -const { randomNumberOutput } = require('./main/randomNumber'); -const convertRomanArabicNumbersOutput = require('./main/convertRomanArabicNumbers'); -const convertDistanceOutput = require('./main/convertDistance'); -const convertTemperatureOutput = require('./main/convertTemperature'); -const armstrongNumberOutput = require('./main/armstrongNumber'); -const weatherRequestOutput = require('./main/weatherRequest'); -const convertCurrencyOutput = require('./main/convertCurrency'); -const calculateAgeOutput = require('./main/calculateAge'); -const heapAlgorithmOutput = require('./main/heapAlgorithm'); -const convertEncodingOutput = require('./main/convertEncoding'); -const randomQuote = require('./main/randomQuote'); -const linkShortener = require('./main/linkShortener'); -const rightPriceOutput = require('./main/rightPrice'); -const isPalindromeOutput = require('./main/isPalindrome'); -const findLongestWordOutput = require('./main/findLongestWord'); -const fibonacciOutput = require('./main/fibonacci'); -const sortArrayOutput = require('./main/sortArray'); +const { randomNumberOutput } = require('./main/randomNumber') +const convertRomanArabicNumbersOutput = require('./main/convertRomanArabicNumbers') +const convertDistanceOutput = require('./main/convertDistance') +const convertTemperatureOutput = require('./main/convertTemperature') +const armstrongNumberOutput = require('./main/armstrongNumber') +const weatherRequestOutput = require('./main/weatherRequest') +const convertCurrencyOutput = require('./main/convertCurrency') +const calculateAgeOutput = require('./main/calculateAge') +const heapAlgorithmOutput = require('./main/heapAlgorithm') +const convertEncodingOutput = require('./main/convertEncoding') +const randomQuote = require('./main/randomQuote') +const linkShortener = require('./main/linkShortener') +const rightPriceOutput = require('./main/rightPrice') +const isPalindromeOutput = require('./main/isPalindrome') +const findLongestWordOutput = require('./main/findLongestWord') +const fibonacciOutput = require('./main/fibonacci') +const sortArrayOutput = require('./main/sortArray') const functionObject = { - randomNumber : randomNumberOutput, - convertRomanArabicNumbers: convertRomanArabicNumbersOutput, - convertDistance : convertDistanceOutput, - convertTemperature : convertTemperatureOutput, - armstrongNumber : armstrongNumberOutput, - weatherRequest : weatherRequestOutput, - convertCurrency : convertCurrencyOutput, - calculateAge : calculateAgeOutput, - heapAlgorithm : heapAlgorithmOutput, - convertEncoding : convertEncodingOutput, - randomQuote : randomQuote, - linkShortener : linkShortener, - rightPrice : rightPriceOutput, - isPalindrome : isPalindromeOutput, - findLongestWord : findLongestWordOutput, - fibonacci : fibonacciOutput, - sortArray : sortArrayOutput, -}; - -// Choisi la fonction à exécuter -function functionToExecute(option) { - return functionObject[option]; + randomNumber: randomNumberOutput, + convertRomanArabicNumbers: convertRomanArabicNumbersOutput, + convertDistance: convertDistanceOutput, + convertTemperature: convertTemperatureOutput, + armstrongNumber: armstrongNumberOutput, + weatherRequest: weatherRequestOutput, + convertCurrency: convertCurrencyOutput, + calculateAge: calculateAgeOutput, + heapAlgorithm: heapAlgorithmOutput, + convertEncoding: convertEncodingOutput, + randomQuote: randomQuote, + linkShortener: linkShortener, + rightPrice: rightPriceOutput, + isPalindrome: isPalindromeOutput, + findLongestWord: findLongestWordOutput, + fibonacci: fibonacciOutput, + sortArray: sortArrayOutput } -module.exports = functionToExecute; \ No newline at end of file +// Choisi la fonction à exécuter +function functionToExecute (option) { + return functionObject[option] +} + +module.exports = functionToExecute diff --git a/api/assets/functions/main/armstrongNumber.js b/api/assets/functions/main/armstrongNumber.js index 2c5d704..971a555 100644 --- a/api/assets/functions/main/armstrongNumber.js +++ b/api/assets/functions/main/armstrongNumber.js @@ -1,46 +1,46 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') -/** +/** * @description Vérifie si un nombre fait partie des nombres d'Armstrong. * @param {Number} number - Le nombre à tester * @returns {Object} Un objet contenant l'explication en html et le booléen si oui ou non c'est un nombre d'armstrong * @examples armstrongNumber(153) → 153 est un nombre d'Armstrong, car 13 + 53 + 33 = 153. - */ -function armstrongNumber(number) { - let numberString = number.toString(); - let numberStringLength = numberString.length; + */ +function armstrongNumber (number) { + const numberString = number.toString() + const numberStringLength = numberString.length - let result = 0; - let resultString = ""; - for (let index = 0; index < numberStringLength; index++) { - result = result + parseInt(numberString[index]) ** numberStringLength; - resultString = resultString + " + " + numberString[index] + "" + numberStringLength + ""; - } + let result = 0 + let resultString = '' + for (let index = 0; index < numberStringLength; index++) { + result = result + parseInt(numberString[index]) ** numberStringLength + resultString = resultString + ' + ' + numberString[index] + '' + numberStringLength + '' + } - const formattedNumber = formatNumberResult(number); - const isArmstrongNumber = (result === number); - return { - isArmstrongNumber, - resultHTML: `

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

` - } + const formattedNumber = formatNumberResult(number) + const isArmstrongNumber = (result === number) + return { + isArmstrongNumber, + resultHTML: `

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

` + } } /* OUTPUTS */ -module.exports = armstrongNumberOutput = ({ res, next }, argsObject) => { - let { number } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(number)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + let { number } = argsObject - // 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 }); - } + // S'il n'y a pas les champs obligatoire + if (!(number)) { + return errorHandling(next, requiredFields) + } - return res.status(200).json(armstrongNumber(number)); -} \ No newline at end of file + // 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 res.status(200).json(armstrongNumber(number)) +} diff --git a/api/assets/functions/main/calculateAge.js b/api/assets/functions/main/calculateAge.js index 7cfd736..410110d 100644 --- a/api/assets/functions/main/calculateAge.js +++ b/api/assets/functions/main/calculateAge.js @@ -1,50 +1,50 @@ -const errorHandling = require('../../utils/errorHandling'); -const moment = require('moment'); -const { requiredFields } = require('../../config/errors'); +const errorHandling = require('../../utils/errorHandling') +const moment = require('moment') +const { requiredFields } = require('../../config/errors') -function calculateAge(currentDate, { birthDateDay, birthDateMonth, birthDateYear }) { - const day = currentDate.getDate(); - const month = currentDate.getMonth(); - const currentDateMoment = moment([currentDate.getFullYear(), month, day]); - const birthDateMoment = moment([birthDateYear, birthDateMonth, birthDateDay]); +function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYear }) { + const day = currentDate.getDate() + const month = currentDate.getMonth() + const currentDateMoment = moment([currentDate.getFullYear(), month, day]) + const birthDateMoment = moment([birthDateYear, birthDateMonth, birthDateDay]) - // Calcule l'âge - Moment.js - const ageYears = currentDateMoment.diff(birthDateMoment, 'year'); - birthDateMoment.add(ageYears, 'years'); - const ageMonths = currentDateMoment.diff(birthDateMoment, 'months'); - birthDateMoment.add(ageMonths, 'months'); - const ageDays = currentDateMoment.diff(birthDateMoment, 'days'); + // Calcule l'âge - Moment.js + const ageYears = currentDateMoment.diff(birthDateMoment, 'year') + birthDateMoment.add(ageYears, 'years') + const ageMonths = currentDateMoment.diff(birthDateMoment, 'months') + birthDateMoment.add(ageMonths, 'months') + const ageDays = currentDateMoment.diff(birthDateMoment, 'days') - const isBirthday = (birthDateDay === day && birthDateMonth === month); - return { ageYears, ageMonths, ageDays, isBirthday }; + const isBirthday = (birthDateDay === day && birthDateMonth === month) + return { ageYears, ageMonths, ageDays, isBirthday } } /* OUTPUTS */ -module.exports = calculateAgeOutput = ({ res, next }, argsObject) => { - let { birthDate } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(birthDate)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + const { birthDate } = argsObject - const birthDateDay = parseInt(birthDate.substring(0, 2)); - 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 }); - } - - let resultHTML; - if (result.isBirthday) { - resultHTML = `

Vous avez ${result.ageYears} ans. Joyeux Anniversaire! 🥳

`; - } else { - resultHTML = `

Vous avez ${result.ageYears} ans, ${result.ageMonths} mois et ${result.ageDays} jour(s).

`; - } - return res.status(200).json({ ...result, resultHTML }); -} \ No newline at end of file + // S'il n'y a pas les champs obligatoire + if (!(birthDate)) { + return errorHandling(next, requiredFields) + } + + const birthDateDay = parseInt(birthDate.substring(0, 2)) + 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 }) + } + + let resultHTML + if (result.isBirthday) { + resultHTML = `

Vous avez ${result.ageYears} ans. Joyeux Anniversaire! 🥳

` + } else { + resultHTML = `

Vous avez ${result.ageYears} ans, ${result.ageMonths} mois et ${result.ageDays} jour(s).

` + } + return res.status(200).json({ ...result, resultHTML }) +} diff --git a/api/assets/functions/main/convertCurrency.js b/api/assets/functions/main/convertCurrency.js index 92bdb29..5a9309a 100644 --- a/api/assets/functions/main/convertCurrency.js +++ b/api/assets/functions/main/convertCurrency.js @@ -1,37 +1,37 @@ -const axios = require('axios'); -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const axios = require('axios') +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') /* OUTPUTS */ -module.exports = convertCurrencyOutput = ({ res, next }, argsObject) => { - let { number, baseCurrency, finalCurrency } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(number && baseCurrency && finalCurrency)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + let { number, baseCurrency, finalCurrency } = 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 }); - } + // S'il n'y a pas les champs obligatoire + if (!(number && baseCurrency && finalCurrency)) { + return errorHandling(next, requiredFields) + } - 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 }); - } - const result = rate * number; - const dateObject = new Date(response.data.date); - const year = dateObject.getFullYear(); - 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}

`; - return res.status(200).json({ date, result, resultHTML }); - }) - .catch(() => errorHandling(next, { message: "La devise n'existe pas.", statusCode: 404 })); -} \ No newline at end of file + // Si ce n'est pas un nombre + number = parseFloat(number) + if (isNaN(number)) { + return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + } + + 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 }) + } + const result = rate * number + const dateObject = new Date(response.data.date) + const year = dateObject.getFullYear() + 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}

` + return res.status(200).json({ date, result, resultHTML }) + }) + .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 9c5acbe..46e00e1 100644 --- a/api/assets/functions/main/convertDistance.js +++ b/api/assets/functions/main/convertDistance.js @@ -1,10 +1,10 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields, generalError } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +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. * @requires {@link correspondancesDistance} * @param {Number} firstValue - Le nombre que vous voulez convertir @@ -13,39 +13,39 @@ const correspondancesDistance = ["pm", null, null, "nm", null, null, "µm", null * @returns {Object|Boolean} → false si arguments non valides et sinon un objet contenant la string et le nombre résultat * @examples convertDistance(500, 'cm', 'm') → { resultNumber: 5, resultString: "5 m" } */ -function convertDistance(firstValue, unitFirstValue, unitFinalValue) { - const index1 = correspondancesDistance.indexOf(unitFirstValue); - const index2 = correspondancesDistance.indexOf(unitFinalValue); - if (index1 !== -1 && index2 !== -1) { - const difference = index1 - index2; - const result = firstValue * Math.pow(10, difference); - return { - result, - resultHTML: `

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

` - }; +function convertDistance (firstValue, unitFirstValue, unitFinalValue) { + const index1 = correspondancesDistance.indexOf(unitFirstValue) + const index2 = correspondancesDistance.indexOf(unitFinalValue) + if (index1 !== -1 && index2 !== -1) { + const difference = index1 - index2 + const result = firstValue * Math.pow(10, difference) + return { + result, + resultHTML: `

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

` } - return false; + } + return false } /* OUTPUTS */ -module.exports = convertDistanceOutput = ({ res, next }, argsObject) => { - let { number, numberUnit, finalUnit } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(number && numberUnit && finalUnit)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + let { number, numberUnit, finalUnit } = 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 }); - } + // S'il n'y a pas les champs obligatoire + if (!(number && numberUnit && finalUnit)) { + return errorHandling(next, requiredFields) + } - const result = convertDistance(number, numberUnit, finalUnit); - if (!result) { - return errorHandling(next, generalError); - } + // 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 res.status(200).json(result); -} \ No newline at end of file + const result = convertDistance(number, numberUnit, finalUnit) + if (!result) { + return errorHandling(next, generalError) + } + + return res.status(200).json(result) +} diff --git a/api/assets/functions/main/convertEncoding.js b/api/assets/functions/main/convertEncoding.js index 8db3b26..9159378 100644 --- a/api/assets/functions/main/convertEncoding.js +++ b/api/assets/functions/main/convertEncoding.js @@ -1,242 +1,239 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields, generalError } = require('../../config/errors'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields, generalError } = require('../../config/errors') -/** +/** * @description Convertis un nombre décimal en binaire. * @param {String} value - Le nombre à convertir en string * @returns {String} - Le nombre en binaire * @examples decimalToBinary('2') → '10' */ -function decimalToBinary(value) { - value = Number(value); - if (isNaN(value)) { - return false; - } else { - return value.toString(2); - } -} +function decimalToBinary (value) { + value = Number(value) + if (isNaN(value)) { + return false + } else { + return value.toString(2) + } +} -/** +/** * @description Convertis un nombre binaire en décimal. * @param {String} value - Le nombre à convertir * @returns {(Number|String)} - Le nombre en décimal soit en nombre ou soit en string si supérieur à 1000 car pour 1000 par exemple formatNumberResult renvoie '1 000' * @examples binaryToDecimal('10') → 2 */ -function binaryToDecimal(value) { - const result = parseInt(Number(value), 2); - if (isNaN(result)) { - return false; - } else { - return result; - } -} +function binaryToDecimal (value) { + const result = parseInt(Number(value), 2) + if (isNaN(result)) { + return false + } else { + return result + } +} -/** +/** * @description Convertis un nombre décimal en hexadécimal. - * @param {String} value - Le nombre à convertir + * @param {String} value - Le nombre à convertir * @returns {String} - Le nombre en hexadécimal * @examples decimalToHexadecimal('15') → 'F' */ -function decimalToHexadecimal(value) { - value = Number(value); - if (isNaN(value)) { - return false; - } else { - return value.toString(16).toUpperCase(); - } -} +function decimalToHexadecimal (value) { + value = Number(value) + if (isNaN(value)) { + return false + } else { + return value.toString(16).toUpperCase() + } +} -/** +/** * @description Convertis un nombre hexadécimal en décimal. * @param {String} value - Le nombre à convertir * @returns {(Number|String)} - Le nombre en décimal soit en nombre ou soit en string si supérieur à 1000 car pour 1000 par exemple formatNumberResult renvoie '1 000' * @examples hexadecimalToDecimal('F') → 15 */ -function hexadecimalToDecimal(value) { - const result = parseInt(value, 16); - if (isNaN(result)) { - return false; - } else { - return result; - } -} +function hexadecimalToDecimal (value) { + const result = parseInt(value, 16) + if (isNaN(result)) { + return false + } else { + return result + } +} -/** - * @description Convertis un nombre binaire en hexadécimal. +/** + * @description Convertis un nombre binaire en hexadécimal. * @param {String} value - Le nombre à convertir * @returns {String} - Le nombre en hexadécimal * @examples binaryToHexadecimal('1111') → 'F' - */ -function binaryToHexadecimal(value) { - value = Number(value); - value = parseInt(value, 2); - if (isNaN(value)) { - return false; - } else { - return parseInt(value).toString(16).toUpperCase(); - } + */ +function binaryToHexadecimal (value) { + value = Number(value) + value = parseInt(value, 2) + if (isNaN(value)) { + return false + } else { + return parseInt(value).toString(16).toUpperCase() + } } -/** - * @description Convertis un nombre hexadécimal en binaire. +/** + * @description Convertis un nombre hexadécimal en binaire. * @param {String} value - Le nombre à convertir * @returns {String} - Le nombre en binaire * @examples hexadecimalToBinary('F') → '1111' - */ -function hexadecimalToBinary(value) { - value = parseInt(value, 16); - if (isNaN(value)) { - return false; - } else { - return parseInt(value).toString(2); - } -} + */ +function hexadecimalToBinary (value) { + value = parseInt(value, 16) + if (isNaN(value)) { + return false + } else { + return parseInt(value).toString(2) + } +} // Convertis des nombres de différentes bases et convertis en UTF-8. (source : http://jsfiddle.net/47zwb41o) -/** - * @description Convertis chaque caractère d'une string en codePoint Unicode. +/** + * @description Convertis chaque caractère d'une string en codePoint Unicode. * @param {String} value - La chaîne de caractère à convertir * @returns {String} * @examples textToNumberUnicode('abc') → '97 98 99' - */ -function textToNumberUnicode(string) { - try { - let resultat = ""; - for (let index in string) { - resultat = resultat + string.codePointAt(index) + " "; - } - return resultat; - } - catch(error) { - return false; + */ +function textToNumberUnicode (string) { + try { + let resultat = '' + for (const index in string) { + resultat = resultat + string.codePointAt(index) + ' ' } + return resultat + } catch (error) { + return false + } } -/** - * @description Convertis chaque codePoint Unicode en caractère. +/** + * @description Convertis chaque codePoint Unicode en caractère. * @param {String} string - Nombre Unicode à convertir espacé par un espace à chaque fois * @returns {String} * @examples numberUnicodeToText('97 98 99') → 'abc' - */ -function numberUnicodeToText(string) { - try { - const array = string.split(" "); - let resultat = ""; - for (let index in array) { - resultat += String.fromCodePoint(parseInt(array[index]).toString()); - } - return resultat; - } - catch(error) { - return false; + */ +function numberUnicodeToText (string) { + try { + const array = string.split(' ') + let resultat = '' + for (const index in array) { + resultat += String.fromCodePoint(parseInt(array[index]).toString()) } + return resultat + } catch (error) { + return false + } } -/** +/** * @description Convertis un Texte en Binaire (UTF-8). * @param {String} s - La chaîne de caractère à convertir * @returns {String} * @examples textToBinary('abc') → '01100001 01100010 01100011' */ -function textToBinary(s) { - try { - s = unescape( encodeURIComponent(s)); - let chr, i = 0, l = s.length, out = ''; - for( ; i < l; i ++ ){ - chr = s.charCodeAt( i ).toString(2); - while(chr.length % 8 != 0 ){ chr = '0' + chr; } - out += chr; - } - return out.replace(/(\d{8})/g, '$1 ').replace(/(^\s+|\s+$)/,''); - } catch (error) { - return false; +function textToBinary (s) { + try { + s = unescape(encodeURIComponent(s)) + 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 } + out += chr } + return out.replace(/(\d{8})/g, '$1 ').replace(/(^\s+|\s+$)/, '') + } catch (error) { + return false + } } -/** +/** * @description Convertis du Binaire (UTF-8) en Texte. * @param {String} s - La chaîne de caractère contenant tous les octets à convertir * @returns {String} * @examples binaryToText('01100001 01100010 01100011') → 'abc' */ -function binaryToText(s){ - try { - s = s.replace(/\s/g,'') - let i = 0, l = s.length, chr, out = ''; - for( ; i < l; i += 8){ - chr = parseInt( s.substr(i, 8 ), 2).toString(16); - out += '%' + ((chr.length % 2 == 0) ? chr : '0' + chr); - } - return decodeURIComponent(out); - } catch (error) { - return false; +function binaryToText (s) { + try { + s = s.replace(/\s/g, '') + 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) } -} + return decodeURIComponent(out) + } catch (error) { + return false + } +} -/** +/** * @description Convertis un Texte en Hexadécimal (UTF-8). * @param {String} s - La chaîne de caractère à convertir * @returns {String} * @examples textToHexadecimal('abc') → '61 62 63' */ function textToHexadecimal (s) { - try { - s = unescape( encodeURIComponent( s ) ); - let chr, i = 0, l = s.length, out = ''; - for( ; i < l; i++ ){ - chr = s.charCodeAt( i ).toString( 16 ); - out += ( chr.length % 2 == 0 ) ? chr : '0' + chr; - out += " "; - } - return out.toUpperCase(); - } - catch (error) { - return false; + try { + s = unescape(encodeURIComponent(s)) + 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 += ' ' } + return out.toUpperCase() + } catch (error) { + return false + } } -/** +/** * @description Convertis de l'Hexadécimal (UTF-8) en Texte. * @param {String} s - La chaîne de caractère contenant tous les nombres Hexadécimal à convertir * @returns {String} * @examples hexadecimalToText('61 62 63') → 'abc' */ function hexadecimalToText (s) { - try { - s = s.replace(/\s/g,''); - return decodeURIComponent( s.replace( /../g, '%$&' ) ); - } - catch (error) { - return false; - } + try { + s = s.replace(/\s/g, '') + return decodeURIComponent(s.replace(/../g, '%$&')) + } catch (error) { + return false + } } /* OUTPUTS */ -const convertEncoding = { decimalToBinary, binaryToDecimal, decimalToHexadecimal, hexadecimalToDecimal, binaryToHexadecimal, hexadecimalToBinary, textToNumberUnicode, numberUnicodeToText, textToBinary, binaryToText, textToHexadecimal, hexadecimalToText }; -function executeFunction(option, value) { - return convertEncoding[option](value); +const convertEncoding = { decimalToBinary, binaryToDecimal, decimalToHexadecimal, hexadecimalToDecimal, binaryToHexadecimal, hexadecimalToBinary, textToNumberUnicode, numberUnicodeToText, textToBinary, binaryToText, textToHexadecimal, hexadecimalToText } +function executeFunction (option, value) { + return convertEncoding[option](value) } -module.exports = convertEncodingOutput = ({ res, next }, argsObject) => { - let { value, functionName } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(value && functionName)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + const { value, functionName } = argsObject - // Si la fonction n'existe pas - if (!convertEncoding.hasOwnProperty(functionName)) { - return errorHandling(next, { message: "Cette conversion n'existe pas.", statusCode: 400 }); - } + // S'il n'y a pas les champs obligatoire + if (!(value && functionName)) { + return errorHandling(next, requiredFields) + } - const result = executeFunction(functionName, value); + // 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 }) + } - // Mauvaise valeur entrée - if (!result) { - return errorHandling(next, generalError); - } + const result = executeFunction(functionName, value) - return res.status(200).json({ result, resultHTML: `

${result}

` }); -} \ No newline at end of file + // Mauvaise valeur entrée + if (!result) { + return errorHandling(next, generalError) + } + + return res.status(200).json({ result, resultHTML: `

${result}

` }) +} diff --git a/api/assets/functions/main/convertRomanArabicNumbers.js b/api/assets/functions/main/convertRomanArabicNumbers.js index ef020a9..8c7c86d 100644 --- a/api/assets/functions/main/convertRomanArabicNumbers.js +++ b/api/assets/functions/main/convertRomanArabicNumbers.js @@ -1,123 +1,122 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields, generalError } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields, generalError } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') /* Variable pour convertRomanArabicNumbers */ const correspondancesRomainArabe = [ - [1000, "M"], - [900, "CM"], - [500, "D"], - [400, "CD"], - [100, "C"], - [90, "XC"], - [50, "L"], - [40, "XL"], - [10, "X"], - [9, "IX"], - [5, "V"], - [4, "IV"], - [1, "I"], -]; + [1000, 'M'], + [900, 'CM'], + [500, 'D'], + [400, 'CD'], + [100, 'C'], + [90, 'XC'], + [50, 'L'], + [40, 'XL'], + [10, 'X'], + [9, 'IX'], + [5, 'V'], + [4, 'IV'], + [1, 'I'] +] -/** +/** * @description Convertis un nombre arabe en nombre romain. * @param {number} nombre - Le nombre arabe à convertir * @returns {string} * @examples convertArabicToRoman(24) → 'XXIV' */ -function convertArabicToRoman(nombre) { - // Initialisation de la variable qui va contenir le résultat de la conversion - let chiffresRomains = ""; +function convertArabicToRoman (nombre) { + // Initialisation de la variable qui va contenir le résultat de la conversion + let chiffresRomains = '' - function extraireChiffreRomain(valeurLettre, lettres) { - while (nombre >= valeurLettre) { - chiffresRomains = chiffresRomains + lettres; - nombre = nombre - valeurLettre; - } + function extraireChiffreRomain (valeurLettre, lettres) { + while (nombre >= valeurLettre) { + chiffresRomains = chiffresRomains + lettres + nombre = nombre - valeurLettre } + } - correspondancesRomainArabe.forEach(correspondance => { - extraireChiffreRomain(correspondance[0], correspondance[1]); - }); + correspondancesRomainArabe.forEach(correspondance => { + extraireChiffreRomain(correspondance[0], correspondance[1]) + }) - return chiffresRomains; -} + return chiffresRomains +} -/** +/** * @description Convertis un nombre romain en nombre arabe. * @param {string} string - Le nombre romain à convertir * @return {number} * @example convertRomanToArabic('XXIV') → 24 */ -function convertRomanToArabic(string) { - let result = 0; - correspondancesRomainArabe.forEach((correspondance) => { - while (string.indexOf(correspondance[1]) === 0) { - // Ajout de la valeur décimale au résultat - result += correspondance[0]; - // Supprimer la lettre romaine correspondante du début - string = string.replace(correspondance[1], ''); - } - }); - if (string != '') { - result = 0; +function convertRomanToArabic (string) { + let result = 0 + correspondancesRomainArabe.forEach((correspondance) => { + while (string.indexOf(correspondance[1]) === 0) { + // Ajout de la valeur décimale au résultat + result += correspondance[0] + // Supprimer la lettre romaine correspondante du début + string = string.replace(correspondance[1], '') } - return result; -} + }) + if (string !== '') { + result = 0 + } + return result +} /* OUTPUTS */ const convertRomanToArabicOutput = ({ res, next }, number) => { - - // S'il n'y a pas les champs obligatoire - if (!(number)) { - return errorHandling(next, requiredFields); - } + // S'il n'y a pas les champs obligatoire + if (!(number)) { + return errorHandling(next, requiredFields) + } - // Formate le paramètre - number = number.toUpperCase(); + // Formate le paramètre + number = number.toUpperCase() - const result = convertRomanToArabic(number); - if (result === 0) { - return errorHandling(next, generalError); - } - - return res.status(200).json({ result, resultHTML: `

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

` }); + const result = convertRomanToArabic(number) + if (result === 0) { + return errorHandling(next, generalError) + } + + 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)) { - 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 }); - } + // S'il n'y a pas les champs obligatoire + if (!(number)) { + return errorHandling(next, requiredFields) + } - const result = convertArabicToRoman(number); - return res.status(200).json({ result, resultHTML: `

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

` }); + // Si ce n'est pas un nombre + number = parseInt(number) + if (isNaN(number)) { + 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.

` }) } -const convertRomanArabicObject = { convertRomanToArabicOutput, convertArabicToRomanOutput }; -function executeFunction(option, value, { res, next }) { - return convertRomanArabicObject[option]({ res, next}, value); +const convertRomanArabicObject = { convertRomanToArabicOutput, convertArabicToRomanOutput } +function executeFunction (option, value, { res, next }) { + return convertRomanArabicObject[option]({ res, next }, value) } -module.exports = convertRomanArabicNumbersOutput = ({ res, next }, argsObject) => { - let { value, functionName } = argsObject; +module.exports = ({ res, next }, argsObject) => { + const { value, functionName } = argsObject - // S'il n'y a pas les champs obligatoire - if (!(value && functionName)) { - return errorHandling(next, requiredFields); - } + // S'il n'y a pas les champs obligatoire + if (!(value && functionName)) { + return errorHandling(next, requiredFields) + } - // Si la fonction n'existe pas - if (!convertRomanArabicObject.hasOwnProperty(functionName)) { - return errorHandling(next, { message: "Cette conversion n'existe pas.", statusCode: 400 }); - } + // 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 }) + } - executeFunction(functionName, value, { res, next }); -} \ No newline at end of file + executeFunction(functionName, value, { res, next }) +} diff --git a/api/assets/functions/main/convertTemperature.js b/api/assets/functions/main/convertTemperature.js index 2940dbb..614eaee 100644 --- a/api/assets/functions/main/convertTemperature.js +++ b/api/assets/functions/main/convertTemperature.js @@ -1,50 +1,48 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields, generalError } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields, generalError } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') -/** +/** * @description Convertis des °C en °F et l'inverse aussi. * @param {Number} degree - Nombre de degrès * @param {String} unit - Unité du nombre (°C ou °F) après conversion * @returns {Object} false si arguments non valides et sinon un objet contenant la string et le nombre résultat * @examples convertTemperature(23, '°F') → { result: 73.4, resultHTML: "73.4 °F" } */ -function convertTemperature(degree, unit) { - let temperatureValue = 0; - if (unit === "°C") { - temperatureValue = (degree - 32) * 5/9; - } - else if (unit === "°F") { - temperatureValue = ((degree * 9/5) + 32); - } - else { - return false; - } - return { - result: temperatureValue, - resultHTML: `

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

` - }; -} +function convertTemperature (degree, unit) { + let temperatureValue = 0 + if (unit === '°C') { + temperatureValue = (degree - 32) * 5 / 9 + } else if (unit === '°F') { + temperatureValue = ((degree * 9 / 5) + 32) + } else { + return false + } + return { + result: temperatureValue, + resultHTML: `

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

` + } +} /* OUTPUTS */ -module.exports = convertTemperatureOutput = ({ res, next }, argsObject) => { - let { degree, unitToConvert } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(degree && unitToConvert)) { - return errorHandling(next, requiredFields); - } - - // Si ce n'est pas un nombre - degree = parseFloat(degree); - if (isNaN(degree)) { - return errorHandling(next, { message: "Veuillez rentré un nombre valide.", statusCode: 400 }); - } +module.exports = ({ res, next }, argsObject) => { + let { degree, unitToConvert } = argsObject - const result = convertTemperature(degree, unitToConvert); - if (!result) { - return errorHandling(next, generalError); - } + // S'il n'y a pas les champs obligatoire + if (!(degree && unitToConvert)) { + return errorHandling(next, requiredFields) + } - return res.status(200).json(result); -} \ No newline at end of file + // Si ce n'est pas un nombre + degree = parseFloat(degree) + if (isNaN(degree)) { + return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 }) + } + + const result = convertTemperature(degree, unitToConvert) + if (!result) { + return errorHandling(next, generalError) + } + + return res.status(200).json(result) +} diff --git a/api/assets/functions/main/fibonacci.js b/api/assets/functions/main/fibonacci.js index 2b0b317..60cae59 100644 --- a/api/assets/functions/main/fibonacci.js +++ b/api/assets/functions/main/fibonacci.js @@ -1,46 +1,46 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') /** * @description Calcule les counter premiers nombres de la suite de fibonacci. - * @param {number} counter + * @param {number} counter */ -function fibonacci(counter, result = [], a = 0, b = 1) { - if (counter === 0) { - return result; - } - counter--; - result.push(a); - return fibonacci(counter, result, b, a + b); +function fibonacci (counter, result = [], a = 0, b = 1) { + if (counter === 0) { + return result + } + counter-- + result.push(a) + return fibonacci(counter, result, b, a + b) } /* OUTPUTS */ -module.exports = fibonacciOutput = ({ res, next }, argsObject) => { - let { counter } = argsObject; - - // S'il n'y a pas les champs obligatoire - 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 }); - } +module.exports = ({ res, next }, argsObject) => { + let { counter } = argsObject - // 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 }); - } + // S'il n'y a pas les champs obligatoire + if (!(counter)) { + return errorHandling(next, requiredFields) + } - const result = fibonacci(counter); - 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(', ')}

` - }); -} \ No newline at end of file + // Si ce n'est pas un nombre + counter = parseInt(counter) + if (isNaN(counter)) { + 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 }) + } + + const result = fibonacci(counter) + 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(', ')}

` + }) +} diff --git a/api/assets/functions/main/findLongestWord.js b/api/assets/functions/main/findLongestWord.js index 13dba3e..085b0b9 100644 --- a/api/assets/functions/main/findLongestWord.js +++ b/api/assets/functions/main/findLongestWord.js @@ -1,39 +1,39 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') /** * @description Renvoie le mot le plus long d'une chaîne de caractères - * @param {string} string + * @param {string} string * @returns {string} * @example findLongestWord('Chaîne de caractères') → 'caractères' */ -function findLongestWord(string) { - const arrayString = string.split(" "); - let stringLength = 0; - let result = ""; +function findLongestWord (string) { + const arrayString = string.split(' ') + let stringLength = 0 + let result = '' - arrayString.forEach((element) => { - if (element.length > stringLength) { - result = element; - stringLength = element.length; - } - }); + arrayString.forEach((element) => { + if (element.length > stringLength) { + result = element + stringLength = element.length + } + }) - return result; + return result } /* OUTPUTS */ -module.exports = findLongestWordOutput = ({ res, next }, argsObject) => { - const { string } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(string)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + const { string } = argsObject - const result = findLongestWord(string); - return res.status(200).json({ - result, - resultHTML: `

Le mot le plus long est :
"${result}"

` - }); -} \ No newline at end of file + // S'il n'y a pas les champs obligatoire + if (!(string)) { + return errorHandling(next, requiredFields) + } + + const result = findLongestWord(string) + return res.status(200).json({ + result, + resultHTML: `

Le mot le plus long est :
"${result}"

` + }) +} diff --git a/api/assets/functions/main/heapAlgorithm.js b/api/assets/functions/main/heapAlgorithm.js index 976630e..7f150cc 100644 --- a/api/assets/functions/main/heapAlgorithm.js +++ b/api/assets/functions/main/heapAlgorithm.js @@ -1,52 +1,52 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') -/** +/** * @description Retourne un tableau contenant toutes les possibilités d'anagramme d'un mot. * @param {String} string - La chaîne de caractère à permuter * @returns {Array} * @examples heapAlgorithm('abc') → ["abc", "acb", "bac", "bca", "cab", "cba"] */ -function heapAlgorithm(string) { - let results = []; +function heapAlgorithm (string) { + const results = [] - if (string.length === 1) { - results.push(string); - return results; - } + if (string.length === 1) { + results.push(string) + return results + } - for (let indexString = 0; indexString < string.length; indexString++) { - const firstChar = string[indexString]; - const charsLeft = string.substring(0, indexString) + string.substring(indexString + 1); - const innerPermutations = heapAlgorithm(charsLeft); - for (let indexPermutation = 0; indexPermutation < innerPermutations.length; indexPermutation++) { - results.push(firstChar + innerPermutations[indexPermutation]); - } + for (let indexString = 0; indexString < string.length; indexString++) { + const firstChar = string[indexString] + const charsLeft = string.substring(0, indexString) + string.substring(indexString + 1) + const innerPermutations = heapAlgorithm(charsLeft) + for (let indexPermutation = 0; indexPermutation < innerPermutations.length; indexPermutation++) { + results.push(firstChar + innerPermutations[indexPermutation]) } - return results; -} + } + return results +} /* OUTPUTS */ -module.exports = heapAlgorithmOutput = ({ res, next }, argsObject) => { - let { string } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(string)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + const { string } = argsObject - // 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 }); - } + // S'il n'y a pas les champs obligatoire + if (!(string)) { + return errorHandling(next, requiredFields) + } - 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) => { - resultHTML += string + "
"; - }); - resultHTML += "

"; - return res.status(200).json({ result, resultHTML }); -} \ No newline at end of file + // 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 }) + } + + 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) => { + resultHTML += string + '
' + }) + resultHTML += '

' + return res.status(200).json({ result, resultHTML }) +} diff --git a/api/assets/functions/main/isPalindrome.js b/api/assets/functions/main/isPalindrome.js index f6fbfd7..151eee3 100644 --- a/api/assets/functions/main/isPalindrome.js +++ b/api/assets/functions/main/isPalindrome.js @@ -1,48 +1,48 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') /** * @description Inverse la chaîne de caractère - * @param {string} string + * @param {string} string * @returns {string} * @example reverseString('Hello') → 'olleH' */ -function reverseString(string) { - return string.split("").reverse().join(''); +function reverseString (string) { + return string.split('').reverse().join('') } /** * @description Vérifie si un mot est un palindrome (un mot qui peut s'écrire dans les deux sens) * @requires reverseString - * @param {string} string + * @param {string} string * @param {string} reverseStringResult La chaîne de caractères inversée * @returns {boolean} * @example isPalindrome('kayak') → true */ -function isPalindrome(string, reverseStringResult) { - return string === reverseStringResult; +function isPalindrome (string, reverseStringResult) { + return string === reverseStringResult } /* OUTPUTS */ -module.exports = isPalindromeOutput = ({ res, next }, argsObject) => { - let { string } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(string)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + let { string } = argsObject - if (typeof string !== 'string') { - return errorHandling(next, { message: "Vous devez rentré une chaîne de caractère valide.", statusCode: 400 }); - } + // S'il n'y a pas les champs obligatoire + if (!(string)) { + return errorHandling(next, requiredFields) + } - string = string.toLowerCase(); + if (typeof string !== 'string') { + return errorHandling(next, { message: 'Vous devez rentré une chaîne de caractère valide.', statusCode: 400 }) + } - const reverseStringResult = reverseString(string); - const isPalindromeResult = isPalindrome(string, reverseStringResult); - return res.status(200).json({ - isPalindrome: isPalindromeResult, - reverseString: reverseStringResult, - resultHTML: `

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

` - }); -} \ No newline at end of file + string = string.toLowerCase() + + const reverseStringResult = reverseString(string) + const isPalindromeResult = isPalindrome(string, reverseStringResult) + return res.status(200).json({ + isPalindrome: isPalindromeResult, + reverseString: 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 8bdc058..14a5eba 100644 --- a/api/assets/functions/main/linkShortener.js +++ b/api/assets/functions/main/linkShortener.js @@ -1,52 +1,52 @@ -const validator = require('validator'); -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields, serverError } = require('../../config/errors'); -const Short_links = require('../../../models/short_links'); +const validator = require('validator') +const errorHandling = require('../../utils/errorHandling') +const { requiredFields, serverError } = require('../../config/errors') +const shortLinks = require('../../../models/short_links') -module.exports = linkShortener = async ({ res, next }, argsObject) => { - let { url, shortcutName } = argsObject; +module.exports = async ({ res, next }, argsObject) => { + let { url, shortcutName } = argsObject - // S'il n'y a pas les champs obligatoire - if (!(url && shortcutName)) { - return errorHandling(next, requiredFields); + // S'il n'y a pas les champs obligatoire + if (!(url && shortcutName)) { + return errorHandling(next, requiredFields) + } + + // Si ce n'est pas une url + if (!validator.isURL(url)) { + 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 }) + } + + // Sanitize shortcutName + shortcutName = validator.escape(shortcutName) + shortcutName = validator.trim(shortcutName) + shortcutName = validator.blacklist(shortcutName, ' ') + + try { + // Si l'url a déjà été raccourcie + 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 }) } - // Si ce n'est pas une url - if (!validator.isURL(url)) { - return errorHandling(next, { message: "Veuillez entré une URL valide.", statusCode: 400 }); + // Si le nom du raccourci existe déjà + 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 }) } - // 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 }); - } - - // Sanitize shortcutName - shortcutName = validator.escape(shortcutName); - shortcutName = validator.trim(shortcutName); - shortcutName = validator.blacklist(shortcutName, ' '); - - try { - // Si l'url a déjà été raccourcie - const urlInDatabase = await Short_links.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 }); - } - - // Si le nom du raccourci existe déjà - const shortcutInDatabase = await Short_links.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 }); - } - - // Ajout du lien raccourci - const result = await Short_links.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 }); - } catch { - console.log(error); - return errorHandling(next, serverError); - } -} \ No newline at end of file + // 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 }) + } 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 28e931d..d47f1a2 100644 --- a/api/assets/functions/main/randomNumber.js +++ b/api/assets/functions/main/randomNumber.js @@ -1,37 +1,37 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') -/** - * @description Génère un nombre aléatoire entre un minimum inclus et un maximum inclus. - * @param {Number} min Nombre Minimum - * @param {Number} max Nombre Maximum - * @returns {Number} Nombre aléatoire +/** + * @description Génère un nombre aléatoire entre un minimum inclus et un maximum inclus. + * @param {Number} min Nombre Minimum + * @param {Number} max Nombre Maximum + * @returns {Number} Nombre aléatoire * @examples randomNumber(1, 2) → retourne soit 1 ou 2 - */ -function randomNumber(min, max) { - return Math.floor(Math.random() * (max - min +1)) + min; + */ +function randomNumber (min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min } /* OUTPUTS */ const randomNumberOutput = ({ res, next }, argsObject) => { - let { min, max } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(min && max)) { - return errorHandling(next, requiredFields); - } - - // Si ce ne sont pas des nombres - 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 }); - } + let { min, max } = argsObject - const result = randomNumber(min, max); - return res.status(200).json({ result, resultHTML: `

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

` }); + // S'il n'y a pas les champs obligatoire + if (!(min && max)) { + return errorHandling(next, requiredFields) + } + + // Si ce ne sont pas des nombres + 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 }) + } + + const result = randomNumber(min, max) + return res.status(200).json({ result, resultHTML: `

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

` }) } -exports.randomNumber = randomNumber; -exports.randomNumberOutput = randomNumberOutput; \ No newline at end of file +exports.randomNumber = randomNumber +exports.randomNumberOutput = randomNumberOutput diff --git a/api/assets/functions/main/randomQuote.js b/api/assets/functions/main/randomQuote.js index 789c3b0..f1f724a 100644 --- a/api/assets/functions/main/randomQuote.js +++ b/api/assets/functions/main/randomQuote.js @@ -1,26 +1,26 @@ -const errorHandling = require('../../utils/errorHandling'); -const { serverError } = require('../../config/errors'); -const Quotes = require('../../../models/quotes'); -const Users = require('../../../models/users'); -const sequelize = require('../../utils/database'); +const errorHandling = require('../../utils/errorHandling') +const { serverError } = require('../../config/errors') +const Quotes = require('../../../models/quotes') +const Users = require('../../../models/users') +const sequelize = require('../../utils/database') -module.exports = randomQuote = async ({ res, next }, _argsObject) => { - try { - const quote = await Quotes.findOne({ - order: sequelize.random(), - include: [ - { model: Users, attributes: ["name", "logo"] } - ], - attributes: { - exclude: ["isValidated"] - }, - where: { - isValidated: 1, - } - }); - return res.status(200).json(quote); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } -} \ No newline at end of file +module.exports = async ({ res, next }, _argsObject) => { + try { + const quote = await Quotes.findOne({ + order: sequelize.random(), + include: [ + { model: Users, attributes: ['name', 'logo'] } + ], + attributes: { + exclude: ['isValidated'] + }, + where: { + isValidated: 1 + } + }) + return res.status(200).json(quote) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } +} diff --git a/api/assets/functions/main/rightPrice.js b/api/assets/functions/main/rightPrice.js index fabe06e..a3e3b8a 100644 --- a/api/assets/functions/main/rightPrice.js +++ b/api/assets/functions/main/rightPrice.js @@ -1,57 +1,57 @@ -const { randomNumber } = require('./randomNumber'); -const errorHandling = require('../../utils/errorHandling'); -const { serverError } = require('../../config/errors'); -const { SCRAPER_API_KEY } = require('../../config/config'); -const axios = require('axios'); -const { JSDOM } = require("jsdom"); +const { randomNumber } = require('./randomNumber') +const errorHandling = require('../../utils/errorHandling') +const { serverError } = require('../../config/errors') +const { SCRAPER_API_KEY } = require('../../config/config') +const axios = require('axios') +const { JSDOM } = require('jsdom') const subjectList = [ - "smartphone", - "pc+gamer", - "pc+portable", - "TV", - "casque", - "clavier", - "souris", - "ecran", - "jeux+vidéos" -]; + 'smartphone', + 'pc+gamer', + 'pc+portable', + 'TV', + 'casque', + 'clavier', + 'souris', + 'ecran', + 'jeux+vidéos' +] -function getRandomArrayElement(array) { - return array[randomNumber(0, array.length - 1)]; +function getRandomArrayElement (array) { + return array[randomNumber(0, array.length - 1)] } -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 amazonProductList = document.querySelectorAll('.s-result-item'); - const productsList = []; - for (let indexProduct in amazonProductList) { - try { - const elementProduct = amazonProductList[indexProduct]; - const productImage = elementProduct.querySelector('.s-image'); - const originalPrice = elementProduct.querySelector(".a-price-whole").innerHTML; - productsList.push({ - name: productImage["alt"], - image: productImage["src"], - price: Number(originalPrice.replace(",", ".").replace(" ", "")) - }); - } catch (_error) { - continue; - } - } - return productsList; -} - -module.exports = rightPriceOutput = async ({ res, next }, _argsObject) => { - const subject = getRandomArrayElement(subjectList); +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 amazonProductList = document.querySelectorAll('.s-result-item') + const productsList = [] + for (const indexProduct in amazonProductList) { try { - const productsList = await getAmazonProductList(subject); - const randomProduct = getRandomArrayElement(productsList); - return res.status(200).json({ subject, ...randomProduct }); - } catch (error) { - console.error(error); - return errorHandling(next, serverError); + const elementProduct = amazonProductList[indexProduct] + const productImage = elementProduct.querySelector('.s-image') + const originalPrice = elementProduct.querySelector('.a-price-whole').innerHTML + productsList.push({ + name: productImage.alt, + image: productImage.src, + price: Number(originalPrice.replace(',', '.').replace(' ', '')) + }) + } catch (_error) { + continue } -} \ No newline at end of file + } + return productsList +} + +module.exports = async ({ res, next }, _argsObject) => { + const subject = getRandomArrayElement(subjectList) + try { + const productsList = await getAmazonProductList(subject) + const randomProduct = getRandomArrayElement(productsList) + return res.status(200).json({ subject, ...randomProduct }) + } catch (error) { + console.error(error) + return errorHandling(next, serverError) + } +} diff --git a/api/assets/functions/main/sortArray.js b/api/assets/functions/main/sortArray.js index 87ce520..f2317c9 100644 --- a/api/assets/functions/main/sortArray.js +++ b/api/assets/functions/main/sortArray.js @@ -1,56 +1,56 @@ -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const formatNumberResult = require('../secondary/formatNumberResult'); +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const formatNumberResult = require('../secondary/formatNumberResult') -function minNumber(array) { - let minNumber = { index: 0, value: array[0] } - for (let index = 1; index < array.length; index++) { - const number = array[index]; - if (number < minNumber.value) { - minNumber = { index: index, value: array[index] } - } +function minNumber (array) { + let minNumber = { index: 0, value: array[0] } + for (let index = 1; index < array.length; index++) { + const number = array[index] + if (number < minNumber.value) { + minNumber = { index: index, value: array[index] } } - return minNumber; + } + return minNumber } -function sortArray(array) { - const arrayDuplicated = [...array]; - const resultArray = []; - while (array.length !== resultArray.length) { - const min = minNumber(arrayDuplicated); - resultArray.push(min.value); - arrayDuplicated.splice(min.index, 1); - } - return resultArray; +function sortArray (array) { + const arrayDuplicated = [...array] + const resultArray = [] + while (array.length !== resultArray.length) { + const min = minNumber(arrayDuplicated) + resultArray.push(min.value) + arrayDuplicated.splice(min.index, 1) + } + return resultArray } /* OUTPUTS */ -module.exports = sortArrayOutput = ({ res, next }, argsObject) => { - let { numbersList } = argsObject; - - // S'il n'y a pas les champs obligatoire - if (!(numbersList)) { - return errorHandling(next, requiredFields); - } +module.exports = ({ res, next }, argsObject) => { + const { numbersList } = argsObject - const numbersListArray = numbersList.split(',').map((number) => number.trim().replace(' ', '')).map(Number); + // S'il n'y a pas les champs obligatoire + if (!(numbersList)) { + return errorHandling(next, requiredFields) + } - // 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 }); - } + const numbersListArray = numbersList.split(',').map((number) => number.trim().replace(' ', '')).map(Number) - // 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 }); - } + // 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 }) + } - const result = sortArray(numbersListArray); - 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(', ')}

` - }); -} \ No newline at end of file + // 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 }) + } + + const result = sortArray(numbersListArray) + 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(', ')}

` + }) +} diff --git a/api/assets/functions/main/weatherRequest.js b/api/assets/functions/main/weatherRequest.js index 0c92d78..c334b77 100644 --- a/api/assets/functions/main/weatherRequest.js +++ b/api/assets/functions/main/weatherRequest.js @@ -1,46 +1,46 @@ -const axios = require('axios'); -const Queue = require('smart-request-balancer'); -const errorHandling = require('../../utils/errorHandling'); -const { requiredFields } = require('../../config/errors'); -const { WEATHER_API_KEY } = require('../../config/config'); -const dateTimeUTC = require('../secondary/dateTimeManagement'); -const capitalize = require('../secondary/capitalize'); +const axios = require('axios') +const Queue = require('smart-request-balancer') +const errorHandling = require('../../utils/errorHandling') +const { requiredFields } = require('../../config/errors') +const { WEATHER_API_KEY } = require('../../config/config') +const dateTimeUTC = require('../secondary/dateTimeManagement') +const capitalize = require('../secondary/capitalize') const queue = new Queue({ - /* + /* rate: number of requests per limit: number of seconds */ - rules: { - weatherRequest: { - rate: 50, - limit: 60, - priority: 1 - }, + rules: { + weatherRequest: { + rate: 50, + limit: 60, + priority: 1 } -}); + } +}) /* OUTPUTS */ -module.exports = weatherRequestOutput = ({ res, next }, argsObject) => { - let { cityName } = argsObject; +module.exports = ({ res, next }, argsObject) => { + let { cityName } = argsObject - // S'il n'y a pas les champs obligatoire - if (!(cityName)) { - return errorHandling(next, requiredFields); - } + // S'il n'y a pas les champs obligatoire + if (!(cityName)) { + return errorHandling(next, requiredFields) + } - cityName = cityName.split(' ').join('+'); + 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'); -} \ No newline at end of file + // 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') +} diff --git a/api/assets/functions/secondary/capitalize.js b/api/assets/functions/secondary/capitalize.js index 858e2d4..135c412 100644 --- a/api/assets/functions/secondary/capitalize.js +++ b/api/assets/functions/secondary/capitalize.js @@ -1,12 +1,12 @@ -/** +/** * @description Majuscule à la 1ère lettre d'une string. - * @param {String} s - * @returns {String} + * @param {String} s + * @returns {String} * @examples capitalize('hello world!') → 'Hello world!' */ -function capitalize(s) { - if (typeof s !== 'string') return '' - return s.charAt(0).toUpperCase() + s.slice(1) +function capitalize (s) { + if (typeof s !== 'string') return '' + return s.charAt(0).toUpperCase() + s.slice(1) } -module.exports = capitalize; \ No newline at end of file +module.exports = capitalize diff --git a/api/assets/functions/secondary/dateTimeManagement.js b/api/assets/functions/secondary/dateTimeManagement.js index 8473d49..5ef5627 100644 --- a/api/assets/functions/secondary/dateTimeManagement.js +++ b/api/assets/functions/secondary/dateTimeManagement.js @@ -1,43 +1,43 @@ -/** +/** * @description Donne la date et l'heure selon l'UTC (Universal Time Coordinated). - * @param {String} utc Heure de décalage par rapport à l'UTC + * @param {String} utc Heure de décalage par rapport à l'UTC * @returns {Function} → showDateTime(enteredOffset) → Retourne l'exécution de la fonction showDateTime - * @examples dateTimeUTC('0') - */ -function dateTimeUTC(utc) { - const timeNow = new Date(); - const utcOffset = timeNow.getTimezoneOffset(); - timeNow.setMinutes(timeNow.getMinutes() + utcOffset); - const enteredOffset = parseFloat(utc)*60; - timeNow.setMinutes(timeNow.getMinutes() + enteredOffset); - return showDateTime(timeNow); -} - -/** - * @description Affiche la date et l'heure (format : dd/mm/yyyy - 00:00:00). - * @requires {@link fonctions_annexes.js: showDateTime} - * @param {String} utc Heure de décalage par rapport à l'UTC - * @returns {Object} Retourne un objet contenant l'année, le mois, le jour, l'heure, les minutes, les secondes et la date formaté - * @examples dateTimeUTC('0') → dateTimeUTC vous renvoie l'exécution de showDateTime - */ -function showDateTime(timeNow) { - const year = timeNow.getFullYear(); - const month = ('0'+(timeNow.getMonth()+1)).slice(-2); - const day = ('0'+timeNow.getDate()).slice(-2); - 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 objectDateTime = { - year: year, - month: month, - day: day, - hour: hour, - minute: minute, - second: second, - showDateTimeValue: showDateTimeValue - }; - return objectDateTime; + * @examples dateTimeUTC('0') + */ +function dateTimeUTC (utc) { + const timeNow = new Date() + const utcOffset = timeNow.getTimezoneOffset() + timeNow.setMinutes(timeNow.getMinutes() + utcOffset) + const enteredOffset = parseFloat(utc) * 60 + timeNow.setMinutes(timeNow.getMinutes() + enteredOffset) + return showDateTime(timeNow) } -module.exports = dateTimeUTC; \ No newline at end of file +/** + * @description Affiche la date et l'heure (format : dd/mm/yyyy - 00:00:00). + * @requires {@link fonctions_annexes.js: showDateTime} + * @param {String} utc Heure de décalage par rapport à l'UTC + * @returns {Object} Retourne un objet contenant l'année, le mois, le jour, l'heure, les minutes, les secondes et la date formaté + * @examples dateTimeUTC('0') → dateTimeUTC vous renvoie l'exécution de showDateTime + */ +function showDateTime (timeNow) { + const year = timeNow.getFullYear() + const month = ('0' + (timeNow.getMonth() + 1)).slice(-2) + const day = ('0' + timeNow.getDate()).slice(-2) + 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 objectDateTime = { + year: year, + month: month, + day: day, + hour: hour, + minute: minute, + second: second, + showDateTimeValue: showDateTimeValue + } + return objectDateTime +} + +module.exports = dateTimeUTC diff --git a/api/assets/functions/secondary/formatNumberResult.js b/api/assets/functions/secondary/formatNumberResult.js index 8308cbb..3348e0e 100644 --- a/api/assets/functions/secondary/formatNumberResult.js +++ b/api/assets/functions/secondary/formatNumberResult.js @@ -1,14 +1,14 @@ -/** +/** * @description Formate un nombre avec des espaces. * @param {Number} number * @param {String} separator Le séparateur utilisé pour la virgule (exemple: "." ou ",") - * @returns {String} - Le nombre formaté + * @returns {String} - Le nombre formaté * @examples formatNumberResult(76120) → '76 120' - */ -function formatNumberResult(number, separator = ".") { - let parts = number.toString().split(separator); - parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, " "); - return parts.join(separator); + */ +function formatNumberResult (number, separator = '.') { + const parts = number.toString().split(separator) + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + return parts.join(separator) } -module.exports = formatNumberResult; \ No newline at end of file +module.exports = formatNumberResult diff --git a/api/assets/utils/database.js b/api/assets/utils/database.js index e2b857d..c055dbe 100644 --- a/api/assets/utils/database.js +++ b/api/assets/utils/database.js @@ -1,9 +1,9 @@ -const Sequelize = require('sequelize'); -const { DATABASE } = require('../config/config'); +const Sequelize = require('sequelize') +const { DATABASE } = require('../config/config') const sequelize = new Sequelize(DATABASE.name, DATABASE.user, DATABASE.password, { - dialect: 'mysql', - host: DATABASE.host -}); + dialect: 'mysql', + host: DATABASE.host +}) -module.exports = sequelize; \ No newline at end of file +module.exports = sequelize diff --git a/api/assets/utils/deleteFilesNameStartWith.js b/api/assets/utils/deleteFilesNameStartWith.js index 153d1d5..1a62b5d 100644 --- a/api/assets/utils/deleteFilesNameStartWith.js +++ b/api/assets/utils/deleteFilesNameStartWith.js @@ -1,19 +1,19 @@ -const fs = require("fs"); -const path = require("path"); +const fs = require('fs') +const path = require('path') -function deleteFilesNameStartWith(pattern, dirPath, callback) { - fs.readdir(path.resolve(dirPath), (_error, fileNames) => { - for (const name of fileNames) { - const splitedName = name.split('.'); - if (splitedName.length === 2) { - const fileName = splitedName[0]; - if (fileName === pattern && name !== 'default.png') { - return fs.unlink(path.join(dirPath, name), callback); - } - } +function deleteFilesNameStartWith (pattern, dirPath, callback) { + fs.readdir(path.resolve(dirPath), (_error, fileNames) => { + for (const name of fileNames) { + const splitedName = name.split('.') + if (splitedName.length === 2) { + const fileName = splitedName[0] + if (fileName === pattern && name !== 'default.png') { + return fs.unlink(path.join(dirPath, name), callback) } - return callback(); - }); + } + } + return callback() + }) } -module.exports = deleteFilesNameStartWith; \ No newline at end of file +module.exports = deleteFilesNameStartWith diff --git a/api/assets/utils/errorHandling.js b/api/assets/utils/errorHandling.js index fde2917..1b77725 100644 --- a/api/assets/utils/errorHandling.js +++ b/api/assets/utils/errorHandling.js @@ -1,7 +1,7 @@ -function errorHandling(next, { statusCode, message }) { - const error = new Error(message); - error.statusCode = statusCode; - next(error); +function errorHandling (next, { statusCode, message }) { + const error = new Error(message) + error.statusCode = statusCode + next(error) } -module.exports = errorHandling; \ No newline at end of file +module.exports = errorHandling diff --git a/api/assets/utils/getPagesHelper.js b/api/assets/utils/getPagesHelper.js index ac6b27b..99d8597 100644 --- a/api/assets/utils/getPagesHelper.js +++ b/api/assets/utils/getPagesHelper.js @@ -1,10 +1,10 @@ -const errorHandling = require('../utils/errorHandling'); -const { serverError } = require('../config/errors'); -const helperQueryNumber = require('../utils/helperQueryNumber'); +const errorHandling = require('../utils/errorHandling') +const { serverError } = require('../config/errors') +const helperQueryNumber = require('../utils/helperQueryNumber') const DEFAULT_OPTIONS = { - order: [['createdAt', 'DESC']] -}; + order: [['createdAt', 'DESC']] +} /** * @description Permet de faire un système de pagination sur un model Sequelize @@ -12,23 +12,23 @@ 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) { - const page = helperQueryNumber(req.query.page, 1); - const limit = helperQueryNumber(req.query.limit, 10); - const offset = (page - 1) * limit; - try { - const result = await Model.findAndCountAll({ - limit, - offset, - ...options - }); - const { count, rows } = result; - const hasMore = (page * limit) < count; - return res.status(200).json({ totalItems: count, hasMore, rows }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } +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 + try { + const result = await Model.findAndCountAll({ + limit, + offset, + ...options + }) + const { count, rows } = result + const hasMore = (page * limit) < count + return res.status(200).json({ totalItems: count, hasMore, rows }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } -module.exports = getPagesHelper; \ No newline at end of file +module.exports = getPagesHelper diff --git a/api/assets/utils/helperQueryNumber.js b/api/assets/utils/helperQueryNumber.js index f61d5d7..0e2e45c 100644 --- a/api/assets/utils/helperQueryNumber.js +++ b/api/assets/utils/helperQueryNumber.js @@ -1,6 +1,6 @@ -function helperQueryNumber(value, defaultValue) { - if (value && !isNaN(value)) return parseInt(value); - return defaultValue; +function helperQueryNumber (value, defaultValue) { + if (value && !isNaN(value)) return parseInt(value) + return defaultValue } -module.exports = helperQueryNumber; \ No newline at end of file +module.exports = helperQueryNumber diff --git a/api/controllers/admin.js b/api/controllers/admin.js index a6a5bdc..2484dab 100644 --- a/api/controllers/admin.js +++ b/api/controllers/admin.js @@ -1,320 +1,319 @@ -const path = require('path'); -const fs = require('fs'); -const { validationResult } = require('express-validator'); -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError } = require('../assets/config/errors'); -const Functions = require('../models/functions'); -const Categories = require('../models/categories'); -const Quotes = require('../models/quotes'); -const Users = require('../models/users'); -const helperQueryNumber = require('../assets/utils/helperQueryNumber'); -const getPagesHelper = require('../assets/utils/getPagesHelper'); -const Sequelize = require('sequelize'); -const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith'); -const { EMAIL_INFO, FRONT_END_HOST } = require('../assets/config/config'); -const transporter = require('../assets/config/transporter'); -const { emailQuoteTemplate } = require('../assets/config/emails'); +const path = require('path') +const fs = require('fs') +const { validationResult } = require('express-validator') +const errorHandling = require('../assets/utils/errorHandling') +const { serverError } = require('../assets/config/errors') +const Functions = require('../models/functions') +const Categories = require('../models/categories') +const Quotes = require('../models/quotes') +const Users = require('../models/users') +const helperQueryNumber = require('../assets/utils/helperQueryNumber') +const getPagesHelper = require('../assets/utils/getPagesHelper') +const Sequelize = require('sequelize') +const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith') +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) => { - resultFunction.title = title; - resultFunction.slug = slug; - resultFunction.description = description; - resultFunction.type = type; - resultFunction.categorieId = categorieId; - resultFunction.isOnline = isOnline; - if (imageName) { - resultFunction.image = `/images/functions/${imageName}`; - } - const result = await resultFunction.save(); - res.status(200).json({ message: "La fonction a bien été modifié!", result }); + resultFunction.title = title + resultFunction.slug = slug + resultFunction.description = description + resultFunction.type = type + resultFunction.categorieId = categorieId + resultFunction.isOnline = isOnline + if (imageName) { + resultFunction.image = `/images/functions/${imageName}` + } + const result = await resultFunction.save() + res.status(200).json({ message: 'La fonction a bien été modifié!', result }) } exports.getFunctions = async (req, res, next) => { - const categoryId = helperQueryNumber(req.query.categoryId, 0); - let search = req.query.search; - try { search = search.toLowerCase(); } catch {}; - const options = { - where: { - // Trie par catégorie - ... (categoryId !== 0) && { categorieId: categoryId }, - // Recherche - ... (search != undefined) && { - [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}%`) } - ] - } - }, - include: [ - { model: Categories, attributes: ["name", "color"] } - ], - attributes: { - exclude: ["updatedAt", "utilizationForm", "article", "isOnline"] - }, - order: [['createdAt', 'DESC']] - }; - return await getPagesHelper({ req, res, next }, Functions, options); + const categoryId = helperQueryNumber(req.query.categoryId, 0) + let search = req.query.search + try { search = search.toLowerCase() } catch {}; + const options = { + where: { + // Trie par catégorie + ...(categoryId !== 0) && { categorieId: categoryId }, + // Recherche + ...(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}%`) } + ] + } + }, + include: [ + { model: Categories, attributes: ['name', 'color'] } + ], + attributes: { + exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] + }, + order: [['createdAt', 'DESC']] + } + return await getPagesHelper({ req, res, next }, Functions, options) } exports.getFunctionBySlug = (req, res, next) => { - const { slug } = req.params; - Functions.findOne({ - where: { slug }, - include: [ - { model: Categories, attributes: ["name", "color"] } - ] + const { slug } = req.params + Functions.findOne({ + where: { slug }, + include: [ + { model: Categories, attributes: ['name', 'color'] } + ] + }) + .then((result) => { + if (!result) { + return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + } + try { result.utilizationForm = JSON.parse(result.utilizationForm) } catch {} + return res.status(200).json(result) + }) + .catch((error) => { + console.log(error) + return errorHandling(next, serverError) }) - .then((result) => { - if (!result) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }); - } - try { result.utilizationForm = JSON.parse(result.utilizationForm); } catch {} - return res.status(200).json(result); - }) - .catch((error) => { - console.log(error); - return errorHandling(next, serverError); - }); } exports.postFunction = (req, res, next) => { - const { title, slug, description, type, categorieId } = req.body; - const image = req.files.image; - const errors = validationResult(req); - if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }); - } - if (!image || image.truncated && ( - image.mimetype !== 'image/png' || - image.mimetype !== 'image/jpg' || + const { title, slug, description, type, categorieId } = req.body + const image = req.files.image + const errors = validationResult(req) + if (!errors.isEmpty()) { + 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 }); + )) { + 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) } - 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); - } - }); + }) } exports.putFunction = async (req, res, next) => { - const { id } = req.params; - const { title, slug, description, type, categorieId, isOnline } = req.body; - const image = req.files.image; - const errors = validationResult(req); - if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }); + const { id } = req.params + const { title, slug, description, type, categorieId, isOnline } = req.body + const image = req.files.image + const errors = validationResult(req) + if (!errors.isEmpty()) { + 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 }) } - 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 }); - } - // 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 }); - } + // 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 }) + } - // Sauvegarde de la fonction - if (image != undefined) { - if (image.truncated && ( - image.mimetype !== 'image/png' || - image.mimetype !== 'image/jpg' || + // 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 }); - } - 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'); - deleteFilesNameStartWith(slug, functionPath, () => { - 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); - }); - }); - } else { - return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline }); - } - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + )) { + 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') + deleteFilesNameStartWith(slug, functionPath, () => { + 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) + }) + }) + } else { + return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline }) } + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.putFunctionArticle = async (req, res, next) => { - const { id } = req.params; - const { article } = req.body; + const { id } = req.params + const { article } = req.body - 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 }); - } - resultFunction.article = article; - const result = await resultFunction.save(); - return res.status(200).json(result); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + 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 }) } + resultFunction.article = article + const result = await resultFunction.save() + return res.status(200).json(result) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.putFunctionForm = async (req, res, next) => { - const { id } = req.params; - const { form } = req.body; + const { id } = req.params + const { form } = req.body - 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 }); - } - resultFunction.utilizationForm = JSON.stringify(form); - const result = await resultFunction.save(); - return res.status(200).json(result); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + 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 }) } + resultFunction.utilizationForm = JSON.stringify(form) + const result = await resultFunction.save() + return res.status(200).json(result) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.deleteFunction = async (req, res, next) => { - const { id } = req.params; - try { - const result = await Functions.findOne({ where: { id } }); - if (!result) { - 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é!"}); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { id } = req.params + try { + const result = await Functions.findOne({ where: { id } }) + if (!result) { + 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é!' }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } 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." }); - } - try { - const result = await Categories.create({ name, color }); - return res.status(201).json({ message: "La catégorie a bien été crée!", result }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } + const { name, color } = req.body + if (!(name && color)) { + 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 }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } 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." }); - } - try { - const category = await Categories.findOne({ where: { id } }); - if (!category) { - return errorHandling(next, { message: "La catégorie n'existe pas." }); - } - 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 }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + 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.' }) + } + try { + const category = await Categories.findOne({ where: { id } }) + if (!category) { + return errorHandling(next, { message: "La catégorie n'existe pas." }) } + 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 }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.deleteCategory = async (req, res, next) => { - const { id } = req.params; - try { - const category = await Categories.findOne({ where: { id } }); - if (!category) { - 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!" }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { id } = req.params + try { + const category = await Categories.findOne({ where: { id } }) + if (!category) { + 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!' }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.getQuotes = async (req, res, next) => { - const options = { - where: { - isValidated: 0, - }, - include: [ - { model: Users, attributes: ["name", "logo"] } - ], - order: [['createdAt', 'DESC']] - }; - return await getPagesHelper({ req, res, next }, Quotes, options); + const options = { + where: { + isValidated: 0 + }, + include: [ + { model: Users, attributes: ['name', 'logo'] } + ], + order: [['createdAt', 'DESC']] + } + return await getPagesHelper({ req, res, next }, Quotes, options) } exports.putQuote = async (req, res, next) => { - const { id } = req.params; - const { isValid } = req.body; - try { - if (typeof isValid !== 'boolean') { - 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"] } - ] - }); - if (!quote) { - return errorHandling(next, { message: "La citation n'existe pas (ou est déjà validé).", statusCode: 404 }); - } - - await transporter.sendMail({ - from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, - to: quote.user.email, - subject: "FunctionProject - Citation proposée", - html: emailQuoteTemplate(isValid, quote, FRONT_END_HOST) - }); - - if (isValid) { - quote.isValidated = true; - await quote.save(); - 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!" }); - } - - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { id } = req.params + const { isValid } = req.body + try { + if (typeof isValid !== 'boolean') { + return errorHandling(next, { message: 'isValid doit être un booléen.', statusCode: 400 }) } -} \ No newline at end of file + const quote = await Quotes.findOne({ + where: { + id, + isValidated: 0 + }, + include: [ + { model: Users, attributes: ['name', 'email'] } + ] + }) + if (!quote) { + return errorHandling(next, { message: "La citation n'existe pas (ou est déjà validé).", statusCode: 404 }) + } + + await transporter.sendMail({ + from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, + to: quote.user.email, + subject: 'FunctionProject - Citation proposée', + html: emailQuoteTemplate(isValid, quote, FRONT_END_HOST) + }) + + if (isValid) { + quote.isValidated = true + await quote.save() + 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!' }) + } + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } +} diff --git a/api/controllers/categories.js b/api/controllers/categories.js index ba24954..7795e04 100644 --- a/api/controllers/categories.js +++ b/api/controllers/categories.js @@ -1,14 +1,14 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const Categories = require('../models/categories'); -const { serverError } = require('../assets/config/errors'); +const errorHandling = require('../assets/utils/errorHandling') +const Categories = require('../models/categories') +const { serverError } = require('../assets/config/errors') exports.getCategories = (_req, res, next) => { - Categories.findAll() - .then((result) => { - res.status(200).json(result); - }) - .catch((error) => { - console.log(error); - return errorHandling(next, serverError); - }); -} \ No newline at end of file + Categories.findAll() + .then((result) => { + res.status(200).json(result) + }) + .catch((error) => { + console.log(error) + return errorHandling(next, serverError) + }) +} diff --git a/api/controllers/comments.js b/api/controllers/comments.js index 1750d2e..767a118 100644 --- a/api/controllers/comments.js +++ b/api/controllers/comments.js @@ -1,72 +1,72 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const Comments = require('../models/comments'); -const Users = require('../models/users'); -const Functions = require('../models/functions'); -const getPagesHelper = require('../assets/utils/getPagesHelper'); -const { serverError } = require('../assets/config/errors'); +const errorHandling = require('../assets/utils/errorHandling') +const Comments = require('../models/comments') +const Users = require('../models/users') +const Functions = require('../models/functions') +const getPagesHelper = require('../assets/utils/getPagesHelper') +const { serverError } = require('../assets/config/errors') exports.getCommentsByFunctionId = async (req, res, next) => { - const { functionId } = req.params; - const options = { - where: { functionId }, - include: [ - { model: Users, attributes: ["name", "logo"] } - ], - order: [['createdAt', 'DESC']] - }; - return await getPagesHelper({ req, res, next }, Comments, options); + const { functionId } = req.params + const options = { + where: { functionId }, + include: [ + { model: Users, attributes: ['name', 'logo'] } + ], + order: [['createdAt', 'DESC']] + } + return await getPagesHelper({ req, res, next }, Comments, options) } exports.postCommentsByFunctionId = async (req, res, next) => { - const { functionId } = req.params; - const { message } = req.body; - try { - const resultFunction = await Functions.findOne({ where: { id: functionId } }); - if (!resultFunction) { - 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 }); - } - const comment = await Comments.create({ message, userId: req.userId, functionId }); - return res.status(201).json(comment); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { functionId } = req.params + const { message } = req.body + try { + const resultFunction = await Functions.findOne({ where: { id: functionId } }) + if (!resultFunction) { + 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 }) + } + const comment = await Comments.create({ message, userId: req.userId, functionId }) + return res.status(201).json(comment) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.deleteCommentById = async (req, res, next) => { - const { commentId } = req.params; - try { - 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 }); - } - await comment.destroy(); - return res.status(200).json({ message: "Le commentaire a bien été supprimé." }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { commentId } = req.params + try { + 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 }) } + await comment.destroy() + return res.status(200).json({ message: 'Le commentaire a bien été supprimé.' }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } 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 }); + const { commentId } = req.params + const { message } = req.body + if (!message) { + 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) } }) + if (!comment) { + return errorHandling(next, { message: "Le commentaire n'existe pas.", statusCode: 404 }) } - try { - 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 }); - } - comment.message = message; - await comment.save(); - return res.status(200).json({ message: "Le commentaire a bien été modifié." }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } -} \ No newline at end of file + comment.message = message + await comment.save() + 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 3e29619..0e1e652 100644 --- a/api/controllers/favorites.js +++ b/api/controllers/favorites.js @@ -1,74 +1,74 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError } = require('../assets/config/errors'); -const Favorites = require('../models/favorites'); -const Functions = require('../models/functions'); +const errorHandling = require('../assets/utils/errorHandling') +const { serverError } = require('../assets/config/errors') +const Favorites = require('../models/favorites') +const Functions = require('../models/functions') exports.getFavoriteByFunctionId = async (req, res, next) => { - const { functionId } = req.params; - const { userId } = req; - try { - const favorite = await Favorites.findOne({ - where: { - userId, - functionId - } - }); - if (!favorite) { - return res.status(200).json({ isFavorite: false }); - } - return res.status(200).json({ isFavorite: true }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { functionId } = req.params + const { userId } = req + try { + const favorite = await Favorites.findOne({ + where: { + userId, + functionId + } + }) + if (!favorite) { + return res.status(200).json({ isFavorite: false }) } + return res.status(200).json({ isFavorite: true }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.postFavoriteByFunctionId = async (req, res, next) => { - const { functionId } = req.params; - const { userId } = req; - try { - const resultFunction = await Functions.findOne({ where: { id: functionId } }); - if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }); - } - const favorite = await Favorites.findOne({ - where: { - userId, - functionId - } - }); - if (!favorite) { - 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 }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { functionId } = req.params + const { userId } = req + try { + const resultFunction = await Functions.findOne({ where: { id: functionId } }) + if (!resultFunction) { + return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) } + const favorite = await Favorites.findOne({ + where: { + userId, + functionId + } + }) + if (!favorite) { + 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 }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.deleteFavoriteByFunctionId = async (req, res, next) => { - const { functionId } = req.params; - const { userId } = req; - try { - const resultFunction = await Functions.findOne({ where: { id: functionId } }); - if (!resultFunction) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }); - } - const favorite = await Favorites.findOne({ - where: { - userId, - functionId - } - }); - if (!favorite) { - 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." }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { functionId } = req.params + const { userId } = req + try { + const resultFunction = await Functions.findOne({ where: { id: functionId } }) + if (!resultFunction) { + return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) } -} \ No newline at end of file + const favorite = await Favorites.findOne({ + where: { + userId, + functionId + } + }) + if (!favorite) { + 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.' }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } +} diff --git a/api/controllers/functions.js b/api/controllers/functions.js index b8fd46a..7620b17 100644 --- a/api/controllers/functions.js +++ b/api/controllers/functions.js @@ -1,69 +1,69 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError } = require('../assets/config/errors'); -const Functions = require('../models/functions'); -const Categories = require('../models/categories'); -const functionToExecute = require('../assets/functions/functionObject'); -const helperQueryNumber = require('../assets/utils/helperQueryNumber'); -const getPagesHelper = require('../assets/utils/getPagesHelper'); -const Sequelize = require('sequelize'); +const errorHandling = require('../assets/utils/errorHandling') +const { serverError } = require('../assets/config/errors') +const Functions = require('../models/functions') +const Categories = require('../models/categories') +const functionToExecute = require('../assets/functions/functionObject') +const helperQueryNumber = require('../assets/utils/helperQueryNumber') +const getPagesHelper = require('../assets/utils/getPagesHelper') +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 {}; - const options = { - where: { - isOnline: 1, - // Trie par catégorie - ... (categoryId !== 0) && { categorieId: categoryId }, - // Recherche - ... (search != undefined) && { - [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}%`) } - ] - } - }, - include: [ - { model: Categories, attributes: ["name", "color"] } - ], - attributes: { - exclude: ["updatedAt", "utilizationForm", "article", "isOnline"] - }, - order: [['createdAt', 'DESC']] - }; - return await getPagesHelper({ req, res, next }, Functions, options); + const categoryId = helperQueryNumber(req.query.categoryId, 0) + let { search } = req.query + try { search = search.toLowerCase() } catch {}; + const options = { + where: { + isOnline: 1, + // Trie par catégorie + ...(categoryId !== 0) && { categorieId: categoryId }, + // Recherche + ...(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}%`) } + ] + } + }, + include: [ + { model: Categories, attributes: ['name', 'color'] } + ], + attributes: { + exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] + }, + order: [['createdAt', 'DESC']] + } + return await getPagesHelper({ req, res, next }, Functions, options) } exports.getFunctionBySlug = (req, res, next) => { - const { slug } = req.params; - Functions.findOne({ - where: { slug, isOnline: 1 }, - attributes: { - exclude: ["updatedAt", "isOnline"] - }, - include: [ - { model: Categories, attributes: ["name", "color"] } - ] + const { slug } = req.params + Functions.findOne({ + where: { slug, isOnline: 1 }, + attributes: { + exclude: ['updatedAt', 'isOnline'] + }, + include: [ + { model: Categories, attributes: ['name', 'color'] } + ] + }) + .then((result) => { + if (!result) { + return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) + } + try { result.utilizationForm = JSON.parse(result.utilizationForm) } catch {} + return res.status(200).json(result) + }) + .catch((error) => { + console.log(error) + return errorHandling(next, serverError) }) - .then((result) => { - if (!result) { - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }); - } - try { result.utilizationForm = JSON.parse(result.utilizationForm); } catch {} - return res.status(200).json(result); - }) - .catch((error) => { - console.log(error); - return errorHandling(next, serverError); - }); } exports.executeFunctionBySlug = (req, res, next) => { - const functionOutput = functionToExecute(req.params.slug); - if (functionOutput !== undefined) { - return functionOutput({ res, next }, req.body); - } - return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }); -} \ No newline at end of file + const functionOutput = functionToExecute(req.params.slug) + if (functionOutput !== undefined) { + return functionOutput({ res, next }, req.body) + } + return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 }) +} diff --git a/api/controllers/quotes.js b/api/controllers/quotes.js index f67dff6..c2f93cb 100644 --- a/api/controllers/quotes.js +++ b/api/controllers/quotes.js @@ -1,37 +1,37 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError, requiredFields } = require('../assets/config/errors'); -const Quotes = require('../models/quotes'); -const Users = require('../models/users'); -const getPagesHelper = require('../assets/utils/getPagesHelper'); +const errorHandling = require('../assets/utils/errorHandling') +const { serverError, requiredFields } = require('../assets/config/errors') +const Quotes = require('../models/quotes') +const Users = require('../models/users') +const getPagesHelper = require('../assets/utils/getPagesHelper') exports.getQuotes = async (req, res, next) => { - const options = { - where: { - isValidated: 1, - }, - include: [ - { model: Users, attributes: ["name", "logo"] } - ], - attributes: { - exclude: ["isValidated"] - }, - order: [['createdAt', 'DESC']] - }; - return await getPagesHelper({ req, res, next }, Quotes, options); + const options = { + where: { + isValidated: 1 + }, + include: [ + { model: Users, attributes: ['name', 'logo'] } + ], + attributes: { + exclude: ['isValidated'] + }, + order: [['createdAt', 'DESC']] + } + return await getPagesHelper({ req, res, next }, Quotes, options) } exports.postQuote = (req, res, next) => { - const { quote, author } = req.body; - // S'il n'y a pas les champs obligatoire - if (!(quote && author)) { - 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." }); - }) - .catch((error) => { - console.log(error); - return errorHandling(next, serverError); - }); -} \ No newline at end of file + const { quote, author } = req.body + // S'il n'y a pas les champs obligatoire + if (!(quote && author)) { + 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." }) + }) + .catch((error) => { + console.log(error) + return errorHandling(next, serverError) + }) +} diff --git a/api/controllers/tasks.js b/api/controllers/tasks.js index e747ec2..2a8dc17 100644 --- a/api/controllers/tasks.js +++ b/api/controllers/tasks.js @@ -1,68 +1,68 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError, requiredFields } = require('../assets/config/errors'); -const Tasks = require('../models/tasks'); +const errorHandling = require('../assets/utils/errorHandling') +const { serverError, requiredFields } = require('../assets/config/errors') +const Tasks = require('../models/tasks') exports.getTasks = async (req, res, next) => { - try { - const tasks = await Tasks.findAll({ - where: { - userId: req.userId - }, - order: [['createdAt', 'DESC']] - }); - return res.status(200).json(tasks); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } + try { + const tasks = await Tasks.findAll({ + where: { + userId: req.userId + }, + order: [['createdAt', 'DESC']] + }) + return res.status(200).json(tasks) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.postTask = async (req, res, next) => { - const { task } = req.body; - try { - if (!task) { - return errorHandling(next, requiredFields); - } - const taskResult = await Tasks.create({ task, userId: req.userId }); - return res.status(201).json(taskResult); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { task } = req.body + try { + if (!task) { + return errorHandling(next, requiredFields) } + const taskResult = await Tasks.create({ task, userId: req.userId }) + return res.status(201).json(taskResult) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.putTask = async (req, res, next) => { - const { id } = req.params; - const { isCompleted } = req.body; - try { - if (typeof isCompleted !== 'boolean') { - return errorHandling(next, { message: "isCompleted doit être un booléen.", statusCode: 400 }); - } - - 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 }); - } - taskResult.isCompleted = isCompleted; - const taskSaved = await taskResult.save(); - return res.status(200).json(taskSaved); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { id } = req.params + const { isCompleted } = req.body + try { + if (typeof isCompleted !== 'boolean') { + return errorHandling(next, { message: 'isCompleted doit être un booléen.', statusCode: 400 }) } + + 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 }) + } + taskResult.isCompleted = isCompleted + const taskSaved = await taskResult.save() + return res.status(200).json(taskSaved) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.deleteTask = async (req, res, next) => { - const { id } = req.params; - try { - 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 }); - } - await taskResult.destroy(); - return res.status(200).json({ message: `La "tâche à faire" a bien été supprimée!` }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { id } = req.params + try { + 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 }) } -} \ No newline at end of file + await taskResult.destroy() + 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 3629958..d6c45ac 100644 --- a/api/controllers/users.js +++ b/api/controllers/users.js @@ -1,292 +1,292 @@ -const path = require('path'); -const { validationResult } = require('express-validator'); -const bcrypt = require('bcryptjs'); -const jwt = require('jsonwebtoken'); -const ms = require('ms'); -const uuid = require('uuid'); -const 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 transporter = require('../assets/config/transporter'); -const { emailUserTemplate } = require('../assets/config/emails'); -const Users = require('../models/users'); -const Favorites = require('../models/favorites'); -const Functions = require('../models/functions'); -const Categories = require('../models/categories'); -const Comments = require('../models/comments'); -const Quotes = require('../models/quotes'); -const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith'); -const getPagesHelper = require('../assets/utils/getPagesHelper'); +const path = require('path') +const { validationResult } = require('express-validator') +const bcrypt = require('bcryptjs') +const jwt = require('jsonwebtoken') +const ms = require('ms') +const uuid = require('uuid') +const 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 transporter = require('../assets/config/transporter') +const { emailUserTemplate } = require('../assets/config/emails') +const Users = require('../models/users') +const Favorites = require('../models/favorites') +const Functions = require('../models/functions') +const Categories = require('../models/categories') +const Comments = require('../models/comments') +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) { - const user = await Users.findOne({ where: { id: userId } }); - user.name = name; - if (user.email !== email) { - const tempToken = uuid.v4(); - user.email = email; - user.isConfirmed = false; - user.tempToken = tempToken; - await transporter.sendMail({ - from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, - to: email, - subject: "FunctionProject - Confirmer l'email", - 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 != undefined) { - user.biography = biography; - } - user.isPublicEmail = isPublicEmail; - if (logoName != undefined && `/images/users/${logoName}` !== user.logo) { - 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 }); +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) { + const tempToken = uuid.v4() + user.email = email + user.isConfirmed = false + user.tempToken = tempToken + await transporter.sendMail({ + from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, + to: email, + subject: "FunctionProject - Confirmer l'email", + 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) { + user.biography = biography + } + user.isPublicEmail = isPublicEmail + if (logoName != null && `/images/users/${logoName}` !== user.logo) { + 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 }) } exports.getUsers = async (req, res, next) => { - let { search } = req.query; - try { search = search.toLowerCase(); } catch {}; - const options = { - where: { - isConfirmed: true, - // Recherche - ...(search != undefined) && { - name: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', `%${search}%`) - } - }, - attributes: { - exclude: ["updatedAt", "isAdmin", "isConfirmed", "password", "tempToken", "tempExpirationToken", "isPublicEmail", "email"] - }, - order: [['createdAt', 'DESC']] - }; - return await getPagesHelper({ req, res, next }, Users, options); + let { search } = req.query + try { search = search.toLowerCase() } catch {}; + const options = { + where: { + isConfirmed: true, + // Recherche + ...(search != null) && { + name: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', `%${search}%`) + } + }, + attributes: { + exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken', 'isPublicEmail', 'email'] + }, + order: [['createdAt', 'DESC']] + } + return await getPagesHelper({ req, res, next }, Users, options) } exports.putUser = async (req, res, next) => { - const { name, email, biography, isPublicEmail } = req.body; - const logo = req.files.logo; - const errors = validationResult(req); - if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }); - } - if (logo != undefined) { - if (!logo || logo.truncated && ( - logo.mimetype !== 'image/png' || - logo.mimetype !== 'image/jpg' || + const { name, email, biography, isPublicEmail } = req.body + const logo = req.files.logo + const errors = validationResult(req) + if (!errors.isEmpty()) { + return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + } + if (logo != 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 }); - } - 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); - }); - }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } - } else { - try { - return await handleEditUser(res, { name, email, biography, isPublicEmail }, req.userId, null); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); - } + )) { + 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) + }) + }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } + } else { + try { + return await handleEditUser(res, { name, email, biography, isPublicEmail }, req.userId, null) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } + } } 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 }); - } - try { - const hashedPassword = await bcrypt.hash(password, 12); - const tempToken = uuid.v4(); - await Users.create({ email, name, password: hashedPassword, tempToken }); - await transporter.sendMail({ - 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.") - }); - 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); - } + const { name, email, password } = req.body + const errors = validationResult(req) + if (!errors.isEmpty()) { + return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + } + try { + const hashedPassword = await bcrypt.hash(password, 12) + const tempToken = uuid.v4() + await Users.create({ email, name, password: hashedPassword, tempToken }) + await transporter.sendMail({ + 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.') + }) + 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) + } } 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 }); + const { email, password } = req.body + const errors = validationResult(req) + if (!errors.isEmpty()) { + return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + } + try { + const user = await Users.findOne({ where: { email } }) + if (!user) { + return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", 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 }); - } - 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 }); - } - if (!user.isConfirmed) { - 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) }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + 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 }) } + if (!user.isConfirmed) { + 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) }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.confirmEmail = async (req, res, next) => { - const { tempToken } = req.params; - if (!tempToken) { - return errorHandling(next, generalError); - } - try { - const user = await Users.findOne({ where: { tempToken, isConfirmed: false } }); - if (!user) { - return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 }); - } - user.tempToken = null; - user.isConfirmed = true; - await user.save(); - return res.redirect(`${FRONT_END_HOST}/users/login?isConfirmed=true`); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { tempToken } = req.params + if (!tempToken) { + return errorHandling(next, generalError) + } + try { + const user = await Users.findOne({ where: { tempToken, isConfirmed: false } }) + if (!user) { + return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 }) } + user.tempToken = null + user.isConfirmed = true + await user.save() + return res.redirect(`${FRONT_END_HOST}/users/login?isConfirmed=true`) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.resetPassword = async (req, res, next) => { - const { email } = req.body; - const errors = validationResult(req); - if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }); - } - try { - const user = await Users.findOne({ where: { email, tempToken: null } }); - if (!user) { - return errorHandling(next, { message: "L'adresse email n'existe pas ou une demande est déjà en cours.", statusCode: 400 }); - } - const tempToken = uuid.v4(); - user.tempExpirationToken = Date.now() + 3600000; // 1 heure - user.tempToken = tempToken; - await user.save(); - await transporter.sendMail({ - from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, - to: email, - subject: "FunctionProject - Réinitialisation du mot de passe", - 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!" }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { email } = req.body + const errors = validationResult(req) + if (!errors.isEmpty()) { + return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + } + try { + const user = await Users.findOne({ where: { email, tempToken: null } }) + if (!user) { + return errorHandling(next, { message: "L'adresse email n'existe pas ou une demande est déjà en cours.", statusCode: 400 }) } + const tempToken = uuid.v4() + user.tempExpirationToken = Date.now() + 3600000 // 1 heure + user.tempToken = tempToken + await user.save() + await transporter.sendMail({ + from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, + to: email, + subject: 'FunctionProject - Réinitialisation du mot de passe', + 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!' }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.newPassword = async (req, res, next) => { - const { tempToken, password } = req.body; - const errors = validationResult(req); - if (!errors.isEmpty()) { - return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }); - } - try { - const user = await Users.findOne({ where: { tempToken } }); - if (!user && parseInt(tempExpirationToken) < Date.now()) { - return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 }); - } - const hashedPassword = await bcrypt.hash(password, 12); - user.password = hashedPassword; - user.tempToken = null; - user.tempExpirationToken = null; - await user.save(); - return res.status(200).json({ result: "Le mot de passe a bien été modifié!" }); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { tempToken, password } = req.body + const errors = validationResult(req) + if (!errors.isEmpty()) { + return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 }) + } + try { + const user = await Users.findOne({ where: { tempToken } }) + if (!user && parseInt(user.tempExpirationToken) < Date.now()) { + return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 }) } + const hashedPassword = await bcrypt.hash(password, 12) + user.password = hashedPassword + user.tempToken = null + user.tempExpirationToken = null + await user.save() + return res.status(200).json({ result: 'Le mot de passe a bien été modifié!' }) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } } exports.getUserInfo = async (req, res, next) => { - const { name } = req.params; - try { - const user = await Users.findOne({ - where: { name, isConfirmed: true }, - attributes: { - exclude: ["updatedAt", "isAdmin", "isConfirmed", "password", "tempToken", "tempExpirationToken"] - }, - }); - if (!user) { - return errorHandling(next, { message: "L'utilisateur n'existe pas.", statusCode: 404 }); - } - const favorites = await Favorites.findAll({ - where: { userId: user.id }, - include: [ - { model: Functions, attributes: { exclude: ["updatedAt", "utilizationForm", "article", "isOnline"] }, include: { model: Categories, attributes: ["name", "color"] } } - ], - order: [['createdAt', 'DESC']], - limit: 5 - }); - 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"] } } - ], - order: [['createdAt', 'DESC']], - limit: 5 - }); - const commentsArray = comments.map((commentObject) => { - return { - id: commentObject.id, - message: commentObject.message, - createdAt: commentObject.createdAt, - function: commentObject.function.dataValues - }; - }); - const quotesArray = await Quotes.findAll({ - where: { userId: user.id }, - attributes: { - exclude: ["updatedAt", "createdAt", "isValidated", "userId", "id"] - }, - order: [['createdAt', 'DESC']], - limit: 5, - }); - const userObject = { - // Si Public Email - ... (user.isPublicEmail) && { email: user.email }, - isPublicEmail: user.isPublicEmail, - name: user.name, - biography: user.biography, - logo: user.logo, - createdAt: user.createdAt, - favoritesArray, - commentsArray, - quotesArray - }; - return res.status(200).json(userObject); - } catch (error) { - console.log(error); - return errorHandling(next, serverError); + const { name } = req.params + try { + const user = await Users.findOne({ + where: { name, isConfirmed: true }, + attributes: { + exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken'] + } + }) + if (!user) { + return errorHandling(next, { message: "L'utilisateur n'existe pas.", statusCode: 404 }) } -} \ No newline at end of file + const favorites = await Favorites.findAll({ + where: { userId: user.id }, + include: [ + { 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 comments = await Comments.findAll({ + where: { userId: user.id }, + include: [ + { model: Functions, attributes: { exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] } } + ], + order: [['createdAt', 'DESC']], + limit: 5 + }) + const commentsArray = comments.map((commentObject) => { + return { + id: commentObject.id, + message: commentObject.message, + createdAt: commentObject.createdAt, + function: commentObject.function.dataValues + } + }) + const quotesArray = await Quotes.findAll({ + where: { userId: user.id }, + attributes: { + exclude: ['updatedAt', 'createdAt', 'isValidated', 'userId', 'id'] + }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + const userObject = { + // Si Public Email + ...(user.isPublicEmail) && { email: user.email }, + isPublicEmail: user.isPublicEmail, + name: user.name, + biography: user.biography, + logo: user.logo, + createdAt: user.createdAt, + favoritesArray, + commentsArray, + quotesArray + } + return res.status(200).json(userObject) + } catch (error) { + console.log(error) + return errorHandling(next, serverError) + } +} diff --git a/api/middlewares/isAdmin.js b/api/middlewares/isAdmin.js index f0ac9e0..c2f6993 100644 --- a/api/middlewares/isAdmin.js +++ b/api/middlewares/isAdmin.js @@ -1,23 +1,23 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError } = require('../assets/config/errors'); -const Users = require('../models/users'); +const errorHandling = require('../assets/utils/errorHandling') +const { serverError } = require('../assets/config/errors') +const Users = require('../models/users') module.exports = (req, _res, next) => { - if (!req.userId) { - return errorHandling(next, { message: "Vous n'êtes pas connecté.", statusCode: 403 }); - } - Users.findOne({ where: { id: req.userId } }) - .then((user) => { - if (!user) { - 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 }); - } - next(); - }) - .catch((error) => { - console.log(error); - return errorHandling(next, serverError); - }); -} \ No newline at end of file + if (!req.userId) { + return errorHandling(next, { message: "Vous n'êtes pas connecté.", statusCode: 403 }) + } + Users.findOne({ where: { id: req.userId } }) + .then((user) => { + if (!user) { + 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 }) + } + next() + }) + .catch((error) => { + console.log(error) + return errorHandling(next, serverError) + }) +} diff --git a/api/middlewares/isAuth.js b/api/middlewares/isAuth.js index c8979fc..a3afeb7 100644 --- a/api/middlewares/isAuth.js +++ b/api/middlewares/isAuth.js @@ -1,24 +1,24 @@ -const jwt = require('jsonwebtoken'); -const errorHandling = require('../assets/utils/errorHandling'); -const { JWT_SECRET } = require('../assets/config/config'); +const jwt = require('jsonwebtoken') +const errorHandling = require('../assets/utils/errorHandling') +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 }); - } + const token = req.get('Authorization') + if (!token) { + 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 }); - } + 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 }) + } - if (!decodedToken) { - 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 }) + } - req.userId = decodedToken.userId; - next(); -} \ No newline at end of file + req.userId = decodedToken.userId + next() +} diff --git a/api/models/categories.js b/api/models/categories.js index 118373f..f48c087 100644 --- a/api/models/categories.js +++ b/api/models/categories.js @@ -1,13 +1,13 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('categorie', { - name: { - type: Sequelize.STRING, - allowNull: false, - }, - color: { - type: Sequelize.STRING, - allowNull: false, - } -}); \ No newline at end of file + name: { + type: Sequelize.STRING, + allowNull: false + }, + color: { + type: Sequelize.STRING, + allowNull: false + } +}) diff --git a/api/models/comments.js b/api/models/comments.js index 5655e07..32d5d97 100644 --- a/api/models/comments.js +++ b/api/models/comments.js @@ -1,9 +1,9 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('comment', { - message: { - type: Sequelize.TEXT, - allowNull: false - } -}); \ No newline at end of file + message: { + type: Sequelize.TEXT, + allowNull: false + } +}) diff --git a/api/models/favorites.js b/api/models/favorites.js index d560f57..4e90284 100644 --- a/api/models/favorites.js +++ b/api/models/favorites.js @@ -1,4 +1,3 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const sequelize = require('../assets/utils/database') -module.exports = sequelize.define('favorite', {}); \ No newline at end of file +module.exports = sequelize.define('favorite', {}) diff --git a/api/models/functions.js b/api/models/functions.js index da86d5a..5d0a96b 100644 --- a/api/models/functions.js +++ b/api/models/functions.js @@ -1,39 +1,39 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('function', { - title: { - type: Sequelize.STRING, - allowNull: false, - }, - slug: { - type: Sequelize.STRING, - allowNull: false, - }, - description: { - type: Sequelize.STRING, - allowNull: false - }, - image: { - type: Sequelize.STRING, - allowNull: false, - defaultValue: "/images/functions/default.png" - }, - type: { - type: Sequelize.STRING, - allowNull: false - }, - article: { - type: Sequelize.TEXT, - allowNull: true - }, - utilizationForm: { - type: Sequelize.TEXT, - allowNull: true - }, - isOnline: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: 0 - } -}); \ No newline at end of file + title: { + type: Sequelize.STRING, + allowNull: false + }, + slug: { + type: Sequelize.STRING, + allowNull: false + }, + description: { + type: Sequelize.STRING, + allowNull: false + }, + image: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: '/images/functions/default.png' + }, + type: { + type: Sequelize.STRING, + allowNull: false + }, + article: { + type: Sequelize.TEXT, + allowNull: true + }, + utilizationForm: { + type: Sequelize.TEXT, + allowNull: true + }, + isOnline: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: 0 + } +}) diff --git a/api/models/quotes.js b/api/models/quotes.js index dcb7f8e..181e1e2 100644 --- a/api/models/quotes.js +++ b/api/models/quotes.js @@ -1,18 +1,18 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('quote', { - quote: { - type: Sequelize.STRING, - allowNull: false, - }, - author: { - type: Sequelize.STRING, - allowNull: false, - }, - isValidated: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: 0 - } -}); \ No newline at end of file + quote: { + type: Sequelize.STRING, + allowNull: false + }, + author: { + type: Sequelize.STRING, + allowNull: false + }, + isValidated: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: 0 + } +}) diff --git a/api/models/short_links.js b/api/models/short_links.js index a6317a8..b994d1c 100644 --- a/api/models/short_links.js +++ b/api/models/short_links.js @@ -1,13 +1,13 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('short_link', { - url: { - type: Sequelize.TEXT, - allowNull: false, - }, - shortcut: { - type: Sequelize.TEXT, - allowNull: false, - } -}); \ No newline at end of file + url: { + type: Sequelize.TEXT, + allowNull: false + }, + shortcut: { + type: Sequelize.TEXT, + allowNull: false + } +}) diff --git a/api/models/tasks.js b/api/models/tasks.js index a80b24c..13e8442 100644 --- a/api/models/tasks.js +++ b/api/models/tasks.js @@ -1,14 +1,14 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('task', { - task: { - type: Sequelize.STRING, - allowNull: false, - }, - isCompleted: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: 0 - } -}); \ No newline at end of file + task: { + type: Sequelize.STRING, + allowNull: false + }, + isCompleted: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: 0 + } +}) diff --git a/api/models/users.js b/api/models/users.js index 383a421..ef319e9 100644 --- a/api/models/users.js +++ b/api/models/users.js @@ -1,45 +1,45 @@ -const Sequelize = require('sequelize'); -const sequelize = require('../assets/utils/database'); +const Sequelize = require('sequelize') +const sequelize = require('../assets/utils/database') module.exports = sequelize.define('user', { - name: { - type: Sequelize.STRING, - allowNull: false, - }, - email: { - type: Sequelize.STRING, - allowNull: false, - }, - password: { - type: Sequelize.STRING, - allowNull: false, - }, - biography: { - type: Sequelize.TEXT, - defaultValue: "" - }, - logo: { - type: Sequelize.STRING, - defaultValue: "/images/users/default.png" - }, - isConfirmed: { - type: Sequelize.BOOLEAN, - defaultValue: false - }, - isPublicEmail: { - type: Sequelize.BOOLEAN, - defaultValue: false - }, - isAdmin: { - type: Sequelize.BOOLEAN, - defaultValue: false - }, - tempToken: { - type: Sequelize.TEXT, - allowNull: true - }, - tempExpirationToken: { - type: Sequelize.DATE, - allowNull: true - } -}); \ No newline at end of file + name: { + type: Sequelize.STRING, + allowNull: false + }, + email: { + type: Sequelize.STRING, + allowNull: false + }, + password: { + type: Sequelize.STRING, + allowNull: false + }, + biography: { + type: Sequelize.TEXT, + defaultValue: '' + }, + logo: { + type: Sequelize.STRING, + defaultValue: '/images/users/default.png' + }, + isConfirmed: { + type: Sequelize.BOOLEAN, + defaultValue: false + }, + isPublicEmail: { + type: Sequelize.BOOLEAN, + defaultValue: false + }, + isAdmin: { + type: Sequelize.BOOLEAN, + defaultValue: false + }, + tempToken: { + type: Sequelize.TEXT, + allowNull: true + }, + tempExpirationToken: { + type: Sequelize.DATE, + allowNull: true + } +}) diff --git a/api/package-lock.json b/api/package-lock.json index 871b6bd..2e7beff 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -4,6 +4,90 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/node": { "version": "13.9.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz", @@ -43,6 +127,12 @@ "acorn-walk": "^7.1.1" } }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, "acorn-walk": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", @@ -60,27 +150,58 @@ } }, "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } } }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "ansicolors": { @@ -103,11 +224,31 @@ "picomatch": "^2.0.4" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -121,6 +262,12 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -173,9 +320,9 @@ "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true }, "bluebird": { @@ -206,18 +353,19 @@ "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" }, "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", "dev": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" } }, "brace-expansion": { @@ -249,6 +397,12 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "busboy": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", @@ -262,10 +416,48 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelize": { @@ -273,12 +465,6 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true - }, "cardinal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", @@ -294,20 +480,42 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", + "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -317,21 +525,45 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" + "readdirp": "~3.4.0" } }, "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", "dev": true }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "cls-bluebird": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", @@ -342,18 +574,18 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "combined-stream": { @@ -370,20 +602,38 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "dev": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" } }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -426,30 +676,23 @@ "vary": "^1" } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, "cssom": { @@ -510,11 +753,26 @@ } } }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, "decimal.js": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -526,6 +784,43 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "deglob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.1.tgz", + "integrity": "sha512-/g+RDZ7yf2HvoW+E5Cy+K94YhgcFgr6C8LuHZD1O5HoNPkf3KY6RfXJ0DBGlB/NkLi5gml+G9zqRzk9S0mHZCg==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^5.0.0", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -559,6 +854,15 @@ "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", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "domexception": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", @@ -580,12 +884,12 @@ "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "^2.0.0" } }, "dotenv": { @@ -627,11 +931,71 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -655,11 +1019,325 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", + "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", + "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "dev": true, + "requires": { + "eslint-utils": "^1.4.2", + "regexpp": "^3.0.0" + }, + "dependencies": { + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", + "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "dev": true, + "requires": { + "eslint-plugin-es": "^2.0.0", + "eslint-utils": "^1.4.2", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", + "resolve": "^1.10.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -675,21 +1353,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, "expect-ct": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", @@ -769,6 +1432,17 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -794,6 +1468,24 @@ "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -817,6 +1509,38 @@ "unpipe": "~1.0.0" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -872,13 +1596,31 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -887,12 +1629,21 @@ "is-property": "^1.0.2" } }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", "dev": true }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -901,47 +1652,70 @@ "assert-plus": "^1.0.0" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "^1.3.5" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" } }, "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", "dev": true, "requires": { - "create-error-class": "^3.0.0", + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" } }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "har-schema": { @@ -958,12 +1732,33 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "helmet": { "version": "3.21.3", "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.3.tgz", @@ -1014,6 +1809,12 @@ "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, "hpkp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", @@ -1042,6 +1843,12 @@ "whatwg-encoding": "^1.0.5" } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -1077,12 +1884,28 @@ "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", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -1100,6 +1923,16 @@ "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -1111,6 +1944,69 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -1121,6 +2017,12 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1135,15 +2037,27 @@ "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "requires": { - "ci-info": "^1.5.0" + "ci-info": "^2.0.0" } }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1166,19 +2080,19 @@ } }, "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" } }, "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", "dev": true }, "is-number": { @@ -1188,19 +2102,16 @@ "dev": true }, "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true }, "is-potential-custom-element-name": { "version": "1.0.0", @@ -1212,29 +2123,47 @@ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, - "is-stream": { + "is-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", "dev": true }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1246,6 +2175,22 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -1284,6 +2229,18 @@ "xml-name-validator": "^3.0.0" } }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -1294,6 +2251,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -1334,6 +2297,16 @@ "verror": "1.10.0" } }, + "jsx-ast-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" + } + }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -1353,13 +2326,22 @@ "safe-buffer": "^5.0.1" } }, - "latest-version": { + "keyv": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, "requires": { - "package-json": "^4.0.0" + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" } }, "levn": { @@ -1371,6 +2353,28 @@ "type-check": "~0.3.2" } }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", @@ -1421,6 +2425,15 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -1437,12 +2450,20 @@ } }, "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "pify": "^3.0.0" + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "media-typer": { @@ -1478,6 +2499,18 @@ "mime-db": "1.43.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1493,6 +2526,15 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -1524,6 +2566,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "mysql2": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz", @@ -1571,11 +2619,23 @@ "lru-cache": "^4.1.3" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "nocache": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", @@ -1587,9 +2647,9 @@ "integrity": "sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA==" }, "nodemon": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", - "integrity": "sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", + "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", "dev": true, "requires": { "chokidar": "^3.2.2", @@ -1601,7 +2661,7 @@ "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" + "update-notifier": "^4.0.0" }, "dependencies": { "debug": { @@ -1612,12 +2672,6 @@ "requires": { "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==", - "dev": true } } }, @@ -1630,20 +2684,29 @@ "abbrev": "1" } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true }, "nwsapi": { "version": "2.2.0", @@ -1660,6 +2723,65 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -1674,6 +2796,24 @@ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -1687,22 +2827,78 @@ "word-wrap": "~1.2.3" } }, - "p-finally": { + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" } }, "parse5": { @@ -1715,10 +2911,16 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { @@ -1727,39 +2929,181 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", - "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, + "pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1780,16 +3124,35 @@ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -1823,13 +3186,51 @@ "strip-json-comments": "~2.0.1" } }, - "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "picomatch": "^2.0.7" + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" } }, "redeyed": { @@ -1845,23 +3246,28 @@ "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", + "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "^1.2.8" } }, "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "^1.2.8" } }, "request": { @@ -1941,6 +3347,40 @@ } } }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "retry-as-promised": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", @@ -1949,6 +3389,36 @@ "any-promise": "^1.3.0" } }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1973,12 +3443,20 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "send": { @@ -2107,11 +3585,48 @@ "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, "smart-request-balancer": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/smart-request-balancer/-/smart-request-balancer-2.1.1.tgz", @@ -2141,12 +3656,117 @@ } } }, + "snazzy": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/snazzy/-/snazzy-8.0.0.tgz", + "integrity": "sha512-59GS69hQD8FvJoNGeDz8aZtbYhkCFxCPQB1BFzAWiVVwPmS/J6Vjaku0k6tGNsdSxQ0kAlButdkn8bPR2hLcBw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "inherits": "^2.0.1", + "minimist": "^1.1.1", + "readable-stream": "^3.0.2", + "standard-json": "^1.0.0", + "strip-ansi": "^4.0.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "optional": true }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -2168,6 +3788,44 @@ "tweetnacl": "~0.14.0" } }, + "standard": { + "version": "14.3.4", + "resolved": "https://registry.npmjs.org/standard/-/standard-14.3.4.tgz", + "integrity": "sha512-+lpOkFssMkljJ6eaILmqxHQ2n4csuEABmcubLTb9almFi1ElDzXb1819fjf/5ygSyePCq4kU2wMdb2fBfb9P9Q==", + "dev": true, + "requires": { + "eslint": "~6.8.0", + "eslint-config-standard": "14.1.1", + "eslint-config-standard-jsx": "8.1.0", + "eslint-plugin-import": "~2.18.0", + "eslint-plugin-node": "~10.0.0", + "eslint-plugin-promise": "~4.2.1", + "eslint-plugin-react": "~7.14.2", + "eslint-plugin-standard": "~4.0.0", + "standard-engine": "^12.0.0" + } + }, + "standard-engine": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.1.0.tgz", + "integrity": "sha512-DVJnWM1CGkag4ucFLGdiYWa5/kJURPONmMmk17p8FT5NE4UnPZB1vxWnXnRo2sPSL78pWJG8xEM+1Tu19z0deg==", + "dev": true, + "requires": { + "deglob": "^4.0.1", + "get-stdin": "^7.0.0", + "minimist": "^1.2.5", + "pkg-conf": "^3.1.0" + } + }, + "standard-json": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/standard-json/-/standard-json-1.1.0.tgz", + "integrity": "sha512-nkonX+n5g3pyVBvJZmvRlFtT/7JyLbNh4CtrYC3Qfxihgs8PKX52f6ONKQXORStuBWJ5PI83EUrNXme7LKfiTQ==", + "dev": true, + "requires": { + "concat-stream": "^2.0.0" + } + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -2184,28 +3842,95 @@ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "strip-json-comments": { @@ -2228,19 +3953,62 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { - "execa": "^0.7.0" + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } } }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", "dev": true }, "to-regex-range": { @@ -2289,6 +4057,12 @@ "punycode": "^2.1.1" } }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2310,6 +4084,12 @@ "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2319,6 +4099,21 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "undefsafe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", @@ -2328,13 +4123,19 @@ "debug": "^2.2.0" } }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "^2.0.0" } }, "unpipe": { @@ -2342,28 +4143,25 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", "dev": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" } }, "uri-js": { @@ -2375,14 +4173,20 @@ } }, "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "^2.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2393,6 +4197,22 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==" }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "validator": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz", @@ -2474,12 +4294,12 @@ } }, "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, "requires": { - "string-width": "^2.1.1" + "string-width": "^4.0.0" } }, "wkx": { @@ -2495,15 +4315,31 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "ws": { @@ -2517,9 +4353,9 @@ "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" }, "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, "xml-name-validator": { @@ -2532,6 +4368,12 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/api/package.json b/api/package.json index d2312da..684353c 100644 --- a/api/package.json +++ b/api/package.json @@ -2,14 +2,11 @@ "name": "api", "version": "2.0.0", "description": "Backend REST API for FunctionProject", - "main": "app.js", "scripts": { "start": "node app.js", - "dev": "nodemon app.js" + "dev": "nodemon app.js", + "format": "standard \"./**/*.js\" --fix | snazzy || exit 0" }, - "keywords": [], - "author": "Divlo", - "license": "MIT", "dependencies": { "axios": "^0.19.2", "bcryptjs": "^2.4.3", @@ -33,6 +30,8 @@ "devDependencies": { "dotenv": "^8.2.0", "morgan": "^1.9.1", - "nodemon": "^2.0.2" + "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 04fb536..17cb27d 100644 --- a/api/routes/admin.js +++ b/api/routes/admin.js @@ -1,205 +1,205 @@ -const { Router } = require('express'); -const fileUpload = require('express-fileupload'); -const { body } = require('express-validator'); -const adminController = require('../controllers/admin'); -const Functions = require('../models/functions'); -const Categories = require('../models/categories'); +const { Router } = require('express') +const fileUpload = require('express-fileupload') +const { body } = require('express-validator') +const adminController = require('../controllers/admin') +const Functions = require('../models/functions') +const Categories = require('../models/categories') -const AdminRouter = Router(); +const AdminRouter = Router() AdminRouter.route('/functions') - // Récupère les fonctions - .get(adminController.getFunctions) +// 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("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("La fonction doit avoir un slug."); - } - return true; - })) - .custom((async (slug) => { - try { - const FunctionSlug = await Functions.findOne({ where: { slug } }); - if (FunctionSlug) { - return Promise.reject("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("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("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('Le type de la fonction peut être : article, form ou page.'); - } - return true; - }) - ], adminController.postFunction); +// 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à...')) + } + } 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 true + }) + ], adminController.postFunction) AdminRouter.route('/functions/:slug') - // Récupère les informations d'une fonction - .get(adminController.getFunctionBySlug); +// 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("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("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("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("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('Le type de la fonction peut être : article, form ou page.'); - } - return true; - }) - ], adminController.putFunction) +// 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!")) + } + } 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 true + }) + ], adminController.putFunction) - // Supprime une fonction avec son id - .delete(adminController.deleteFunction); +// Supprime une fonction avec son id + .delete(adminController.deleteFunction) AdminRouter.route('/functions/article/:id') - .put(adminController.putFunctionArticle); + .put(adminController.putFunctionArticle) AdminRouter.route('/functions/form/:id') - .put(adminController.putFunctionForm); + .put(adminController.putFunctionForm) AdminRouter.route('/categories') - // Crée une catégorie - .post(adminController.postCategory); +// Crée une catégorie + .post(adminController.postCategory) AdminRouter.route('/categories/:id') - // Modifier une catégorie avec son id - .put(adminController.putCategory) +// Modifier une catégorie avec son id + .put(adminController.putCategory) - // Supprime une catégorie avec son id - .delete(adminController.deleteCategory); +// Supprime une catégorie avec son id + .delete(adminController.deleteCategory) AdminRouter.route('/quotes') - // Récupère les citations pas encore validées - .get(adminController.getQuotes); +// Récupère les citations pas encore validées + .get(adminController.getQuotes) AdminRouter.route('/quotes/:id') - // Valide ou supprime une citation - .put(adminController.putQuote); +// Valide ou supprime une citation + .put(adminController.putQuote) -module.exports = AdminRouter; \ No newline at end of file +module.exports = AdminRouter diff --git a/api/routes/categories.js b/api/routes/categories.js index dd28f0e..74ad2d3 100644 --- a/api/routes/categories.js +++ b/api/routes/categories.js @@ -1,11 +1,11 @@ -const { Router } = require('express'); -const categoriesController = require('../controllers/categories'); +const { Router } = require('express') +const categoriesController = require('../controllers/categories') -const CategoriesRouter = Router(); +const CategoriesRouter = Router() CategoriesRouter.route('/') - // Récupère les catégories - .get(categoriesController.getCategories); +// Récupère les catégories + .get(categoriesController.getCategories) -module.exports = CategoriesRouter; \ No newline at end of file +module.exports = CategoriesRouter diff --git a/api/routes/comments.js b/api/routes/comments.js index dc201f2..d370db9 100644 --- a/api/routes/comments.js +++ b/api/routes/comments.js @@ -1,23 +1,23 @@ -const { Router } = require('express'); -const commentsController = require('../controllers/comments'); -const isAuth = require('../middlewares/isAuth'); +const { Router } = require('express') +const commentsController = require('../controllers/comments') +const isAuth = require('../middlewares/isAuth') -const CommentsRouter = Router(); +const CommentsRouter = Router() CommentsRouter.route('/:commentId') - // Modifier un commentaire - .put(isAuth, commentsController.putCommentsById) +// Modifier un commentaire + .put(isAuth, commentsController.putCommentsById) - // Supprime un commentaire - .delete(isAuth, commentsController.deleteCommentById); +// Supprime un commentaire + .delete(isAuth, commentsController.deleteCommentById) CommentsRouter.route('/:functionId') - // Récupère les commentaires - .get(commentsController.getCommentsByFunctionId) +// Récupère les commentaires + .get(commentsController.getCommentsByFunctionId) - // Permet à un utilisateur de poster un commentaire sur une fonction - .post(isAuth, commentsController.postCommentsByFunctionId); +// Permet à un utilisateur de poster un commentaire sur une fonction + .post(isAuth, commentsController.postCommentsByFunctionId) -module.exports = CommentsRouter; \ No newline at end of file +module.exports = CommentsRouter diff --git a/api/routes/favorites.js b/api/routes/favorites.js index af2799f..b3c287d 100644 --- a/api/routes/favorites.js +++ b/api/routes/favorites.js @@ -1,18 +1,18 @@ -const { Router } = require('express'); -const favoritesController = require('../controllers/favorites'); -const isAuth = require('../middlewares/isAuth'); +const { Router } = require('express') +const favoritesController = require('../controllers/favorites') +const isAuth = require('../middlewares/isAuth') -const FavoritesRouter = Router(); +const FavoritesRouter = Router() FavoritesRouter.route('/:functionId') - // Récupère si une fonction est en favoris (d'un utilisateur) - .get(isAuth, favoritesController.getFavoriteByFunctionId) +// 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 - .post(isAuth, favoritesController.postFavoriteByFunctionId) +// Permet à un utilisateur d'ajouter une fonction aux favoris + .post(isAuth, favoritesController.postFavoriteByFunctionId) - // Supprime une fonction des favoris d'un utilisateur - .delete(isAuth, favoritesController.deleteFavoriteByFunctionId); +// Supprime une fonction des favoris d'un utilisateur + .delete(isAuth, favoritesController.deleteFavoriteByFunctionId) -module.exports = FavoritesRouter; \ No newline at end of file +module.exports = FavoritesRouter diff --git a/api/routes/functions.js b/api/routes/functions.js index 2605107..c713923 100644 --- a/api/routes/functions.js +++ b/api/routes/functions.js @@ -1,19 +1,19 @@ -const { Router } = require('express'); -const functionsController = require('../controllers/functions'); +const { Router } = require('express') +const functionsController = require('../controllers/functions') -const FunctionsRouter = Router(); +const FunctionsRouter = Router() FunctionsRouter.route('/') - // Récupère les fonctions - .get(functionsController.getFunctions); +// Récupère les fonctions + .get(functionsController.getFunctions) FunctionsRouter.route('/:slug') - // Récupère les informations de la fonction par son slug - .get(functionsController.getFunctionBySlug) +// Récupère les informations de la fonction par son slug + .get(functionsController.getFunctionBySlug) - // Exécute la fonction demandée en paramètre - .post(functionsController.executeFunctionBySlug); +// Exécute la fonction demandée en paramètre + .post(functionsController.executeFunctionBySlug) -module.exports = FunctionsRouter; \ No newline at end of file +module.exports = FunctionsRouter diff --git a/api/routes/quotes.js b/api/routes/quotes.js index 296d96a..523b3c6 100644 --- a/api/routes/quotes.js +++ b/api/routes/quotes.js @@ -1,15 +1,15 @@ -const { Router } = require('express'); -const quotesController = require('../controllers/quotes'); -const isAuth = require('../middlewares/isAuth'); +const { Router } = require('express') +const quotesController = require('../controllers/quotes') +const isAuth = require('../middlewares/isAuth') -const QuotesRouter = Router(); +const QuotesRouter = Router() QuotesRouter.route('/') - // Récupère les citations - .get(quotesController.getQuotes) +// Récupère les citations + .get(quotesController.getQuotes) - // Proposer une citation - .post(isAuth, quotesController.postQuote); +// Proposer une citation + .post(isAuth, quotesController.postQuote) -module.exports = QuotesRouter; \ No newline at end of file +module.exports = QuotesRouter diff --git a/api/routes/tasks.js b/api/routes/tasks.js index 0f7c721..b156fb1 100644 --- a/api/routes/tasks.js +++ b/api/routes/tasks.js @@ -1,23 +1,23 @@ -const { Router } = require('express'); -const tasksController = require('../controllers/tasks'); -const isAuth = require('../middlewares/isAuth'); +const { Router } = require('express') +const tasksController = require('../controllers/tasks') +const isAuth = require('../middlewares/isAuth') -const TasksRouter = Router(); +const TasksRouter = Router() TasksRouter.route('/') - // Récupère les tâches à faire d'un user - .get(isAuth, tasksController.getTasks) +// Récupère les tâches à faire d'un user + .get(isAuth, tasksController.getTasks) - // Poster une nouvelle tâche à faire - .post(isAuth, tasksController.postTask); +// Poster une nouvelle tâche à faire + .post(isAuth, tasksController.postTask) TasksRouter.route('/:id') - // Permet de mettre une tâche à faire en isCompleted ou !isCompleted - .put(isAuth, tasksController.putTask) +// Permet de mettre une tâche à faire en isCompleted ou !isCompleted + .put(isAuth, tasksController.putTask) - // Supprimer une tâche à faire - .delete(isAuth, tasksController.deleteTask); +// Supprimer une tâche à faire + .delete(isAuth, tasksController.deleteTask) -module.exports = TasksRouter; \ No newline at end of file +module.exports = TasksRouter diff --git a/api/routes/users.js b/api/routes/users.js index d3736a7..76112b9 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -1,145 +1,145 @@ -const { Router } = require('express'); -const { body } = require('express-validator'); -const fileUpload = require('express-fileupload'); -const usersController = require('../controllers/users'); -const { requiredFields } = require('../assets/config/errors'); -const Users = require('../models/users'); -const isAuth = require('../middlewares/isAuth'); +const { Router } = require('express') +const { body } = require('express-validator') +const fileUpload = require('express-fileupload') +const usersController = require('../controllers/users') +const { requiredFields } = require('../assets/config/errors') +const Users = require('../models/users') +const isAuth = require('../middlewares/isAuth') -const UsersRouter = Router(); +const UsersRouter = Router() UsersRouter.route('/') - // Récupère les utilisateurs - .get(usersController.getUsers) +// Récupère les utilisateurs + .get(usersController.getUsers) - // Permet de modifier son profil - .put(isAuth, - fileUpload({ - useTempFiles: true, - safeFileNames: true, - preserveExtension: Number, - limits: { fileSize: 5 * 1024 * 1024 }, // 5mb, - parseNested: true +// Permet de modifier son profil + .put(isAuth, + fileUpload({ + useTempFiles: true, + safeFileNames: true, + preserveExtension: Number, + limits: { fileSize: 5 * 1024 * 1024 }, // 5mb, + parseNested: true }), [ - body('email') - .isEmail() - .withMessage("Veuillez rentré une adresse mail valide.") - .custom((async (email) => { - try { - const user = await Users.findOne({ where: { email } }); - if (user && user.email !== email) { - return Promise.reject("L'adresse email existe déjà..."); - } - } catch (error) { - return console.log(error); - } - return true; - })) - .normalizeEmail(), - body('name') - .trim() - .not() - .isEmpty() - .withMessage("Vous devez avoir un nom (ou pseudo).") - .isAlphanumeric() - .withMessage("Votre nom ne peut contenir que des lettres ou/et des nombres.") - .isLength({ max: 30 }) - .withMessage("Votre nom est trop long") - .custom(async (name) => { - try { - const user = await Users.findOne({ where: { name } }); - if (user && user.name !== name) { - return Promise.reject("Le nom existe déjà..."); - } - } catch (error) { - console.log(error); - } - return true; - }), - body('isPublicEmail') - .isBoolean() - .withMessage("L'adresse email peut être public ou privé, rien d'autre."), - body('biography') - .trim() - .escape() - ], usersController.putUser); - -// Permet de se connecter -UsersRouter.post('/login', [ - body('email') - .not() - .isEmpty() - .withMessage(requiredFields.message), - body('password') - .not() - .isEmpty() - .withMessage(requiredFields.message) -], usersController.login); - -// Récupère les informations public d'un profil -UsersRouter.get('/:name', usersController.getUserInfo); - -// Permet de s'inscrire -UsersRouter.post('/register', [ - body('email') + body('email') .isEmail() - .withMessage("Veuillez rentré une adresse mail valide.") - .custom((async (email) => { - try { - const user = await Users.findOne({ where: { email } }); - if (user) { - return Promise.reject("L'adresse email existe déjà..."); - } - } catch (error) { - return console.log(error); + .withMessage('Veuillez rentré une adresse mail valide.') + .custom(async (email) => { + try { + const user = await Users.findOne({ where: { email } }) + if (user && user.email !== email) { + return Promise.reject(new Error("L'adresse email existe déjà...")) } - return true; - })) + } catch (error) { + return console.log(error) + } + return true + }) .normalizeEmail(), - body('password') - .isLength({ min: 4 }) - .withMessage("Votre mot de passe est trop court!"), - body('name') + body('name') .trim() .not() .isEmpty() - .withMessage("Vous devez avoir un nom (ou pseudo).") + .withMessage('Vous devez avoir un nom (ou pseudo).') .isAlphanumeric() - .withMessage("Votre nom ne peut contenir que des lettres ou/et des nombres.") + .withMessage('Votre nom ne peut contenir que des lettres ou/et des nombres.') .isLength({ max: 30 }) - .withMessage("Votre nom est trop long") + .withMessage('Votre nom est trop long') .custom(async (name) => { - try { - const user = await Users.findOne({ where: { name } }); - if (user) { - return Promise.reject("Le nom existe déjà..."); - } - } catch (error) { - console.log(error); + try { + const user = await Users.findOne({ where: { name } }) + if (user && user.name !== name) { + return Promise.reject(new Error('Le nom existe déjà...')) } - return true; - }) -], usersController.register); + } catch (error) { + console.log(error) + } + return true + }), + body('isPublicEmail') + .isBoolean() + .withMessage("L'adresse email peut être public ou privé, rien d'autre."), + body('biography') + .trim() + .escape() + ], usersController.putUser) + +// Permet de se connecter +UsersRouter.post('/login', [ + body('email') + .not() + .isEmpty() + .withMessage(requiredFields.message), + body('password') + .not() + .isEmpty() + .withMessage(requiredFields.message) +], usersController.login) + +// Récupère les informations public d'un profil +UsersRouter.get('/:name', usersController.getUserInfo) + +// Permet de s'inscrire +UsersRouter.post('/register', [ + body('email') + .isEmail() + .withMessage('Veuillez rentré une adresse mail valide.') + .custom(async (email) => { + try { + const user = await Users.findOne({ where: { email } }) + if (user) { + return Promise.reject(new Error("L'adresse email existe déjà...")) + } + } catch (error) { + return console.log(error) + } + return true + }) + .normalizeEmail(), + body('password') + .isLength({ min: 4 }) + .withMessage('Votre mot de passe est trop court!'), + body('name') + .trim() + .not() + .isEmpty() + .withMessage('Vous devez avoir un nom (ou pseudo).') + .isAlphanumeric() + .withMessage('Votre nom ne peut contenir que des lettres ou/et des nombres.') + .isLength({ max: 30 }) + .withMessage('Votre nom est trop long') + .custom(async (name) => { + try { + const user = await Users.findOne({ where: { name } }) + if (user) { + return Promise.reject(new Error('Le nom existe déjà...')) + } + } catch (error) { + console.log(error) + } + return true + }) +], usersController.register) // Confirme l'inscription -UsersRouter.get('/confirm-email/:tempToken', usersController.confirmEmail); +UsersRouter.get('/confirm-email/:tempToken', usersController.confirmEmail) UsersRouter.route('/reset-password') - // Demande une réinitialisation du mot de passe - .post([ - body('email') - .isEmail() - .withMessage("Veuillez rentré une adresse mail valide.") - ], usersController.resetPassword) +// Demande une réinitialisation du mot de passe + .post([ + body('email') + .isEmail() + .withMessage('Veuillez rentré une adresse mail valide.') + ], usersController.resetPassword) - // Nouveau mot de passe - .put([ - body('password') - .isLength({ min: 4 }) - .withMessage("Votre mot de passe est trop court!") - ], usersController.newPassword); +// Nouveau mot de passe + .put([ + body('password') + .isLength({ min: 4 }) + .withMessage('Votre mot de passe est trop court!') + ], usersController.newPassword) -module.exports = UsersRouter; \ No newline at end of file +module.exports = UsersRouter diff --git a/website/.env.example b/website/.env.example new file mode 100644 index 0000000..403aca4 --- /dev/null +++ b/website/.env.example @@ -0,0 +1 @@ +NEXT_PUBLIC_API_URL = "http://localhost:8080" \ No newline at end of file diff --git a/website/.gitignore b/website/.gitignore index 922d92a..cea93e8 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -17,7 +17,7 @@ # misc .DS_Store -.env* +.env # debug npm-debug.log* diff --git a/website/components/CodeBlock.js b/website/components/CodeBlock.js index 3d29fca..31424f9 100644 --- a/website/components/CodeBlock.js +++ b/website/components/CodeBlock.js @@ -1,12 +1,12 @@ -import SyntaxHighlighter from "react-syntax-highlighter"; -import { atomOneDark as styles} from "react-syntax-highlighter/dist/cjs/styles/hljs"; +import SyntaxHighlighter from 'react-syntax-highlighter' +import { atomOneDark as styles } from 'react-syntax-highlighter/dist/cjs/styles/hljs' const CodeBlock = ({ language, value }) => { - return ( - - {value} - - ); -}; + return ( + + {value} + + ) +} -export default CodeBlock; \ No newline at end of file +export default CodeBlock diff --git a/website/components/Footer/Footer.js b/website/components/Footer/Footer.js index 57b85cc..c346c87 100644 --- a/website/components/Footer/Footer.js +++ b/website/components/Footer/Footer.js @@ -1,16 +1,16 @@ -import Link from 'next/link'; -import './Footer.css'; +import Link from 'next/link' +import './Footer.css' -export default function Footer() { - return ( - - ); -} \ No newline at end of file +export default function Footer () { + return ( + + ) +} diff --git a/website/components/FunctionAdmin/AddEditFunction.js b/website/components/FunctionAdmin/AddEditFunction.js index cdbcbb0..3c9be52 100644 --- a/website/components/FunctionAdmin/AddEditFunction.js +++ b/website/components/FunctionAdmin/AddEditFunction.js @@ -1,129 +1,126 @@ -import { Fragment, useState, useEffect } from 'react'; -import htmlParser from 'html-react-parser'; -import Loader from '../../components/Loader'; -import useAPI from '../../hooks/useAPI'; -import api from '../../utils/api'; -import '../../public/css/pages/admin.css'; +import { useState, useEffect } from 'react' +import htmlParser from 'html-react-parser' +import Loader from '../../components/Loader' +import useAPI from '../../hooks/useAPI' +import api from '../../utils/api' +import '../../public/css/pages/admin.css' const AddEditFunction = (props) => { + const [, categories] = useAPI('/categories') + const [inputState, setInputState] = useState(props.defaultInputState) + const [message, setMessage] = useState('') + const [isLoading, setIsLoading] = useState(false) - const [, categories] = useAPI('/categories'); - const [inputState, setInputState] = useState(props.defaultInputState); - const [message, setMessage] = useState(""); - const [isLoading, setIsLoading] = useState(false); - - useEffect(() => { - if (categories.length > 0 && !props.isEditing) { - handleChange({ - target: { - name: "categorieId", - value: categories[0].id - } - }); + useEffect(() => { + if (categories.length > 0 && !props.isEditing) { + handleChange({ + target: { + name: 'categorieId', + value: categories[0].id } - }, [categories]); + }) + } + }, [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 + setInputState(inputStateNew) + } + + const handleSubmit = (event) => { + event.preventDefault() + setIsLoading(true) + const formData = new window.FormData() + formData.append('type', inputState.type) + formData.append('categorieId', inputState.categorieId) + formData.append('title', inputState.title) + formData.append('slug', inputState.slug) + formData.append('description', inputState.description) + formData.append('image', inputState.image) + + if (props.isEditing) { + formData.append('isOnline', inputState.isOnline) } - const handleChange = (event, isTypeCheck = false) => { - const inputStateNew = { ...inputState }; - inputStateNew[event.target.name] = (event.target.files != undefined) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value; - setInputState(inputStateNew); - } + apiCallFunction(formData) + .then(() => { + setIsLoading(false) + window.location.reload(true) + }) + .catch((error) => { + setMessage(`

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

`) + setIsLoading(false) + }) + } - const handleSubmit = (event) => { - event.preventDefault(); - setIsLoading(true); - const formData = new FormData(); - formData.append('type', inputState.type); - formData.append('categorieId', inputState.categorieId); - formData.append('title', inputState.title); - formData.append('slug', inputState.slug); - formData.append('description', inputState.description); - formData.append('image', inputState.image); + return ( + <> +
+
+ + +
- if (props.isEditing) { - formData.append('isOnline', inputState.isOnline); +
+ + +
+ +
+ + -
- -
- - -
- -
- - -
- -
- -
- -
- - {(props.isEditing) && -
- handleChange(event, true)} type="checkbox" name="isOnline" checked={inputState.isOnline} className="custom-control-input" id="isOnline" /> - -
- } - -
- -
-
- -
- { - (isLoading) ? - - : - htmlParser(message) - } -
- - ); +
+ + ) } -export default AddEditFunction; \ No newline at end of file +export default AddEditFunction diff --git a/website/components/FunctionAdmin/EditArticleFunction.js b/website/components/FunctionAdmin/EditArticleFunction.js index 66667eb..8e49ea6 100644 --- a/website/components/FunctionAdmin/EditArticleFunction.js +++ b/website/components/FunctionAdmin/EditArticleFunction.js @@ -1,46 +1,45 @@ -import { useState } from 'react'; -import dynamic from 'next/dynamic'; -import { complex } from '../../utils/sunEditorConfig'; -import api from '../../utils/api'; -import FunctionArticle from '../FunctionPage/FunctionArticle'; -import 'notyf/notyf.min.css'; -import '../../public/css/suneditor.min.css'; +import { useState } from 'react' +import dynamic from 'next/dynamic' +import { complex } from '../../utils/sunEditorConfig' +import api from '../../utils/api' +import FunctionArticle from '../FunctionPage/FunctionArticle' +import 'notyf/notyf.min.css' +import '../../public/css/suneditor.min.css' const SunEditor = dynamic( - () => import('suneditor-react'), - { ssr: false } -); + () => import('suneditor-react'), + { ssr: false } +) const EditArticleFunction = (props) => { + const [htmlContent, setHtmlContent] = useState('') - const [htmlContent, setHtmlContent] = useState(""); + const handleEditorChange = (content) => { + setHtmlContent(content) + } - const handleEditorChange = (content) => { - setHtmlContent(content); + const handleSave = async (content) => { + let Notyf + if (typeof window !== 'undefined') { + Notyf = require('notyf') } - - const handleSave = async (content) => { - let Notyf; - if (typeof window != 'undefined') { - Notyf = require('notyf'); - } - const notyf = new Notyf.Notyf({ - duration: 5000 - }); - try { - await api.put(`/admin/functions/article/${props.functionInfo.id}`, { article: content }, { headers: { 'Authorization': props.user.token } }); - notyf.success('Sauvegardé!'); - } catch { - notyf.error('Erreur!'); - } + const notyf = new Notyf.Notyf({ + duration: 5000 + }) + try { + await api.put(`/admin/functions/article/${props.functionInfo.id}`, { article: content }, { headers: { Authorization: props.user.token } }) + notyf.success('Sauvegardé!') + } catch { + notyf.error('Erreur!') } + } - return ( -
- - -
- ); + return ( +
+ + +
+ ) } -export default EditArticleFunction; \ No newline at end of file +export default EditArticleFunction diff --git a/website/components/FunctionAdmin/EditFormFunction.js b/website/components/FunctionAdmin/EditFormFunction.js index 74b0d63..58f5c67 100644 --- a/website/components/FunctionAdmin/EditFormFunction.js +++ b/website/components/FunctionAdmin/EditFormFunction.js @@ -1,158 +1,154 @@ -import { useState, Fragment } from 'react'; -import api from '../../utils/api'; -import 'notyf/notyf.min.css'; +import { useState } from 'react' +import api from '../../utils/api' +import 'notyf/notyf.min.css' const EditFormFunction = (props) => { + const [inputsArray, setInputsArray] = useState(props.functionInfo.utilizationForm || []) - const [inputsArray, setInputsArray] = useState(props.functionInfo.utilizationForm || []); + const addInput = () => { + const newInputsArray = [...inputsArray] + newInputsArray.push({ name: '', label: '', placeholder: '', type: 'text' }) + setInputsArray(newInputsArray) + } - const addInput = () => { - const newInputsArray = [...inputsArray]; - newInputsArray.push({ name: "", label: "", placeholder: "", type: "text" }); - setInputsArray(newInputsArray); + const addOption = (event) => { + const newInputsArray = [...inputsArray] + const index = event.target.id.split('-')[1] + const inputObject = newInputsArray[index] + inputObject.options.push({ name: '', value: '' }) + setInputsArray(newInputsArray) + } + + const handleChangeOption = (inputIndex, optionIndex, event) => { + const newInputsArray = [...inputsArray] + const inputObject = newInputsArray[inputIndex] + const optionObject = inputObject.options[optionIndex] + optionObject[event.target.name] = event.target.value + setInputsArray(newInputsArray) + } + + const handleChangeInput = (event) => { + const newInputsArray = [...inputsArray] + const index = event.target.id.split('-')[1] + const inputObject = newInputsArray[index] + inputObject[event.target.name] = event.target.value + if (event.target.value === 'select') { + inputObject.options = [] } + setInputsArray(newInputsArray) + } - const addOption = (event) => { - const newInputsArray = [...inputsArray]; - const index = event.target.id.split('-')[1]; - const inputObject = newInputsArray[index]; - inputObject.options.push({ name: "", value: "" }); - setInputsArray(newInputsArray); + const handleSubmit = async (event) => { + event.preventDefault() + let Notyf + if (typeof window !== 'undefined') { + Notyf = require('notyf') } - - const handleChangeOption = (inputIndex, optionIndex, event) => { - const newInputsArray = [...inputsArray]; - const inputObject = newInputsArray[inputIndex]; - const optionObject = inputObject.options[optionIndex]; - optionObject[event.target.name] = event.target.value; - setInputsArray(newInputsArray); + const notyf = new Notyf.Notyf({ + duration: 5000 + }) + try { + await api.put(`/admin/functions/form/${props.functionInfo.id}`, { form: inputsArray }, { headers: { Authorization: props.user.token } }) + notyf.success('Sauvegardé!') + } catch (error) { + notyf.error('Erreur!') } + } - const handleChangeInput = (event) => { - const newInputsArray = [...inputsArray]; - const index = event.target.id.split('-')[1]; - const inputObject = newInputsArray[index]; - inputObject[event.target.name] = event.target.value; - if (event.target.value === "select") { - inputObject.options = []; - } - setInputsArray(newInputsArray); - } + const handleRemoveInput = (event) => { + const newInputsArray = [...inputsArray] + const index = event.target.id.split('-')[1] + newInputsArray.splice(index, 1) + setInputsArray(newInputsArray) + } - const handleSubmit = async (event) => { - event.preventDefault(); - let Notyf; - if (typeof window != 'undefined') { - Notyf = require('notyf'); - } - const notyf = new Notyf.Notyf({ - duration: 5000 - }); - try { - await api.put(`/admin/functions/form/${props.functionInfo.id}`, { form: inputsArray }, { headers: { 'Authorization': props.user.token } }); - notyf.success('Sauvegardé!'); - } catch (error) { - notyf.error('Erreur!'); - } - } + const handleRemoveOption = (inputIndex, optionIndex) => { + const newInputsArray = [...inputsArray] + const inputObject = newInputsArray[inputIndex] + const optionsArray = inputObject.options + optionsArray.splice(optionIndex, 1) + setInputsArray(newInputsArray) + } - const handleRemoveInput = (event) => { - const newInputsArray = [...inputsArray]; - const index = event.target.id.split('-')[1]; - newInputsArray.splice(index, 1); - setInputsArray(newInputsArray); - } + return ( +
- const handleRemoveOption = (inputIndex, optionIndex) => { - const newInputsArray = [...inputsArray]; - const inputObject = newInputsArray[inputIndex]; - const optionsArray = inputObject.options; - optionsArray.splice(optionIndex, 1); - setInputsArray(newInputsArray); - } +
- return ( -
+ {(inputsArray.length > 0) && +
+ +
} - + {inputsArray.map((input, index) => { + return ( +
- {(inputsArray.length > 0) && -
- -
- } +
+ +
- {inputsArray.map((input, index) => { + + +
+ + + +
+ + {(input.type !== 'select') && + <> + + +
+ } + + + + + {(input.type === 'select') && +
+ + + + {input.options.map((option, optionIndex) => { return ( -
- -
- -
- - - -
- - - -
- - {(input.type !== "select") && - - - -
-
- } - - - - - {(input.type === "select") && -
- - - - {input.options.map((option, optionIndex) => { - return ( -
-
- -
- - - handleChangeOption(index, optionIndex, event)} value={option.name} id={`optionName-${optionIndex}-${index}`} name="name" type="text"className="form-control" placeholder="Nom de l'option" /> - -
- - handleChangeOption(index, optionIndex, event)} value={option.value} id={`optionValue-${optionIndex}-${index}`} name="value" type="text"className="form-control" placeholder="Valeur de l'option" /> -
- ); - })} - -
- -
-
- } +
+
+
- ); - })} - + + handleChangeOption(index, optionIndex, event)} value={option.name} id={`optionName-${optionIndex}-${index}`} name='name' type='text' className='form-control' placeholder="Nom de l'option" /> -
- +
+ + handleChangeOption(index, optionIndex, event)} value={option.value} id={`optionValue-${optionIndex}-${index}`} name='value' type='text' className='form-control' placeholder="Valeur de l'option" /> +
+ ) + })} + +
+ +
+
}
-
- ); + ) + })} + + + +
+ +
+
+ ) } -export default EditFormFunction; \ No newline at end of file +export default EditFormFunction diff --git a/website/components/FunctionCard/FunctionCard.js b/website/components/FunctionCard/FunctionCard.js index 8fe7bc3..cab8dca 100644 --- a/website/components/FunctionCard/FunctionCard.js +++ b/website/components/FunctionCard/FunctionCard.js @@ -1,58 +1,56 @@ -import Link from 'next/link'; -import { useState, forwardRef } from 'react'; -import date from 'date-and-time'; -import Loader from '../Loader'; -import { API_URL } from '../../utils/config/config'; -import './FunctionCard.css'; +import Link from 'next/link' +import { useState, forwardRef } from 'react' +import date from 'date-and-time' +import Loader from '../Loader' +import { API_URL } from '../../utils/config/config' +import './FunctionCard.css' const FunctionCard = forwardRef((props, ref) => { + const [isLoading, setIsLoading] = useState(true) - const [isLoading, setIsLoading] = useState(true); + const handleLoad = () => { + setIsLoading(false) + } - const handleLoad = () => { - setIsLoading(false); - } + const handleError = (event) => { + event.target.src = API_URL + '/images/functions/default.png' + } - const handleError = (event) => { - event.target.src = API_URL + "/images/functions/default.png"; - } + const isFormOrArticle = (props.type === 'form' || props.type === 'article') - const isFormOrArticle = (props.type === 'form' || props.type === 'article'); + return ( + + {/* FunctionCard a une hauteur pendant chargement */} +
- return ( - - {/* FunctionCard a une hauteur pendant chargement */} -
+ {isLoading && } - {isLoading && } +
+
+ {props.title} +

{props.title}

+

{props.description}

+
+
+

{props.categorie.name}

+

{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}

+
+
+
+ + ) +}) -
-
- {props.title} -

{props.title}

-

{props.description}

-
-
-

{props.categorie.name}

-

{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}

-
-
-
- - ); -}); - -export default FunctionCard; \ No newline at end of file +export default FunctionCard diff --git a/website/components/FunctionPage/CommentCard/CommentCard.js b/website/components/FunctionPage/CommentCard/CommentCard.js index ab64825..19fe939 100644 --- a/website/components/FunctionPage/CommentCard/CommentCard.js +++ b/website/components/FunctionPage/CommentCard/CommentCard.js @@ -1,108 +1,109 @@ -import Link from 'next/link'; -import { useEffect, useState, forwardRef, useContext, Fragment } from 'react'; -import date from 'date-and-time'; -import htmlParser from 'html-react-parser'; -import { UserContext } from '../../../contexts/UserContext'; -import { API_URL } from '../../../utils/config/config'; -import ReactMarkdown from 'react-markdown'; -import CodeBlock from "../../CodeBlock"; -import api from '../../../utils/api'; -import './CommentCard.css'; +import Link from 'next/link' +import { useEffect, useState, forwardRef, useContext } from 'react' +import date from 'date-and-time' +import htmlParser from 'html-react-parser' +import { UserContext } from '../../../contexts/UserContext' +import { API_URL } from '../../../utils/config/config' +import ReactMarkdown from 'react-markdown' +import CodeBlock from '../../CodeBlock' +import api from '../../../utils/api' +import './CommentCard.css' const CommentCard = forwardRef((props, ref) => { + const { isAuth, user } = useContext(UserContext) + const [isEditing, setEditing] = useState(false) + const [editInput, setEditInput] = useState('') + const [message, setMessage] = useState('') - const { isAuth, user } = useContext(UserContext); - const [isEditing, setEditing] = useState(false); - const [editInput, setEditInput] = useState(""); - const [message, setMessage] = useState(""); + useEffect(() => { + setEditInput(props.message) + }, []) - useEffect(() => { - setEditInput(props.message); - }, []); - - const deleteCommentById = async () => { - props.manageComment.setLoadingComments(true); - if (isAuth && user.token != undefined) { - try { - await api.delete(`/comments/${props.id}`, { headers: { 'Authorization': user.token } }); - const newCommentsData = { ...props.manageComment.commentsData }; - const commentIndex = newCommentsData.rows.findIndex((value) => value.id === props.id); - newCommentsData.rows.splice(commentIndex, 1); - props.manageComment.setCommentsData({ hasMore: props.manageComment.commentsData.hasMore, rows: newCommentsData.rows }); - } catch {} - } - props.manageComment.setLoadingComments(false); + const deleteCommentById = async () => { + props.manageComment.setLoadingComments(true) + if (isAuth && user.token != null) { + try { + await api.delete(`/comments/${props.id}`, { headers: { Authorization: user.token } }) + const newCommentsData = { ...props.manageComment.commentsData } + const commentIndex = newCommentsData.rows.findIndex((value) => value.id === props.id) + newCommentsData.rows.splice(commentIndex, 1) + props.manageComment.setCommentsData({ hasMore: props.manageComment.commentsData.hasMore, rows: newCommentsData.rows }) + } catch {} } + props.manageComment.setLoadingComments(false) + } - const handleChange = (event) => { - setEditInput(event.target.value); - } + const handleChange = (event) => { + setEditInput(event.target.value) + } - const handleSubmit = (event) => { - event.preventDefault(); - api.put(`/comments/${props.id}`, { message: editInput }, { headers: { 'Authorization': user.token } }) - .then((_response) => { - setEditing(false); - }) - .catch((error) => { - setMessage(`

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

`); - }); - } + const handleSubmit = (event) => { + event.preventDefault() + api.put(`/comments/${props.id}`, { message: editInput }, { headers: { Authorization: user.token } }) + .then((_response) => { + setEditing(false) + }) + .catch((error) => { + setMessage(`

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

`) + }) + } - const editComment = () => { - setEditing(true); - setMessage(""); - } + const editComment = () => { + setEditing(true) + setMessage('') + } - return ( -
-
-
- - {props.user.name} - - - - {props.user.name} - -  - {date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', true)} - -
-
-
- { - (!isEditing) ? - -
- -
- {(isAuth && user.name === props.user.name) && -

- supprimer -  -  - modifier -

- } -
- : -
-
- - -
-
- -
-
- {htmlParser(message)} -
-
- } -
-
-
+ return ( +
+
+
+ + {props.user.name} + + + + {props.user.name} + +  - {date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', true)} +
- ); -}); +
+
+ { + (!isEditing) + ? ( + <> +
+ +
+ {(isAuth && user.name === props.user.name) && +

+ supprimer +  -  + modifier +

} + + ) + : ( +
+
+ + -
-
- -
-
- : -

- Vous devez être connecté pour poster un commentaire. -

- } -
-
-
-
- {isLoadingComments && -
- -
- } -
- {commentsData.rows.map((comment, index) => { - // Si c'est le dernier élément - if (commentsData.rows.length === index + 1) { - return ; - } - return ; - })} -
-
- - ); +
+
+
+
+ {isLoadingComments && +
+ +
} +
+ {commentsData.rows.map((comment, index) => { + // Si c'est le dernier élément + if (commentsData.rows.length === index + 1) { + return + } + return + })} +
+
+ + ) } -export default FunctionComments; \ No newline at end of file +export default FunctionComments diff --git a/website/components/FunctionPage/FunctionComponentTop.js b/website/components/FunctionPage/FunctionComponentTop.js index 2765cf3..7046372 100644 --- a/website/components/FunctionPage/FunctionComponentTop.js +++ b/website/components/FunctionPage/FunctionComponentTop.js @@ -1,71 +1,69 @@ -import { useState, useEffect, useContext } from 'react'; -import Link from 'next/link'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faStar } from '@fortawesome/free-solid-svg-icons'; -import { faStar as farStar } from '@fortawesome/free-regular-svg-icons'; -import date from 'date-and-time'; -import { UserContext } from '../../contexts/UserContext'; -import api from '../../utils/api'; -import { API_URL } from '../../utils/config/config'; -import '../FunctionCard/FunctionCard.css'; +import { useState, useEffect, useContext } from 'react' +import Link from 'next/link' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faStar } from '@fortawesome/free-solid-svg-icons' +import { faStar as farStar } from '@fortawesome/free-regular-svg-icons' +import date from 'date-and-time' +import { UserContext } from '../../contexts/UserContext' +import api from '../../utils/api' +import { API_URL } from '../../utils/config/config' +import '../FunctionCard/FunctionCard.css' const FunctionComponentTop = (props) => { + const { isAuth, user } = useContext(UserContext) + const [isFavorite, setIsFavorite] = useState(false) - const { isAuth, user } = useContext(UserContext); - const [isFavorite, setIsFavorite] = useState(false); + useEffect(() => { + if (isAuth && user.token != null) { + fetchFavorite() + } + }, [isAuth]) - useEffect(() => { - if (isAuth && user.token != undefined) { - fetchFavorite(); + const fetchFavorite = async () => { + try { + const favoriteResponse = await api.get(`/favorites/${props.id}`, { headers: { Authorization: user.token } }) + setIsFavorite(favoriteResponse.data.isFavorite) + } catch {} + } + + const toggleFavorite = async () => { + if (isAuth && user.token != null) { + try { + if (isFavorite) { + const response = await api.delete(`/favorites/${props.id}`, { headers: { Authorization: user.token } }) + if (response.status === 200) return setIsFavorite(false) } - }, [isAuth]); - - const fetchFavorite = async () => { - try { - const favoriteResponse = await api.get(`/favorites/${props.id}`, { headers: { 'Authorization': user.token } }); - setIsFavorite(favoriteResponse.data.isFavorite); - } catch {} + const response = await api.post(`/favorites/${props.id}`, {}, { headers: { Authorization: user.token } }) + if (response.status === 201) return setIsFavorite(true) + } catch {} } + } - const toggleFavorite = async () => { - if (isAuth && user.token != undefined) { - try { - if (isFavorite) { - const response = await api.delete(`/favorites/${props.id}`, { headers: { 'Authorization': user.token } }); - if (response.status === 200) return setIsFavorite(false); - } - const response = await api.post(`/favorites/${props.id}`, {}, { headers: { 'Authorization': user.token } }); - if (response.status === 201) return setIsFavorite(true); - } catch {} - } - } + const handleError = (event) => { + event.target.src = API_URL + '/images/functions/default.png' + } - const handleError = (event) => { - event.target.src = API_URL + "/images/functions/default.png"; - } + return ( +
+
+
- return ( -
-
-
+ {(isAuth) && + } - {(isAuth) && - - } - - {props.title} -

{props.title}

-

{props.description}

-
- - {props.categorie.name} - -

{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}

-
-
-
+ {props.title} +

{props.title}

+

{props.description}

+
+ + {props.categorie.name} + +

{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}

+
- ); +
+
+ ) } -export default FunctionComponentTop; \ No newline at end of file +export default FunctionComponentTop diff --git a/website/components/FunctionPage/FunctionForm.js b/website/components/FunctionPage/FunctionForm.js index 95fdcd2..9be4b58 100644 --- a/website/components/FunctionPage/FunctionForm.js +++ b/website/components/FunctionPage/FunctionForm.js @@ -1,161 +1,160 @@ -import { Fragment, useState, useEffect } from 'react'; -import Loader from '../Loader'; -import htmlParser from 'html-react-parser'; -import dynamic from 'next/dynamic'; -import api from '../../utils/api'; -import fr from 'date-fns/locale/fr'; -import { registerLocale } from "react-datepicker"; -import date from 'date-and-time'; -import "react-datepicker/dist/react-datepicker.css"; +import { useState, useEffect } from 'react' +import Loader from '../Loader' +import htmlParser from 'html-react-parser' +import dynamic from 'next/dynamic' +import api from '../../utils/api' +import fr from 'date-fns/locale/fr' +import { registerLocale } from 'react-datepicker' +import date from 'date-and-time' +import 'react-datepicker/dist/react-datepicker.css' -registerLocale('fr', fr); +registerLocale('fr', fr) const FunctionForm = (props) => { + const [inputState, setInputState] = useState({}) + const [message, setMessage] = useState('') + const [isLoading, setIsLoading] = useState(false) - const [inputState, setInputState] = useState({}); - const [message, setMessage] = useState(""); - const [isLoading, setIsLoading] = useState(false); + // inputState par défaut + useEffect(() => { + const inputStateNew = { ...inputState } + props.inputsArray.forEach((input) => { + if (input.type === 'select' && input.options.length > 0) { + inputStateNew[input.name] = input.options[0].value + } + }) + setInputState(inputStateNew) + }, []) - // inputState par défaut - useEffect(() => { - const inputStateNew = { ...inputState }; - props.inputsArray.forEach((input) => { - if (input.type === "select" && input.options.length > 0) { - inputStateNew[input.name] = input.options[0].value; - } - }); - setInputState(inputStateNew); - }, []); + const handleSubmit = (event) => { + setIsLoading(true) + event.preventDefault() + api.post(`/functions/${props.slug}`, inputState) + .then((response) => { + setMessage(response.data.resultHTML) + setIsLoading(false) + }) + .catch((error) => { + setMessage(error.response.data.message) + setIsLoading(false) + }) + } - const handleSubmit = (event) => { - setIsLoading(true); - event.preventDefault(); - api.post(`/functions/${props.slug}`, inputState) - .then((response) => { - setMessage(response.data.resultHTML); - setIsLoading(false); - }) - .catch((error) => { - setMessage(error.response.data.message); - setIsLoading(false); - }); - } + const handleChange = (event) => { + const inputStateNew = { ...inputState } + inputStateNew[event.target.name] = event.target.value + setInputState(inputStateNew) + } - const handleChange = (event) => { - const inputStateNew = { ...inputState }; - inputStateNew[event.target.name] = event.target.value; - setInputState(inputStateNew); - } - - if (props.inputsArray.length <= 0) { - return ( -
-

La fonction n'est pas encore disponible.

-
- ); - } + if (props.inputsArray.length <= 0) { return ( - -
- {props.inputsArray.map((input, index) => { - switch (input.type) { - case "text": - return ( -
- - -
- ); - case "integer": - case "float": - return ( -
- - -
- ); - case "calendar": - // Permet au datepicker de prendre la hauteur - if (typeof window != 'undefined') { - const newScript = document.createElement("script"); - newScript.src = "/js/extraHeightCSS.js"; - document.body.appendChild(newScript); - } - const DatePicker = dynamic( - () => import('react-datepicker'), - { ssr: false } - ); - return ( -
- -
- { - try { - if (inputState[input.name] != undefined) { - const dateArray = inputState[input.name].split('/'); - const year = dateArray[2]; - const month = dateArray[1]; - const day = dateArray[0]; - return new Date(year, parseInt(month) - 1, parseInt(day) + 1); - } - throw "Not a valid date"; - } catch { - return new Date(); - } - })()} - locale="fr" - dateFormat="dd/MM/yyyy" - fixedHeight - placeholderText={input.placeholder} - onChange={(dateObject) => { - try { - const formattedDate = date.format(dateObject, 'DD/MM/YYYY', true); - handleChange({ - target: { - name: input.name, - value: formattedDate - } - }); - } catch {} - }} - /> -
- ); - case "select": - return ( -
- - -
- ); - default: - return ( -

Erreur, l'input n'est pas valide...

- ); - } - })} - -
- +
+

La fonction n'est pas encore disponible.

+
+ ) + } + return ( + <> + + {props.inputsArray.map((input, index) => { + switch (input.type) { + case 'text': + return ( +
+ +
- -
- { - (isLoading) ? - - : - htmlParser(message) - } -
- - ); + ) + case 'integer': + case 'float': + return ( +
+ + +
+ ) + case 'calendar': + // Permet au datepicker de prendre la hauteur + if (typeof window !== 'undefined') { + const newScript = document.createElement('script') + newScript.src = '/js/extraHeightCSS.js' + document.body.appendChild(newScript) + } + // eslint-disable-next-line + const DatePicker = dynamic( + () => import('react-datepicker'), + { ssr: false } + ) + return ( +
+ +
+ { + try { + if (inputState[input.name] != null) { + const dateArray = inputState[input.name].split('/') + const year = dateArray[2] + const month = dateArray[1] + const day = dateArray[0] + return new Date(year, parseInt(month) - 1, parseInt(day) + 1) + } + throw new Error('Not a valid date') + } catch { + return new Date() + } + })()} + locale='fr' + dateFormat='dd/MM/yyyy' + fixedHeight + placeholderText={input.placeholder} + onChange={(dateObject) => { + try { + const formattedDate = date.format(dateObject, 'DD/MM/YYYY', true) + handleChange({ + target: { + name: input.name, + value: formattedDate + } + }) + } catch {} + }} + /> +
+ ) + case 'select': + return ( +
+ + +
+ ) + default: + return ( +

Erreur, l'input n'est pas valide...

+ ) + } + })} + +
+ +
+ +
+ { + (isLoading) + ? + : htmlParser(message) + } +
+ + ) } -export default FunctionForm; \ No newline at end of file +export default FunctionForm diff --git a/website/components/FunctionPage/FunctionPage.js b/website/components/FunctionPage/FunctionPage.js index 21d4a45..fd2d60a 100644 --- a/website/components/FunctionPage/FunctionPage.js +++ b/website/components/FunctionPage/FunctionPage.js @@ -1,24 +1,23 @@ -import { Fragment, useState } from 'react'; -import { API_URL } from '../../utils/config/config'; -import HeadTag from '../HeadTag'; -import FunctionTabsTop from './FunctionTabsTop'; -import FunctionComponentTop from './FunctionComponentTop'; +import { useState } from 'react' +import { API_URL } from '../../utils/config/config' +import HeadTag from '../HeadTag' +import FunctionTabsTop from './FunctionTabsTop' +import FunctionComponentTop from './FunctionComponentTop' const FunctionPage = (props) => { + const [slideIndex, setSlideIndex] = useState(0) - const [slideIndex, setSlideIndex] = useState(0); + return ( + <> + - return ( - - - -
- - - -
-
- ); +
+ + + +
+ + ) } -export default FunctionPage; \ No newline at end of file +export default FunctionPage diff --git a/website/components/FunctionPage/FunctionTabs.js b/website/components/FunctionPage/FunctionTabs.js index 027a8f9..bc3734c 100644 --- a/website/components/FunctionPage/FunctionTabs.js +++ b/website/components/FunctionPage/FunctionTabs.js @@ -1,14 +1,14 @@ -import SwipeableViews from 'react-swipeable-views'; -import './FunctionTabs.css'; +import SwipeableViews from 'react-swipeable-views' +import './FunctionTabs.css' const FunctionTabs = (props) => { - return ( -
- props.setSlideIndex(index)} index={props.slideIndex}> - {props.children} - -
- ); + return ( +
+ props.setSlideIndex(index)} index={props.slideIndex}> + {props.children} + +
+ ) } -export default FunctionTabs; \ No newline at end of file +export default FunctionTabs diff --git a/website/components/FunctionPage/FunctionTabsTop.js b/website/components/FunctionPage/FunctionTabsTop.js index d0a9f0c..f6e6ac0 100644 --- a/website/components/FunctionPage/FunctionTabsTop.js +++ b/website/components/FunctionPage/FunctionTabsTop.js @@ -1,24 +1,24 @@ const FunctionTabsTop = (props) => { - return ( -
-
- -
-
- ); + return ( +
+
+ +
+
+ ) } -export default FunctionTabsTop; \ No newline at end of file +export default FunctionTabsTop diff --git a/website/components/FunctionsList/FunctionsList.js b/website/components/FunctionsList/FunctionsList.js index cf71519..17f9c87 100644 --- a/website/components/FunctionsList/FunctionsList.js +++ b/website/components/FunctionsList/FunctionsList.js @@ -1,104 +1,103 @@ -import { useState, useEffect, useRef, useCallback } from 'react'; -import { useRouter } from 'next/router'; -import FunctionCard from '../FunctionCard/FunctionCard'; -import Loader from '../Loader'; -import api from '../../utils/api'; -import useAPI from '../../hooks/useAPI'; -import './FunctionsList.css'; +import { useState, useEffect, useRef, useCallback } from 'react' +import { useRouter } from 'next/router' +import FunctionCard from '../FunctionCard/FunctionCard' +import Loader from '../Loader' +import api from '../../utils/api' +import useAPI from '../../hooks/useAPI' +import './FunctionsList.css' -let pageFunctions = 1; +let pageFunctions = 1 const FunctionsList = (props) => { + const { categoryId } = useRouter().query - const { categoryId } = useRouter().query; + // State de recherche et de catégories + const [, categories] = useAPI('/categories') + const [inputSearch, setInputSearch] = useState({ search: '', selectedCategory: categoryId || '0' }) - // State de recherche et de catégories - const [, categories] = useAPI('/categories'); - const [inputSearch, setInputSearch] = useState({ search: "", selectedCategory: categoryId || "0" }); + // State pour afficher les fonctions + const [functionsData, setFunctionsData] = useState({ hasMore: true, rows: [] }) + const [isLoadingFunctions, setLoadingFunctions] = useState(true) - // State pour afficher les fonctions - const [functionsData, setFunctionsData] = useState({ hasMore: true, rows: [] }); - const [isLoadingFunctions, setLoadingFunctions] = useState(true); - - // Récupère la catégorie avec la query categoryId - useEffect(() => { - if (categoryId) { - handleChange({ target: { name: "selectedCategory", value: categoryId } }); - } - }, [categoryId]); - - // Récupère les fonctions si la catégorie/recherche change - useEffect(() => { - pageFunctions = 1; - getFunctionsData().then((data) => setFunctionsData(data)); - }, [inputSearch]); - - // Permet la pagination au scroll - const observer = useRef(); - const lastFunctionCardRef = useCallback((node) => { - if (isLoadingFunctions) return; - if (observer.current) observer.current.disconnect(); - observer.current = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting && functionsData.hasMore) { - pageFunctions += 1; - getFunctionsData().then((data) => { - setFunctionsData((oldData) => { - return { - hasMore: data.hasMore, - rows: [...oldData.rows, ...data.rows] - } - }); - }); - } - }, { threshold: 1 }); - if (node) observer.current.observe(node); - }, [isLoadingFunctions, functionsData.hasMore]); - - const getFunctionsData = async () => { - setLoadingFunctions(true); - const URL = `${(props.isAdmin) ? "/admin/functions" : "/functions"}?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`; - const { data } = await api.get(URL, { - headers: { - ...(props.isAdmin && props.token != undefined) && { 'Authorization': props.token } - } - }); - setLoadingFunctions(false); - return data; + // Récupère la catégorie avec la query categoryId + useEffect(() => { + if (categoryId) { + handleChange({ target: { name: 'selectedCategory', value: categoryId } }) } + }, [categoryId]) - const handleChange = (event) => { - const inputSearchNew = { ...inputSearch }; - inputSearchNew[event.target.name] = event.target.value; - setInputSearch(inputSearchNew); - } + // Récupère les fonctions si la catégorie/recherche change + useEffect(() => { + pageFunctions = 1 + getFunctionsData().then((data) => setFunctionsData(data)) + }, [inputSearch]) - return ( -
-
- {props.children} -
+ // Permet la pagination au scroll + const observer = useRef() + const lastFunctionCardRef = useCallback((node) => { + if (isLoadingFunctions) return + if (observer.current) observer.current.disconnect() + observer.current = new window.IntersectionObserver((entries) => { + if (entries[0].isIntersecting && functionsData.hasMore) { + pageFunctions += 1 + getFunctionsData().then((data) => { + setFunctionsData((oldData) => { + return { + hasMore: data.hasMore, + rows: [...oldData.rows, ...data.rows] + } + }) + }) + } + }, { threshold: 1 }) + if (node) observer.current.observe(node) + }, [isLoadingFunctions, functionsData.hasMore]) -
- - -
+ const getFunctionsData = async () => { + setLoadingFunctions(true) + const URL = `${(props.isAdmin) ? '/admin/functions' : '/functions'}?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}` + const { data } = await api.get(URL, { + headers: { + ...(props.isAdmin && props.token != null) && { Authorization: props.token } + } + }) + setLoadingFunctions(false) + return data + } -
- {functionsData.rows.map((currentFunction, index) => { - // Si c'est le dernier élément - if (functionsData.rows.length === index + 1) { - return ; - } - return ; - })} -
- {isLoadingFunctions && } -
- ); + const handleChange = (event) => { + const inputSearchNew = { ...inputSearch } + inputSearchNew[event.target.name] = event.target.value + setInputSearch(inputSearchNew) + } + + return ( +
+
+ {props.children} +
+ +
+ + +
+ +
+ {functionsData.rows.map((currentFunction, index) => { + // Si c'est le dernier élément + if (functionsData.rows.length === index + 1) { + return + } + return + })} +
+ {isLoadingFunctions && } +
+ ) } -export default FunctionsList; \ No newline at end of file +export default FunctionsList diff --git a/website/components/HeadTag.js b/website/components/HeadTag.js index 74fd2aa..2636977 100644 --- a/website/components/HeadTag.js +++ b/website/components/HeadTag.js @@ -1,43 +1,43 @@ -import Head from 'next/head'; +import Head from 'next/head' const HeadTag = ({ title, image, description }) => ( - - {title || ""} - + + {title || ''} + - {/* Meta Tag */} - - - - - + {/* Meta Tag */} + + + + + - {/* Open Graph Metadata */} - - - - - - - + {/* Open Graph Metadata */} + + + + + + + - {/* Twitter card Metadata */} - - - - - - + {/* Twitter card Metadata */} + + + + + + - {/* Preloader script */} - - -); + {/* Preloader script */} +