From 221a878685d8bdf9b067167188c552e3a43a1437 Mon Sep 17 00:00:00 2001 From: MohammadAlHabil Date: Sat, 28 May 2022 21:07:12 +0300 Subject: [PATCH 1/3] fix protected routes --- client/src/App/index.jsx | 59 +++++++++--------- .../Dashboard/Categories/FormModal.jsx | 6 +- .../General/BookStatus/StatusNumber/index.jsx | 2 +- .../Dashboard/General/BookStatus/index.jsx | 2 +- client/src/Components/Login/index.jsx | 8 ++- client/src/Components/Navbar/RightMenu.jsx | 60 +++++++++++-------- client/src/Contexts/adminContext.jsx | 48 --------------- client/src/Contexts/userContext.jsx | 7 ++- client/src/Pages/Dashboard/index.jsx | 10 ++-- client/src/Pages/NotFound/index.jsx | 8 +-- client/src/Pages/NotFound/style.css | 4 +- client/src/Pages/Profile/Cover.jsx | 14 ++--- client/src/Pages/Profile/index.jsx | 27 ++++----- client/src/ProtectedRoute/AdminProtected.jsx | 19 ++++-- client/src/ProtectedRoute/UserProtected.jsx | 9 ++- server/controllers/index.ts | 7 ++- server/controllers/logout/index.ts | 7 +-- server/controllers/signin/index.ts | 7 ++- server/controllers/signinAdmin/index.ts | 10 ++-- server/controllers/signup/index.ts | 1 + server/queries/book/getUserBooksQuery.ts | 3 +- server/routes/index.ts | 2 - 22 files changed, 143 insertions(+), 177 deletions(-) delete mode 100644 client/src/Contexts/adminContext.jsx diff --git a/client/src/App/index.jsx b/client/src/App/index.jsx index 69d08bc..9f9cbd7 100644 --- a/client/src/App/index.jsx +++ b/client/src/App/index.jsx @@ -25,7 +25,6 @@ import LayoutUser from '../Components/Layout'; import { CategoriesProvider } from '../Contexts/CategoriesContext'; import { ModalLoginProvider } from '../Contexts/ModalLogin'; import { UserProvider } from '../Contexts/userContext'; -import { AdminProvider } from '../Contexts/adminContext'; import { AdminProtected, UserProtected } from '../ProtectedRoute'; function App() { @@ -34,40 +33,38 @@ function App() { - - - } /> - }> - }> - } /> - } /> - } /> - } /> - } /> - + + } /> + }> + }> + } /> + } /> + } /> + } /> + } /> + - }> - } /> - } - /> - } /> - } - /> - } /> - }> - } /> - } /> - + }> + } /> + } + /> + } /> + } + /> + } /> + }> + } /> + } /> + - } /> - - + } /> + diff --git a/client/src/Components/Dashboard/Categories/FormModal.jsx b/client/src/Components/Dashboard/Categories/FormModal.jsx index 7060c2c..fe33274 100644 --- a/client/src/Components/Dashboard/Categories/FormModal.jsx +++ b/client/src/Components/Dashboard/Categories/FormModal.jsx @@ -17,15 +17,15 @@ function FormModal({ const [loading, setLoading] = useState(false); const [form] = Form.useForm(); - let categroyData = {}; - categroyData = { + let categoryData = {}; + categoryData = { name: categoryRecord.name, description: categoryRecord.description, image: isUpload ? uploadedImage : categoryRecord.image, }; useEffect(() => { - form.setFieldsValue(categroyData); + form.setFieldsValue(categoryData); }, [visible]); const uploadImage = (e) => { diff --git a/client/src/Components/Dashboard/General/BookStatus/StatusNumber/index.jsx b/client/src/Components/Dashboard/General/BookStatus/StatusNumber/index.jsx index c81362c..175d872 100644 --- a/client/src/Components/Dashboard/General/BookStatus/StatusNumber/index.jsx +++ b/client/src/Components/Dashboard/General/BookStatus/StatusNumber/index.jsx @@ -22,7 +22,7 @@ function StatusNumber({ title, number, backgroundColor, icon }) { StatusNumber.propTypes = { title: PropTypes.string.isRequired, - number: PropTypes.string.isRequired, + number: PropTypes.number.isRequired, backgroundColor: PropTypes.string.isRequired, icon: PropTypes.element.isRequired, }; diff --git a/client/src/Components/Dashboard/General/BookStatus/index.jsx b/client/src/Components/Dashboard/General/BookStatus/index.jsx index c02f28c..56ae15e 100644 --- a/client/src/Components/Dashboard/General/BookStatus/index.jsx +++ b/client/src/Components/Dashboard/General/BookStatus/index.jsx @@ -70,7 +70,7 @@ function BookStatus() { diff --git a/client/src/Components/Login/index.jsx b/client/src/Components/Login/index.jsx index 6a062d7..ca88195 100644 --- a/client/src/Components/Login/index.jsx +++ b/client/src/Components/Login/index.jsx @@ -4,9 +4,9 @@ import axios from 'axios'; import PropTypes from 'prop-types'; import { Form, Input, Button, message, Spin } from 'antd'; import { MailOutlined, LockOutlined, LoadingOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; import { ModalLoginContext } from '../../Contexts/ModalLogin'; import { userContext } from '../../Contexts/userContext'; -import { adminContext } from '../../Contexts/adminContext'; const antIcon = ; @@ -15,7 +15,7 @@ function Login({ admin }) { const [isloading, setIsLoading] = useState(false); const [error, setError] = useState(''); const { setIsLogged } = useContext(userContext); - const { setIsAdminLogged } = useContext(adminContext); + const navigate = useNavigate(); const onFinish = ({ email, password }) => { setIsLoading(true); @@ -40,11 +40,13 @@ function Login({ admin }) { const onAdminFinish = ({ email, password }) => { setIsLoading(true); setError(''); + axios .post('/api/v1/admin/signin', { email, password }) .then(() => { - setIsAdminLogged(true); + setIsLogged(true); setIsLoading(false); + navigate('/dashboard', { replace: true }); }) .catch((err) => { if (err.response) { diff --git a/client/src/Components/Navbar/RightMenu.jsx b/client/src/Components/Navbar/RightMenu.jsx index c2f3565..2ef5ebc 100644 --- a/client/src/Components/Navbar/RightMenu.jsx +++ b/client/src/Components/Navbar/RightMenu.jsx @@ -16,32 +16,40 @@ function RightMenu({ mode, avatarMenu }) { }; return ( - - - {isLogged ? ( - - - -
- } /> -  {userInfo.name}  - -
-
-
-
- ) : ( - - )} -
- - - - - -
+ + + +
+ } /> +  {userInfo.name}  + +
+
+
+ + ) : ( + + ), + }, + { + key: 'book', + label: ( + + + + ), + }, + ]} + /> ); } diff --git a/client/src/Contexts/adminContext.jsx b/client/src/Contexts/adminContext.jsx deleted file mode 100644 index 69ca829..0000000 --- a/client/src/Contexts/adminContext.jsx +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-disable camelcase */ -import React, { createContext, useState, useEffect, useMemo } from 'react'; -import PropTypes from 'prop-types'; -import jwt_decode from 'jwt-decode'; -import { useNavigate, useLocation } from 'react-router-dom'; - -const adminContext = createContext(); - -function AdminProvider({ children }) { - const navigate = useNavigate(); - const [adminInfo, setAdminInfo] = useState({}); - const [isAdminLogged, setIsAdminLogged] = useState(false); - const token = document.cookie.split('tokenAdmin=')[1]; - const { pathname } = useLocation(); - const adminData = useMemo( - () => ({ adminInfo, setAdminInfo, isAdminLogged, setIsAdminLogged }), - [isAdminLogged, adminInfo] - ); - useEffect(() => { - if (token) { - setAdminInfo(jwt_decode(token)); - switch (pathname) { - case '/dashboard/contact': - navigate('/dashboard/contact', { replace: true }); - break; - case '/dashboard/books': - navigate('/dashboard/books', { replace: true }); - break; - case '/dashboard/services': - navigate('/dashboard/services', { replace: true }); - break; - case '/dashboard/categories': - navigate('/dashboard/categories', { replace: true }); - break; - default: - navigate('/dashboard', { replace: true }); - break; - } - } - }, [isAdminLogged]); - return ( - {children} - ); -} -AdminProvider.propTypes = { - children: PropTypes.node.isRequired, -}; -export { AdminProvider, adminContext }; diff --git a/client/src/Contexts/userContext.jsx b/client/src/Contexts/userContext.jsx index 879094f..26f0e15 100644 --- a/client/src/Contexts/userContext.jsx +++ b/client/src/Contexts/userContext.jsx @@ -29,8 +29,11 @@ function UserProvider({ children }) { if (token) { setUserInfo(jwt_decode(token)); setIsLogged(true); - if (pathname === '/profile') { - navigate('/profile', { replace: true }); + if (userInfo.role === 'admin' && pathname === '/login/admin') { + navigate('/dashboard', { replace: true }); + } + if (pathname === '/profile' || pathname === '/book') { + navigate(pathname, { replace: true }); setIsOpen(false); } } else { diff --git a/client/src/Pages/Dashboard/index.jsx b/client/src/Pages/Dashboard/index.jsx index 738e97d..d13fc81 100644 --- a/client/src/Pages/Dashboard/index.jsx +++ b/client/src/Pages/Dashboard/index.jsx @@ -21,7 +21,7 @@ import { LogoutOutlined, BellOutlined, } from '@ant-design/icons'; -import { adminContext } from '../../Contexts/adminContext'; +import { userContext } from '../../Contexts/userContext'; import './style.css'; import logo from '../../Assets/images/logo.svg'; import user from '../../Assets/images/user.png'; @@ -64,14 +64,14 @@ const breadcrumbNameMap = { }; function Dashboard() { - const { adminInfo, setIsAdminLogged } = useContext(adminContext); + const { userInfo, setIsLogged } = useContext(userContext); const { pathname } = useLocation(); const navigate = useNavigate(); const logout = () => { axios - .get('/api/v1/logoutAdmin') + .get('/api/v1/logout') .then(() => { - setIsAdminLogged(false); + setIsLogged(false); navigate('/login/admin', { replace: true }); }) .catch(() => { @@ -131,7 +131,7 @@ function Dashboard() { /> } /> - {adminInfo.name} + {userInfo.name} diff --git a/client/src/Pages/NotFound/index.jsx b/client/src/Pages/NotFound/index.jsx index a959a25..4fe1752 100644 --- a/client/src/Pages/NotFound/index.jsx +++ b/client/src/Pages/NotFound/index.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { Image, Typography, Button } from 'antd'; -import notfoundimage from '../../Assets/images/notfoundimage.png'; +import notfoundImage from '../../Assets/images/notfoundimage.png'; import './style.css'; const { Title } = Typography; @@ -9,13 +9,13 @@ const { Title } = Typography; function NotFound() { return (
- Not Found Image + Not Found Image - We can not seem to find the page you are looking for + لا توجد هذه الصفحة
diff --git a/client/src/Pages/NotFound/style.css b/client/src/Pages/NotFound/style.css index 5a50d1a..38bed55 100644 --- a/client/src/Pages/NotFound/style.css +++ b/client/src/Pages/NotFound/style.css @@ -8,6 +8,6 @@ .go-home-button{ border-radius: 3px !important; margin-top: 10px; - border-color: #f2473f !important; - background: #f2473f !important; + border-color: var(--main) !important; + background: var(--main) !important; } \ No newline at end of file diff --git a/client/src/Pages/Profile/Cover.jsx b/client/src/Pages/Profile/Cover.jsx index 5de6771..614f425 100644 --- a/client/src/Pages/Profile/Cover.jsx +++ b/client/src/Pages/Profile/Cover.jsx @@ -1,11 +1,12 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import React, { useContext } from 'react'; import { Avatar, Card, Image } from 'antd'; import headerBackground from '../../Assets/images/headerBackground.svg'; +import { userContext } from '../../Contexts/userContext'; const { Meta } = Card; -function Cover({ userInfo }) { +function Cover() { + const { userInfo } = useContext(userContext); return (
@@ -33,10 +34,5 @@ function Cover({ userInfo }) {
); } -Cover.propTypes = { - userInfo: PropTypes.shape({ - name: PropTypes.string.isRequired, - location: PropTypes.string.isRequired, - }).isRequired, -}; + export default Cover; diff --git a/client/src/Pages/Profile/index.jsx b/client/src/Pages/Profile/index.jsx index beedcf1..e5c7149 100644 --- a/client/src/Pages/Profile/index.jsx +++ b/client/src/Pages/Profile/index.jsx @@ -16,20 +16,19 @@ function Profile() { useEffect(() => { setLoading(true); const cancelTokenSource = axios.CancelToken.source(); - axios - .get(`/api/v1//user/${userInfo.id}/book`, { - cancelToken: cancelTokenSource.token, - }) - .then(({ data: { data } }) => { - setMyBooks(data); - }) - .catch(() => { - message.error('حدث خطأ ما'); - }) - .finally(() => setLoading(false)); - + if (userInfo) { + axios + .get(`/api/v1/user/${userInfo.id}/book`, { + cancelToken: cancelTokenSource.token, + }) + .then(({ data: { data } }) => { + setMyBooks(data); + }) + .catch(() => {}) + .finally(() => setLoading(false)); + } return () => cancelTokenSource.cancel(); - }, [userInfo]); + }, []); return ( @@ -40,7 +39,7 @@ function Profile() { lg={{ span: 20 }} xl={{ span: 20 }} > - + {loading ? ( ) : myBooks.length ? ( diff --git a/client/src/ProtectedRoute/AdminProtected.jsx b/client/src/ProtectedRoute/AdminProtected.jsx index 519cfbe..753eadf 100644 --- a/client/src/ProtectedRoute/AdminProtected.jsx +++ b/client/src/ProtectedRoute/AdminProtected.jsx @@ -1,12 +1,21 @@ import React, { useContext } from 'react'; import { Navigate, Outlet } from 'react-router-dom'; -import { adminContext } from '../Contexts/adminContext'; +import { userContext } from '../Contexts/userContext'; function AdminProtected() { - const { adminInfo } = useContext(adminContext); - if (!(adminInfo.role === 'admin')) { - return ; - } + const userPromise = new Promise((resolve, reject) => { + const { userInfo } = useContext(userContext); + if (userInfo) resolve(userInfo); + else reject(new Error('no user')); + }); + + userPromise + .then((userInfo) => { + if (!(userInfo.role === 'admin')) ; + }) + .catch(() => {}); + return ; } + export default AdminProtected; diff --git a/client/src/ProtectedRoute/UserProtected.jsx b/client/src/ProtectedRoute/UserProtected.jsx index 5c31e61..231494b 100644 --- a/client/src/ProtectedRoute/UserProtected.jsx +++ b/client/src/ProtectedRoute/UserProtected.jsx @@ -7,10 +7,13 @@ function UserProtected() { const navigate = useNavigate(); const { userInfo } = useContext(userContext); const { setIsOpen } = useContext(ModalLoginContext); + useEffect(() => { - if (!userInfo.name) { - navigate('/', { replace: true }); - setIsOpen(true); + if (typeof userInfo === 'string') { + if (!userInfo.name) { + navigate('/', { replace: true }); + setIsOpen(true); + } } }, []); return ; diff --git a/server/controllers/index.ts b/server/controllers/index.ts index 9a28835..0da3f0f 100644 --- a/server/controllers/index.ts +++ b/server/controllers/index.ts @@ -8,10 +8,12 @@ import { getContacts, addContact, deleteContact, updateContactStatus, } from './contact'; import signup from './signup'; -import { logout, logoutAdmin } from './logout'; +import logout from './logout'; import { checkAuth, checkAdmin } from './middlewares/auth'; import signin from './signin'; -import { postService, archivedService, putService, getServices } from './services'; +import { + postService, archivedService, putService, getServices, +} from './services'; import { getBooks, getUserBooks, getBook, postBook, deleteBook, getStatus, getBookDay, getBookMonth, updateBook, updateStatusBook, @@ -45,7 +47,6 @@ export { validateLink, signinAdmin, updateContactStatus, - logoutAdmin, getStatus, deleteBook, getBookDay, diff --git a/server/controllers/logout/index.ts b/server/controllers/logout/index.ts index e01490d..1793b82 100644 --- a/server/controllers/logout/index.ts +++ b/server/controllers/logout/index.ts @@ -1,10 +1,7 @@ import { RequestHandler } from 'express'; -const logout: RequestHandler = async (req, res, next) => { +const logout: RequestHandler = async (req, res) => { res.clearCookie('token').json({ message: 'You have been successfully logged out', status: 200 }); }; -const logoutAdmin: RequestHandler = async (req, res, next) => { - res.clearCookie('tokenAdmin').json({ message: 'You have been successfully logged out', status: 200 }); -}; -export { logout, logoutAdmin }; +export default logout; diff --git a/server/controllers/signin/index.ts b/server/controllers/signin/index.ts index c41bc89..0846972 100644 --- a/server/controllers/signin/index.ts +++ b/server/controllers/signin/index.ts @@ -15,8 +15,8 @@ const signin: RequestHandler = async (req, res, next) => { if (rowCount === 0) { throw new CustomizedError(400, 'There have error with email or password'); } - const resultComapre = await compare(password, data[0].password); - if (!resultComapre) { + const resultCompare = await compare(password, data[0].password); + if (!resultCompare) { throw new CustomizedError(400, 'There have error with email or password'); } const token = await jwtSign({ @@ -27,8 +27,9 @@ const signin: RequestHandler = async (req, res, next) => { location: data[0].location, lat: data[0].lat, lng: data[0].lng, + role: 'user', }); - res.cookie('token', token).json({ message:'You have been successfully logged in', status: 200 }); + res.cookie('token', token).json({ message: 'You have been successfully logged in', status: 200 }); } catch (error: any) { if (error.name === 'ValidationError') { return next(new CustomizedError(400, error.errors[0])); diff --git a/server/controllers/signinAdmin/index.ts b/server/controllers/signinAdmin/index.ts index e49dbfa..53d2e4b 100644 --- a/server/controllers/signinAdmin/index.ts +++ b/server/controllers/signinAdmin/index.ts @@ -6,9 +6,7 @@ import { jwtSign, CustomizedError } from '../../utils'; import { signinSchema } from '../../validation'; dotenv.config(); -const { - NODE_ENV, -} = process.env; + const signinAdmin: RequestHandler = async (req, res, next) => { try { const { @@ -18,14 +16,14 @@ const signinAdmin: RequestHandler = async (req, res, next) => { if (rowCount === 0) { throw new CustomizedError(400, 'There have error with email or password'); } - const resultComapre = await compare(password, data[0].password); - if (!resultComapre) { + const resultCompare = await compare(password, data[0].password); + if (!resultCompare) { throw new CustomizedError(400, 'There have error with email or password'); } const token = await jwtSign({ id: data[0].id, email, role: 'admin', name: data[0].name, }); - res.cookie('tokenAdmin', token).json({ message: 'You have been successfully logged in', status: 200 }); + res.cookie('token', token).json({ message: 'You have been successfully logged in', status: 200 }); } catch (error: any) { if (error.name === 'ValidationError') { return next(new CustomizedError(400, error.errors[0])); diff --git a/server/controllers/signup/index.ts b/server/controllers/signup/index.ts index 67b9ee0..18b00f3 100644 --- a/server/controllers/signup/index.ts +++ b/server/controllers/signup/index.ts @@ -31,6 +31,7 @@ const signup: RequestHandler = async (req, res, next) => { name, phone, locationDetails, + role: 'user', }); res.cookie('token', token).status(201).json({ message: 'You have been successfully register', status: 201 }); } catch (error: any) { diff --git a/server/queries/book/getUserBooksQuery.ts b/server/queries/book/getUserBooksQuery.ts index 29e1497..bdf1e59 100644 --- a/server/queries/book/getUserBooksQuery.ts +++ b/server/queries/book/getUserBooksQuery.ts @@ -13,5 +13,6 @@ const getUserBooksQuery = (id:number) => connection.query(` FROM appointments AS a JOIN users AS u ON a.user_id = u.id - WHERE a.user_id = $1`, [id]); + WHERE a.user_id = $1 + ORDER BY a.id DESC`, [id]); export default getUserBooksQuery; diff --git a/server/routes/index.ts b/server/routes/index.ts index e0fbea0..072c6af 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -22,7 +22,6 @@ import { signinAdmin, updateContactStatus, checkAuth, - logoutAdmin, getStatus, deleteBook, getBookDay, @@ -43,7 +42,6 @@ router.delete('/contact/archives/:id', validateLink, deleteContact); router.put('/contact/status/:id', validateLink, updateContactStatus); router.post('/signup', signup); router.get('/logout', logout); -router.get('/logoutAdmin', logoutAdmin); router.post('/signin', signin); router.post('/admin/signin', signinAdmin); router.get('/book/status', getStatus); From 1745ada0b75247274bddb5e6f0235f46b8dba556 Mon Sep 17 00:00:00 2001 From: MohammadAlHabil Date: Sat, 28 May 2022 21:58:19 +0300 Subject: [PATCH 2/3] fix protected routes --- client/src/Pages/Profile/index.jsx | 2 +- package.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/Pages/Profile/index.jsx b/client/src/Pages/Profile/index.jsx index e5c7149..060d99a 100644 --- a/client/src/Pages/Profile/index.jsx +++ b/client/src/Pages/Profile/index.jsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from 'react'; import axios from 'axios'; import uuid from 'react-uuid'; -import { Row, Col, message, Descriptions, Typography, Spin, Empty } from 'antd'; +import { Row, Col, Descriptions, Typography, Spin, Empty } from 'antd'; import { userContext } from '../../Contexts/userContext'; import Cover from './Cover'; import './style.css'; diff --git a/package.json b/package.json index fb73fd3..3c43d4f 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "description": "", "main": "index.js", + "engines": { + "node": "16.14.0" + }, "scripts": { "start": "NODE_ENV=production node dist/", "dev": "cross-env NODE_ENV=development ts-node-dev server/index.ts", From e4a34e6e2368b29c8fa6154441cb3951446db800 Mon Sep 17 00:00:00 2001 From: MohammadAlHabil Date: Sun, 29 May 2022 00:56:16 +0300 Subject: [PATCH 3/3] sdfklsadhflksdjfl --- client/src/App/index.jsx | 6 +- .../src/Components/BookNow/BookContainer.jsx | 4 +- .../Components/BookNow/UserInformation.jsx | 4 +- .../Components/Dashboard/AdminLogin/index.jsx | 1 + client/src/Components/Login/index.jsx | 55 ++------- client/src/Components/Navbar/RightMenu.jsx | 4 +- client/src/Components/Navbar/index.jsx | 35 +----- client/src/Components/Register/index.jsx | 27 +---- client/src/Contexts/userContext copy.jsx | 71 ++++++++++++ client/src/Contexts/userContext.jsx | 108 +++++++++++++++--- client/src/Pages/Dashboard/index.jsx | 21 +--- client/src/Pages/Profile/Cover.jsx | 4 +- client/src/Pages/Profile/index.jsx | 4 +- client/src/ProtectedRoute/AdminProtected.jsx | 60 +++++++--- client/src/ProtectedRoute/UserProtected.jsx | 4 +- server/controllers/signin/index.ts | 18 ++- 16 files changed, 261 insertions(+), 165 deletions(-) create mode 100644 client/src/Contexts/userContext copy.jsx diff --git a/client/src/App/index.jsx b/client/src/App/index.jsx index 9f9cbd7..4a44e57 100644 --- a/client/src/App/index.jsx +++ b/client/src/App/index.jsx @@ -24,7 +24,7 @@ import './app.css'; import LayoutUser from '../Components/Layout'; import { CategoriesProvider } from '../Contexts/CategoriesContext'; import { ModalLoginProvider } from '../Contexts/ModalLogin'; -import { UserProvider } from '../Contexts/userContext'; +import { ProvideAuth } from '../Contexts/userContext'; import { AdminProtected, UserProtected } from '../ProtectedRoute'; function App() { @@ -32,7 +32,7 @@ function App() {
- + } /> }> @@ -65,7 +65,7 @@ function App() { } /> - +
diff --git a/client/src/Components/BookNow/BookContainer.jsx b/client/src/Components/BookNow/BookContainer.jsx index 1e92db2..2e82b3b 100644 --- a/client/src/Components/BookNow/BookContainer.jsx +++ b/client/src/Components/BookNow/BookContainer.jsx @@ -10,7 +10,7 @@ import Summary from './Summary'; import CompleteBook from './CompleteBook'; import cities from '../../cities.json'; import './style.css'; -import { userContext } from '../../Contexts/userContext'; +import { useAuth } from '../../Contexts/userContext'; const { Step } = Steps; @@ -27,7 +27,7 @@ function BookContainer() { ]); const [isModalVisible, setIsModalVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); - const { userInfo } = useContext(userContext); + const { userInfo } = useAuth(); useEffect(() => { if (typeof userInfo !== 'string') { diff --git a/client/src/Components/BookNow/UserInformation.jsx b/client/src/Components/BookNow/UserInformation.jsx index 256f287..df9fe35 100644 --- a/client/src/Components/BookNow/UserInformation.jsx +++ b/client/src/Components/BookNow/UserInformation.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Typography, Form, Input, Row, Col, Select } from 'antd'; import LeafMap from '../Map'; import cities from '../../cities.json'; -import { userContext } from '../../Contexts/userContext'; +import { useAuth } from '../../Contexts/userContext'; const { Title } = Typography; const { Option } = Select; @@ -13,7 +13,7 @@ function UserInformation({ position, setPosition, }) { - const { userInfo } = useContext(userContext); + const { userInfo } = useAuth(); return (
diff --git a/client/src/Components/Dashboard/AdminLogin/index.jsx b/client/src/Components/Dashboard/AdminLogin/index.jsx index a01d93e..f235518 100644 --- a/client/src/Components/Dashboard/AdminLogin/index.jsx +++ b/client/src/Components/Dashboard/AdminLogin/index.jsx @@ -8,6 +8,7 @@ import './style.css'; function AdminLogin() { return (
+ {console.log('admin login')} ; function Login({ admin }) { - const { setIsOpen } = useContext(ModalLoginContext); - const [isloading, setIsLoading] = useState(false); - const [error, setError] = useState(''); - const { setIsLogged } = useContext(userContext); - const navigate = useNavigate(); + const { isloading, error, login } = useAuth(); + // const { setIsOpen } = useContext(ModalLoginContext); + // const [isloading, setIsLoading] = useState(false); + // const [error, setError] = useState(''); + // const { setIsLogged } = useContext(userContext); + // const navigate = useNavigate(); const onFinish = ({ email, password }) => { - setIsLoading(true); - setError(''); - axios - .post('/api/v1/signin', { email, password }) - .then(() => { - setIsLoading(false); - setIsOpen(false); - setIsLogged(true); - }) - .catch((err) => { - if (err.response) { - setError('يوجد خطأ بالإيميل أو كلمة السر'); - setIsLoading(false); - } else { - message.error('حدث خطأ ما'); - setIsLoading(false); - } - }); + login({ email, password }); }; - const onAdminFinish = ({ email, password }) => { - setIsLoading(true); - setError(''); - axios - .post('/api/v1/admin/signin', { email, password }) - .then(() => { - setIsLogged(true); - setIsLoading(false); - navigate('/dashboard', { replace: true }); - }) - .catch((err) => { - if (err.response) { - setError('يوجد خطأ بالإيميل أو كلمة السر'); - setIsLoading(false); - } else { - message.error('حدث خطأ ما'); - setIsLoading(false); - } - }); - }; return (
{ diff --git a/client/src/Components/Navbar/index.jsx b/client/src/Components/Navbar/index.jsx index 19cd450..5098762 100644 --- a/client/src/Components/Navbar/index.jsx +++ b/client/src/Components/Navbar/index.jsx @@ -1,35 +1,21 @@ /* eslint-disable no-undef */ import React, { useState, useContext } from 'react'; -import axios from 'axios'; -import { Link, useNavigate } from 'react-router-dom'; -import { - Layout, - Menu, - Image, - Button, - Dropdown, - Space, - Drawer, - message, -} from 'antd'; +import { Link } from 'react-router-dom'; +import { Layout, Menu, Image, Button, Dropdown, Space, Drawer } from 'antd'; import { DownOutlined, MenuOutlined } from '@ant-design/icons'; import logo from '../../Assets/images/logo.svg'; import LeftMenu from './LeftMenu'; import RightMenu from './RightMenu'; import './navbar.css'; - import { CategoriesContext } from '../../Contexts/CategoriesContext'; -import { userContext } from '../../Contexts/userContext'; -import { ModalLoginContext } from '../../Contexts/ModalLogin'; +import { useAuth } from '../../Contexts/userContext'; const { Header } = Layout; function Navbar() { const [visible, setVisible] = useState(false); const { categories } = useContext(CategoriesContext); - const { setIsLogged } = useContext(userContext); - const { setIsOpen } = useContext(ModalLoginContext); - const navigate = useNavigate(); + const { logout } = useAuth(); const showDrawer = () => { setVisible(true); @@ -69,19 +55,6 @@ function Navbar() { label: item, })); - const logout = () => { - axios - .get('/api/v1/logout') - .then(() => { - setIsLogged(false); - navigate('/', { replace: true }); - setIsOpen(false); - }) - .catch(() => { - message.error('حدث خطأ ما'); - }); - }; - const avatarMenu = ( ; function Register() { - const { setIsOpen } = useContext(ModalLoginContext); - const [isloading, setIsLoading] = useState(false); - const [errorEmail, setErrorEmail] = useState(''); - const { setIsLogged } = useContext(userContext); + const { setIsOpen, errorEmail } = useContext(ModalLoginContext); + // const [isloading, setIsLoading] = useState(false); + // const [errorEmail, setErrorEmail] = useState(''); + const { isloading, register } = useAuth(); const onFinish = ({ name, email, phone, password, location }) => { setIsLoading(true); @@ -31,22 +31,7 @@ function Register() { lat: +locationData.coordinates.lat, lng: +locationData.coordinates.lng, }; - axios - .post('/api/v1/signup', { name, email, phone, password, locationDetails }) - .then(() => { - setIsLoading(false); - setIsOpen(false); - setIsLogged(true); - }) - .catch((err) => { - if (err.response) { - setErrorEmail('الإيميل موجود مسبقاً'); - setIsLoading(false); - } else { - message.error('حدث خطأ ما'); - setIsLoading(false); - } - }); + register({ name, email, phone, password, locationDetails }); }; return (
diff --git a/client/src/Contexts/userContext copy.jsx b/client/src/Contexts/userContext copy.jsx new file mode 100644 index 0000000..490450d --- /dev/null +++ b/client/src/Contexts/userContext copy.jsx @@ -0,0 +1,71 @@ +/* eslint-disable camelcase */ +import React, { + createContext, + useState, + useEffect, + useMemo, + useContext, +} from 'react'; +import PropTypes from 'prop-types'; +import jwt_decode from 'jwt-decode'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { message } from 'antd'; +import axios from 'axios'; +import { ModalLoginContext } from './ModalLogin'; + +const userContext = createContext(); +const useAuth = () => useContext(userContext); +function UserProvider({ children }) { + const [userInfo, setUserInfo] = useState(''); + const [isLogged, setIsLogged] = useState(false); + const token = document.cookie.split('token=')[1]; + const { setIsOpen } = useContext(ModalLoginContext); + const { pathname } = useLocation(); + const navigate = useNavigate(); + + const logout = () => { + axios + .get('/api/v1/logout') + .then(() => { + setIsLogged(false); + navigate('/', { replace: true }); + setIsOpen(false); + }) + .catch(() => { + message.error('حدث خطأ ما'); + }); + }; + + const userData = useMemo( + () => ({ userInfo, setUserInfo, isLogged, setIsLogged, logout }), + [userInfo, setUserInfo] + ); + useEffect(() => { + if (token) { + console.log('userInfo', userInfo); + setUserInfo(jwt_decode(token)); + setIsLogged(true); + if (userInfo.role === 'admin' && pathname === '/login/admin') { + navigate('/dashboard', { replace: true }); + } + if (userInfo.role === 'admin') { + navigate(pathname, { replace: true }); + } + if (pathname === '/profile' || pathname === '/book') { + navigate(pathname, { replace: true }); + setIsOpen(false); + } + } else { + setIsLogged(false); + setUserInfo(''); + } + }, [isLogged]); + + return ( + {children} + ); +} +UserProvider.propTypes = { + children: PropTypes.node.isRequired, +}; +export { UserProvider, userContext, useAuth }; diff --git a/client/src/Contexts/userContext.jsx b/client/src/Contexts/userContext.jsx index 26f0e15..3268411 100644 --- a/client/src/Contexts/userContext.jsx +++ b/client/src/Contexts/userContext.jsx @@ -1,37 +1,95 @@ /* eslint-disable camelcase */ -import React, { - createContext, - useState, - useEffect, - useMemo, - useContext, -} from 'react'; +import React, { createContext, useState, useEffect, useContext } from 'react'; import PropTypes from 'prop-types'; import jwt_decode from 'jwt-decode'; import { useLocation, useNavigate } from 'react-router-dom'; +import { message } from 'antd'; +import axios from 'axios'; import { ModalLoginContext } from './ModalLogin'; -const userContext = createContext(); +const AuthContext = createContext(); -function UserProvider({ children }) { +const useAuth = () => useContext(AuthContext); + +function useProvideAuth() { const [userInfo, setUserInfo] = useState(''); const [isLogged, setIsLogged] = useState(false); + const [isloading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + const [errorEmail, setErrorEmail] = useState(''); const token = document.cookie.split('token=')[1]; const { setIsOpen } = useContext(ModalLoginContext); const { pathname } = useLocation(); const navigate = useNavigate(); - const userData = useMemo( - () => ({ userInfo, setUserInfo, isLogged, setIsLogged }), - [userInfo, setUserInfo] - ); + const register = ({ name, email, phone, password, locationDetails }) => { + axios + .post('/api/v1/signup', { name, email, phone, password, locationDetails }) + .then(() => { + setIsLoading(false); + setIsOpen(false); + setIsLogged(true); + }) + .catch((err) => { + if (err.response) { + setErrorEmail('الإيميل موجود مسبقاً'); + setIsLoading(false); + } else { + message.error('حدث خطأ ما'); + setIsLoading(false); + } + }); + }; + + const login = ({ email, password }) => { + setIsLoading(true); + setError(''); + axios + .post('/api/v1/signin', { email, password }) + .then(({ data }) => { + if (data.data === 'admin') { + navigate('/dashboard', { replace: true }); + } + setIsLoading(false); + setIsOpen(false); + setIsLogged(true); + }) + .catch((err) => { + if (err.response) { + setError('يوجد خطأ بالإيميل أو كلمة السر'); + setIsLoading(false); + } else { + message.error('حدث خطأ ما'); + setIsLoading(false); + } + }); + }; + + const logout = () => { + axios + .get('/api/v1/logout') + .then(() => { + setIsLogged(false); + setUserInfo(''); + navigate('/', { replace: true }); + // setIsOpen(false); + }) + .catch(() => { + message.error('حدث خطأ ما'); + }); + }; + useEffect(() => { if (token) { + console.log('userInfo', userInfo); setUserInfo(jwt_decode(token)); setIsLogged(true); if (userInfo.role === 'admin' && pathname === '/login/admin') { navigate('/dashboard', { replace: true }); } + if (userInfo.role === 'admin') { + navigate(pathname, { replace: true }); + } if (pathname === '/profile' || pathname === '/book') { navigate(pathname, { replace: true }); setIsOpen(false); @@ -42,11 +100,25 @@ function UserProvider({ children }) { } }, [isLogged]); - return ( - {children} - ); + return { + userInfo, + register, + logout, + login, + errorEmail, + error, + isLogged, + isloading, + }; +} + +function ProvideAuth({ children }) { + const auth = useProvideAuth(); + return {children}; } -UserProvider.propTypes = { + +ProvideAuth.propTypes = { children: PropTypes.node.isRequired, }; -export { UserProvider, userContext }; + +export { ProvideAuth, useAuth }; diff --git a/client/src/Pages/Dashboard/index.jsx b/client/src/Pages/Dashboard/index.jsx index d13fc81..aec2381 100644 --- a/client/src/Pages/Dashboard/index.jsx +++ b/client/src/Pages/Dashboard/index.jsx @@ -1,6 +1,5 @@ import React, { useContext } from 'react'; -import axios from 'axios'; -import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom'; +import { Link, Outlet, useLocation } from 'react-router-dom'; import { Layout, Menu, @@ -10,7 +9,6 @@ import { Avatar, Button, Badge, - message, } from 'antd'; import { PieChartOutlined, @@ -21,7 +19,7 @@ import { LogoutOutlined, BellOutlined, } from '@ant-design/icons'; -import { userContext } from '../../Contexts/userContext'; +import { useAuth } from '../../Contexts/userContext'; import './style.css'; import logo from '../../Assets/images/logo.svg'; import user from '../../Assets/images/user.png'; @@ -64,20 +62,9 @@ const breadcrumbNameMap = { }; function Dashboard() { - const { userInfo, setIsLogged } = useContext(userContext); + const { userInfo, logout } = useAuth(); const { pathname } = useLocation(); - const navigate = useNavigate(); - const logout = () => { - axios - .get('/api/v1/logout') - .then(() => { - setIsLogged(false); - navigate('/login/admin', { replace: true }); - }) - .catch(() => { - message.error('حدث خطأ ما'); - }); - }; + return (
diff --git a/client/src/Pages/Profile/index.jsx b/client/src/Pages/Profile/index.jsx index 060d99a..52a9282 100644 --- a/client/src/Pages/Profile/index.jsx +++ b/client/src/Pages/Profile/index.jsx @@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from 'react'; import axios from 'axios'; import uuid from 'react-uuid'; import { Row, Col, Descriptions, Typography, Spin, Empty } from 'antd'; -import { userContext } from '../../Contexts/userContext'; +import { useAuth } from '../../Contexts/userContext'; import Cover from './Cover'; import './style.css'; @@ -11,7 +11,7 @@ const { Title } = Typography; function Profile() { const [myBooks, setMyBooks] = useState([]); const [loading, setLoading] = useState(false); - const { userInfo } = useContext(userContext); + const { userInfo } = useAuth(); useEffect(() => { setLoading(true); diff --git a/client/src/ProtectedRoute/AdminProtected.jsx b/client/src/ProtectedRoute/AdminProtected.jsx index 753eadf..4f30f0a 100644 --- a/client/src/ProtectedRoute/AdminProtected.jsx +++ b/client/src/ProtectedRoute/AdminProtected.jsx @@ -1,21 +1,51 @@ -import React, { useContext } from 'react'; -import { Navigate, Outlet } from 'react-router-dom'; -import { userContext } from '../Contexts/userContext'; +import React, { useContext, useEffect } from 'react'; +import { Navigate, Outlet, useNavigate } from 'react-router-dom'; +import { userContext, useAuth } from '../Contexts/userContext'; -function AdminProtected() { - const userPromise = new Promise((resolve, reject) => { - const { userInfo } = useContext(userContext); - if (userInfo) resolve(userInfo); - else reject(new Error('no user')); - }); +function AdminProtected({ children }) { + // const navigate = useNavigate(); + // const userPromise = new Promise((resolve, reject) => { + // const { userInfo } = useContext(userContext); + // if (userInfo) resolve(userInfo); + // else reject(new Error('no user')); + // }); - userPromise - .then((userInfo) => { - if (!(userInfo.role === 'admin')) ; - }) - .catch(() => {}); + // userPromise + // .then((userInfo) => { + // if (userInfo.role !== 'admin' || userInfo.role === 'user') { + // return navigate('/login/admin', { replace: true }); + // } + // // if (userInfo.role === 'admin') { + // // return navigate('/dashboard', { replace: true }); + // // } + // }) + // .catch(() => console.log('no user')); - return ; + // const { userInfo } = useContext(userContext); + // console.log('Admin Protected userInfo', userInfo); + // if (userInfo.role !== 'admin' || userInfo.role === 'user') { + // return ; + // } + + const { userInfo } = useAuth(); + console.log('Admin Protected userInfo', userInfo); + if (userInfo?.role !== 'admin' || userInfo?.role === 'user') { + return ; + } + return children || ; + + // useEffect(() => { + // console.log('Admin Protected userInfo', typeof userInfo !== 'string'); + // console.log('Admin Protected Before userInfo', userInfo); + // if (typeof userInfo !== 'string') { + // console.log('Admin Protected userInfo', userInfo); + // if (!userInfo.role || userInfo.role === 'user') { + // navigate('/', { replace: true }); + // } + // } + // console.log('Admin Protected userInfo', userInfo); + // }, []); + // return ; } export default AdminProtected; diff --git a/client/src/ProtectedRoute/UserProtected.jsx b/client/src/ProtectedRoute/UserProtected.jsx index 231494b..154c9af 100644 --- a/client/src/ProtectedRoute/UserProtected.jsx +++ b/client/src/ProtectedRoute/UserProtected.jsx @@ -1,11 +1,11 @@ import React, { useContext, useEffect } from 'react'; import { useNavigate, Outlet } from 'react-router-dom'; -import { userContext } from '../Contexts/userContext'; +import { useAuth } from '../Contexts/userContext'; import { ModalLoginContext } from '../Contexts/ModalLogin'; function UserProtected() { const navigate = useNavigate(); - const { userInfo } = useContext(userContext); + const { userInfo } = useAuth(); const { setIsOpen } = useContext(ModalLoginContext); useEffect(() => { diff --git a/server/controllers/signin/index.ts b/server/controllers/signin/index.ts index 0846972..9e111db 100644 --- a/server/controllers/signin/index.ts +++ b/server/controllers/signin/index.ts @@ -1,7 +1,7 @@ import { RequestHandler } from 'express'; import { compare } from 'bcrypt'; import dotenv from 'dotenv'; -import { checkEmailExistsQuery } from '../../queries'; +import { checkEmailExistsQuery, checkEmailAdminExistsQuery } from '../../queries'; import { jwtSign, CustomizedError } from '../../utils'; import { signinSchema } from '../../validation'; @@ -13,7 +13,19 @@ const signin: RequestHandler = async (req, res, next) => { } = await signinSchema.validate(req.body, { abortEarly: false }); const { rowCount, rows: data } = await checkEmailExistsQuery(email); if (rowCount === 0) { - throw new CustomizedError(400, 'There have error with email or password'); + const { rowCount: count, rows: dataUser } = await checkEmailAdminExistsQuery(email); + if (count === 0) { + throw new CustomizedError(400, 'There have error with email or password'); + } else { + const resultCompare = await compare(password, dataUser[0].password); + if (!resultCompare) { + throw new CustomizedError(400, 'There have error with email or password'); + } + const token = await jwtSign({ + id: dataUser[0].id, email, role: 'admin', name: dataUser[0].name, + }); + res.cookie('token', token).json({ message: 'You have been successfully logged in', status: 200, data: 'admin' }); + } } const resultCompare = await compare(password, data[0].password); if (!resultCompare) { @@ -29,7 +41,7 @@ const signin: RequestHandler = async (req, res, next) => { lng: data[0].lng, role: 'user', }); - res.cookie('token', token).json({ message: 'You have been successfully logged in', status: 200 }); + res.cookie('token', token).json({ message: 'You have been successfully logged in', status: 200, data: 'user' }); } catch (error: any) { if (error.name === 'ValidationError') { return next(new CustomizedError(400, error.errors[0]));