Refactor code structure and remove redundant changes

This commit is contained in:
pacnpal
2025-11-09 16:31:34 -05:00
parent 2884bc23ce
commit eb68cf40c6
1080 changed files with 27361 additions and 56687 deletions

View 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;
}

View 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>
);
}

View 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;
}