diff --git a/api/.gitignore b/api/.gitignore index 8692cf6..0cc182c 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -18,6 +18,7 @@ .env.development.local .env.test.local .env.production.local +/temp npm-debug.log* yarn-debug.log* diff --git a/api/assets/config/emails.js b/api/assets/config/emails.js index c2d3de6..83dc957 100644 --- a/api/assets/config/emails.js +++ b/api/assets/config/emails.js @@ -1,10 +1,10 @@ -exports.signupEmail = (url) => ` +exports.emailTemplate = (subtitle, buttonText, url, footerText) => `
- +
- +
@@ -21,21 +21,17 @@ exports.signupEmail = (url) => `
@@ -12,7 +12,7 @@ exports.signupEmail = (url) => `
-

FunctionProject

+

FunctionProject

- +
- diff --git a/api/controllers/admin.js b/api/controllers/admin.js index 9a31389..d029150 100644 --- a/api/controllers/admin.js +++ b/api/controllers/admin.js @@ -1,8 +1,33 @@ -const errorHandling = require('../assets/utils/errorHandling'); -const { serverError } = require('../assets/config/errors'); -const Users = require('../models/users'); +const path = require('path'); +const { validationResult } = require('express-validator'); +const errorHandling = require('../assets/utils/errorHandling'); +const { serverError } = require('../assets/config/errors'); +const Functions = require('../models/functions'); exports.postFunction = (req, res, next) => { - // TODO: Pouvoir créé une fonction - res.status(200).json({ message: "test"}); + const { title, slug, description, type, categorieId } = req.body; + const image = req.files.image; + const errors = validationResult(req); + if (!errors.isEmpty()) { + console.log(errors.array()) + 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 }); + } + const imageName = slug + image.name; + image.mv(path.join(__dirname, '..', 'assets', 'images', 'functions') + '/' + imageName, async (error) => { + if (error) return errorHandling(next, serverError); + try { + await Functions.create({ title, slug, description, type, categorieId, image: `/images/functions/${imageName}` }); + return res.status(201).json({ message: "La fonction a été correctement ajouté!"}); + } catch (error) { + console.log(error); + errorHandling(next, serverError); + } + }); } \ No newline at end of file diff --git a/api/controllers/users.js b/api/controllers/users.js index 1f5d2ab..483c35c 100644 --- a/api/controllers/users.js +++ b/api/controllers/users.js @@ -7,7 +7,7 @@ const { serverError, generalError } = require('../assets/config/errors'); const { JWT_SECRET } = require('../assets/config/config'); const transporter = require('../assets/config/transporter'); const { EMAIL_INFO, HOST } = require('../assets/config/config'); -const { signupEmail } = require('../assets/config/emails'); +const { emailTemplate } = require('../assets/config/emails'); const Users = require('../models/users'); exports.register = async (req, res, next) => { @@ -24,9 +24,9 @@ exports.register = async (req, res, next) => { from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`, to: email, subject: "FunctionProject - Confirmer l'inscription", - html: signupEmail(`${HOST}/users/confirm-email/${tempToken}`) + html: emailTemplate("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 votre boite d'emails pour confirmer l'inscription." }); + return res.status(201).json({ result: "Vous y êtes presque, veuillez vérifier vos emails pour confirmer l'inscription." }); } catch (error) { console.log(error); errorHandling(next, serverError); @@ -36,7 +36,7 @@ exports.register = async (req, res, next) => { exports.login = async (req, res, next) => { const { email, password } = req.body; try { - const user = await Users.findOne({ where: { email, confirmed: true } }); + const user = await Users.findOne({ where: { email, isConfirmed: true } }); if (!user) { return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 400 }); } diff --git a/api/package-lock.json b/api/package-lock.json index c645886..a49edb7 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -173,6 +173,14 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, + "busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "requires": { + "dicer": "0.3.0" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -384,6 +392,14 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "requires": { + "streamsearch": "0.1.2" + } + }, "dns-prefetch-control": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz", @@ -516,6 +532,14 @@ "vary": "~1.1.2" } }, + "express-fileupload": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.6.tgz", + "integrity": "sha512-w24zPWT8DkoIxSVkbxYPo9hkTiLpCQQzNsLRTCnecBhfbYv+IkIC5uLw2MIUAxBZ+7UMmXPjGxlhzUXo4RcbZw==", + "requires": { + "busboy": "^0.3.1" + } + }, "express-validator": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz", @@ -1596,6 +1620,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", diff --git a/api/package.json b/api/package.json index f77ef92..f06ed0a 100644 --- a/api/package.json +++ b/api/package.json @@ -14,6 +14,7 @@ "bcryptjs": "^2.4.3", "cors": "^2.8.5", "express": "^4.17.1", + "express-fileupload": "^1.1.6", "express-validator": "^6.4.0", "helmet": "^3.21.3", "jsonwebtoken": "^8.5.1", diff --git a/api/routes/admin.js b/api/routes/admin.js index 6e9cb72..8a6f4b0 100644 --- a/api/routes/admin.js +++ b/api/routes/admin.js @@ -1,11 +1,79 @@ const { Router } = require('express'); +const fileUpload = require('express-fileupload'); +const { body } = require('express-validator'); const adminController = require('../controllers/admin'); -const isAuth = require('../middlewares/isAuth'); -const isAdmin = require('../middlewares/isAdmin'); +const isAuth = require('../middlewares/isAuth'); +const isAdmin = require('../middlewares/isAdmin'); +const Functions = require('../models/functions'); +const Categories = require('../models/categories'); const AdminRouter = Router(); // Permet de créé une fonction -AdminRouter.post('/functions', isAuth, isAdmin, adminController.postFunction); +AdminRouter.post('/functions', + isAuth, + isAdmin, + 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."), + body('slug') + .not() + .isEmpty() + .withMessage("La fonction doit avoir un slug.") + .isLength({ max: 100 }) + .withMessage("Le slug est trop long.") + .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 }) + .withMessage("La description est trop longue."), + body('categorieId') + .not() + .isEmpty() + .withMessage("La fonction doit avoir une catégorie.") + .custom(async (categorieId) => { + try { + const categorieFound = await Categories.findOne({ where: { id: 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 +); module.exports = AdminRouter; \ No newline at end of file diff --git a/api/routes/users.js b/api/routes/users.js index 25a0804..88c0f44 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -22,6 +22,7 @@ UsersRouter.post('/register', [ } catch (error) { return console.log(error); } + return true; })) .normalizeEmail(), body('password') @@ -34,16 +35,19 @@ UsersRouter.post('/register', [ .withMessage("Vous devez avoir un nom (ou pseudo).") .isAlphanumeric() .withMessage("Votre nom ne peut contenir que des lettres ou/et des nombres.") - .custom((async (name) => { + .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("Le nom existe déjà..."); } } catch (error) { - return console.log(error); + console.log(error); } - })) + return true; + }) ], usersController.register); // Confirme l'inscription diff --git a/website/components/FunctionComponentTop.js b/website/components/FunctionComponentTop.js index f6ce7f8..a8a1497 100644 --- a/website/components/FunctionComponentTop.js +++ b/website/components/FunctionComponentTop.js @@ -4,7 +4,7 @@ const FunctionComponentTop = (props) => (
- {props.title} + {props.title}

{props.title}

{props.description}

-

- Veuillez confirmer l'inscription +

+

+ ${subtitle}

- Oui, je m'inscris. + ${buttonText}
-

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.

+

${footerText}