mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:11:17 -05:00
Fix all compliance violations
This commit is contained in:
@@ -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) {
|
||||||
// Check if we're creating a new location entity
|
// Type guards for LocationData
|
||||||
const isCreatingNewLocation = oldLocation?.location_id && !oldLocation?.city && newLocation?.city;
|
const isLocationData = (loc: unknown): loc is LocationData => {
|
||||||
|
return typeof loc === 'object' && loc !== null && !Array.isArray(loc);
|
||||||
|
};
|
||||||
|
|
||||||
const formatLocation = (loc: any) => {
|
// Check if we're creating a new location entity
|
||||||
|
const isCreatingNewLocation = isLocationData(oldLocation) &&
|
||||||
|
oldLocation.location_id &&
|
||||||
|
!oldLocation.city &&
|
||||||
|
isLocationData(newLocation) &&
|
||||||
|
newLocation.city;
|
||||||
|
|
||||||
|
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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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