authentication implemented
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -22,3 +22,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.env
|
||||
+12
-2
@@ -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: <MainLayout />,
|
||||
loader: () => getAuthToken() || redirect('/login'),
|
||||
children: [
|
||||
{ index: true, element: <Home /> },
|
||||
{ path: 'home', element: <Home /> },
|
||||
@@ -39,8 +43,14 @@ const router = createBrowserRouter([{
|
||||
{ path: 'password', element: <Password /> },
|
||||
{ path: 'friends', element: <Friends /> },
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
}, {
|
||||
path: '/login', element: <AuthenticationPage isLogin={true} />, action: loginAction, loader: () => { if (getAuthToken()) return redirect('/home'); else return null; }
|
||||
}, {
|
||||
path: '/signup', element: <AuthenticationPage isLogin={false} />, action: signupAction, loader: () => { if (getAuthToken()) return redirect('/signup'); else return null; }
|
||||
}, {
|
||||
path: '/logout', loader: () => { getAuthToken() || redirect('/home') }, action: logoutAction
|
||||
}]);
|
||||
|
||||
function App() {
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<Modal opened={isOpen} onClose={close} title={<Title order={3}>Logout</Title>} centered>
|
||||
<Text>Are you sure you want to logout?</Text>
|
||||
<Flex gap={'sm'} my="20px">
|
||||
<Form action='/logout' method='POST'>
|
||||
<Button type="submit" color='red' px='xl'>Logout</Button>
|
||||
</Form>
|
||||
<Button color='gray'>Cancel</Button>
|
||||
</Flex>
|
||||
</Modal>
|
||||
<Button onClick={open} type="submit" color='red' size='md' px='xl'>Logout</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const logoutAction = ({ request }) => {
|
||||
localStorage.removeItem('token');
|
||||
return redirect('/login');
|
||||
}
|
||||
|
||||
export default Logout
|
||||
@@ -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 = () => {
|
||||
<NavbarLinks />
|
||||
</Navbar.Section>
|
||||
<Navbar.Section>
|
||||
<Button color='red' size='md' px='xl'>Logout</Button>
|
||||
<Logout />
|
||||
</Navbar.Section>
|
||||
</Navbar>
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<Container maw="100%" bg="gray" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||
<Card shadow="md" p="lg" style={{ maxWidth: 400, width: '100%' }}>
|
||||
<Text align="center" variant="h4" style={{ marginBottom: 20 }}>
|
||||
{isLogin ? 'Login' : 'Sign Up'}
|
||||
</Text>
|
||||
<div>
|
||||
{isLogin ? <LoginForm /> : <SignupForm />}
|
||||
|
||||
{/* Toggle between login and signup */}
|
||||
<Text align="center" style={{ marginBottom: 10 }}>
|
||||
{isLogin ? 'Don\'t have an account?' : 'Already have an account?'}
|
||||
<Button
|
||||
variant="link"
|
||||
color="lime"
|
||||
size="md"
|
||||
p="10px"
|
||||
onClick={() => { navigate(isLogin ? '/signup' : '/login', { replace: true }) }}
|
||||
>
|
||||
<Text color='lime'>
|
||||
{isLogin ? 'Sign Up' : 'Login'}
|
||||
</Text>
|
||||
</Button>
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
const LoginForm = () => {
|
||||
return (
|
||||
<Form action='/login' method='POST'>
|
||||
<TextInput name="username" type="text" placeholder="username" style={{ marginBottom: 10, padding: '10px' }} />
|
||||
<TextInput name="password" type="password" placeholder="Password" style={{ marginBottom: 10, padding: '10px' }} />
|
||||
<Button type="submit" fullWidth variant="filled" color="lime" style={{ marginBottom: 10 }}>
|
||||
Login
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
const SignupForm = () => {
|
||||
return (
|
||||
<Form action='/signup' method='POST'>
|
||||
<TextInput name="username" type="text" placeholder="Username" style={{ marginBottom: 10, padding: '10px' }} />
|
||||
<TextInput name="email" type="email" placeholder="Email" style={{ marginBottom: 10, padding: '10px' }} />
|
||||
<TextInput name="password" type="password" placeholder="Password" style={{ marginBottom: 10, padding: '10px' }} />
|
||||
<Button type="submit" fullWidth variant="filled" color="lime" style={{ marginBottom: 10 }}>
|
||||
Sign Up
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -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");
|
||||
}
|
||||
Reference in New Issue
Block a user