Skip to content

Commit 2d69bfb

Browse files
authored
Merge pull request #10 from Hadi2102060/main
Last update
2 parents 36f32be + 9889b1e commit 2d69bfb

10 files changed

Lines changed: 603 additions & 339 deletions

File tree

src/assets/image.png

-1.56 MB
Loading

src/assets/unnamed.jpg

82.2 KB
Loading

src/components/Hero.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Button } from "./ui/button";
22
import { ArrowRight, Calendar, Trophy } from "lucide-react";
33
import { Link } from "react-router-dom";
4-
import bannerImage from "@/assets/hero-cricket.jpg";
4+
import bannerImage from "@/assets/unnamed.jpg";
55
import { useEffect, useState } from "react";
66
import { fetchAllTournaments } from "@/lib/api";
77

src/config/api.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,4 @@ export function buildUrl(path: string): string {
1313
const base = API_BASE.endsWith("/") ? API_BASE.slice(0, -1) : API_BASE;
1414
const p = path.startsWith("/") ? path : `/${path}`;
1515
return `${base}${p}`;
16-
}
17-
18-
16+
}

src/context/AuthContext.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useRef, useState } from "react";
22
import { AuthContext, AuthContextValue, User, Credentials } from './auth-shared';
3-
import { login as apiLogin, registerUser as apiRegister, getAuthToken, fetchCurrentPlayerProfile } from "@/lib/api";
3+
import { login as apiLogin, registerUser as apiRegister, getAuthToken, fetchCurrentPlayerProfile, setGlobalLogoutHandler, clearAuthTokens } from "@/lib/api";
44

55
const STORAGE_USERS = "cpl_users";
66
const STORAGE_CURRENT = "cpl_current_user";
@@ -116,12 +116,38 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
116116

117117
const logout = async () => {
118118
try {
119-
localStorage.removeItem(ACCESS_TOKEN_KEY);
119+
clearAuthTokens();
120120
} finally {
121121
persistCurrent(null);
122122
}
123123
};
124124

125+
// Set up global logout handler for session expiry
126+
useEffect(() => {
127+
setGlobalLogoutHandler(() => {
128+
persistCurrent(null);
129+
// Redirect to login page if we're in a protected route
130+
if (window.location.pathname !== '/auth' && !window.location.pathname.startsWith('/admin')) {
131+
window.location.href = '/auth';
132+
}
133+
});
134+
135+
// Listen for session expiry from BroadcastChannel
136+
let bc: BroadcastChannel | null = null;
137+
try {
138+
bc = new BroadcastChannel('auth-updates');
139+
bc.onmessage = (ev: MessageEvent) => {
140+
if (ev.data?.type === 'session-expired') {
141+
logout();
142+
}
143+
};
144+
} catch {}
145+
146+
return () => {
147+
try { bc?.close(); } catch {}
148+
};
149+
}, []);
150+
125151
const updateUser = async (patch: Partial<User>) => {
126152
// Without a dedicated profile API, update only local state for now.
127153
if (!user) throw new Error('Not authenticated');

src/lib/api.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,21 @@ export function getAuthToken(): string | null {
416416
}
417417
}
418418

419+
// Global logout handler for session expiry
420+
let globalLogoutHandler: (() => void) | null = null;
421+
export function setGlobalLogoutHandler(handler: () => void) {
422+
globalLogoutHandler = handler;
423+
}
424+
425+
// Clear auth tokens
426+
export function clearAuthTokens() {
427+
try {
428+
localStorage.removeItem(ACCESS_TOKEN_KEY_PRIMARY);
429+
localStorage.removeItem(ACCESS_TOKEN_KEY_FALLBACK);
430+
localStorage.removeItem("cpl_current_user");
431+
} catch {}
432+
}
433+
419434
async function apiFetchJson<T>(path: string, init?: JsonInit): Promise<T> {
420435
const { body, headers, ...rest } = init || {};
421436
const token = getAuthToken();
@@ -424,6 +439,24 @@ async function apiFetchJson<T>(path: string, init?: JsonInit): Promise<T> {
424439
body: body !== undefined ? JSON.stringify(body) : undefined,
425440
...rest,
426441
});
442+
443+
// Check for session expiry (401 Unauthorized or 403 Forbidden)
444+
if (res.status === 401 || res.status === 403) {
445+
// Clear tokens
446+
clearAuthTokens();
447+
// Trigger global logout handler if set
448+
if (globalLogoutHandler) {
449+
globalLogoutHandler();
450+
}
451+
// Also trigger logout via BroadcastChannel for cross-tab communication
452+
try {
453+
const bc = new BroadcastChannel('auth-updates');
454+
bc.postMessage({ type: 'session-expired' });
455+
bc.close();
456+
} catch {}
457+
throw new Error('Session expired. Please login again.');
458+
}
459+
427460
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
428461
// Handle 204 No Content
429462
if (res.status === 204) return undefined as unknown as T;
@@ -456,6 +489,21 @@ export async function uploadPlayerProfileImage(file: File): Promise<unknown> {
456489
fd.set("file", file);
457490
const token = getAuthToken();
458491
const res = await fetch(buildUrl(API_PATHS.uploadPlayerProfile), { method: "POST", headers: token ? { Authorization: `Bearer ${token}` } : undefined, body: fd });
492+
493+
// Check for session expiry
494+
if (res.status === 401 || res.status === 403) {
495+
clearAuthTokens();
496+
if (globalLogoutHandler) {
497+
globalLogoutHandler();
498+
}
499+
try {
500+
const bc = new BroadcastChannel('auth-updates');
501+
bc.postMessage({ type: 'session-expired' });
502+
bc.close();
503+
} catch {}
504+
throw new Error('Session expired. Please login again.');
505+
}
506+
459507
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
460508
return await res.json();
461509
}

src/pages/About.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Footer from "@/components/Footer";
33
import { Card, CardContent } from "@/components/ui/card";
44
import { Button } from "@/components/ui/button";
55
import { Trophy, Users, Target, Shield, Sparkles, Handshake } from "lucide-react";
6-
import heroImg from "@/assets/hero-cricket.jpg";
6+
import heroImg from "@/assets/unnamed.jpg";
77
import { Link } from "react-router-dom";
88
import Stats from "@/components/Stats";
99

0 commit comments

Comments
 (0)