From 039b9c1ff4c0cd3d4f8d319ffa81da8f96776c04 Mon Sep 17 00:00:00 2001 From: Thibault Pouch Date: Tue, 3 Mar 2026 09:48:54 +0100 Subject: [PATCH] feat: enhance AuthContext to integrate API for authentication and user management --- nest-front/src/contexts/AuthContext.tsx | 145 +++++++++++------------- 1 file changed, 64 insertions(+), 81 deletions(-) diff --git a/nest-front/src/contexts/AuthContext.tsx b/nest-front/src/contexts/AuthContext.tsx index d14d158..7ba4470 100644 --- a/nest-front/src/contexts/AuthContext.tsx +++ b/nest-front/src/contexts/AuthContext.tsx @@ -2,11 +2,12 @@ import React, { createContext, useCallback, useContext, + useEffect, useMemo, useState, } from 'react'; -import type { User, UserRole } from '../types'; -import { MOCK_USERS } from '../data/mockData'; +import type { User } from '../types'; +import { authApi, usersApi, getToken, setToken, clearToken } from '../utils/api'; // ── Types ────────────────────────────────────────────────────────────────────── @@ -15,12 +16,11 @@ interface AuthContextValue { isAuthenticated: boolean; isStaff: boolean; isAdmin: boolean; + isLoading: boolean; login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>; register: (username: string, email: string, password: string) => Promise<{ success: boolean; error?: string }>; logout: () => void; - updateUsername: (username: string) => void; - // Dev helper: quickly switch role for testing - devSetRole: (role: UserRole) => void; + updateUsername: (username: string) => Promise<{ success: boolean; error?: string }>; } // ── Context ──────────────────────────────────────────────────────────────────── @@ -31,16 +31,6 @@ const AuthContext = createContext(null); const STORAGE_KEY = 'crowmate_auth_user'; -function loadUserFromStorage(): User | null { - try { - const raw = localStorage.getItem(STORAGE_KEY); - if (!raw) return null; - return JSON.parse(raw) as User; - } catch { - return null; - } -} - function saveUserToStorage(user: User | null): void { if (user) { localStorage.setItem(STORAGE_KEY, JSON.stringify(user)); @@ -50,96 +40,89 @@ function saveUserToStorage(user: User | null): void { } export function AuthProvider({ children }: { children: React.ReactNode }) { - const [user, setUser] = useState(loadUserFromStorage); + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + // On mount: validate stored token and restore session + useEffect(() => { + const token = getToken(); + if (!token) { + setIsLoading(false); + return; + } + + authApi.me() + .then((freshUser) => { + setUser(freshUser); + saveUserToStorage(freshUser); + }) + .catch(() => { + clearToken(); + saveUserToStorage(null); + }) + .finally(() => setIsLoading(false)); + }, []); const isAuthenticated = user !== null; const isStaff = user?.role === 'dev' || user?.role === 'com'; const isAdmin = user?.isAdmin === true; const login = useCallback( - async (email: string, _password: string): Promise<{ success: boolean; error?: string }> => { - // Simulate network delay - await new Promise((r) => setTimeout(r, 400)); - - const found = MOCK_USERS.find( - (u) => u.email.toLowerCase() === email.toLowerCase() - ); - - if (!found) { - return { success: false, error: 'No account found with that email address.' }; + async (email: string, password: string): Promise<{ success: boolean; error?: string }> => { + try { + const { token, user: loggedInUser } = await authApi.login(email, password); + setToken(token); + setUser(loggedInUser); + saveUserToStorage(loggedInUser); + return { success: true }; + } catch (err) { + const message = err instanceof Error ? err.message : 'Login failed.'; + return { success: false, error: message }; } - if (found.isBanned) { - return { success: false, error: 'This account has been suspended.' }; - } - - setUser(found); - saveUserToStorage(found); - return { success: true }; }, [] ); const register = useCallback( - async (username: string, email: string, _password: string): Promise<{ success: boolean; error?: string }> => { - await new Promise((r) => setTimeout(r, 500)); - - const emailTaken = MOCK_USERS.some( - (u) => u.email.toLowerCase() === email.toLowerCase() - ); - if (emailTaken) { - return { success: false, error: 'An account with this email already exists.' }; + async (username: string, email: string, password: string): Promise<{ success: boolean; error?: string }> => { + try { + const { token, user: newUser } = await authApi.register(username, email, password); + setToken(token); + setUser(newUser); + saveUserToStorage(newUser); + return { success: true }; + } catch (err) { + const message = err instanceof Error ? err.message : 'Registration failed.'; + return { success: false, error: message }; } - - const usernameTaken = MOCK_USERS.some( - (u) => u.username.toLowerCase() === username.toLowerCase() - ); - if (usernameTaken) { - return { success: false, error: 'This username is already taken.' }; - } - - const newUser: User = { - id: `u${Date.now()}`, - username, - email, - role: 'user', - isAdmin: false, - isBanned: false, - createdAt: new Date().toISOString(), - }; - - setUser(newUser); - saveUserToStorage(newUser); - return { success: true }; }, [] ); const logout = useCallback(() => { + clearToken(); setUser(null); saveUserToStorage(null); }, []); - const updateUsername = useCallback((username: string) => { - setUser((prev) => { - if (!prev) return prev; - const updated = { ...prev, username }; - saveUserToStorage(updated); - return updated; - }); - }, []); - - const devSetRole = useCallback((role: UserRole) => { - setUser((prev) => { - if (!prev) return prev; - const updated = { ...prev, role, isAdmin: role === 'dev' }; - saveUserToStorage(updated); - return updated; - }); - }, []); + const updateUsername = useCallback( + async (username: string): Promise<{ success: boolean; error?: string }> => { + try { + const updatedUser = await usersApi.updateUsername(username); + setUser(updatedUser); + saveUserToStorage(updatedUser); + return { success: true }; + } catch (err) { + const message = err instanceof Error ? err.message : 'Failed to update username.'; + return { success: false, error: message }; + } + }, + [] + ); const value = useMemo( - () => ({ user, isAuthenticated, isStaff, isAdmin, login, register, logout, updateUsername, devSetRole }), - [user, isAuthenticated, isStaff, isAdmin, login, register, logout, updateUsername, devSetRole] + () => ({ user, isAuthenticated, isStaff, isAdmin, isLoading, login, register, logout, updateUsername }), + [user, isAuthenticated, isStaff, isAdmin, isLoading, login, register, logout, updateUsername] ); return {children};