diff --git a/backend/routes/user.js b/backend/routes/user.js index 5a109d9..38a55dc 100644 --- a/backend/routes/user.js +++ b/backend/routes/user.js @@ -10,12 +10,18 @@ const { catchAsync } = require("../util/errors"); router.use( catchAsync(async (req, res, next) => { let userID = req.url.split("/")[1]; + if (userID?.length !== 24) + return res.status(404).json({ userMessage: "User not found", devMessage: "Invalid user ID" }); let userData = await User.findById(userID); if (userData) { req.userData = userData; next(); } else { - res.status(404).json({ success: false, error: { message: "User not found" } }); + res.status(404).json({ + success: false, + userMessage: "User not found", + devMessage: "User ID does not exists", + }); } }) ); @@ -24,23 +30,12 @@ router.use( // get user details router.get( "/:userid", - checkAuth, catchAsync(async (req, res, next) => { let userData = req.userData; - let { - id, - username, - email, - fname, - lname, - country, - location, - } = userData; + let { id, username, email, fname, lname, country, location } = userData; let friends = await userData.getFriends(); let games = await userData.getGames(); - let resData = { id, username, email, friends, fname, lname, country, location, games }; - console.log(resData) - return res.json({ success: true, data: resData }); + return res.status(200).json({ id, username, email, friends, fname, lname, country, location, games }); }) ); @@ -52,7 +47,7 @@ router.patch( catchAsync(async (req, res, next) => { let { userid } = req.params; let updatedData = req.body; - console.log('Updated data: ',updatedData) + console.log("Updated data: ", updatedData); // console.log(updatedData) await User.findByIdAndUpdate(userid, { ...updatedData }); let { id, username, email, fname, lname, location, country, fullName } = await User.findById(userid); diff --git a/frontend/index.html b/frontend/index.html index 4b98589..28b631b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,20 +1,23 @@ - - - - - Chess - - - -
- - + + + + + Chess + + + +
+
+ + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c0a59ee..3a71efb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,6 +16,7 @@ "@radix-ui/react-icons": "^1.3.0", "@tabler/icons-react": "^2.23.0", "chess.js": "^1.0.0-beta.6", + "js-cookie": "^3.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.45.2", @@ -2953,6 +2954,14 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1c30563..e126d72 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "@radix-ui/react-icons": "^1.3.0", "@tabler/icons-react": "^2.23.0", "chess.js": "^1.0.0-beta.6", + "js-cookie": "^3.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.45.2", diff --git a/frontend/src/components/Logout.jsx b/frontend/src/components/Logout.jsx index d2a16f4..65374be 100644 --- a/frontend/src/components/Logout.jsx +++ b/frontend/src/components/Logout.jsx @@ -24,6 +24,7 @@ const Logout = () => { console.log('Logged out') localStorage.removeItem('user'); close(); + localStorage.removeItem('loggedIn'); return navigate('/login'); } else { return setErrorMsg(resData.userMessage || "Something went wrong") diff --git a/frontend/src/components/MainLoader.jsx b/frontend/src/components/MainLoader.jsx index 4a5551b..72c16ee 100644 --- a/frontend/src/components/MainLoader.jsx +++ b/frontend/src/components/MainLoader.jsx @@ -1,13 +1,31 @@ import React from 'react' +import { createPortal } from 'react-dom' import loaderImage from '../assets/images/chess_board_loader.png' -import { Loader } from '@mantine/core' +import { Loader, Title } from '@mantine/core' -const MainLoader = () => { +const MainLoader = ({ errorMessage }) => { return ( -
- - -
+ <> + { + createPortal(
+ + { + errorMessage + ? + + {errorMessage} + + : + + } +
, document.querySelector('#main-loader')) + + } + + //
+ // + // + //
) } diff --git a/frontend/src/context/user-data-context.jsx b/frontend/src/context/user-data-context.jsx index f7dda22..7358627 100644 --- a/frontend/src/context/user-data-context.jsx +++ b/frontend/src/context/user-data-context.jsx @@ -1,32 +1,32 @@ import React, { createContext, useEffect, useState } from 'react' -import { getAuthToken, getUserData } from '../../utils/auth'; +import Cookie from 'js-cookie' export const UserDataContext = createContext(); const UserDataContextProvider = ({ children }) => { - let { id: userid } = getUserData(); + // TODO: use more secure mechanism insted of localstorage + const [isLoggedIn, setIsLoggedIn] = useState(JSON.parse(localStorage.getItem('loggedIn'))); const [user, setUser] = useState(null); - const [friends, setFriends] = useState(null) - const [games, setGames] = useState(null); + const [errorMessage, setErrorMessage] = useState(""); async function fetchUserDetails() { - let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${userid}`; - let response = await fetch(url, { - headers: { - Authorization: `Bearer ${getAuthToken()}` + try { + if (isLoggedIn) { + let { id: userid } = JSON.parse(localStorage.getItem('user')); + let userDetailsUrl = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${userid}` + const response = await fetch(userDetailsUrl, { + credentials: 'include' + }); + const resData = await response.json(); + if (response.ok) { + setUser(resData); + } else { + console.log(resData.devMessage); + setErrorMessage(resData.userMessage); + } } - }); - let resData = await response.json(); - if (resData.success) { - let { id, username, email, friends: _friends_, fname, lname, fullName, country, location, games: _games_ } = resData.data; - setUser({ id, username, email, fname, lname, fullName, country, location }) - setFriends(_friends_) - setGames(_games_) - localStorage.setItem('user', JSON.stringify({ id, username, email, fname, lname, fullName, country, location })); - localStorage.setItem('friends', JSON.stringify(_friends_)); - localStorage.setItem('games', JSON.stringify(_games_)); - } else { - throw resData.error + } catch (err) { + setErrorMessage("Something went wrong"); } } @@ -35,7 +35,7 @@ const UserDataContextProvider = ({ children }) => { }, []); return ( - + {children} ) diff --git a/frontend/src/layout/MainLayout.jsx b/frontend/src/layout/MainLayout.jsx index 9e42dc7..0604802 100644 --- a/frontend/src/layout/MainLayout.jsx +++ b/frontend/src/layout/MainLayout.jsx @@ -1,9 +1,10 @@ import { AppShell, Burger, Container, Header, MediaQuery, Navbar, Paper, Text, createStyles, useMantineTheme } from '@mantine/core' -import React, { useState } from 'react' +import React, { useContext, useState } from 'react' import NavbarLinks from '../components/NavbarLinks'; import { Outlet } from 'react-router-dom' import Logout from '../components/Logout'; -import UserDataContextProvider from '../context/user-data-context'; +import UserDataContextProvider, { UserDataContext } from '../context/user-data-context'; +import MainLoader from '../components/MainLoader'; const useStyles = createStyles((theme) => ({ body: { @@ -26,6 +27,12 @@ const MainLayout = () => { const { classes } = useStyles(); const theme = useMantineTheme(); const [opened, setOpened] = useState(false); + const { isLoggedIn,errorMessage } = useContext(UserDataContext); + + if (!isLoggedIn) { + return + } + return ( diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 776e19d..fff52ec 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -3,9 +3,12 @@ import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' import { MantineProvider } from '@mantine/styles' +import UserDataContextProvider from './context/user-data-context.jsx' ReactDOM.createRoot(document.getElementById('root')).render( - - - + + + + + ) \ No newline at end of file diff --git a/frontend/src/pages/Authentication/Authentication.jsx b/frontend/src/pages/Authentication/Authentication.jsx index f826602..f2669c5 100644 --- a/frontend/src/pages/Authentication/Authentication.jsx +++ b/frontend/src/pages/Authentication/Authentication.jsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { Button, Card, Container, Text, TextInput, Title } from '@mantine/core'; import { Form, redirect, useNavigate } from 'react-router-dom'; import { ZodError, z } from 'zod'; import { useForm } from 'react-hook-form' +import { UserDataContext } from '../../context/user-data-context'; let host = import.meta.env.VITE_BACKEND_HOST; @@ -52,6 +53,7 @@ const AuthenticationPage = (props) => { }; const LoginForm = () => { + const { setIsLoggedIn } = useContext(UserDataContext) const { register, handleSubmit, setError, formState: { errors } } = useForm(); const [errorMsg, setErrorMsg] = useState("") const [isLoading, setIsLoading] = useState(false); @@ -67,9 +69,11 @@ const LoginForm = () => { setIsLoading(false); if (response.ok) { localStorage.setItem('user', JSON.stringify(resData.user)) + localStorage.setItem('loggedIn', true); + setIsLoggedIn(true); return navigate('/'); } else { - setErrorMsg(resData.userMessage || "Something went wrong") + setErrorMsg(resData.userMessage || "Something went wrong"); } } catch (err) { setIsLoading(false); @@ -99,6 +103,7 @@ const LoginForm = () => { const SignupForm = () => { const navigate = useNavigate(); + const { setIsLoggedIn } = useContext(UserDataContext) const { register, handleSubmit, setError, formState: { errors } } = useForm(); const [errorMsg, setErrorMsg] = useState("") const [isLoading, setIsLoading] = useState(false); @@ -112,6 +117,8 @@ const SignupForm = () => { const resData = await response.json(); if (response.ok) { localStorage.setItem('user', JSON.stringify(resData.user)) + localStorage.setItem('loggedIn', true); + setIsLoggedIn(true); return navigate('/'); } else { setIsLoading(false); diff --git a/frontend/src/pages/Settings/Profile.jsx b/frontend/src/pages/Settings/Profile.jsx index 9cd54a4..c270f45 100644 --- a/frontend/src/pages/Settings/Profile.jsx +++ b/frontend/src/pages/Settings/Profile.jsx @@ -4,12 +4,13 @@ import { getAuthToken, getUserData } from '../../../utils/auth' import { UserDataContext } from '../../context/user-data-context' import { useForm } from '@mantine/form' import { Form } from 'react-router-dom' +import MainLoader from '../../components/MainLoader' const Profile = () => { let { user } = useContext(UserDataContext); - if(!user) { - return + if (!user) { + return } let { username, email, fname, lname, country, location } = user; @@ -83,7 +84,7 @@ export const action = async ({ request }) => { method: 'PATCH', headers: { Authorization: `Bearer ${getAuthToken()}`, - 'Content-Type':'application/json' + 'Content-Type': 'application/json' } }) const resData = await response.json();