mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 06:31:14 -05:00
feat: Implement comprehensive error handling
This commit is contained in:
@@ -4,6 +4,7 @@ import { useAuth } from './useAuth';
|
||||
import { useToast } from './use-toast';
|
||||
import { getErrorMessage } from '@/lib/errorHandler';
|
||||
import { getSubmissionTypeLabel } from '@/lib/moderation/entities';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
interface QueuedSubmission {
|
||||
submission_id: string;
|
||||
@@ -45,7 +46,11 @@ export const useModerationQueue = (config?: UseModerationQueueConfig) => {
|
||||
try {
|
||||
await supabase.rpc('release_expired_locks');
|
||||
} catch (error: unknown) {
|
||||
// Silent failure - lock release happens periodically
|
||||
// Log expected periodic failure for debugging without user toast
|
||||
logger.debug('Periodic lock release failed', {
|
||||
operation: 'release_expired_locks',
|
||||
error: getErrorMessage(error)
|
||||
});
|
||||
}
|
||||
}, 120000); // 2 minutes
|
||||
|
||||
@@ -76,16 +81,20 @@ export const useModerationQueue = (config?: UseModerationQueueConfig) => {
|
||||
{ pendingCount: 0, avgWaitHours: 0 }
|
||||
);
|
||||
|
||||
setQueueStats({
|
||||
pendingCount: totals.pendingCount,
|
||||
assignedToMe: assignedCount || 0,
|
||||
avgWaitHours: slaData.length > 0 ? totals.avgWaitHours / slaData.length : 0,
|
||||
});
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
// Silent failure - stats are refreshed periodically
|
||||
setQueueStats({
|
||||
pendingCount: totals.pendingCount,
|
||||
assignedToMe: assignedCount || 0,
|
||||
avgWaitHours: slaData.length > 0 ? totals.avgWaitHours / slaData.length : 0,
|
||||
});
|
||||
}
|
||||
}, [user]);
|
||||
} catch (error: unknown) {
|
||||
// Log stats fetch failure for debugging without user toast
|
||||
logger.debug('Queue stats fetch failed', {
|
||||
operation: 'fetchStats',
|
||||
error: getErrorMessage(error)
|
||||
});
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// Start countdown timer for lock expiry with improved memory leak prevention
|
||||
const startLockTimer = useCallback((expiresAt: Date) => {
|
||||
@@ -348,7 +357,13 @@ export const useModerationQueue = (config?: UseModerationQueueConfig) => {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({ message: 'Failed to claim submission' }));
|
||||
const errorData = await response.json().catch((parseError) => {
|
||||
logger.warn('Failed to parse claim error response', {
|
||||
error: getErrorMessage(parseError),
|
||||
status: response.status
|
||||
});
|
||||
return { message: 'Failed to claim submission' };
|
||||
});
|
||||
throw new Error(errorData.message || 'Failed to claim submission');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,65 @@
|
||||
/**
|
||||
* Central Supabase Client Export
|
||||
* Central Supabase Client Export with Automatic Breadcrumb Tracking
|
||||
*
|
||||
* All application code should import from this file instead of the base client.
|
||||
* This provides a central point for potential future enhancements without breaking imports.
|
||||
* This wrapper automatically tracks all database operations as breadcrumbs for error debugging.
|
||||
*/
|
||||
|
||||
export { supabase } from '@/integrations/supabase/client';
|
||||
import { supabase as baseClient } from '@/integrations/supabase/client';
|
||||
import { breadcrumb } from './errorBreadcrumbs';
|
||||
|
||||
type SupabaseClient = typeof baseClient;
|
||||
|
||||
/**
|
||||
* Wrap Supabase client to automatically track API calls as breadcrumbs
|
||||
*/
|
||||
function wrapSupabaseClient(client: SupabaseClient): SupabaseClient {
|
||||
return new Proxy(client, {
|
||||
get(target, prop: string | symbol) {
|
||||
const value = target[prop as keyof typeof target];
|
||||
|
||||
// Only wrap 'from' and 'rpc' methods for database operations
|
||||
if ((prop === 'from' || prop === 'rpc') && typeof value === 'function') {
|
||||
return (...args: any[]) => {
|
||||
const result = (value as any).apply(target, args);
|
||||
const endpoint = prop === 'from' ? `/table/${args[0]}` : `/rpc/${args[0]}`;
|
||||
|
||||
// Return a proxy for chained query methods
|
||||
return new Proxy(result, {
|
||||
get(queryTarget: any, queryProp: string | symbol) {
|
||||
const queryValue = queryTarget[queryProp];
|
||||
|
||||
// If it's a function, wrap it to track the call
|
||||
if (typeof queryValue === 'function') {
|
||||
return async (...queryArgs: any[]) => {
|
||||
try {
|
||||
const response = await queryValue.apply(queryTarget, queryArgs);
|
||||
|
||||
// Log breadcrumb after response
|
||||
breadcrumb.apiCall(
|
||||
endpoint,
|
||||
String(queryProp).toUpperCase(),
|
||||
response.error ? 400 : 200
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Log breadcrumb for exceptions
|
||||
breadcrumb.apiCall(endpoint, String(queryProp).toUpperCase(), 500);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return queryValue;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}) as SupabaseClient;
|
||||
}
|
||||
|
||||
export const supabase = wrapSupabaseClient(baseClient);
|
||||
|
||||
Reference in New Issue
Block a user