From c97fdc76e5bc7c7473f02be3a322a4fdaa8d7fc4 Mon Sep 17 00:00:00 2001 From: JesseBrault0709 <62299747+JesseBrault0709@users.noreply.github.com> Date: Sat, 22 Jun 2024 09:41:49 +0200 Subject: [PATCH] Basic login working with backend. --- src/auth.tsx | 58 ++++++++++++++++++++++++++++++++++++++------ src/routes/_auth.tsx | 2 +- src/routes/login.tsx | 30 ++++++++++++++++++----- 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/auth.tsx b/src/auth.tsx index 101468e..2784772 100644 --- a/src/auth.tsx +++ b/src/auth.tsx @@ -1,26 +1,70 @@ import React, { useContext, createContext, useState } from 'react' export interface AuthContextType { - isAuthenticated: boolean - login(): void + token: string | null + error: string | null + login(username: string, password: string): Promise logout(): void } +interface LoginView { + username: string + accessToken: string +} + const AuthContext = createContext(null) export const AuthProvider = ({ children }: React.PropsWithChildren) => { - const [isAuthenticated, setIsAuthenticated] = useState(false) + const [token, setToken] = useState(null) + const [error, setError] = useState(null) - const login: AuthContextType['login'] = () => { - setIsAuthenticated(true) + const login: AuthContextType['login'] = async (username, password) => { + try { + const response = await fetch( + import.meta.env.VITE_MME_API_URL + '/auth/login', + { + body: JSON.stringify({ username, password }), + headers: { + 'Content-type': 'application/json' + }, + method: 'POST', + mode: 'cors' + } + ) + if (response.ok) { + const body = (await response.json()) as LoginView + setToken(body.accessToken) + setError(null) + return true + } else { + setToken(null) + if (response.status === 401) { + setError('Invalid username or password.') + } else if (response.status === 500) { + setError( + 'There was an internal server error. Please try again later.' + ) + } else { + setError('Unknown error.') + console.error( + `Unknown error: ${response.status} ${response.statusText}` + ) + } + return false + } + } catch (fetchError) { + setError('Network error. Please try again later.') + console.error(`Unknown error: ${fetchError}`) + return false + } } const logout: AuthContextType['logout'] = () => { - setIsAuthenticated(false) + setToken(null) } return ( - + {children} ) diff --git a/src/routes/_auth.tsx b/src/routes/_auth.tsx index e5a27c0..e75e6d5 100644 --- a/src/routes/_auth.tsx +++ b/src/routes/_auth.tsx @@ -2,7 +2,7 @@ import { Outlet, createFileRoute, redirect } from '@tanstack/react-router' export const Route = createFileRoute('/_auth')({ beforeLoad({ context, location }) { - if (!context.auth.isAuthenticated) { + if (!context.auth.token) { throw redirect({ to: '/login', search: { diff --git a/src/routes/login.tsx b/src/routes/login.tsx index 44d4fc3..602c48f 100644 --- a/src/routes/login.tsx +++ b/src/routes/login.tsx @@ -5,23 +5,41 @@ import { useRouter, useSearch } from '@tanstack/react-router' +import { FormEvent } from 'react' import { z } from 'zod' const Login = () => { const login = useRouteContext({ from: '/login', select: s => s.auth.login }) + const error = useRouteContext({ from: '/login', select: s => s.auth.error }) const router = useRouter() const search = useSearch({ from: '/login' }) - const onLogin = async () => { - login() - await router.invalidate() - router.navigate({ to: search.redirect || '/' }) + const onSubmit = async (event: FormEvent) => { + event.preventDefault() + const formData = new FormData(event.currentTarget) + const username = (formData.get('username') as string | null) ?? '' + const password = (formData.get('password') as string | null) ?? '' + const success = await login(username, password) + if (success) { + await router.invalidate() + router.navigate({ to: search.redirect || '/' }) + } } return (

Login Page

- +
+ + + + + + + + + {error ?

{error}

:

} +
) } @@ -31,7 +49,7 @@ export const Route = createFileRoute('/login')({ redirect: z.string().optional().catch('') }), beforeLoad({ context, search }) { - if (context.auth.isAuthenticated) { + if (context.auth.token) { throw redirect({ to: search.redirect || '/' }) } },