mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 00:31:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
87
src-old/contexts/APIConnectivityContext.tsx
Normal file
87
src-old/contexts/APIConnectivityContext.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { createContext, useContext, ReactNode, useState, useEffect } from 'react';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
interface APIConnectivityContextType {
|
||||
isAPIReachable: boolean;
|
||||
isBannerDismissed: boolean;
|
||||
dismissBanner: () => void;
|
||||
}
|
||||
|
||||
const APIConnectivityContext = createContext<APIConnectivityContextType | undefined>(undefined);
|
||||
|
||||
const DISMISSAL_DURATION = 15 * 60 * 1000; // 15 minutes
|
||||
const DISMISSAL_KEY = 'api-connectivity-dismissed-until';
|
||||
const REACHABILITY_KEY = 'api-reachable';
|
||||
|
||||
export function APIConnectivityProvider({ children }: { children: ReactNode }) {
|
||||
const [isAPIReachable, setIsAPIReachable] = useState<boolean>(() => {
|
||||
const stored = sessionStorage.getItem(REACHABILITY_KEY);
|
||||
return stored !== 'false'; // Default to true, only false if explicitly set
|
||||
});
|
||||
|
||||
const [dismissedUntil, setDismissedUntil] = useState<number | null>(() => {
|
||||
const stored = localStorage.getItem(DISMISSAL_KEY);
|
||||
return stored ? parseInt(stored) : null;
|
||||
});
|
||||
|
||||
const dismissBanner = () => {
|
||||
const until = Date.now() + DISMISSAL_DURATION;
|
||||
localStorage.setItem(DISMISSAL_KEY, until.toString());
|
||||
setDismissedUntil(until);
|
||||
logger.info('API status banner dismissed', { until: new Date(until).toISOString() });
|
||||
};
|
||||
|
||||
const isBannerDismissed = dismissedUntil ? Date.now() < dismissedUntil : false;
|
||||
|
||||
// Auto-clear dismissal when API is healthy again
|
||||
useEffect(() => {
|
||||
if (isAPIReachable && dismissedUntil) {
|
||||
localStorage.removeItem(DISMISSAL_KEY);
|
||||
setDismissedUntil(null);
|
||||
logger.info('API status banner dismissal cleared (API recovered)');
|
||||
}
|
||||
}, [isAPIReachable, dismissedUntil]);
|
||||
|
||||
// Listen for custom events from error handler
|
||||
useEffect(() => {
|
||||
const handleAPIDown = () => {
|
||||
logger.warn('API connectivity lost');
|
||||
setIsAPIReachable(false);
|
||||
sessionStorage.setItem(REACHABILITY_KEY, 'false');
|
||||
};
|
||||
|
||||
const handleAPIUp = () => {
|
||||
logger.info('API connectivity restored');
|
||||
setIsAPIReachable(true);
|
||||
sessionStorage.setItem(REACHABILITY_KEY, 'true');
|
||||
};
|
||||
|
||||
window.addEventListener('api-connectivity-down', handleAPIDown);
|
||||
window.addEventListener('api-connectivity-up', handleAPIUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('api-connectivity-down', handleAPIDown);
|
||||
window.removeEventListener('api-connectivity-up', handleAPIUp);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<APIConnectivityContext.Provider
|
||||
value={{
|
||||
isAPIReachable,
|
||||
isBannerDismissed,
|
||||
dismissBanner,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</APIConnectivityContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAPIConnectivity() {
|
||||
const context = useContext(APIConnectivityContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAPIConnectivity must be used within APIConnectivityProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
59
src-old/contexts/AuthModalContext.tsx
Normal file
59
src-old/contexts/AuthModalContext.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React, { createContext, useState, ReactNode } from 'react';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { AuthModal } from '@/components/auth/AuthModal';
|
||||
|
||||
interface AuthModalContextType {
|
||||
openAuthModal: (defaultTab?: 'signin' | 'signup') => void;
|
||||
requireAuth: (callback: () => void, message?: string) => void;
|
||||
authModalOpen: boolean;
|
||||
setAuthModalOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const AuthModalContext = createContext<AuthModalContextType | undefined>(undefined);
|
||||
|
||||
interface AuthModalProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function AuthModalProvider({ children }: AuthModalProviderProps) {
|
||||
const { user } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const [authModalOpen, setAuthModalOpen] = useState(false);
|
||||
const [authModalTab, setAuthModalTab] = useState<'signin' | 'signup'>('signin');
|
||||
|
||||
const openAuthModal = (defaultTab: 'signin' | 'signup' = 'signin') => {
|
||||
setAuthModalTab(defaultTab);
|
||||
setAuthModalOpen(true);
|
||||
};
|
||||
|
||||
const requireAuth = (callback: () => void, message?: string) => {
|
||||
if (user) {
|
||||
callback();
|
||||
} else {
|
||||
toast({
|
||||
title: "Authentication Required",
|
||||
description: message || "Please sign in to continue",
|
||||
});
|
||||
openAuthModal('signin');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthModalContext.Provider
|
||||
value={{
|
||||
openAuthModal,
|
||||
requireAuth,
|
||||
authModalOpen,
|
||||
setAuthModalOpen,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<AuthModal
|
||||
open={authModalOpen}
|
||||
onOpenChange={setAuthModalOpen}
|
||||
defaultTab={authModalTab}
|
||||
/>
|
||||
</AuthModalContext.Provider>
|
||||
);
|
||||
}
|
||||
156
src-old/contexts/MFAStepUpContext.tsx
Normal file
156
src-old/contexts/MFAStepUpContext.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* MFA Step-Up Context
|
||||
*
|
||||
* Provides global MFA step-up functionality that automatically
|
||||
* prompts users for MFA verification when operations require AAL2
|
||||
*/
|
||||
|
||||
import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { MFAStepUpModal } from '@/components/auth/MFAStepUpModal';
|
||||
import { getEnrolledFactors } from '@/lib/authService';
|
||||
import { isAAL2PolicyError, getAAL2ErrorMessage, MFACancelledError } from '@/lib/aalErrorDetection';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
interface MFAStepUpContextType {
|
||||
/**
|
||||
* Wrap an operation that may require AAL2 verification
|
||||
* If operation fails due to AAL2, prompts user for MFA and retries
|
||||
*/
|
||||
requireAAL2: <T>(operation: () => Promise<T>, errorMessage?: string) => Promise<T>;
|
||||
|
||||
/**
|
||||
* Check if error is AAL2-related
|
||||
*/
|
||||
isAAL2Error: (error: unknown) => boolean;
|
||||
}
|
||||
|
||||
const MFAStepUpContext = createContext<MFAStepUpContextType | undefined>(undefined);
|
||||
|
||||
interface MFAStepUpProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function MFAStepUpProvider({ children }: MFAStepUpProviderProps) {
|
||||
const { session } = useAuth();
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [factorId, setFactorId] = useState<string>('');
|
||||
const [pendingOperation, setPendingOperation] = useState<(() => Promise<any>) | null>(null);
|
||||
const [pendingResolve, setPendingResolve] = useState<((value: any) => void) | null>(null);
|
||||
const [pendingReject, setPendingReject] = useState<((error: any) => void) | null>(null);
|
||||
|
||||
/**
|
||||
* Handle successful MFA verification
|
||||
* Retries the pending operation with upgraded AAL2 session
|
||||
*/
|
||||
const handleMFASuccess = useCallback(async () => {
|
||||
setModalOpen(false);
|
||||
|
||||
if (!pendingOperation || !pendingResolve || !pendingReject) {
|
||||
// Invalid state - missing pending operations
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.log('[MFAStepUp] Retrying operation after AAL2 upgrade');
|
||||
const result = await pendingOperation();
|
||||
pendingResolve(result);
|
||||
} catch (error) {
|
||||
// Operation failed after AAL2 - will be handled by caller
|
||||
pendingReject(error);
|
||||
} finally {
|
||||
// Clean up
|
||||
setPendingOperation(null);
|
||||
setPendingResolve(null);
|
||||
setPendingReject(null);
|
||||
}
|
||||
}, [pendingOperation, pendingResolve, pendingReject]);
|
||||
|
||||
/**
|
||||
* Handle MFA cancellation
|
||||
*/
|
||||
const handleMFACancel = useCallback(() => {
|
||||
setModalOpen(false);
|
||||
|
||||
if (pendingReject) {
|
||||
pendingReject(new MFACancelledError());
|
||||
}
|
||||
|
||||
// Clean up
|
||||
setPendingOperation(null);
|
||||
setPendingResolve(null);
|
||||
setPendingReject(null);
|
||||
}, [pendingReject]);
|
||||
|
||||
/**
|
||||
* Wrap an operation with AAL2 requirement checking
|
||||
*/
|
||||
const requireAAL2 = useCallback(
|
||||
async <T,>(operation: () => Promise<T>, customErrorMessage?: string): Promise<T> => {
|
||||
try {
|
||||
// Try the operation first
|
||||
return await operation();
|
||||
} catch (error) {
|
||||
// Check if error is AAL2-related
|
||||
if (!isAAL2PolicyError(error)) {
|
||||
// Not an AAL2 error, re-throw
|
||||
throw error;
|
||||
}
|
||||
|
||||
logger.log('[MFAStepUp] AAL2 error detected, prompting for MFA', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
// Get enrolled factors
|
||||
const factors = await getEnrolledFactors();
|
||||
|
||||
if (factors.length === 0) {
|
||||
// No MFA set up
|
||||
throw new Error('MFA is not set up for your account');
|
||||
}
|
||||
|
||||
const factor = factors[0];
|
||||
setFactorId(factor.id);
|
||||
|
||||
// Return a promise that resolves when MFA is verified
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
setPendingOperation(() => operation);
|
||||
setPendingResolve(() => resolve);
|
||||
setPendingReject(() => reject);
|
||||
setModalOpen(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const value: MFAStepUpContextType = {
|
||||
requireAAL2,
|
||||
isAAL2Error: isAAL2PolicyError,
|
||||
};
|
||||
|
||||
return (
|
||||
<MFAStepUpContext.Provider value={value}>
|
||||
{children}
|
||||
<MFAStepUpModal
|
||||
open={modalOpen}
|
||||
factorId={factorId}
|
||||
onSuccess={handleMFASuccess}
|
||||
onCancel={handleMFACancel}
|
||||
/>
|
||||
</MFAStepUpContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to access MFA step-up functionality
|
||||
*/
|
||||
export function useMFAStepUp(): MFAStepUpContextType {
|
||||
const context = useContext(MFAStepUpContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useMFAStepUp must be used within MFAStepUpProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user