Implement bulletproof rejection flow

- Adds atomic rejection transaction edge function and RPC
- Updates moderation client to use new rejection path
- Introduces rejection transaction migration and supporting readouts
- Moves photo-related approval handling toward RPC-based flow
- Lays groundwork for idempotency and resilience in moderation actions
This commit is contained in:
gpt-engineer-app[bot]
2025-11-10 13:20:16 +00:00
parent 403bc78765
commit 92b5d6e33d
3 changed files with 355 additions and 9 deletions

View File

@@ -443,15 +443,61 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
});
return;
} else if (action === 'rejected') {
await supabase
.from('submission_items')
.update({
status: 'rejected',
rejection_reason: moderatorNotes || 'Parent submission rejected',
updated_at: new Date().toISOString(),
})
.eq('submission_id', item.id)
.eq('status', 'pending');
// Use atomic rejection transaction for submission items
const {
data,
error,
requestId,
attempts,
cached,
conflictRetries
} = await invokeWithResilience(
'process-selective-rejection',
{
itemIds: submissionItems.map((i) => i.id),
submissionId: item.id,
rejectionReason: moderatorNotes || 'Parent submission rejected',
},
'rejection',
submissionItems.map((i) => i.id),
config.user?.id,
3, // Max 3 conflict retries
30000 // 30s timeout
);
// Log retry attempts
if (attempts && attempts > 1) {
logger.log(`Rejection succeeded after ${attempts} network retries`, {
submissionId: item.id,
requestId,
});
}
if (conflictRetries && conflictRetries > 0) {
logger.log(`Resolved 409 conflict after ${conflictRetries} retries`, {
submissionId: item.id,
requestId,
cached: !!cached,
});
}
if (error) {
// Enhance error with context for better UI feedback
if (is409Conflict(error)) {
throw new Error(
'This rejection is being processed by another request. Please wait and try again if it does not complete.'
);
}
throw error;
}
toast({
title: cached ? 'Cached Result' : 'Submission Rejected',
description: cached
? `Returned cached result for ${submissionItems.length} item(s)`
: `Successfully rejected ${submissionItems.length} item(s)${requestId ? ` (Request: ${requestId.substring(0, 8)})` : ''}`,
});
return;
}
}