feat: update nginx configuration and enhance AuthContext for improved authentication handling

This commit is contained in:
Thibault Pouch
2026-03-02 09:38:34 +01:00
parent 90efb6175f
commit 65282832cb
3 changed files with 50 additions and 34 deletions

View File

@@ -3,6 +3,12 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
location /api/ {
proxy_pass http://api:3000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / { location / {
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
} }

View File

@@ -5,8 +5,7 @@ import React, {
useMemo, useMemo,
useState, useState,
} from 'react'; } from 'react';
import type { User, UserRole } from '../types'; import type { User } from '../types';
import { MOCK_USERS } from '../data/mockData';
// ── Types ────────────────────────────────────────────────────────────────────── // ── Types ──────────────────────────────────────────────────────────────────────
@@ -18,20 +17,20 @@ interface AuthContextValue {
login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>; login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>;
logout: () => void; logout: () => void;
updateUsername: (username: string) => void; updateUsername: (username: string) => void;
devSetRole: (role: UserRole) => void;
} }
// ── Context ──────────────────────────────────────────────────────────────────── // ── Context ────────────────────────────────────────────────────────────────────
const AuthContext = createContext<AuthContextValue | null>(null); const AuthContext = createContext<AuthContextValue | null>(null);
// ── Provider ─────────────────────────────────────────────────────────────────── // ── Storage helpers ────────────────────────────────────────────────────────────
const STORAGE_KEY = 'crowmate_intra_user'; const USER_KEY = 'crowmate_intra_user';
const TOKEN_KEY = 'crowmate_intra_token';
function loadUserFromStorage(): User | null { function loadUserFromStorage(): User | null {
try { try {
const raw = localStorage.getItem(STORAGE_KEY); const raw = localStorage.getItem(USER_KEY);
if (!raw) return null; if (!raw) return null;
return JSON.parse(raw) as User; return JSON.parse(raw) as User;
} catch { } catch {
@@ -41,12 +40,19 @@ function loadUserFromStorage(): User | null {
function saveUserToStorage(user: User | null): void { function saveUserToStorage(user: User | null): void {
if (user) { if (user) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(user)); localStorage.setItem(USER_KEY, JSON.stringify(user));
} else { } else {
localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(USER_KEY);
} }
} }
/** Returns the stored JWT for use in authenticated fetch calls. */
export function getToken(): string | null {
return localStorage.getItem(TOKEN_KEY);
}
// ── Provider ───────────────────────────────────────────────────────────────────
export function AuthProvider({ children }: { children: React.ReactNode }) { export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(loadUserFromStorage); const [user, setUser] = useState<User | null>(loadUserFromStorage);
@@ -55,26 +61,35 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const isAdmin = user?.isAdmin === true; const isAdmin = user?.isAdmin === true;
const login = useCallback( const login = useCallback(
async (email: string, _password: string): Promise<{ success: boolean; error?: string }> => { async (email: string, password: string): Promise<{ success: boolean; error?: string }> => {
// Simulate network delay let data: { token?: string; user?: User; error?: string };
await new Promise((r) => setTimeout(r, 400));
const found = MOCK_USERS.find( try {
(u) => u.email.toLowerCase() === email.toLowerCase() const res = await fetch('/api/auth/login', {
); method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
data = await res.json();
if (!res.ok) {
return { success: false, error: data.error ?? 'Login failed.' };
}
} catch {
return { success: false, error: 'Cannot reach the server. Please try again.' };
}
if (!found) { const loggedInUser = data.user!;
return { success: false, error: 'No account found with that email address.' };
} if (loggedInUser.role === 'user') {
if (found.isBanned) {
return { success: false, error: 'This account has been suspended.' };
}
if (found.role === 'user') {
return { success: false, error: 'Access denied. Staff accounts only.' }; return { success: false, error: 'Access denied. Staff accounts only.' };
} }
if (loggedInUser.isBanned) {
return { success: false, error: 'This account has been suspended.' };
}
setUser(found); localStorage.setItem(TOKEN_KEY, data.token!);
saveUserToStorage(found); saveUserToStorage(loggedInUser);
setUser(loggedInUser);
return { success: true }; return { success: true };
}, },
[] []
@@ -83,6 +98,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const logout = useCallback(() => { const logout = useCallback(() => {
setUser(null); setUser(null);
saveUserToStorage(null); saveUserToStorage(null);
localStorage.removeItem(TOKEN_KEY);
}, []); }, []);
const updateUsername = useCallback((username: string) => { const updateUsername = useCallback((username: string) => {
@@ -94,18 +110,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}); });
}, []); }, []);
const devSetRole = useCallback((role: UserRole) => {
setUser((prev) => {
if (!prev) return prev;
const updated = { ...prev, role, isAdmin: role === 'dev' };
saveUserToStorage(updated);
return updated;
});
}, []);
const value = useMemo<AuthContextValue>( const value = useMemo<AuthContextValue>(
() => ({ user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername, devSetRole }), () => ({ user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername }),
[user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername, devSetRole] [user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername]
); );
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;

View File

@@ -9,5 +9,8 @@ export default defineConfig({
], ],
server: { server: {
port: 5174, port: 5174,
proxy: {
'/api': 'http://localhost:3000',
},
}, },
}) })