Basic login working with backend.
This commit is contained in:
		
							parent
							
								
									011151227a
								
							
						
					
					
						commit
						c97fdc76e5
					
				
							
								
								
									
										58
									
								
								src/auth.tsx
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								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<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> | ||||
|     ) | ||||
|  | ||||
| @ -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: { | ||||
|  | ||||
| @ -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<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 || '/' }) | ||||
|         } | ||||
|     }, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 JesseBrault0709
						JesseBrault0709