From 76298b6087d5cbc8c76bb49124e808132d9c447e Mon Sep 17 00:00:00 2001
From: Divlo
Date: Mon, 6 Apr 2020 16:46:03 +0200
Subject: [PATCH] frontend et backend: Connexion d'un utilisateur
---
api/controllers/users.js | 11 +++-
api/routes/users.js | 20 ++++++--
website/components/Header/Header.js | 23 +++++++--
website/contexts/UserContext.js | 56 +++++++++++++++++++++
website/package-lock.json | 21 ++++++++
website/package.json | 3 +-
website/pages/404.js | 7 ---
website/pages/_app.js | 8 +--
website/pages/login.js | 48 +++++++++++++++---
website/public/css/pages/register-login.css | 8 +++
website/utils/redirect.js | 10 ++--
11 files changed, 184 insertions(+), 31 deletions(-)
create mode 100644 website/contexts/UserContext.js
diff --git a/api/controllers/users.js b/api/controllers/users.js
index 8123c9e..189a473 100644
--- a/api/controllers/users.js
+++ b/api/controllers/users.js
@@ -35,8 +35,12 @@ exports.register = async (req, res, next) => {
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 });
+ }
try {
- const user = await Users.findOne({ where: { email, isConfirmed: true } });
+ 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 });
}
@@ -44,6 +48,9 @@ exports.login = async (req, res, next) => {
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: '1h' });
@@ -67,7 +74,7 @@ exports.confirmEmail = async (req, res, next) => {
user.tempToken = null;
user.isConfirmed = true;
await user.save();
- return res.redirect(`${FRONT_END_HOST}/login`);
+ return res.redirect(`${FRONT_END_HOST}/login?isConfirmed=true`);
} catch (error) {
console.log(error);
errorHandling(next, serverError);
diff --git a/api/routes/users.js b/api/routes/users.js
index d9c763f..b8a4d73 100644
--- a/api/routes/users.js
+++ b/api/routes/users.js
@@ -1,12 +1,22 @@
-const { Router } = require('express');
-const { body } = require('express-validator');
-const usersController = require('../controllers/users');
-const Users = require('../models/users');
+const { Router } = require('express');
+const { body } = require('express-validator');
+const usersController = require('../controllers/users');
+const { requiredFields } = require('../assets/config/errors');
+const Users = require('../models/users');
const UsersRouter = Router();
// Permet de se connecter
-UsersRouter.post('/login', usersController.login);
+UsersRouter.post('/login', [
+ body('email')
+ .not()
+ .isEmpty()
+ .withMessage(requiredFields.message),
+ body('password')
+ .not()
+ .isEmpty()
+ .withMessage(requiredFields.message)
+], usersController.login);
// TODO: Récupère les informations public d'un profil
// UsersRouter.get('/profile/:userName', usersController.getUserInfo);
diff --git a/website/components/Header/Header.js b/website/components/Header/Header.js
index 88acc02..b11027d 100644
--- a/website/components/Header/Header.js
+++ b/website/components/Header/Header.js
@@ -1,10 +1,12 @@
-import { useState } from 'react';
+import { Fragment, useState, useContext } from 'react';
+import { UserContext } from '../../contexts/UserContext';
import Link from 'next/link';
import NavigationLink from './NavigationLink';
import './Header.css';
export default function Header() {
+ const { isAuth, logoutUser } = useContext(UserContext);
const [isActive, setIsActive] = useState(false);
const toggleNavbar = () => {
@@ -33,8 +35,23 @@ export default function Header() {
-
-
+
+ {
+ (!isAuth) ?
+
+
+
+
+ :
+
+
+ -
+
+ Se déconnecter
+
+
+
+ }
diff --git a/website/contexts/UserContext.js b/website/contexts/UserContext.js
new file mode 100644
index 0000000..77617b8
--- /dev/null
+++ b/website/contexts/UserContext.js
@@ -0,0 +1,56 @@
+import { createContext, useState, useEffect } from 'react';
+import Cookies from "universal-cookie";
+import api from '../utils/api';
+
+const cookies = new Cookies();
+
+export const UserContext = createContext();
+
+function UserContextProvider(props) {
+
+ const [user, setUser] = useState(null);
+ const [isAuth, setIsAuth] = useState(false);
+ const [loginLoading, setLoginLoading] = useState(false);
+ const [messageLogin, setMessageLogin] = useState("");
+
+ useEffect(() => {
+ const user = cookies.get('user');
+ setUser(user);
+ if (user != undefined) {
+ setIsAuth(true);
+ }
+ }, []);
+
+ const loginUser = ({ email, password }) => {
+ setLoginLoading(true);
+ api.post('/users/login', { email, password })
+ .then((response) => {
+ const user = response.data;
+ cookies.set('user', user);
+ setUser(user);
+ setIsAuth(true);
+ setMessageLogin('Succès: Connexion réussi!
');
+ setLoginLoading(false);
+ })
+ .catch((error) => {
+ setMessageLogin(`Erreur: ${error.response.data.message}
`);
+ setLoginLoading(false);
+ setIsAuth(false);
+ setUser(null);
+ });
+ }
+
+ const logoutUser = () => {
+ setUser(null);
+ setIsAuth(false);
+ cookies.remove('user');
+ }
+
+ return (
+
+ {props.children}
+
+ );
+}
+
+export default UserContextProvider;
\ No newline at end of file
diff --git a/website/package-lock.json b/website/package-lock.json
index b7273ce..970c778 100644
--- a/website/package-lock.json
+++ b/website/package-lock.json
@@ -1400,11 +1400,21 @@
"resolved": "https://registry.npmjs.org/@next/polyfill-nomodule/-/polyfill-nomodule-9.3.2.tgz",
"integrity": "sha512-kEa7v3trZmW6iWeTJrhg+ZsE9njae7mLkgyZB5M1r975JHr5PQ69B5aX7hrEAj7aAJYvCKETgAczx4gGR8MOzQ=="
},
+ "@types/cookie": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
+ "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
+ },
"@types/domhandler": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@types/domhandler/-/domhandler-2.4.1.tgz",
"integrity": "sha512-cfBw6q6tT5sa1gSPFSRKzF/xxYrrmeiut7E0TxNBObiLSBTuFEHibcfEe3waQPEDbqBsq+ql/TOniw65EyDFMA=="
},
+ "@types/object-assign": {
+ "version": "4.0.30",
+ "resolved": "https://registry.npmjs.org/@types/object-assign/-/object-assign-4.0.30.tgz",
+ "integrity": "sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI="
+ },
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@@ -9774,6 +9784,17 @@
"imurmurhash": "^0.1.4"
}
},
+ "universal-cookie": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.3.tgz",
+ "integrity": "sha512-YbEHRs7bYOBTIWedTR9koVEe2mXrq+xdjTJZcoKJK/pQaE6ni28ak2AKXFpevb+X6w3iU5SXzWDiJkmpDRb9qw==",
+ "requires": {
+ "@types/cookie": "^0.3.3",
+ "@types/object-assign": "^4.0.30",
+ "cookie": "^0.4.0",
+ "object-assign": "^4.1.1"
+ }
+ },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
diff --git a/website/package.json b/website/package.json
index 2059530..1422328 100644
--- a/website/package.json
+++ b/website/package.json
@@ -17,7 +17,8 @@
"react": "16.13.0",
"react-dom": "16.13.0",
"react-swipeable-views": "^0.13.9",
- "react-swipeable-views-utils": "^0.13.9"
+ "react-swipeable-views-utils": "^0.13.9",
+ "universal-cookie": "^4.0.3"
},
"devDependencies": {
"cross-env": "^7.0.2"
diff --git a/website/pages/404.js b/website/pages/404.js
index ec11efc..ef79cca 100644
--- a/website/pages/404.js
+++ b/website/pages/404.js
@@ -16,13 +16,6 @@ const Error404 = () => (
Cette page n'existe pas! Revenir à la page d'accueil ?
-
);
diff --git a/website/pages/_app.js b/website/pages/_app.js
index 2ef368e..b090e89 100644
--- a/website/pages/_app.js
+++ b/website/pages/_app.js
@@ -1,5 +1,4 @@
/* Libraries Imports */
-import { Fragment } from 'react';
import Router from 'next/router'
import NProgress from 'nprogress';
@@ -7,6 +6,9 @@ import NProgress from 'nprogress';
import Header from '../components/Header/Header';
import Footer from '../components/Footer/Footer';
+/* Contexts Imports */
+import UserContextProvider from '../contexts/UserContext';
+
/* CSS Imports */
import '../public/fonts/Montserrat/Montserrat.css';
import '../public/css/normalize.css';
@@ -19,13 +21,13 @@ Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
const App = ({ Component, pageProps }) => (
-
+
-
+
);
export default App;
\ No newline at end of file
diff --git a/website/pages/login.js b/website/pages/login.js
index 0b247c4..4edb762 100644
--- a/website/pages/login.js
+++ b/website/pages/login.js
@@ -1,9 +1,29 @@
-import { Fragment } from 'react';
+import { Fragment, useContext, useState } from 'react';
+import { useRouter } from 'next/router';
+import Link from 'next/link';
+import htmlParser from 'html-react-parser';
+import Loader from '../components/Loader';
import HeadTag from '../components/HeadTag';
+import { UserContext } from '../contexts/UserContext';
import '../public/css/pages/register-login.css';
const Login = () => {
+ const router = useRouter();
+ const [inputState, setInputState] = useState({});
+ const { loginUser, messageLogin, loginLoading } = useContext(UserContext);
+
+ const handleChange = () => {
+ const inputStateNew = { ...inputState };
+ inputStateNew[event.target.name] = event.target.value;
+ setInputState(inputStateNew);
+ }
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ loginUser(inputState);
+ }
+
return (
{
Se connecter
-
-
+
+ {(router.query.isConfirmed !== undefined) &&
Succès: Votre compte a bien été confirmé, vous pouvez maintenant vous connectez!
}
+ {
+ (loginLoading) ?
+
+ :
+ htmlParser(messageLogin)
+ }
+
);
}
+
export default Login;
\ No newline at end of file
diff --git a/website/public/css/pages/register-login.css b/website/public/css/pages/register-login.css
index d02a231..7ec2e3f 100644
--- a/website/public/css/pages/register-login.css
+++ b/website/public/css/pages/register-login.css
@@ -11,4 +11,12 @@
}
.Register-Login__title {
text-align: center;
+}
+.Register-Login__Forgot-password {
+ color: var(--text-color);
+ font-size: 16px;
+}
+.Register-Login__Forgot-password:hover {
+ color: var(--important);
+ text-decoration: none;
}
\ No newline at end of file
diff --git a/website/utils/redirect.js b/website/utils/redirect.js
index 643e8da..e7bea71 100644
--- a/website/utils/redirect.js
+++ b/website/utils/redirect.js
@@ -1,6 +1,10 @@
-function redirect (ctx, path) {
- ctx.res.writeHead(302, { Location: path });
- ctx.res.end();
+function redirect(ctx, path) {
+ if (ctx.res) {
+ ctx.res.writeHead(302, { Location: path });
+ ctx.res.end();
+ } else {
+ document.location.pathname = path;
+ }
}
module.exports = redirect;
\ No newline at end of file