backend: POST /admin/functions créé une fonction
This commit is contained in:
parent
96af78f967
commit
cec6881308
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 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);
|
||||
}
|
||||
});
|
||||
}
|
@ -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 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