Implement Phase 3: Enhanced Error Handling

This commit implements Phase 3 of the Sacred Pipeline, focusing on enhanced error handling. It includes:

- **Transaction Status Polling Endpoint**: A new edge function `check-transaction-status` allows clients to poll the status of moderation transactions using idempotency keys.
- **Expanded Error Sanitizer Patterns**: The `src/lib/errorSanitizer.ts` file has been updated with more comprehensive patterns to remove sensitive information from error messages, making them safer for display and logging. User-friendly replacements for common errors are also included.
- **Rate Limiting for Submission Creation**: Client-side rate limiting has been implemented in `src/lib/submissionRateLimiter.ts` and applied to key submission functions within `src/lib/entitySubmissionHelpers.ts` (e.g., `submitParkCreation`, `submitRideCreation`, `submitParkUpdate`, `submitRideUpdate`) to prevent abuse and accidental duplicate submissions.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-07 18:22:27 +00:00
parent 44f50f1f3c
commit 91a5b0e7dd
6 changed files with 945 additions and 0 deletions

View File

@@ -17,6 +17,8 @@ import {
validateRideModelCreateFields,
assertValid
} from './submissionValidation';
import { checkSubmissionRateLimit, recordSubmissionAttempt } from './submissionRateLimiter';
import { sanitizeErrorMessage } from './errorSanitizer';
// ============================================
// COMPOSITE SUBMISSION TYPES
@@ -198,6 +200,37 @@ export interface RideModelFormData {
_technical_specifications?: TechnicalSpecification[];
}
/**
* ═══════════════════════════════════════════════════════════════════
* RATE LIMITING HELPER
* ═══════════════════════════════════════════════════════════════════
*
* Checks rate limits before allowing submission creation
* Part of Sacred Pipeline Phase 3: Enhanced Error Handling
*/
function checkRateLimitOrThrow(userId: string, action: string): void {
const rateLimit = checkSubmissionRateLimit(userId);
if (!rateLimit.allowed) {
const sanitizedMessage = sanitizeErrorMessage(rateLimit.reason || 'Rate limit exceeded');
logger.warn('[RateLimit] Submission blocked', {
userId,
action,
reason: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
});
throw new Error(sanitizedMessage);
}
logger.info('[RateLimit] Submission allowed', {
userId,
action,
remaining: rateLimit.remaining,
});
}
/**
* ═══════════════════════════════════════════════════════════════════
* COMPOSITE SUBMISSION HANDLER
@@ -220,6 +253,9 @@ async function submitCompositeCreation(
userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
try {
// Phase 3: Rate limiting check
checkRateLimitOrThrow(userId, 'composite_creation');
breadcrumb.userAction('Start composite submission', 'submitCompositeCreation', {
primaryType: primaryEntity.type,
dependencyCount: dependencies.length,
@@ -624,6 +660,9 @@ export async function submitParkCreation(
data: ParkFormData & { _compositeSubmission?: any },
userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
// Phase 3: Rate limiting check
checkRateLimitOrThrow(userId, 'park_creation');
console.info('[submitParkCreation] Received data:', {
hasLocation: !!data.location,
hasLocationId: !!data.location_id,
@@ -884,6 +923,9 @@ export async function submitParkUpdate(
data: ParkFormData,
userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
// Phase 3: Rate limiting check
checkRateLimitOrThrow(userId, 'park_update');
const { withRetry, isRetryableError } = await import('./retryHelpers');
// Check if user is banned - with retry for transient failures
@@ -1120,6 +1162,9 @@ export async function submitRideCreation(
},
userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
// Phase 3: Rate limiting check
checkRateLimitOrThrow(userId, 'ride_creation');
// Validate required fields client-side
assertValid(validateRideCreateFields(data));
@@ -1504,6 +1549,9 @@ export async function submitRideUpdate(
data: RideFormData,
userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
// Phase 3: Rate limiting check
checkRateLimitOrThrow(userId, 'ride_update');
const { withRetry, isRetryableError } = await import('./retryHelpers');
// Check if user is banned - with retry for transient failures