Basic login working with backend.

This commit is contained in:
JesseBrault0709 2024-06-22 09:41:49 +02:00
parent 011151227a
commit c97fdc76e5
3 changed files with 76 additions and 14 deletions

View File

@ -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<boolean>
logout(): void
}
interface LoginView {
username: string
accessToken: string
}
const AuthContext = createContext<AuthContextType | null>(null)
export const AuthProvider = ({ children }: React.PropsWithChildren) => {
const [isAuthenticated, setIsAuthenticated] = useState(false)
const [token, setToken] = useState<string | null>(null)
const [error, setError] = useState<string | null>(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 (
<AuthContext.Provider value={{ isAuthenticated, login, logout }}>
<AuthContext.Provider value={{ token, error, login, logout }}>
{children}
</AuthContext.Provider>
)

View File

@ -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: {

View File

@ -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()
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
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 (
<div>
<h2>Login Page</h2>
<button onClick={onLogin}>Login</button>
<form onSubmit={onSubmit}>
<label htmlFor="username">Username</label>
<input id="username" name="username" type="text" />
<label htmlFor="password">Password</label>
<input id="password" name="password" type="password" />
<input type="submit" />
{error ? <p>{error}</p> : <p> </p>}
</form>
</div>
)
}
@ -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 || '/' })
}
},