backend: POST /admin/functions créé une fonction
This commit is contained in:
		
							
								
								
									
										1
									
								
								api/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								api/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -18,6 +18,7 @@
 | 
			
		||||
.env.development.local
 | 
			
		||||
.env.test.local
 | 
			
		||||
.env.production.local
 | 
			
		||||
/temp
 | 
			
		||||
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
exports.signupEmail = (url) => `
 | 
			
		||||
exports.emailTemplate = (subtitle, buttonText, url, footerText) => `
 | 
			
		||||
<center>
 | 
			
		||||
    <table border="0" cellpadding="20" cellspacing="0" height="100%" width="100%" style="background-color:#eeeeee">
 | 
			
		||||
    <table border="0" cellpadding="20" cellspacing="0" height="100%" width="100%" style="background-color:#181818">
 | 
			
		||||
        <tbody>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td align="center" valign="top">
 | 
			
		||||
                    <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width:600px;border-radius:6px;background-color:none">
 | 
			
		||||
                    <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width:600px;border-radius:6px">
 | 
			
		||||
                        <tbody>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td align="center" valign="top">
 | 
			
		||||
@@ -12,7 +12,7 @@ exports.signupEmail = (url) => `
 | 
			
		||||
                                        <tbody>
 | 
			
		||||
                                            <tr>
 | 
			
		||||
                                                <td>
 | 
			
		||||
                                                    <h1 style="font-family:Arial, Helvetica, sans-serif;font-size:28px;line-height:110%;margin-bottom:30px;margin-top:0;padding:0">FunctionProject</h1>
 | 
			
		||||
                                                    <h1 style="font-family:Arial, Helvetica, sans-serif;color:#ffd800;font-size:28px;line-height:110%;margin-bottom:30px;margin-top:0;padding:0">FunctionProject</h1>
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        </tbody>
 | 
			
		||||
@@ -21,21 +21,17 @@ exports.signupEmail = (url) => `
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td align="center" valign="top">
 | 
			
		||||
                                    <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width:600px;border-radius:6px;background-color:#ffffff">
 | 
			
		||||
                                    <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width:600px;border-radius:6px;">
 | 
			
		||||
                                        <tbody>
 | 
			
		||||
                                            <tr>
 | 
			
		||||
                                                <td align="left" valign="top" style="line-height:150%;font-family:Helvetica;font-size:14px;color:#333333;padding:20px">
 | 
			
		||||
                                                    <h2 style="font-size:22px;line-height:28px;margin:0 0 12px 0">
 | 
			
		||||
                                                        Veuillez confirmer l'inscription
 | 
			
		||||
                                                <td align="left" valign="top" style="line-height:150%;font-family:Helvetica;font-size:14px;color:rgb(222, 222, 222);padding:30px;box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);border: 1px solid black;border-radius: 1rem;">
 | 
			
		||||
                                                    <h2 style="font-size:22px;line-height:28px;margin:0 0 12px 0;">
 | 
			
		||||
                                                        ${subtitle}
 | 
			
		||||
                                                    </h2>
 | 
			
		||||
                                                    <a href="${url}" style="color:#ffffff!important;display:inline-block;font-weight:500;font-size:16px;line-height:42px;font-family:'Helvetica',Arial,sans-serif;width:auto;white-space:nowrap;height:42px;margin:12px 5px 12px 0;padding:0 22px;text-decoration:none;text-align:center;border:0;border-radius:3px;vertical-align:top;background-color:#5d5d5d!important" target="_blank" rel="noopener noreferrer"><span
 | 
			
		||||
                                                            style="display:inline;font-family:'Helvetica',Arial,sans-serif;text-decoration:none;font-weight:500;font-style:normal;font-size:16px;line-height:42px;border:none;background-color:#5d5d5d!important;color:#ffffff!important">Oui, je m'inscris.</span></a>
 | 
			
		||||
                                                    <a href="${url}" style="display:inline-block;font-weight:500;font-size:16px;line-height:42px;font-family:'Helvetica',Arial,sans-serif;width:auto;white-space:nowrap;height:42px;margin:12px 5px 12px 0;padding:0 22px;text-decoration:none;text-align:center;border:0;border-radius:3px;vertical-align:top;background-color: #343a40;border-color: #343a40;" target="_blank" rel="noopener noreferrer"><span style="display:inline;font-family:'Helvetica',Arial,sans-serif;text-decoration:none;font-weight:500;font-style:normal;font-size:16px;line-height:42px;border:none;color: #fff;">${buttonText}</span></a>
 | 
			
		||||
                                                    <br>
 | 
			
		||||
                                                    <div>
 | 
			
		||||
                                                        <p style="padding:0 0 10px 0">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.</p>
 | 
			
		||||
                                                        <p style="padding:0 0 10px 0">${footerText}</p>
 | 
			
		||||
                                                    </div>
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,33 @@
 | 
			
		||||
const path                 = require('path');
 | 
			
		||||
const { validationResult } = require('express-validator');
 | 
			
		||||
const errorHandling        = require('../assets/utils/errorHandling');
 | 
			
		||||
const { serverError }      = require('../assets/config/errors');
 | 
			
		||||
const Users           = require('../models/users');
 | 
			
		||||
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);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -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 });
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								api/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										29
									
								
								api/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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 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;
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ const FunctionComponentTop = (props) => (
 | 
			
		||||
    <div className="container-fluid">
 | 
			
		||||
        <div className="row justify-content-center text-center">
 | 
			
		||||
            <div className="FunctionComponent__top col-24">
 | 
			
		||||
                <img src={props.API_URL + props.image} alt={props.title} />
 | 
			
		||||
                <img style={{ width: '150px' }} src={props.API_URL + props.image} alt={props.title} />
 | 
			
		||||
                <h1 className="FunctionComponent__title title-important">{props.title}</h1>
 | 
			
		||||
                <p className="FunctionComponent__description">{props.description}</p>
 | 
			
		||||
                <div className="FunctionCard__info">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user