Improve error handling and environment configuration across the application

Enhance input validation, update environment variable usage for Supabase and Turnstile, and refine image upload and auth logic for better robustness and developer experience.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cb061c75-702e-4b89-a8d1-77a96cdcdfbb
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/cb061c75-702e-4b89-a8d1-77a96cdcdfbb/ANdRXVZ
This commit is contained in:
pac7
2025-10-07 14:42:22 +00:00
parent 01c2df62a8
commit 1addcbc0dd
8 changed files with 412 additions and 116 deletions

View File

@@ -1,7 +1,8 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import React, { createContext, useContext, useEffect, useState, useRef } from 'react';
import type { User, Session } from '@supabase/supabase-js';
import { supabase } from '@/integrations/supabase/client';
import type { Profile } from '@/types/database';
import { toast } from '@/hooks/use-toast';
interface AuthContextType {
user: User | null;
@@ -23,6 +24,11 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const [loading, setLoading] = useState(true);
const [pendingEmail, setPendingEmail] = useState<string | null>(null);
const [previousEmail, setPreviousEmail] = useState<string | null>(null);
// Refs for lifecycle and cleanup management
const isMountedRef = useRef(true);
const profileFetchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const novuUpdateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const fetchProfile = async (userId: string) => {
try {
@@ -34,12 +40,33 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
if (error && error.code !== 'PGRST116') {
console.error('Error fetching profile:', error);
// Show user-friendly error notification
if (isMountedRef.current) {
toast({
title: "Profile Loading Error",
description: "Unable to load your profile. Please refresh the page or try again later.",
variant: "destructive",
});
}
return;
}
setProfile(data as Profile);
// Only update state if component is still mounted
if (isMountedRef.current) {
setProfile(data as Profile);
}
} catch (error) {
console.error('Error fetching profile:', error);
// Show user-friendly error notification
if (isMountedRef.current) {
toast({
title: "Profile Loading Error",
description: "An unexpected error occurred while loading your profile.",
variant: "destructive",
});
}
}
};
@@ -52,6 +79,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Get initial session
supabase.auth.getSession().then(({ data: { session } }) => {
if (!isMountedRef.current) return;
setSession(session);
setUser(session?.user ?? null);
if (session?.user) {
@@ -64,6 +93,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, session) => {
if (!isMountedRef.current) return;
const currentEmail = session?.user?.email;
const newEmailPending = session?.user?.new_email;
@@ -81,8 +112,15 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
currentEmail !== previousEmail &&
!newEmailPending
) {
// Clear any existing Novu update timeout
if (novuUpdateTimeoutRef.current) {
clearTimeout(novuUpdateTimeoutRef.current);
}
// Defer Novu update and notifications to avoid blocking auth
setTimeout(async () => {
novuUpdateTimeoutRef.current = setTimeout(async () => {
if (!isMountedRef.current) return;
try {
// Update Novu subscriber with confirmed email
const { notificationService } = await import('@/lib/notificationService');
@@ -119,6 +157,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}
} catch (error) {
console.error('Error updating Novu after email confirmation:', error);
} finally {
novuUpdateTimeoutRef.current = null;
}
}, 0);
}
@@ -129,18 +169,40 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}
if (session?.user) {
// Clear any existing profile fetch timeout
if (profileFetchTimeoutRef.current) {
clearTimeout(profileFetchTimeoutRef.current);
}
// Defer profile fetch to avoid deadlock
setTimeout(() => {
profileFetchTimeoutRef.current = setTimeout(() => {
if (!isMountedRef.current) return;
fetchProfile(session.user.id);
profileFetchTimeoutRef.current = null;
}, 0);
} else {
setProfile(null);
if (isMountedRef.current) {
setProfile(null);
}
}
setLoading(false);
});
return () => subscription.unsubscribe();
return () => {
isMountedRef.current = false;
subscription.unsubscribe();
// Clear any pending timeouts
if (profileFetchTimeoutRef.current) {
clearTimeout(profileFetchTimeoutRef.current);
profileFetchTimeoutRef.current = null;
}
if (novuUpdateTimeoutRef.current) {
clearTimeout(novuUpdateTimeoutRef.current);
novuUpdateTimeoutRef.current = null;
}
};
}, []);
const signOut = async () => {