Working on auth

This commit is contained in:
Benjamin Sherriff
2023-10-17 20:49:27 -04:00
parent 140488c925
commit 3b15f520c8
18 changed files with 454 additions and 49 deletions

16
ui/package-lock.json generated
View File

@@ -14,6 +14,7 @@
"@mantine/modals": "^7.1.2",
"@mantine/notifications": "^7.1.2",
"axios": "^1.5.1",
"js-cookie": "^3.0.5",
"next": "^13.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -23,6 +24,7 @@
"recoil": "^0.7.7"
},
"devDependencies": {
"@types/js-cookie": "^3.0.4",
"@types/node": "20.8.2",
"@types/react": "18.2.24",
"@types/react-dom": "18.2.8",
@@ -586,6 +588,12 @@
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
"integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g=="
},
"node_modules/@types/js-cookie": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.4.tgz",
"integrity": "sha512-vMMnFF+H5KYqdd/myCzq6wLDlPpteJK+jGFgBus3Da7lw+YsDmx2C8feGTzY2M3Fo823yON+HC2CL240j4OV+w==",
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
@@ -3294,6 +3302,14 @@
"reflect.getprototypeof": "^1.0.3"
}
},
"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",

View File

@@ -15,6 +15,7 @@
"@mantine/modals": "^7.1.2",
"@mantine/notifications": "^7.1.2",
"axios": "^1.5.1",
"js-cookie": "^3.0.5",
"next": "^13.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -24,6 +25,7 @@
"recoil": "^0.7.7"
},
"devDependencies": {
"@types/js-cookie": "^3.0.4",
"@types/node": "20.8.2",
"@types/react": "18.2.24",
"@types/react-dom": "18.2.8",

View File

@@ -1,18 +1,25 @@
import axios, { AxiosResponse } from 'axios';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
const serviceHost = process.env.SERVICE_HOST || 'http://localhost';
const servicePort = process.env.SERVICE_PORT || 5000;
export async function getRequest(endpoint: string, params: any): Promise<AxiosResponse<any, any> | undefined> {
export async function getRequest(
url: string,
params: AxiosRequestConfig<any>
): Promise<AxiosResponse<any, any> | undefined> {
const response = await axios
.get(`${serviceHost}:${servicePort}/${endpoint}`, { params })
.get(`${serviceHost}:${servicePort}/${url}`, { params })
.catch((error) => console.error(error));
return response || undefined;
}
export async function postRequest(endpoint: string, body: any): Promise<AxiosResponse<any, any> | undefined> {
export async function postRequest(
url: string,
data?: any,
config?: AxiosRequestConfig<any>
): Promise<AxiosResponse<any, any> | undefined> {
const response = await axios
.post(`${serviceHost}:${servicePort}/${endpoint}`, body || {})
.post(`${serviceHost}:${servicePort}/${url}`, data, config)
.catch((error) => console.error(error));
return response || undefined;
}

9
ui/src/api/users.ts Normal file
View File

@@ -0,0 +1,9 @@
import { postRequest } from '.';
export async function login(email: string, password: string) {
return await postRequest('users/login', { email, password }, { withCredentials: true });
}
export async function logout() {
return await postRequest('users/logout', {}, { withCredentials: true });
}

View File

@@ -3,6 +3,25 @@
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import './topbar.css';
import {
Anchor,
Avatar,
Button,
Checkbox,
Container,
Group,
Menu,
Modal,
Paper,
PasswordInput,
Text,
TextInput,
Title
} from '@mantine/core';
import Cookies from 'js-cookie';
import { useEffect, useState } from 'react';
import { useForm } from '@mantine/form';
import { login, logout } from '@/api/users';
const headerItems = [
{
@@ -41,21 +60,118 @@ const headerItems = [
export default function Topbar() {
const pathName = usePathname();
const [showLogin, setShowLogin] = useState(false);
const [authenticated, setAuthenticated] = useState(false);
// Check if the auth cookie is set
// If it is, show the user avatar
// If not, show the login button
useEffect(() => {
console.log('cookies', Cookies.get());
if (Cookies.get('auth')) {
setAuthenticated(true);
}
}, []);
return (
<nav className='navbar'>
<div className='left'>
<Link href={'/'} className='title'>
Siren
</Link>
<div className='header-items'>
{headerItems.map((item) => (
<Link className={`header-item ${pathName == item.link && 'active'}`} href={item.link} key={item.name}>
{item.name}
</Link>
))}
<>
<nav className='navbar'>
<div className='left'>
<Link href={'/'} className='title'>
Siren
</Link>
<div className='header-items'>
{headerItems.map((item) => (
<Link className={`header-item ${pathName == item.link && 'active'}`} href={item.link} key={item.name}>
{item.name}
</Link>
))}
</div>
</div>
</div>
</nav>
<div className='user'>
<Menu shadow='md' width={200} trigger='hover' openDelay={100} closeDelay={400}>
<Menu.Target>
<Avatar style={{ cursor: 'pointer' }} />
</Menu.Target>
<Menu.Dropdown>
{!authenticated && <Menu.Item onClick={() => setShowLogin(true)}>Login</Menu.Item>}
{authenticated && (
<Menu.Item
onClick={async () => {
const response = await logout();
if (response?.status == 200) {
Cookies.remove('auth');
setAuthenticated(false);
}
}}
>
Logout
</Menu.Item>
)}
</Menu.Dropdown>
</Menu>
</div>
</nav>
<LoginModal showLogin={showLogin} setShowLogin={setShowLogin} setAuthenticated={setAuthenticated} />
</>
);
}
function LoginModal({
showLogin,
setShowLogin,
setAuthenticated
}: {
showLogin: boolean;
setShowLogin: (show: boolean) => void;
setAuthenticated: (authenticated: boolean) => void;
}) {
const form = useForm({
initialValues: {
email: '',
password: ''
}
});
return (
<Modal opened={showLogin} onClose={() => setShowLogin(false)} withCloseButton={false}>
<Container>
<Title ta='center'>Welcome back!</Title>
<Text c='dimmed' size='sm' ta='center' mt={5}>
Do not have an account yet?{' '}
<Anchor size='sm' component='button'>
Create account
</Anchor>
</Text>
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
<form
onSubmit={form.onSubmit(async (values) => {
const response = await login(values.email, values.password);
if (response?.status == 200) {
setShowLogin(false);
setAuthenticated(true);
}
})}
>
<TextInput label='Email' placeholder='you@example.com' required {...form.getInputProps('email')} />
<PasswordInput
label='Password'
placeholder='Your password'
required
mt='md'
{...form.getInputProps('password')}
/>
<Group justify='space-between' mt='lg'>
<Checkbox label='Remember me' />
<Anchor component='button' size='sm'>
Forgot password?
</Anchor>
</Group>
<Button type='submit' fullWidth mt='xl'>
Sign in
</Button>
</form>
</Paper>
</Container>
</Modal>
);
}

View File

@@ -9,6 +9,11 @@
display: flex;
}
.navbar .user {
padding-left: 1em;
padding-right: 1em;
}
.navbar .title {
padding-left: 2em;
padding-right: 2em;
@@ -44,4 +49,4 @@
.header-items .active {
border-bottom: 2px solid #5f5f5f;
}
}