frontend et backend: Connexion d'un utilisateur
This commit is contained in:
		| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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() { | ||||
|                     <ul className={`navbar__list ${(isActive) ? "navbar__list-active" : ""}`}> | ||||
|                         <NavigationLink name="Accueil" path="/" /> | ||||
|                         <NavigationLink name="Fonctions" path="/functions" /> | ||||
|                         <NavigationLink name="S'inscrire" path="/register" /> | ||||
|                         <NavigationLink name="Connexion" path="/login" /> | ||||
|  | ||||
|                         { | ||||
|                             (!isAuth) ?  | ||||
|                                 <Fragment> | ||||
|                                     <NavigationLink name="S'inscrire" path="/register" /> | ||||
|                                     <NavigationLink name="Connexion" path="/login" /> | ||||
|                                 </Fragment> | ||||
|                             : | ||||
|                                 <Fragment> | ||||
|                                     <NavigationLink name="Profil" path="/profile" /> | ||||
|                                     <li className="navbar-item"> | ||||
|                                         <Link href={"/"}> | ||||
|                                             <a onClick={logoutUser} className="navbar-link">Se déconnecter</a> | ||||
|                                         </Link> | ||||
|                                     </li> | ||||
|                                 </Fragment> | ||||
|                         } | ||||
|                     </ul> | ||||
|                 </nav> | ||||
|  | ||||
|   | ||||
							
								
								
									
										56
									
								
								website/contexts/UserContext.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								website/contexts/UserContext.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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('<p class="form-success"><b>Succès:</b> Connexion réussi!</p>'); | ||||
|                 setLoginLoading(false); | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|                 setMessageLogin(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`); | ||||
|                 setLoginLoading(false); | ||||
|                 setIsAuth(false); | ||||
|                 setUser(null); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     const logoutUser = () => { | ||||
|         setUser(null); | ||||
|         setIsAuth(false); | ||||
|         cookies.remove('user'); | ||||
|     }  | ||||
|  | ||||
|     return ( | ||||
|         <UserContext.Provider value={{ user, loginUser, logoutUser, loginLoading, messageLogin, isAuth }}> | ||||
|             {props.children} | ||||
|         </UserContext.Provider> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export default UserContextProvider; | ||||
							
								
								
									
										21
									
								
								website/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								website/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -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", | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -16,13 +16,6 @@ const Error404 = () => ( | ||||
|                 Cette page n'existe pas! <Link href={"/"}><a>Revenir à la page d'accueil ?</a></Link> | ||||
|             </p> | ||||
|         </div> | ||||
|         <style> | ||||
|             {` | ||||
|                 #__next { | ||||
|                     padding-top: 0; | ||||
|                 } | ||||
|             `} | ||||
|         </style> | ||||
|     </Fragment> | ||||
| ); | ||||
|  | ||||
|   | ||||
| @@ -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 }) => ( | ||||
|     <Fragment> | ||||
|     <UserContextProvider> | ||||
|         <Header /> | ||||
|             <div className="content container-fluid"> | ||||
|                 <Component {...pageProps} /> | ||||
|             </div> | ||||
|         <Footer /> | ||||
|     </Fragment> | ||||
|     </UserContextProvider> | ||||
| ); | ||||
|  | ||||
| export default App; | ||||
| @@ -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 ( | ||||
|         <Fragment> | ||||
|             <HeadTag  | ||||
| @@ -14,26 +34,40 @@ const Login = () => { | ||||
|                 <div className="row Register-Login__row justify-content-center"> | ||||
|                     <div className="col-20"> | ||||
|                         <h1 className="Register-Login__title">Se connecter</h1> | ||||
|                         <form> | ||||
|                         <form onSubmit={handleSubmit}> | ||||
|                             <div className="form-group"> | ||||
|                                 <label className="form-label" htmlFor="name">Email :</label> | ||||
|                                 <input type="email" name="email" id="email" className="form-control" placeholder="email@gmail.com" /> | ||||
|                                 <label className="form-label" htmlFor="email">Email :</label> | ||||
|                                 <input onChange={handleChange} type="email" name="email" id="email" className="form-control" placeholder="email@gmail.com" /> | ||||
|                             </div> | ||||
|  | ||||
|                             <div className="form-group"> | ||||
|                                 <label className="form-label" htmlFor="name">Mot de passe :</label> | ||||
|                                 <input type="password" name="password" id="password" className="form-control" placeholder="******" /> | ||||
|                                 <label className="form-label" htmlFor="password">Mot de passe :</label> | ||||
|                                 <input onChange={handleChange} type="password" name="password" id="password" className="form-control" placeholder="******" /> | ||||
|                                 <p> | ||||
|                                     <Link href={"/register"}> | ||||
|                                         <a className="Register-Login__Forgot-password">Mot de passe oublié ?</a> | ||||
|                                     </Link> | ||||
|                                 </p> | ||||
|                             </div> | ||||
|  | ||||
|                             <div className="form-group text-center"> | ||||
|                                 <button type="submit" className="btn btn-dark">Envoyer</button> | ||||
|                             </div> | ||||
|                         </form> | ||||
|                         <div className="form-result text-center"></div> | ||||
|                         <div className="form-result text-center"> | ||||
|                             {(router.query.isConfirmed !== undefined) && <p className="form-success"><b>Succès:</b> Votre compte a bien été confirmé, vous pouvez maintenant vous connectez!</p>} | ||||
|                             { | ||||
|                                 (loginLoading) ?  | ||||
|                                     <Loader /> | ||||
|                                 : | ||||
|                                     htmlParser(messageLogin) | ||||
|                             } | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export default Login; | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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; | ||||
		Reference in New Issue
	
	Block a user