import React from 'react' import ReactDOM from 'react-dom/client' import { BrowserRouter } from 'react-router-dom' import { HelmetProvider } from 'react-helmet-async' import App from './App.tsx' import './index.css' import { CurrencyProvider } from './contexts/CurrencyContext.tsx' import { ThemeProvider } from './contexts/ThemeContext.tsx' // ── Global fetch interceptor ───────────────────────────────────────────── // Attaches Bearer token to all API requests and auto-redirects to login on 401. ;((() => { const API_HOSTS = ['ravikumar289-002-site1.anytempurl.com', 'localhost:7076', 'localhost:7077']; const _fetch = (window.fetch as typeof fetch).bind(window); // Endpoints that handle 401 gracefully themselves — do NOT trigger a hard // redirect for these. Redirecting on their 401 causes infinite reload loops // because the component error-handles the failure and re-renders, which // re-triggers the same API call on the new page load. const SILENT_401_PATHS = [ // Auth / profile (mock-user fallback) '/api/PageVisibility/', '/api/register/by-email/', '/api/users', '/api/UserCredits/', // Feed & social content — these are [AllowAnonymous] on the backend; // 401 here means an expired token, handled by showing empty state // Feed & all social/content endpoints — components handle their own errors '/api/feed', '/api/Feed', '/api/notifications/', '/api/Notifications/', '/api/followers', '/api/Followers', '/api/stories', '/api/Stories', '/api/reels', '/api/Reels', '/api/ads', '/api/Ads', '/api/pages', '/api/Pages', '/api/marketplace', '/api/Marketplace', '/api/DirectChat', '/api/directchat', '/api/GroupChat', '/api/groupchat', '/api/messages', '/api/Messages', '/api/search', '/api/Search', '/api/activity', '/api/Activity', // Chat notifications — polled frequently, fail silently when server is down '/api/private-chat/notifications', '/api/PrivateChat/notifications', '/api/chat/notifications', ]; // Endpoints that should NEVER trigger a hard redirect even on 500. // These are polled frequently — a transient server error should just be ignored. const SILENT_500_PATHS = [ '/api/private-chat/notifications', '/api/UserCredits/', '/api/notifications/', '/api/feed', '/api/pages', '/api/stories', '/api/ads', ]; // Guard: prevent concurrent 401 responses from firing multiple simultaneous // redirects. Only the first one takes effect; rest are no-ops until reload. let _redirecting = false; function isApiUrl(url: string): boolean { return API_HOSTS.some(h => url.includes(h)); } function isSilent401(url: string): boolean { return SILENT_401_PATHS.some(p => url.includes(p)); } function isSilent500(url: string): boolean { return SILENT_500_PATHS.some(p => url.includes(p)); } function buildHeaders(existing: HeadersInit | undefined, token: string): Headers { const h = new Headers(); if (existing) { if (existing instanceof Headers) { existing.forEach((v, k) => h.set(k, v)); } else if (Array.isArray(existing)) { (existing as [string, string][]).forEach(([k, v]) => h.set(k, v)); } else { Object.entries(existing as Record).forEach(([k, v]) => h.set(k, v)); } } if (!h.has('Authorization')) h.set('Authorization', `Bearer ${token}`); return h; } window.fetch = function interceptedFetch(input: RequestInfo | URL, init?: RequestInit): Promise { try { const url = input instanceof Request ? input.url : input instanceof URL ? input.href : String(input); if (isApiUrl(url)) { const token = localStorage.getItem('auth_token'); if (token) { init = { ...(init ?? {}), headers: buildHeaders(init?.headers, token) }; } return _fetch(input as RequestInfo, init).then(res => { // Write operations (POST/PUT/PATCH/DELETE) always redirect on 401 — // a write failure means the session expired and the user must re-login. // Only GET/HEAD reads are silenced (they show empty state instead). const method = ((init?.method) ?? 'GET').toUpperCase(); const isWriteRequest = method !== 'GET' && method !== 'HEAD'; // 500 on non-critical polling endpoints (e.g. chat notifications, credits) // should be swallowed — typically caused by transient DB/server issues. // The component's catch block will handle it gracefully. if (res.status >= 500 && isSilent500(url)) { return res; // return as-is, component handles silently } if ( res.status === 401 && !window.location.pathname.includes('/login') && !_redirecting && (!isSilent401(url) || isWriteRequest) ) { _redirecting = true; localStorage.removeItem('auth_token'); localStorage.removeItem('auth_user'); window.location.href = '/login'; } return res; }); } } catch { /* never break fetch */ } return _fetch(input as RequestInfo, init); }; })()); ReactDOM.createRoot(document.getElementById('root')!).render( , )