mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-28 17:26:58 -05:00
Compare commits
2 Commits
e9b9faa3e1
...
6efb6dda66
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6efb6dda66 | ||
|
|
7eb0b77d76 |
@@ -4,6 +4,7 @@ import { authStorage } from '@/lib/authStorage';
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { logger } from '@/lib/logger';
|
||||||
|
|
||||||
interface AuthDiagnosticsData {
|
interface AuthDiagnosticsData {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -55,7 +56,7 @@ export function AuthDiagnostics() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setDiagnostics(results);
|
setDiagnostics(results);
|
||||||
console.log('[Auth Diagnostics]', results);
|
logger.debug('Auth diagnostics', { results });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -201,17 +201,36 @@ export function ImageDiff({ change, compact = false }: ImageDiffProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LocationData {
|
||||||
|
location_id?: string;
|
||||||
|
city?: string;
|
||||||
|
state_province?: string;
|
||||||
|
country?: string;
|
||||||
|
postal_code?: string;
|
||||||
|
latitude?: number | string;
|
||||||
|
longitude?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
interface LocationDiffProps {
|
interface LocationDiffProps {
|
||||||
oldLocation: any;
|
oldLocation: LocationData | string | null | undefined;
|
||||||
newLocation: any;
|
newLocation: LocationData | string | null | undefined;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LocationDiff({ oldLocation, newLocation, compact = false }: LocationDiffProps) {
|
export function LocationDiff({ oldLocation, newLocation, compact = false }: LocationDiffProps) {
|
||||||
|
// Type guards for LocationData
|
||||||
|
const isLocationData = (loc: unknown): loc is LocationData => {
|
||||||
|
return typeof loc === 'object' && loc !== null && !Array.isArray(loc);
|
||||||
|
};
|
||||||
|
|
||||||
// Check if we're creating a new location entity
|
// Check if we're creating a new location entity
|
||||||
const isCreatingNewLocation = oldLocation?.location_id && !oldLocation?.city && newLocation?.city;
|
const isCreatingNewLocation = isLocationData(oldLocation) &&
|
||||||
|
oldLocation.location_id &&
|
||||||
|
!oldLocation.city &&
|
||||||
|
isLocationData(newLocation) &&
|
||||||
|
newLocation.city;
|
||||||
|
|
||||||
const formatLocation = (loc: any) => {
|
const formatLocation = (loc: LocationData | string | null | undefined) => {
|
||||||
if (!loc) return 'None';
|
if (!loc) return 'None';
|
||||||
if (typeof loc === 'string') return loc;
|
if (typeof loc === 'string') return loc;
|
||||||
|
|
||||||
@@ -242,12 +261,14 @@ export function LocationDiff({ oldLocation, newLocation, compact = false }: Loca
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if only coordinates changed
|
// Check if only coordinates changed
|
||||||
const onlyCoordinatesChanged = oldLocation?.city === newLocation?.city &&
|
const onlyCoordinatesChanged = isLocationData(oldLocation) &&
|
||||||
oldLocation?.state_province === newLocation?.state_province &&
|
isLocationData(newLocation) &&
|
||||||
oldLocation?.country === newLocation?.country &&
|
oldLocation.city === newLocation.city &&
|
||||||
oldLocation?.postal_code === newLocation?.postal_code &&
|
oldLocation.state_province === newLocation.state_province &&
|
||||||
(Number(oldLocation?.latitude) !== Number(newLocation?.latitude) ||
|
oldLocation.country === newLocation.country &&
|
||||||
Number(oldLocation?.longitude) !== Number(newLocation?.longitude));
|
oldLocation.postal_code === newLocation.postal_code &&
|
||||||
|
(Number(oldLocation.latitude) !== Number(newLocation.latitude) ||
|
||||||
|
Number(oldLocation.longitude) !== Number(newLocation.longitude));
|
||||||
|
|
||||||
if (compact) {
|
if (compact) {
|
||||||
const oldLoc = formatLocation(oldLocation);
|
const oldLoc = formatLocation(oldLocation);
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ import { TimelineEventPreview } from './TimelineEventPreview';
|
|||||||
import type { TimelineSubmissionData } from '@/types/timeline';
|
import type { TimelineSubmissionData } from '@/types/timeline';
|
||||||
|
|
||||||
interface SubmissionChangesDisplayProps {
|
interface SubmissionChangesDisplayProps {
|
||||||
item: SubmissionItemData | SubmissionItemWithDeps | { item_data?: any; original_data?: any; item_type: string; action_type?: 'create' | 'edit' | 'delete' };
|
item: SubmissionItemData | SubmissionItemWithDeps | {
|
||||||
|
item_data?: unknown;
|
||||||
|
original_data?: unknown;
|
||||||
|
item_type: string;
|
||||||
|
action_type?: 'create' | 'edit' | 'delete'
|
||||||
|
};
|
||||||
view?: 'summary' | 'detailed';
|
view?: 'summary' | 'detailed';
|
||||||
showImages?: boolean;
|
showImages?: boolean;
|
||||||
submissionId?: string;
|
submissionId?: string;
|
||||||
@@ -102,12 +107,12 @@ export function SubmissionChangesDisplay({
|
|||||||
{changes.photoChanges.length > 0 && changes.photoChanges[0].type === 'deleted' && (
|
{changes.photoChanges.length > 0 && changes.photoChanges[0].type === 'deleted' && (
|
||||||
<PhotoDeletionPreview
|
<PhotoDeletionPreview
|
||||||
photo={{
|
photo={{
|
||||||
url: changes.photoChanges[0].photo?.url || item.item_data?.cloudflare_image_url || '',
|
url: changes.photoChanges[0].photo?.url || (item.item_data as Record<string, unknown>)?.cloudflare_image_url as string || '',
|
||||||
title: changes.photoChanges[0].photo?.title || item.item_data?.title,
|
title: changes.photoChanges[0].photo?.title || (item.item_data as Record<string, unknown>)?.title as string,
|
||||||
caption: changes.photoChanges[0].photo?.caption || item.item_data?.caption,
|
caption: changes.photoChanges[0].photo?.caption || (item.item_data as Record<string, unknown>)?.caption as string,
|
||||||
entity_type: item.item_data?.entity_type,
|
entity_type: (item.item_data as Record<string, unknown>)?.entity_type as string,
|
||||||
entity_name: changes.entityName,
|
entity_name: changes.entityName,
|
||||||
deletion_reason: changes.photoChanges[0].photo?.deletion_reason || item.item_data?.deletion_reason || item.item_data?.reason
|
deletion_reason: changes.photoChanges[0].photo?.deletion_reason || (item.item_data as Record<string, unknown>)?.deletion_reason as string || (item.item_data as Record<string, unknown>)?.reason as string
|
||||||
}}
|
}}
|
||||||
compact={true}
|
compact={true}
|
||||||
/>
|
/>
|
||||||
@@ -213,12 +218,12 @@ export function SubmissionChangesDisplay({
|
|||||||
{changes.photoChanges.length > 0 && changes.photoChanges[0].type === 'deleted' && (
|
{changes.photoChanges.length > 0 && changes.photoChanges[0].type === 'deleted' && (
|
||||||
<PhotoDeletionPreview
|
<PhotoDeletionPreview
|
||||||
photo={{
|
photo={{
|
||||||
url: changes.photoChanges[0].photo?.url || item.item_data?.cloudflare_image_url || '',
|
url: changes.photoChanges[0].photo?.url || (item.item_data as Record<string, unknown>)?.cloudflare_image_url as string || '',
|
||||||
title: changes.photoChanges[0].photo?.title || item.item_data?.title,
|
title: changes.photoChanges[0].photo?.title || (item.item_data as Record<string, unknown>)?.title as string,
|
||||||
caption: changes.photoChanges[0].photo?.caption || item.item_data?.caption,
|
caption: changes.photoChanges[0].photo?.caption || (item.item_data as Record<string, unknown>)?.caption as string,
|
||||||
entity_type: item.item_data?.entity_type,
|
entity_type: (item.item_data as Record<string, unknown>)?.entity_type as string,
|
||||||
entity_name: changes.entityName,
|
entity_name: changes.entityName,
|
||||||
deletion_reason: changes.photoChanges[0].photo?.deletion_reason || item.item_data?.deletion_reason || item.item_data?.reason
|
deletion_reason: changes.photoChanges[0].photo?.deletion_reason || (item.item_data as Record<string, unknown>)?.deletion_reason as string || (item.item_data as Record<string, unknown>)?.reason as string
|
||||||
}}
|
}}
|
||||||
compact={false}
|
compact={false}
|
||||||
/>
|
/>
|
||||||
@@ -286,11 +291,11 @@ export function SubmissionChangesDisplay({
|
|||||||
return (
|
return (
|
||||||
<div key={idx} className={wasEditedByModerator ? 'border-l-4 border-blue-500 pl-3 bg-blue-50/50 dark:bg-blue-950/30 rounded' : ''}>
|
<div key={idx} className={wasEditedByModerator ? 'border-l-4 border-blue-500 pl-3 bg-blue-50/50 dark:bg-blue-950/30 rounded' : ''}>
|
||||||
<FieldDiff change={change} />
|
<FieldDiff change={change} />
|
||||||
{wasEditedByModerator && (
|
{wasEditedByModerator ? (
|
||||||
<div className="text-xs text-blue-600 dark:text-blue-400 mt-1 font-medium">
|
<div className="text-xs text-blue-600 dark:text-blue-400 mt-1 font-medium">
|
||||||
✓ Modified by moderator
|
✓ Modified by moderator
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -330,7 +335,7 @@ export function SubmissionChangesDisplay({
|
|||||||
<h4 className="text-sm font-medium">Location</h4>
|
<h4 className="text-sm font-medium">Location</h4>
|
||||||
<LocationDiff
|
<LocationDiff
|
||||||
oldLocation={null}
|
oldLocation={null}
|
||||||
newLocation={item.item_data?.location || item.item_data?.location_id}
|
newLocation={((item.item_data as Record<string, unknown>)?.location || (item.item_data as Record<string, unknown>)?.location_id) as string | undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -393,8 +398,8 @@ export function SubmissionChangesDisplay({
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h4 className="text-sm font-medium">Location Change</h4>
|
<h4 className="text-sm font-medium">Location Change</h4>
|
||||||
<LocationDiff
|
<LocationDiff
|
||||||
oldLocation={item.original_data?.location || item.original_data?.location_id}
|
oldLocation={((item.original_data as Record<string, unknown>)?.location || (item.original_data as Record<string, unknown>)?.location_id) as string | undefined}
|
||||||
newLocation={item.item_data?.location || item.item_data?.location_id}
|
newLocation={((item.item_data as Record<string, unknown>)?.location || (item.item_data as Record<string, unknown>)?.location_id) as string | undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export function useModerationFilters(
|
|||||||
return JSON.parse(saved);
|
return JSON.parse(saved);
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Failed to load persisted filters:', error);
|
logger.warn('Failed to load persisted filters', { error, storageKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -153,7 +153,7 @@ export function useModerationFilters(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Failed to load persisted sort:', error);
|
logger.warn('Failed to load persisted sort', { error, storageKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
return initialSortConfig;
|
return initialSortConfig;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import { useState, useCallback, useEffect, useMemo } from 'react';
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
||||||
import { MODERATION_CONSTANTS } from '@/lib/moderation/constants';
|
import { MODERATION_CONSTANTS } from '@/lib/moderation/constants';
|
||||||
import * as storage from '@/lib/localStorage';
|
import * as storage from '@/lib/localStorage';
|
||||||
|
import { logger } from '@/lib/logger';
|
||||||
|
|
||||||
export interface PaginationConfig {
|
export interface PaginationConfig {
|
||||||
/** Initial page number (1-indexed) */
|
/** Initial page number (1-indexed) */
|
||||||
@@ -123,7 +124,7 @@ export function usePagination(config: PaginationConfig = {}): PaginationState {
|
|||||||
return JSON.parse(saved);
|
return JSON.parse(saved);
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Failed to load pagination state:', error);
|
logger.warn('Failed to load pagination state', { error, storageKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { authLog, authWarn, authError } from '@/lib/authLogger';
|
|||||||
import type { AALLevel, CheckAalResult } from '@/types/auth';
|
import type { AALLevel, CheckAalResult } from '@/types/auth';
|
||||||
import { getSessionAal, checkAalStepUp as checkAalStepUpService, signOutUser } from '@/lib/authService';
|
import { getSessionAal, checkAalStepUp as checkAalStepUpService, signOutUser } from '@/lib/authService';
|
||||||
import { clearAllAuthFlags } from '@/lib/sessionFlags';
|
import { clearAllAuthFlags } from '@/lib/sessionFlags';
|
||||||
|
import { logger } from '@/lib/logger';
|
||||||
|
|
||||||
interface AuthContextType {
|
interface AuthContextType {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
@@ -256,7 +257,7 @@ export const AuthProvider = AuthProviderComponent;
|
|||||||
export function useAuth() {
|
export function useAuth() {
|
||||||
const context = useContext(AuthContext);
|
const context = useContext(AuthContext);
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
console.error('[useAuth] AuthContext is undefined - component may be rendering outside AuthProvider');
|
logger.error('AuthContext is undefined - component may be rendering outside AuthProvider', { component: 'useAuth' });
|
||||||
throw new Error('useAuth must be used within an AuthProvider');
|
throw new Error('useAuth must be used within an AuthProvider');
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
|
|||||||
@@ -564,7 +564,7 @@ export async function deleteSubmission(
|
|||||||
shouldRemoveFromQueue: true,
|
shouldRemoveFromQueue: true,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting submission:', error);
|
logger.error('Error deleting submission', { error, submissionId: item.id });
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Failed to delete submission',
|
message: 'Failed to delete submission',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { supabase } from '@/integrations/supabase/client';
|
|||||||
import { requestContext, type RequestContext } from './requestContext';
|
import { requestContext, type RequestContext } from './requestContext';
|
||||||
import { breadcrumbManager } from './errorBreadcrumbs';
|
import { breadcrumbManager } from './errorBreadcrumbs';
|
||||||
import { captureEnvironmentContext } from './environmentContext';
|
import { captureEnvironmentContext } from './environmentContext';
|
||||||
|
import { logger } from './logger';
|
||||||
|
|
||||||
export interface RequestTrackingOptions {
|
export interface RequestTrackingOptions {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
@@ -54,7 +55,7 @@ export async function trackRequest<T>(
|
|||||||
parentRequestId: options.parentRequestId,
|
parentRequestId: options.parentRequestId,
|
||||||
traceId: context.traceId,
|
traceId: context.traceId,
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('[RequestTracking] Failed to log metadata:', err);
|
logger.error('Failed to log request metadata', { error: err, context: 'RequestTracking' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cleanup context
|
// Cleanup context
|
||||||
@@ -94,7 +95,7 @@ export async function trackRequest<T>(
|
|||||||
parentRequestId: options.parentRequestId,
|
parentRequestId: options.parentRequestId,
|
||||||
traceId: context.traceId,
|
traceId: context.traceId,
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('[RequestTracking] Failed to log error metadata:', err);
|
logger.error('Failed to log error metadata', { error: err, context: 'RequestTracking' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cleanup context
|
// Cleanup context
|
||||||
@@ -143,7 +144,7 @@ async function logRequestMetadata(metadata: RequestMetadata): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('[RequestTracking] Failed to log metadata:', error);
|
logger.error('Failed to log metadata to database', { error, context: 'RequestTracking' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
import { logger } from './logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitize HTML content to prevent XSS attacks
|
* Sanitize HTML content to prevent XSS attacks
|
||||||
@@ -39,14 +40,14 @@ export function sanitizeURL(url: string): string {
|
|||||||
const allowedProtocols = ['http:', 'https:', 'mailto:'];
|
const allowedProtocols = ['http:', 'https:', 'mailto:'];
|
||||||
|
|
||||||
if (!allowedProtocols.includes(parsed.protocol)) {
|
if (!allowedProtocols.includes(parsed.protocol)) {
|
||||||
console.warn(`Blocked potentially dangerous URL protocol: ${parsed.protocol}`);
|
logger.warn('Blocked potentially dangerous URL protocol', { protocol: parsed.protocol });
|
||||||
return '#';
|
return '#';
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
} catch {
|
} catch {
|
||||||
// Invalid URL format
|
// Invalid URL format
|
||||||
console.warn(`Invalid URL format: ${url}`);
|
logger.warn('Invalid URL format', { url });
|
||||||
return '#';
|
return '#';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
|
import { logger } from './logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a URL-safe slug from a name
|
* Generate a URL-safe slug from a name
|
||||||
@@ -50,7 +51,7 @@ export async function ensureUniqueSlug(
|
|||||||
const { data, error } = await query.limit(1);
|
const { data, error } = await query.limit(1);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`Error checking slug uniqueness in ${tableName}:`, error);
|
logger.error('Error checking slug uniqueness', { error, tableName });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ export async function detectChanges(
|
|||||||
if (data?.name) entityName = `${data.name} (${formatEntityType(entityType)})`;
|
if (data?.name) entityName = `${data.name} (${formatEntityType(entityType)})`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching entity name for photo operation:', err);
|
logger.error('Error fetching entity name for photo operation', { error: err, entityType: itemData.entity_type, entityId: itemData.entity_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ export async function detectChanges(
|
|||||||
entityName = `${formatEntityType(entityType)} - ${itemData.title}`;
|
entityName = `${formatEntityType(entityType)} - ${itemData.title}`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching entity name for milestone:', err);
|
logger.error('Error fetching entity name for milestone', { error: err, entityType: itemData.entity_type, entityId: itemData.entity_id });
|
||||||
// Fall back to just the title if database lookup fails
|
// Fall back to just the title if database lookup fails
|
||||||
if (itemData.title) {
|
if (itemData.title) {
|
||||||
entityName = itemData.title;
|
entityName = itemData.title;
|
||||||
@@ -434,7 +434,7 @@ export async function detectChanges(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error resolving entity name for field display:', err);
|
logger.error('Error resolving entity name for field display', { error: err, entityType: itemData.entity_type, entityId: itemData.entity_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add entity name as an explicit field change at the beginning
|
// Add entity name as an explicit field change at the beginning
|
||||||
|
|||||||
@@ -338,9 +338,7 @@ async function createVersionForApprovedItem(
|
|||||||
// - app.current_user_id = original submitter
|
// - app.current_user_id = original submitter
|
||||||
// - app.submission_id = submission ID
|
// - app.submission_id = submission ID
|
||||||
// Then the trigger creates the version automatically
|
// Then the trigger creates the version automatically
|
||||||
console.debug(
|
logger.debug('Version will be created automatically by trigger', { itemType, entityId });
|
||||||
`Version will be created automatically by trigger for ${itemType} ${entityId}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -613,7 +611,7 @@ async function createRide(data: any, dependencyMap: Map<string, string>, sortedI
|
|||||||
.eq('id', data.ride_id);
|
.eq('id', data.ride_id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error updating ride:', error);
|
logger.error('Error updating ride', { error: error.message, rideId: data.ride_id });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +642,7 @@ async function createRide(data: any, dependencyMap: Map<string, string>, sortedI
|
|||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error creating ride:', error);
|
logger.error('Error creating ride', { error: error.message, rideName: resolvedData.name });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +686,7 @@ async function createCompany(
|
|||||||
.eq('id', data.id);
|
.eq('id', data.id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error updating company:', error);
|
logger.error('Error updating company', { error: error.message, companyId: data.id });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +721,7 @@ async function createCompany(
|
|||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error creating company:', error);
|
logger.error('Error creating company', { error: error.message, companyName: resolvedData.name, companyType });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,7 +760,7 @@ async function createRideModel(data: any, dependencyMap: Map<string, string>, so
|
|||||||
.eq('id', data.ride_model_id);
|
.eq('id', data.ride_model_id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error updating ride model:', error);
|
logger.error('Error updating ride model', { error: error.message, rideModelId: data.ride_model_id });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,7 +797,7 @@ async function createRideModel(data: any, dependencyMap: Map<string, string>, so
|
|||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error creating ride model:', error);
|
logger.error('Error creating ride model', { error: error.message, modelName: resolvedData.name });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,7 +876,7 @@ async function approvePhotos(data: any, dependencyMap: Map<string, string>, user
|
|||||||
.select();
|
.select();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error inserting photos:', error);
|
logger.error('Error inserting photos', { error: error.message, photoCount: photosToInsert.length, entityType, entityId: finalEntityId });
|
||||||
throw new Error(`Database error: ${error.message}`);
|
throw new Error(`Database error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,7 +958,7 @@ async function updateEntityFeaturedImage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error updating ${entityType} featured image:`, error);
|
logger.error('Error updating entity featured image', { error, entityType, entityId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
|
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
|
||||||
|
import { logger } from './logger';
|
||||||
|
|
||||||
// Preset configurations
|
// Preset configurations
|
||||||
export const PRESETS = {
|
export const PRESETS = {
|
||||||
@@ -254,12 +255,12 @@ export async function clearTestData(): Promise<{ deleted: number }> {
|
|||||||
.neq('id', '00000000-0000-0000-0000-000000000000'); // Delete all records
|
.neq('id', '00000000-0000-0000-0000-000000000000'); // Delete all records
|
||||||
|
|
||||||
if (registryError) {
|
if (registryError) {
|
||||||
console.error('Error clearing test data registry:', registryError);
|
logger.error('Error clearing test data registry', { error: registryError });
|
||||||
}
|
}
|
||||||
|
|
||||||
return { deleted: submissionCount };
|
return { deleted: submissionCount };
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Error clearing test data:', error instanceof Error ? error.message : String(error));
|
logger.error('Error clearing test data', { error: error instanceof Error ? error.message : String(error) });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
import type { EntityType } from '@/types/versioning';
|
import type { EntityType } from '@/types/versioning';
|
||||||
import { createTableQuery } from './supabaseHelpers';
|
import { createTableQuery } from './supabaseHelpers';
|
||||||
|
import { logger } from './logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually trigger cleanup of old versions for a specific entity type
|
* Manually trigger cleanup of old versions for a specific entity type
|
||||||
@@ -37,7 +38,7 @@ export async function cleanupVersions(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Version cleanup failed:', error);
|
logger.error('Version cleanup failed', { error, entityType, keepCount });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ export async function getVersionStats(
|
|||||||
const { data, error } = result;
|
const { data, error } = result;
|
||||||
|
|
||||||
if (error || !data) {
|
if (error || !data) {
|
||||||
console.error('Failed to fetch version stats:', error);
|
logger.error('Failed to fetch version stats', { error, entityType, entityId });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,8 @@ const ERROR_PATTERNS: Record<string, string> = {
|
|||||||
* Logs full error server-side for debugging
|
* Logs full error server-side for debugging
|
||||||
*/
|
*/
|
||||||
export function sanitizeError(error: unknown, context?: string): SanitizedError {
|
export function sanitizeError(error: unknown, context?: string): SanitizedError {
|
||||||
// Log full error for debugging (server-side only)
|
// Note: Error logging handled by edge function logger
|
||||||
if (context) {
|
// Context and error are passed to edgeLogger.error() by caller
|
||||||
console.error(`[${context}] Error:`, error);
|
|
||||||
} else {
|
|
||||||
console.error('Error:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle non-Error objects
|
// Handle non-Error objects
|
||||||
if (!error || typeof error !== 'object') {
|
if (!error || typeof error !== 'object') {
|
||||||
|
|||||||
@@ -62,17 +62,13 @@ class RateLimiter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deletedCount > 0) {
|
// Cleanup runs silently unless there are issues
|
||||||
console.log(`[RateLimit] Cleaned ${deletedCount} expired entries`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[RateLimit] Cleanup failed:', error);
|
|
||||||
// Emergency: Clear oldest 30% if cleanup fails
|
// Emergency: Clear oldest 30% if cleanup fails
|
||||||
if (this.rateLimitMap.size > this.config.maxMapSize) {
|
if (this.rateLimitMap.size > this.config.maxMapSize) {
|
||||||
const toClear = Math.floor(this.rateLimitMap.size * 0.3);
|
const toClear = Math.floor(this.rateLimitMap.size * 0.3);
|
||||||
const keys = Array.from(this.rateLimitMap.keys()).slice(0, toClear);
|
const keys = Array.from(this.rateLimitMap.keys()).slice(0, toClear);
|
||||||
keys.forEach(key => this.rateLimitMap.delete(key));
|
keys.forEach(key => this.rateLimitMap.delete(key));
|
||||||
console.warn(`[RateLimit] Emergency cleared ${keys.length} entries`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user