mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 12:51:14 -05:00
Refactor: Complete error handling overhaul
This commit is contained in:
@@ -24,8 +24,13 @@ export async function invokeWithTracking<T = any>(
|
||||
payload: any = {},
|
||||
userId?: string,
|
||||
parentRequestId?: string,
|
||||
traceId?: string
|
||||
traceId?: string,
|
||||
timeout: number = 30000 // Default 30s timeout
|
||||
): Promise<{ data: T | null; error: any; requestId: string; duration: number }> {
|
||||
// Create AbortController for timeout
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
try {
|
||||
const { result, requestId, duration } = await trackRequest(
|
||||
{
|
||||
@@ -39,6 +44,7 @@ export async function invokeWithTracking<T = any>(
|
||||
// Include client request ID in payload for correlation
|
||||
const { data, error } = await supabase.functions.invoke<T>(functionName, {
|
||||
body: { ...payload, clientRequestId: context.requestId },
|
||||
signal: controller.signal, // Add abort signal for timeout
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
@@ -46,10 +52,25 @@ export async function invokeWithTracking<T = any>(
|
||||
}
|
||||
);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
return { data: result, error: null, requestId, duration };
|
||||
} catch (error: unknown) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// Handle AbortError specifically
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
return {
|
||||
data: null,
|
||||
error: {
|
||||
message: `Request timeout: ${functionName} took longer than ${timeout}ms to respond`,
|
||||
code: 'TIMEOUT',
|
||||
},
|
||||
requestId: 'timeout',
|
||||
duration: timeout,
|
||||
};
|
||||
}
|
||||
|
||||
const errorMessage = getErrorMessage(error);
|
||||
// On error, we don't have tracking info, so create basic response
|
||||
return {
|
||||
data: null,
|
||||
error: { message: errorMessage },
|
||||
|
||||
79
src/lib/moderation/validation.ts
Normal file
79
src/lib/moderation/validation.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Runtime Data Validation for Moderation Queue
|
||||
*
|
||||
* Uses Zod to validate data shapes from the database at runtime.
|
||||
* Prevents runtime errors if database schema changes unexpectedly.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
// Profile schema
|
||||
const ProfileSchema = z.object({
|
||||
username: z.string(),
|
||||
display_name: z.string().optional().nullable(),
|
||||
avatar_url: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
// Submission item schema
|
||||
const SubmissionItemSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
status: z.string(),
|
||||
item_type: z.string().optional(),
|
||||
item_data: z.record(z.string(), z.any()).optional().nullable(),
|
||||
original_data: z.record(z.string(), z.any()).optional().nullable(),
|
||||
error_message: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
// Main moderation item schema
|
||||
export const ModerationItemSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
status: z.enum(['pending', 'approved', 'rejected', 'partially_approved', 'flagged']),
|
||||
type: z.string(),
|
||||
submission_type: z.string(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string().optional().nullable(),
|
||||
content: z.record(z.string(), z.any()),
|
||||
submitter_id: z.string().uuid(),
|
||||
assigned_to: z.string().uuid().optional().nullable(),
|
||||
locked_until: z.string().optional().nullable(),
|
||||
reviewed_at: z.string().optional().nullable(),
|
||||
reviewed_by: z.string().uuid().optional().nullable(),
|
||||
reviewer_notes: z.string().optional().nullable(),
|
||||
submission_items: z.array(SubmissionItemSchema).optional(),
|
||||
submitter_profile: ProfileSchema.optional().nullable(),
|
||||
assigned_profile: ProfileSchema.optional().nullable(),
|
||||
reviewer_profile: ProfileSchema.optional().nullable(),
|
||||
});
|
||||
|
||||
export const ModerationItemArraySchema = z.array(ModerationItemSchema);
|
||||
|
||||
/**
|
||||
* Validate moderation items array
|
||||
*
|
||||
* @param data - Data to validate
|
||||
* @returns Validation result with typed data or error
|
||||
*/
|
||||
export function validateModerationItems(data: unknown): {
|
||||
success: boolean;
|
||||
data?: any[];
|
||||
error?: string
|
||||
} {
|
||||
const result = ModerationItemArraySchema.safeParse(data);
|
||||
|
||||
if (!result.success) {
|
||||
logger.error('❌ Data validation failed', {
|
||||
errors: result.error.issues.slice(0, 5) // Log first 5 issues
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'Received invalid data format from server. Please refresh the page.',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result.data,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user