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;
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 / {
try_files $uri $uri/ /index.html;
}

View File

@@ -5,8 +5,7 @@ import React, {
useMemo,
useState,
} from 'react';
import type { User, UserRole } from '../types';
import { MOCK_USERS } from '../data/mockData';
import type { User } from '../types';
// ── Types ──────────────────────────────────────────────────────────────────────
@@ -18,20 +17,20 @@ interface AuthContextValue {
login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>;
logout: () => void;
updateUsername: (username: string) => void;
devSetRole: (role: UserRole) => void;
}
// ── Context ────────────────────────────────────────────────────────────────────
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 {
try {
const raw = localStorage.getItem(STORAGE_KEY);
const raw = localStorage.getItem(USER_KEY);
if (!raw) return null;
return JSON.parse(raw) as User;
} catch {
@@ -41,12 +40,19 @@ function loadUserFromStorage(): User | null {
function saveUserToStorage(user: User | null): void {
if (user) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(user));
localStorage.setItem(USER_KEY, JSON.stringify(user));
} 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 }) {
const [user, setUser] = useState<User | null>(loadUserFromStorage);
@@ -55,26 +61,35 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
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));
async (email: string, password: string): Promise<{ success: boolean; error?: string }> => {
let data: { token?: string; user?: User; error?: string };
const found = MOCK_USERS.find(
(u) => u.email.toLowerCase() === email.toLowerCase()
);
try {
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) {
return { success: false, error: 'No account found with that email address.' };
}
if (found.isBanned) {
return { success: false, error: 'This account has been suspended.' };
}
if (found.role === 'user') {
const loggedInUser = data.user!;
if (loggedInUser.role === 'user') {
return { success: false, error: 'Access denied. Staff accounts only.' };
}
if (loggedInUser.isBanned) {
return { success: false, error: 'This account has been suspended.' };
}
setUser(found);
saveUserToStorage(found);
localStorage.setItem(TOKEN_KEY, data.token!);
saveUserToStorage(loggedInUser);
setUser(loggedInUser);
return { success: true };
},
[]
@@ -83,6 +98,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const logout = useCallback(() => {
setUser(null);
saveUserToStorage(null);
localStorage.removeItem(TOKEN_KEY);
}, []);
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>(
() => ({ user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername, devSetRole }),
[user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername, devSetRole]
() => ({ user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername }),
[user, isAuthenticated, isStaff, isAdmin, login, logout, updateUsername]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;

View File

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