diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 5b71b58..6843335 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -31,6 +31,7 @@ router.post("/signup", async (req, res, next) => { if (user) errors.username = "Username already exists"; if (Object.keys(errors).length > 0) { + console.log(errors); return res.status(422).json({ message: "User signup failed due to validation errors.", errors, diff --git a/frontend/.gitignore b/frontend/.gitignore index a547bf3..3b0b403 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -22,3 +22,5 @@ dist-ssr *.njsproj *.sln *.sw? + +.env \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a2971ae..460419e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3,7 +3,7 @@ import reactLogo from './assets/react.svg' import viteLogo from '/vite.svg' import './App.css' import { Button } from '@mantine/core' -import { createBrowserRouter, RouterProvider } from 'react-router-dom' +import { createBrowserRouter, redirect, RouterProvider } from 'react-router-dom' import Home from './pages/Home' import MainLayout from './layout/MainLayout' import Settings from './pages/Settings/Settings' @@ -14,12 +14,16 @@ import Themes from './pages/Settings/Themes' import PlayLayout from './pages/Play/Layout' import PlayFriend from './pages/Play/PlayFriend' import Play from './pages/Play/Play' +import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication' +import { getAuthToken } from '../utils/auth' +import { logoutAction } from './components/Logout' const router = createBrowserRouter([{ path: '/', element: , + loader: () => getAuthToken() || redirect('/login'), children: [ { index: true, element: }, { path: 'home', element: }, @@ -39,8 +43,14 @@ const router = createBrowserRouter([{ { path: 'password', element: }, { path: 'friends', element: }, ] - } + }, ] +}, { + path: '/login', element: , action: loginAction, loader: () => { if (getAuthToken()) return redirect('/home'); else return null; } +}, { + path: '/signup', element: , action: signupAction, loader: () => { if (getAuthToken()) return redirect('/signup'); else return null; } +}, { + path: '/logout', loader: () => { getAuthToken() || redirect('/home') }, action: logoutAction }]); function App() { diff --git a/frontend/src/components/Logout.jsx b/frontend/src/components/Logout.jsx new file mode 100644 index 0000000..f92b7b4 --- /dev/null +++ b/frontend/src/components/Logout.jsx @@ -0,0 +1,30 @@ +import React from 'react' +import { Button, Flex, Modal, Text, Title } from '@mantine/core' +import { Form, redirect } from 'react-router-dom' +import { useDisclosure } from '@mantine/hooks' + +const Logout = () => { + const [isOpen, { open, close }] = useDisclosure(false); + + return ( + <> + Logout} centered> + Are you sure you want to logout? + +
+ +
+ +
+
+ + + ) +} + +export const logoutAction = ({ request }) => { + localStorage.removeItem('token'); + return redirect('/login'); +} + +export default Logout \ No newline at end of file diff --git a/frontend/src/layout/MainLayout.jsx b/frontend/src/layout/MainLayout.jsx index 9c62ce9..d274644 100644 --- a/frontend/src/layout/MainLayout.jsx +++ b/frontend/src/layout/MainLayout.jsx @@ -2,6 +2,7 @@ import { AppShell, Button, Container, Navbar, Paper, Text, useMantineTheme } fro import React, { useState } from 'react' import NavbarLinks from '../components/NavbarLinks'; import { Outlet } from 'react-router-dom' +import Logout from '../components/Logout'; const MainLayout = () => { const theme = useMantineTheme(); @@ -27,7 +28,7 @@ const MainLayout = () => { - + } diff --git a/frontend/src/pages/Authentication/Authentication.jsx b/frontend/src/pages/Authentication/Authentication.jsx new file mode 100644 index 0000000..721837a --- /dev/null +++ b/frontend/src/pages/Authentication/Authentication.jsx @@ -0,0 +1,116 @@ +import React, { useState } from 'react'; +import { Button, Card, Container, Text, TextInput } from '@mantine/core'; +import { Form, redirect, useNavigate } from 'react-router-dom'; +import { isLoggedIn } from '../../../utils/auth'; + +const AuthenticationPage = (props) => { + const navigate = useNavigate(); + const { isLogin } = props; + + return ( + + + + {isLogin ? 'Login' : 'Sign Up'} + +
+ {isLogin ? : } + + {/* Toggle between login and signup */} + + {isLogin ? 'Don\'t have an account?' : 'Already have an account?'} + + +
+
+
+ ); +}; + +const LoginForm = () => { + return ( +
+ + + + + ); +}; + +const SignupForm = () => { + return ( +
+ + + + + + ); +}; + +export const loginAction = async ({ request }) => { + const data = await request.formData(); + let url = `${import.meta.env.VITE_BACKEND_HOST}/api/auth/login`; + + const authData = { + username: data.get('username'), + password: data.get('password') + } + + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(authData), + headers: { + 'Content-Type': 'application/json' + } + }) + + if (response.status >= 400) return response; + else { + const responseBody = await response.json(); + localStorage.setItem('token', responseBody.token); + return redirect('/home'); + } +} + +export const signupAction = async ({ request }) => { + const data = await request.formData(); + let url = `${import.meta.env.VITE_BACKEND_HOST}/api/auth/signup`; + + const authData = { + username: data.get('username'), + email: data.get('email'), + password: data.get('password') + } + + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(authData), + headers: { + 'Content-Type': 'application/json' + } + }) + + if (response.status >= 400) return response; + else { + const responseBody = await response.json(); + localStorage.setItem('token', responseBody.token); + return redirect('/home'); + } +} + +export default AuthenticationPage; diff --git a/frontend/utils/auth.js b/frontend/utils/auth.js new file mode 100644 index 0000000..b046139 --- /dev/null +++ b/frontend/utils/auth.js @@ -0,0 +1,16 @@ +import { redirect } from "react-router-dom"; + +export function checkAuthStatus() { + const token = localStorage.getItem("token"); + if (token) return token; + else return redirect("/login"); +} + +export function isLoggedIn() { + if(localStorage.getItem('token')) return true; + else return false; +} + +export function getAuthToken() { + return localStorage.getItem("token"); +}