mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 01:51:12 -05:00
Approve database migration
This commit is contained in:
@@ -134,6 +134,9 @@ export interface CompanyFormData {
|
||||
card_image_id?: string;
|
||||
}
|
||||
|
||||
// Import timeline types
|
||||
import type { TimelineEventFormData, TimelineSubmissionData, EntityType } from '@/types/timeline';
|
||||
|
||||
/**
|
||||
* ⚠️ CRITICAL SECURITY PATTERN ⚠️
|
||||
*
|
||||
@@ -896,3 +899,185 @@ export async function submitPropertyOwnerUpdate(
|
||||
|
||||
return { submitted: true, submissionId: submissionData.id };
|
||||
}
|
||||
|
||||
/**
|
||||
* ⚠️ CRITICAL SECURITY PATTERN ⚠️
|
||||
*
|
||||
* Submits a new timeline event for an entity through the moderation queue.
|
||||
*
|
||||
* DO NOT write directly to entity_timeline_events:
|
||||
* ❌ await supabase.from('entity_timeline_events').insert(data) // BYPASSES MODERATION!
|
||||
* ✅ await submitTimelineEvent(entityType, entityId, data, userId) // CORRECT
|
||||
*
|
||||
* Flow: User Submit → Moderation Queue → Approval → Database Write
|
||||
*
|
||||
* @param entityType - Type of entity (park, ride, company)
|
||||
* @param entityId - ID of the entity
|
||||
* @param data - The timeline event form data
|
||||
* @param userId - The ID of the user submitting
|
||||
* @returns Object containing submitted boolean and submissionId
|
||||
*/
|
||||
export async function submitTimelineEvent(
|
||||
entityType: EntityType,
|
||||
entityId: string,
|
||||
data: TimelineEventFormData,
|
||||
userId: string
|
||||
): Promise<{ submitted: boolean; submissionId: string }> {
|
||||
// Validate user
|
||||
if (!userId) {
|
||||
throw new Error('User ID is required for timeline event submission');
|
||||
}
|
||||
|
||||
// Create submission content (minimal reference data only)
|
||||
const content: Json = {
|
||||
action: 'create',
|
||||
entity_type: entityType,
|
||||
entity_id: entityId,
|
||||
};
|
||||
|
||||
// Create the main submission record
|
||||
const { data: submission, error: submissionError } = await supabase
|
||||
.from('content_submissions')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
submission_type: 'timeline_event',
|
||||
content,
|
||||
status: 'pending',
|
||||
approval_mode: 'full',
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (submissionError || !submission) {
|
||||
console.error('Failed to create timeline event submission:', submissionError);
|
||||
throw new Error('Failed to submit timeline event for review');
|
||||
}
|
||||
|
||||
// Create submission item with actual data
|
||||
const itemData: Record<string, any> = {
|
||||
entity_type: entityType,
|
||||
entity_id: entityId,
|
||||
event_type: data.event_type,
|
||||
event_date: data.event_date.toISOString().split('T')[0],
|
||||
event_date_precision: data.event_date_precision,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
from_value: data.from_value,
|
||||
to_value: data.to_value,
|
||||
from_entity_id: data.from_entity_id,
|
||||
to_entity_id: data.to_entity_id,
|
||||
from_location_id: data.from_location_id,
|
||||
to_location_id: data.to_location_id,
|
||||
is_public: data.is_public ?? true,
|
||||
};
|
||||
|
||||
const { error: itemError } = await supabase
|
||||
.from('submission_items')
|
||||
.insert({
|
||||
submission_id: submission.id,
|
||||
item_type: 'timeline_event',
|
||||
action_type: 'create',
|
||||
item_data: itemData as unknown as Json,
|
||||
status: 'pending',
|
||||
order_index: 0,
|
||||
});
|
||||
|
||||
if (itemError) {
|
||||
console.error('Failed to create timeline event item:', itemError);
|
||||
throw new Error('Failed to submit timeline event item for review');
|
||||
}
|
||||
|
||||
return {
|
||||
submitted: true,
|
||||
submissionId: submission.id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ⚠️ CRITICAL SECURITY PATTERN ⚠️
|
||||
*
|
||||
* Submits an update to an existing timeline event through the moderation queue.
|
||||
*
|
||||
* @param eventId - ID of the existing timeline event
|
||||
* @param data - The updated timeline event data
|
||||
* @param userId - The ID of the user submitting the update
|
||||
* @returns Object containing submitted boolean and submissionId
|
||||
*/
|
||||
export async function submitTimelineEventUpdate(
|
||||
eventId: string,
|
||||
data: TimelineEventFormData,
|
||||
userId: string
|
||||
): Promise<{ submitted: boolean; submissionId: string }> {
|
||||
// Fetch original event
|
||||
const { data: originalEvent, error: fetchError } = await supabase
|
||||
.from('entity_timeline_events')
|
||||
.select('*')
|
||||
.eq('id', eventId)
|
||||
.single();
|
||||
|
||||
if (fetchError || !originalEvent) {
|
||||
throw new Error('Failed to fetch original timeline event');
|
||||
}
|
||||
|
||||
// Create submission
|
||||
const content: Json = {
|
||||
action: 'edit',
|
||||
event_id: eventId,
|
||||
entity_type: originalEvent.entity_type,
|
||||
};
|
||||
|
||||
const { data: submission, error: submissionError } = await supabase
|
||||
.from('content_submissions')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
submission_type: 'timeline_event',
|
||||
content,
|
||||
status: 'pending',
|
||||
approval_mode: 'full',
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (submissionError || !submission) {
|
||||
throw new Error('Failed to create timeline event update submission');
|
||||
}
|
||||
|
||||
// Create submission item
|
||||
const itemData: Record<string, any> = {
|
||||
entity_type: originalEvent.entity_type,
|
||||
entity_id: originalEvent.entity_id,
|
||||
event_type: data.event_type,
|
||||
event_date: data.event_date.toISOString().split('T')[0],
|
||||
event_date_precision: data.event_date_precision,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
from_value: data.from_value,
|
||||
to_value: data.to_value,
|
||||
from_entity_id: data.from_entity_id,
|
||||
to_entity_id: data.to_entity_id,
|
||||
from_location_id: data.from_location_id,
|
||||
to_location_id: data.to_location_id,
|
||||
is_public: data.is_public ?? true,
|
||||
};
|
||||
|
||||
const { error: itemError } = await supabase
|
||||
.from('submission_items')
|
||||
.insert({
|
||||
submission_id: submission.id,
|
||||
item_type: 'timeline_event',
|
||||
action_type: 'edit',
|
||||
item_data: itemData as unknown as Json,
|
||||
original_data: originalEvent as unknown as Json,
|
||||
status: 'pending',
|
||||
order_index: 0,
|
||||
});
|
||||
|
||||
if (itemError) {
|
||||
throw new Error('Failed to submit timeline event update item');
|
||||
}
|
||||
|
||||
return {
|
||||
submitted: true,
|
||||
submissionId: submission.id,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user