diff --git a/api/.env.example b/api/.env.example
index 9ab37e8..570e038 100644
--- a/api/.env.example
+++ b/api/.env.example
@@ -1,5 +1,10 @@
+HOST = "http://localhost:8080"
OpenWeatherMap_API_KEY = ""
DB_HOST = ""
DB_NAME = ""
DB_USER = ""
-DB_PASS = ""
\ No newline at end of file
+DB_PASS = ""
+JWT_SECRET = ""
+EMAIL_HOST = ""
+EMAIL_USER = ""
+EMAIL_PASSWORD = ""
\ No newline at end of file
diff --git a/api/app.js b/api/app.js
index 4fb5494..cae56c7 100644
--- a/api/app.js
+++ b/api/app.js
@@ -22,6 +22,8 @@ app.use(express.json());
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', require('./routes/admin'));
/* Errors Handling */
app.use((_req, _res, next) => errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" })); // 404
diff --git a/api/assets/config/config.js b/api/assets/config/config.js
index a01ae23..5942a2e 100644
--- a/api/assets/config/config.js
+++ b/api/assets/config/config.js
@@ -1,5 +1,6 @@
const config = {
PORT: process.env.PORT || 8080,
+ HOST: process.env.HOST,
WEATHER_API_KEY: process.env.OpenWeatherMap_API_KEY,
DATABASE: {
host: process.env.DB_HOST,
@@ -7,6 +8,16 @@ const config = {
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
+ }
+ }
};
module.exports = config;
\ No newline at end of file
diff --git a/api/assets/config/emails.js b/api/assets/config/emails.js
new file mode 100644
index 0000000..c2d3de6
--- /dev/null
+++ b/api/assets/config/emails.js
@@ -0,0 +1,53 @@
+exports.signupEmail = (url) => `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FunctionProject
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Veuillez confirmer l'inscription
+
+ Oui, je m'inscris.
+
+
+ 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.
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+
+
+`;
\ No newline at end of file
diff --git a/api/assets/config/transporter.js b/api/assets/config/transporter.js
new file mode 100644
index 0000000..6f32737
--- /dev/null
+++ b/api/assets/config/transporter.js
@@ -0,0 +1,6 @@
+const nodemailer = require('nodemailer');
+const { EMAIL_INFO } = require('./config');
+
+const transporter = nodemailer.createTransport(EMAIL_INFO);
+
+module.exports = transporter;
\ No newline at end of file
diff --git a/api/assets/images/functions/default.png b/api/assets/images/functions/default.png
new file mode 100644
index 0000000..292dee9
Binary files /dev/null and b/api/assets/images/functions/default.png differ
diff --git a/api/assets/images/users/default.png b/api/assets/images/users/default.png
new file mode 100644
index 0000000..d3d6fbe
Binary files /dev/null and b/api/assets/images/users/default.png differ
diff --git a/api/controllers/admin.js b/api/controllers/admin.js
new file mode 100644
index 0000000..9a31389
--- /dev/null
+++ b/api/controllers/admin.js
@@ -0,0 +1,8 @@
+const errorHandling = require('../assets/utils/errorHandling');
+const { serverError } = require('../assets/config/errors');
+const Users = require('../models/users');
+
+exports.postFunction = (req, res, next) => {
+ // TODO: Pouvoir créé une fonction
+ res.status(200).json({ message: "test"});
+}
\ No newline at end of file
diff --git a/api/controllers/users.js b/api/controllers/users.js
new file mode 100644
index 0000000..408fe65
--- /dev/null
+++ b/api/controllers/users.js
@@ -0,0 +1,75 @@
+const { validationResult } = require('express-validator');
+const bcrypt = require('bcryptjs');
+const jwt = require('jsonwebtoken');
+const uuid = require('uuid');
+const errorHandling = require('../assets/utils/errorHandling');
+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 Users = require('../models/users');
+
+exports.signup = 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: signupEmail(`${HOST}/users/confirm-email/${tempToken}`)
+ });
+ return res.status(201).json({ result: "Vous y êtes presque, veuillez vérifier votre boite d'emails pour confirmer l'inscription." });
+ } catch (error) {
+ console.log(error);
+ errorHandling(next, serverError);
+ }
+}
+
+exports.login = async (req, res, next) => {
+ const { email, password } = req.body;
+ try {
+ const user = await Users.findOne({ where: { email, confirmed: true } });
+ 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 });
+ }
+ const token = jwt.sign({
+ email: user.email, userId: user.id
+ }, JWT_SECRET, { expiresIn: '1h' });
+ return res.status(200).json({ token, id: user.id, name: user.name, email: user.email, logo: user.logo, isAdmin: user.isAdmin, createdAt: user.createdAt });
+ } catch (error) {
+ console.log(error);
+ 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('https://function.divlo.fr');
+ } catch (error) {
+ console.log(error);
+ errorHandling(next, serverError);
+ }
+}
\ No newline at end of file
diff --git a/api/middlewares/isAdmin.js b/api/middlewares/isAdmin.js
new file mode 100644
index 0000000..d783a23
--- /dev/null
+++ b/api/middlewares/isAdmin.js
@@ -0,0 +1,23 @@
+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: 401 });
+ }
+ 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: 400 });
+ }
+ if (!user.isAdmin) {
+ return errorHandling(next, { message: "Vous n'êtes pas administrateur.", statusCode: 400 });
+ }
+ next();
+ })
+ .catch((error) => {
+ console.log(error);
+ errorHandling(next, serverError);
+ });
+}
\ No newline at end of file
diff --git a/api/middlewares/isAuth.js b/api/middlewares/isAuth.js
new file mode 100644
index 0000000..5eb3b56
--- /dev/null
+++ b/api/middlewares/isAuth.js
@@ -0,0 +1,25 @@
+const jwt = require('jsonwebtoken');
+const errorHandling = require('../assets/utils/errorHandling');
+const { serverError } = require('../assets/config/errors');
+const { JWT_SECRET } = require('../assets/config/config');
+
+module.exports = (req, _res, next) => {
+ const token = req.get('Authorization');
+ if (!token) {
+ return errorHandling(next, { message: "Vous n'êtes pas connecté.", statusCode: 401 });
+ }
+
+ let decodedToken;
+ try {
+ decodedToken = jwt.verify(token, JWT_SECRET);
+ } catch (error) {
+ return errorHandling(next, serverError);
+ }
+
+ if (!decodedToken) {
+ return errorHandling(next, { message: "Vous n'êtes pas connecté.", statusCode: 401 });
+ }
+
+ req.userId = decodedToken.userId;
+ next();
+}
\ No newline at end of file
diff --git a/api/models/functions.js b/api/models/functions.js
index dc3fb6e..4fa6bed 100644
--- a/api/models/functions.js
+++ b/api/models/functions.js
@@ -22,7 +22,8 @@ module.exports = sequelize.define('function', {
},
image: {
type: Sequelize.STRING,
- allowNull: false
+ allowNull: false,
+ defaultValue: "/images/functions/default.png"
},
type: {
type: Sequelize.STRING,
diff --git a/api/models/users.js b/api/models/users.js
new file mode 100644
index 0000000..c1d794c
--- /dev/null
+++ b/api/models/users.js
@@ -0,0 +1,50 @@
+const Sequelize = require('sequelize');
+const sequelize = require('../assets/utils/database');
+
+module.exports = sequelize.define('user', {
+ id: {
+ type: Sequelize.INTEGER,
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true
+ },
+ name: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ email: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ password: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ biography: {
+ type: Sequelize.TEXT,
+ },
+ 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
diff --git a/api/package-lock.json b/api/package-lock.json
index 2ed1cf1..c645886 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -96,6 +96,11 @@
"safe-buffer": "5.1.2"
}
},
+ "bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+ },
"binary-extensions": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
@@ -163,6 +168,11 @@
"fill-range": "^7.0.1"
}
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -410,6 +420,14 @@
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -498,6 +516,22 @@
"vary": "~1.1.2"
}
},
+ "express-validator": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz",
+ "integrity": "sha512-Fs+x0yDOSiUV+o5jIRloMyBxqpSzJiMM8KQW1IRVv2l49F6ATU0F9uPa+3K6vXNlLlhUjauv2FCGLFPMaNr24w==",
+ "requires": {
+ "lodash": "^4.17.15",
+ "validator": "^12.1.0"
+ },
+ "dependencies": {
+ "validator": {
+ "version": "12.2.0",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz",
+ "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ=="
+ }
+ }
+ },
"feature-policy": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz",
@@ -873,6 +907,49 @@
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
+ "jsonwebtoken": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"latest-version": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
@@ -887,6 +964,41 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+ },
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
@@ -1052,6 +1164,11 @@
"resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz",
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
},
+ "nodemailer": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.6.tgz",
+ "integrity": "sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA=="
+ },
"nodemon": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz",
@@ -1303,8 +1420,7 @@
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"semver-diff": {
"version": "2.1.0",
@@ -1386,6 +1502,11 @@
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
@@ -1457,6 +1578,11 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
@@ -1624,9 +1750,9 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
+ "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw=="
},
"validator": {
"version": "10.11.0",
diff --git a/api/package.json b/api/package.json
index 426d009..f77ef92 100644
--- a/api/package.json
+++ b/api/package.json
@@ -11,13 +11,18 @@
"license": "MIT",
"dependencies": {
"axios": "^0.19.2",
+ "bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.17.1",
+ "express-validator": "^6.4.0",
"helmet": "^3.21.3",
+ "jsonwebtoken": "^8.5.1",
"moment": "^2.24.0",
"mysql2": "^2.1.0",
+ "nodemailer": "^6.4.6",
"sequelize": "^5.21.5",
- "smart-request-balancer": "^2.1.1"
+ "smart-request-balancer": "^2.1.1",
+ "uuid": "^7.0.2"
},
"devDependencies": {
"dotenv": "^8.2.0",
diff --git a/api/routes/admin.js b/api/routes/admin.js
new file mode 100644
index 0000000..6e9cb72
--- /dev/null
+++ b/api/routes/admin.js
@@ -0,0 +1,11 @@
+const { Router } = require('express');
+const adminController = require('../controllers/admin');
+const isAuth = require('../middlewares/isAuth');
+const isAdmin = require('../middlewares/isAdmin');
+
+const AdminRouter = Router();
+
+// Permet de créé une fonction
+AdminRouter.post('/functions', isAuth, isAdmin, adminController.postFunction);
+
+module.exports = AdminRouter;
\ No newline at end of file
diff --git a/api/routes/users.js b/api/routes/users.js
new file mode 100644
index 0000000..d0c4879
--- /dev/null
+++ b/api/routes/users.js
@@ -0,0 +1,52 @@
+const { Router } = require('express');
+const { body } = require('express-validator');
+const usersController = require('../controllers/users');
+const Users = require('../models/users');
+
+const UsersRouter = Router();
+
+// Permet de se connecter
+UsersRouter.post('/login', usersController.login);
+
+// Permet de s'inscrire
+UsersRouter.post('/signup', [
+ 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);
+ }
+ }))
+ .normalizeEmail(),
+ body('password')
+ .isLength({ min: 4 })
+ .withMessage("Votre mot de passe est trop court!"),
+ body('name')
+ .trim()
+ .not()
+ .isEmpty()
+ .withMessage("Votre nom ne peut pas être vide.")
+ .isAlphanumeric()
+ .withMessage("Votre nom ne peut contenir que des lettres ou/et des nombres.")
+ .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);
+ }
+ }))
+], usersController.signup);
+
+// Confirme l'inscription
+UsersRouter.get('/confirm-email/:tempToken', usersController.confirmEmail);
+
+module.exports = UsersRouter;
\ No newline at end of file