frontend et backend: Connexion d'un utilisateur
This commit is contained in:
parent
8c37dbaaf4
commit
76298b6087
@ -35,8 +35,12 @@ exports.register = async (req, res, next) => {
|
|||||||
|
|
||||||
exports.login = async (req, res, next) => {
|
exports.login = async (req, res, next) => {
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
|
const errors = validationResult(req);
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const user = await Users.findOne({ where: { email, isConfirmed: true } });
|
const user = await Users.findOne({ where: { email } });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 400 });
|
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) {
|
if (!isEqual) {
|
||||||
return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 400 });
|
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({
|
const token = jwt.sign({
|
||||||
email: user.email, userId: user.id
|
email: user.email, userId: user.id
|
||||||
}, JWT_SECRET, { expiresIn: '1h' });
|
}, JWT_SECRET, { expiresIn: '1h' });
|
||||||
@ -67,7 +74,7 @@ exports.confirmEmail = async (req, res, next) => {
|
|||||||
user.tempToken = null;
|
user.tempToken = null;
|
||||||
user.isConfirmed = true;
|
user.isConfirmed = true;
|
||||||
await user.save();
|
await user.save();
|
||||||
return res.redirect(`${FRONT_END_HOST}/login`);
|
return res.redirect(`${FRONT_END_HOST}/login?isConfirmed=true`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
errorHandling(next, serverError);
|
errorHandling(next, serverError);
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
const { Router } = require('express');
|
const { Router } = require('express');
|
||||||
const { body } = require('express-validator');
|
const { body } = require('express-validator');
|
||||||
const usersController = require('../controllers/users');
|
const usersController = require('../controllers/users');
|
||||||
const Users = require('../models/users');
|
const { requiredFields } = require('../assets/config/errors');
|
||||||
|
const Users = require('../models/users');
|
||||||
|
|
||||||
const UsersRouter = Router();
|
const UsersRouter = Router();
|
||||||
|
|
||||||
// Permet de se connecter
|
// 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
|
// TODO: Récupère les informations public d'un profil
|
||||||
// UsersRouter.get('/profile/:userName', usersController.getUserInfo);
|
// 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 Link from 'next/link';
|
||||||
import NavigationLink from './NavigationLink';
|
import NavigationLink from './NavigationLink';
|
||||||
import './Header.css';
|
import './Header.css';
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
|
|
||||||
|
const { isAuth, logoutUser } = useContext(UserContext);
|
||||||
const [isActive, setIsActive] = useState(false);
|
const [isActive, setIsActive] = useState(false);
|
||||||
|
|
||||||
const toggleNavbar = () => {
|
const toggleNavbar = () => {
|
||||||
@ -33,8 +35,23 @@ export default function Header() {
|
|||||||
<ul className={`navbar__list ${(isActive) ? "navbar__list-active" : ""}`}>
|
<ul className={`navbar__list ${(isActive) ? "navbar__list-active" : ""}`}>
|
||||||
<NavigationLink name="Accueil" path="/" />
|
<NavigationLink name="Accueil" path="/" />
|
||||||
<NavigationLink name="Fonctions" path="/functions" />
|
<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>
|
</ul>
|
||||||
</nav>
|
</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",
|
"resolved": "https://registry.npmjs.org/@next/polyfill-nomodule/-/polyfill-nomodule-9.3.2.tgz",
|
||||||
"integrity": "sha512-kEa7v3trZmW6iWeTJrhg+ZsE9njae7mLkgyZB5M1r975JHr5PQ69B5aX7hrEAj7aAJYvCKETgAczx4gGR8MOzQ=="
|
"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": {
|
"@types/domhandler": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/domhandler/-/domhandler-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/domhandler/-/domhandler-2.4.1.tgz",
|
||||||
"integrity": "sha512-cfBw6q6tT5sa1gSPFSRKzF/xxYrrmeiut7E0TxNBObiLSBTuFEHibcfEe3waQPEDbqBsq+ql/TOniw65EyDFMA=="
|
"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": {
|
"@webassemblyjs/ast": {
|
||||||
"version": "1.8.5",
|
"version": "1.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
|
||||||
@ -9774,6 +9784,17 @@
|
|||||||
"imurmurhash": "^0.1.4"
|
"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": {
|
"unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
"react": "16.13.0",
|
"react": "16.13.0",
|
||||||
"react-dom": "16.13.0",
|
"react-dom": "16.13.0",
|
||||||
"react-swipeable-views": "^0.13.9",
|
"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": {
|
"devDependencies": {
|
||||||
"cross-env": "^7.0.2"
|
"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>
|
Cette page n'existe pas! <Link href={"/"}><a>Revenir à la page d'accueil ?</a></Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
|
||||||
{`
|
|
||||||
#__next {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
</style>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/* Libraries Imports */
|
/* Libraries Imports */
|
||||||
import { Fragment } from 'react';
|
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
|
|
||||||
@ -7,6 +6,9 @@ import NProgress from 'nprogress';
|
|||||||
import Header from '../components/Header/Header';
|
import Header from '../components/Header/Header';
|
||||||
import Footer from '../components/Footer/Footer';
|
import Footer from '../components/Footer/Footer';
|
||||||
|
|
||||||
|
/* Contexts Imports */
|
||||||
|
import UserContextProvider from '../contexts/UserContext';
|
||||||
|
|
||||||
/* CSS Imports */
|
/* CSS Imports */
|
||||||
import '../public/fonts/Montserrat/Montserrat.css';
|
import '../public/fonts/Montserrat/Montserrat.css';
|
||||||
import '../public/css/normalize.css';
|
import '../public/css/normalize.css';
|
||||||
@ -19,13 +21,13 @@ Router.events.on('routeChangeComplete', () => NProgress.done());
|
|||||||
Router.events.on('routeChangeError', () => NProgress.done());
|
Router.events.on('routeChangeError', () => NProgress.done());
|
||||||
|
|
||||||
const App = ({ Component, pageProps }) => (
|
const App = ({ Component, pageProps }) => (
|
||||||
<Fragment>
|
<UserContextProvider>
|
||||||
<Header />
|
<Header />
|
||||||
<div className="content container-fluid">
|
<div className="content container-fluid">
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Fragment>
|
</UserContextProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default App;
|
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 HeadTag from '../components/HeadTag';
|
||||||
|
import { UserContext } from '../contexts/UserContext';
|
||||||
import '../public/css/pages/register-login.css';
|
import '../public/css/pages/register-login.css';
|
||||||
|
|
||||||
const Login = () => {
|
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 (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HeadTag
|
<HeadTag
|
||||||
@ -14,26 +34,40 @@ const Login = () => {
|
|||||||
<div className="row Register-Login__row justify-content-center">
|
<div className="row Register-Login__row justify-content-center">
|
||||||
<div className="col-20">
|
<div className="col-20">
|
||||||
<h1 className="Register-Login__title">Se connecter</h1>
|
<h1 className="Register-Login__title">Se connecter</h1>
|
||||||
<form>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label className="form-label" htmlFor="name">Email :</label>
|
<label className="form-label" htmlFor="email">Email :</label>
|
||||||
<input type="email" name="email" id="email" className="form-control" placeholder="email@gmail.com" />
|
<input onChange={handleChange} type="email" name="email" id="email" className="form-control" placeholder="email@gmail.com" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label className="form-label" htmlFor="name">Mot de passe :</label>
|
<label className="form-label" htmlFor="password">Mot de passe :</label>
|
||||||
<input type="password" name="password" id="password" className="form-control" placeholder="******" />
|
<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>
|
||||||
|
|
||||||
<div className="form-group text-center">
|
<div className="form-group text-center">
|
||||||
<button type="submit" className="btn btn-dark">Envoyer</button>
|
<button type="submit" className="btn btn-dark">Envoyer</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Login;
|
export default Login;
|
@ -11,4 +11,12 @@
|
|||||||
}
|
}
|
||||||
.Register-Login__title {
|
.Register-Login__title {
|
||||||
text-align: center;
|
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) {
|
function redirect(ctx, path) {
|
||||||
ctx.res.writeHead(302, { Location: path });
|
if (ctx.res) {
|
||||||
ctx.res.end();
|
ctx.res.writeHead(302, { Location: path });
|
||||||
|
ctx.res.end();
|
||||||
|
} else {
|
||||||
|
document.location.pathname = path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = redirect;
|
module.exports = redirect;
|
Reference in New Issue
Block a user