/** * Mode detection system for GridPilot website * * Controls whether the site shows pre-launch content or alpha platform * Based on NEXT_PUBLIC_GRIDPILOT_MODE environment variable */ export type AppMode = 'pre-launch' | 'alpha'; const VALID_MODES: readonly AppMode[] = ['pre-launch', 'alpha'] as const; /** * Get the current application mode from environment variable * Defaults to 'pre-launch' if not set or invalid * * @throws {Error} If mode is set but invalid (development only) * @returns {AppMode} The current application mode */ export function getAppMode(): AppMode { const mode = process.env.NEXT_PUBLIC_GRIDPILOT_MODE; if (!mode) { return 'pre-launch'; } if (!isValidMode(mode)) { const validModes = VALID_MODES.join(', '); const error = `Invalid NEXT_PUBLIC_GRIDPILOT_MODE: "${mode}". Must be one of: ${validModes}`; if (process.env.NODE_ENV === 'development') { throw new Error(error); } console.error(error); return 'pre-launch'; } return mode; } /** * Type guard to check if a string is a valid AppMode */ function isValidMode(mode: string): mode is AppMode { return VALID_MODES.includes(mode as AppMode); } /** * Check if currently in pre-launch mode */ export function isPreLaunch(): boolean { return getAppMode() === 'pre-launch'; } /** * Check if currently in alpha mode */ export function isAlpha(): boolean { return getAppMode() === 'alpha'; } /** * Get list of public routes that are always accessible */ export function getPublicRoutes(): readonly string[] { return [ // Core public pages '/', // Public content routes (leagues, drivers, teams, leaderboards, races) '/leagues', '/drivers', '/teams', '/leaderboards', '/races', // Auth routes '/api/signup', '/api/auth/signup', '/api/auth/login', '/api/auth/forgot-password', '/api/auth/reset-password', '/api/auth/demo-login', '/api/auth/session', '/api/auth/logout', '/auth/login', '/auth/signup', '/auth/forgot-password', '/auth/reset-password', '/auth/iracing', '/auth/iracing/start', '/auth/iracing/callback', ] as const; } /** * Check if a route is public (accessible in all modes) * Supports both exact matches and prefix matches for nested routes */ export function isPublicRoute(pathname: string): boolean { const publicRoutes = getPublicRoutes(); // Check exact match first if (publicRoutes.includes(pathname)) { return true; } // Check prefix matches for nested routes // e.g., '/leagues' should match '/leagues/123', '/leagues/create', etc. const publicPrefixes = [ '/leagues', '/drivers', '/teams', '/leaderboards', '/races', ]; return publicPrefixes.some(prefix => pathname === prefix || pathname.startsWith(prefix + '/') ); }