mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 09:51:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
205
src-old/hooks/useTransactionResilience.ts
Normal file
205
src-old/hooks/useTransactionResilience.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Transaction Resilience Hook
|
||||
*
|
||||
* Combines timeout detection, lock auto-release, and idempotency lifecycle
|
||||
* into a unified hook for moderation transactions.
|
||||
*
|
||||
* Part of Sacred Pipeline Phase 4: Transaction Resilience
|
||||
*/
|
||||
|
||||
import { useEffect, useCallback, useRef } from 'react';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import {
|
||||
withTimeout,
|
||||
isTimeoutError,
|
||||
getTimeoutErrorMessage,
|
||||
type TimeoutError,
|
||||
} from '@/lib/timeoutDetection';
|
||||
import {
|
||||
autoReleaseLockOnError,
|
||||
setupAutoReleaseOnUnload,
|
||||
setupInactivityAutoRelease,
|
||||
} from '@/lib/moderation/lockAutoRelease';
|
||||
import {
|
||||
generateAndRegisterKey,
|
||||
validateAndStartProcessing,
|
||||
markKeyCompleted,
|
||||
markKeyFailed,
|
||||
is409Conflict,
|
||||
getRetryAfter,
|
||||
sleep,
|
||||
} from '@/lib/idempotencyHelpers';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
interface TransactionResilientOptions {
|
||||
submissionId: string;
|
||||
/** Timeout in milliseconds (default: 30000) */
|
||||
timeoutMs?: number;
|
||||
/** Enable auto-release on unload (default: true) */
|
||||
autoReleaseOnUnload?: boolean;
|
||||
/** Enable inactivity auto-release (default: true) */
|
||||
autoReleaseOnInactivity?: boolean;
|
||||
/** Inactivity timeout in minutes (default: 10) */
|
||||
inactivityMinutes?: number;
|
||||
}
|
||||
|
||||
export function useTransactionResilience(options: TransactionResilientOptions) {
|
||||
const { submissionId, timeoutMs = 30000, autoReleaseOnUnload = true, autoReleaseOnInactivity = true, inactivityMinutes = 10 } = options;
|
||||
const { user } = useAuth();
|
||||
const cleanupFnsRef = useRef<Array<() => void>>([]);
|
||||
|
||||
// Setup auto-release mechanisms
|
||||
useEffect(() => {
|
||||
if (!user?.id) return;
|
||||
|
||||
const cleanupFns: Array<() => void> = [];
|
||||
|
||||
// Setup unload auto-release
|
||||
if (autoReleaseOnUnload) {
|
||||
const cleanup = setupAutoReleaseOnUnload(submissionId, user.id);
|
||||
cleanupFns.push(cleanup);
|
||||
}
|
||||
|
||||
// Setup inactivity auto-release
|
||||
if (autoReleaseOnInactivity) {
|
||||
const cleanup = setupInactivityAutoRelease(submissionId, user.id, inactivityMinutes);
|
||||
cleanupFns.push(cleanup);
|
||||
}
|
||||
|
||||
cleanupFnsRef.current = cleanupFns;
|
||||
|
||||
// Cleanup on unmount
|
||||
return () => {
|
||||
cleanupFns.forEach(fn => fn());
|
||||
};
|
||||
}, [submissionId, user?.id, autoReleaseOnUnload, autoReleaseOnInactivity, inactivityMinutes]);
|
||||
|
||||
/**
|
||||
* Execute a transaction with full resilience (timeout, idempotency, auto-release)
|
||||
*/
|
||||
const executeTransaction = useCallback(
|
||||
async <T,>(
|
||||
action: 'approval' | 'rejection' | 'retry',
|
||||
itemIds: string[],
|
||||
transactionFn: (idempotencyKey: string) => Promise<T>
|
||||
): Promise<T> => {
|
||||
if (!user?.id) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
|
||||
// Generate and register idempotency key
|
||||
const { key: idempotencyKey } = await generateAndRegisterKey(
|
||||
action,
|
||||
submissionId,
|
||||
itemIds,
|
||||
user.id
|
||||
);
|
||||
|
||||
logger.info('[TransactionResilience] Starting transaction', {
|
||||
action,
|
||||
submissionId,
|
||||
itemIds,
|
||||
idempotencyKey,
|
||||
});
|
||||
|
||||
try {
|
||||
// Validate key and mark as processing
|
||||
const isValid = await validateAndStartProcessing(idempotencyKey);
|
||||
|
||||
if (!isValid) {
|
||||
throw new Error('Idempotency key validation failed - possible duplicate request');
|
||||
}
|
||||
|
||||
// Execute transaction with timeout
|
||||
const result = await withTimeout(
|
||||
() => transactionFn(idempotencyKey),
|
||||
timeoutMs,
|
||||
'edge-function'
|
||||
);
|
||||
|
||||
// Mark key as completed
|
||||
await markKeyCompleted(idempotencyKey);
|
||||
|
||||
logger.info('[TransactionResilience] Transaction completed', {
|
||||
action,
|
||||
submissionId,
|
||||
idempotencyKey,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Check for timeout
|
||||
if (isTimeoutError(error)) {
|
||||
const timeoutError = error as TimeoutError;
|
||||
const message = getTimeoutErrorMessage(timeoutError);
|
||||
|
||||
logger.error('[TransactionResilience] Transaction timed out', {
|
||||
action,
|
||||
submissionId,
|
||||
idempotencyKey,
|
||||
duration: timeoutError.duration,
|
||||
});
|
||||
|
||||
// Auto-release lock on timeout
|
||||
await autoReleaseLockOnError(submissionId, user.id, error);
|
||||
|
||||
// Mark key as failed
|
||||
await markKeyFailed(idempotencyKey, message);
|
||||
|
||||
toast({
|
||||
title: 'Transaction Timeout',
|
||||
description: message,
|
||||
variant: 'destructive',
|
||||
});
|
||||
|
||||
throw timeoutError;
|
||||
}
|
||||
|
||||
// Check for 409 Conflict (duplicate request)
|
||||
if (is409Conflict(error)) {
|
||||
const retryAfter = getRetryAfter(error);
|
||||
|
||||
logger.warn('[TransactionResilience] Duplicate request detected', {
|
||||
action,
|
||||
submissionId,
|
||||
idempotencyKey,
|
||||
retryAfter,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: 'Duplicate Request',
|
||||
description: `This action is already being processed. Please wait ${retryAfter}s.`,
|
||||
});
|
||||
|
||||
// Wait and return (don't auto-release, the other request is handling it)
|
||||
await sleep(retryAfter * 1000);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Generic error handling
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
|
||||
logger.error('[TransactionResilience] Transaction failed', {
|
||||
action,
|
||||
submissionId,
|
||||
idempotencyKey,
|
||||
error: errorMessage,
|
||||
});
|
||||
|
||||
// Auto-release lock on error
|
||||
await autoReleaseLockOnError(submissionId, user.id, error);
|
||||
|
||||
// Mark key as failed
|
||||
await markKeyFailed(idempotencyKey, errorMessage);
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[submissionId, user?.id, timeoutMs]
|
||||
);
|
||||
|
||||
return {
|
||||
executeTransaction,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user