Files
thrilltrack-explorer/supabase/functions/seed-test-data/index.ts
2025-10-06 18:48:24 +00:00

284 lines
9.1 KiB
TypeScript

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
interface SeedOptions {
preset: 'small' | 'medium' | 'large' | 'stress';
entityTypes: string[];
includeDependencies: boolean;
includeConflicts: boolean;
includeVersionChains: boolean;
includeEscalated: boolean;
includeExpiredLocks: boolean;
}
interface SeedPlan {
parks: number;
rides: number;
companies: number;
rideModels: number;
}
const PRESETS: Record<string, SeedPlan> = {
small: { parks: 5, rides: 10, companies: 3, rideModels: 2 },
medium: { parks: 20, rides: 50, companies: 20, rideModels: 10 },
large: { parks: 100, rides: 250, companies: 100, rideModels: 50 },
stress: { parks: 400, rides: 1000, companies: 400, rideModels: 200 }
};
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseServiceKey);
// Get auth header
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response(JSON.stringify({ error: 'No authorization header' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
// Verify user is moderator
const token = authHeader.replace('Bearer ', '');
const { data: { user }, error: userError } = await supabase.auth.getUser(token);
if (userError || !user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
const { data: isMod } = await supabase.rpc('is_moderator', { _user_id: user.id });
if (!isMod) {
return new Response(JSON.stringify({ error: 'Must be moderator' }), {
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
// Parse request
const { preset = 'small', entityTypes = [], includeDependencies = true, includeEscalated = false, includeExpiredLocks = false }: SeedOptions = await req.json();
const plan = PRESETS[preset];
if (!plan) {
return new Response(JSON.stringify({ error: 'Invalid preset' }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
const startTime = Date.now();
const summary = { parks: 0, rides: 0, companies: 0, rideModels: 0 };
const createdParks: string[] = [];
const createdCompanies: Record<string, string[]> = { manufacturer: [], operator: [], designer: [], property_owner: [] };
// Helper to create submission
async function createSubmission(userId: string, type: string, itemData: any, options: { escalated?: boolean; expiredLock?: boolean } = {}) {
const submissionId = crypto.randomUUID();
const itemId = crypto.randomUUID();
const contentData = {
action: 'create',
metadata: {
is_test_data: true,
generated_at: new Date().toISOString(),
generator_version: '1.0.0',
preset
}
};
// Create content_submission
const submissionData: any = {
id: submissionId,
user_id: userId,
submission_type: type,
status: 'pending',
content: contentData,
submitted_at: new Date().toISOString(),
priority: options.escalated ? 10 : Math.floor(Math.random() * 5) + 1
};
if (options.escalated) {
submissionData.escalated = true;
submissionData.escalation_reason = 'Test escalation';
}
if (options.expiredLock) {
submissionData.assigned_to = user.id;
submissionData.locked_until = new Date(Date.now() - 1000 * 60 * 30).toISOString(); // 30 min ago
}
const { error: subError } = await supabase
.from('content_submissions')
.insert(submissionData);
if (subError) throw subError;
// Create submission_item
const { error: itemError } = await supabase
.from('submission_items')
.insert({
id: itemId,
submission_id: submissionId,
item_type: type,
item_data: itemData,
status: 'pending',
order_index: 0
});
if (itemError) throw itemError;
// Create type-specific submission record
const typeTableMap: Record<string, string> = {
park: 'park_submissions',
ride: 'ride_submissions',
manufacturer: 'company_submissions',
operator: 'company_submissions',
designer: 'company_submissions',
property_owner: 'company_submissions',
ride_model: 'ride_model_submissions'
};
const table = typeTableMap[type];
if (table) {
const typeData = { ...itemData, submission_id: submissionId };
if (table === 'company_submissions') {
typeData.company_type = type;
}
const { error: typeError } = await supabase
.from(table)
.insert(typeData);
if (typeError) throw typeError;
}
return submissionId;
}
// Create parks
if (entityTypes.includes('parks')) {
for (let i = 0; i < plan.parks; i++) {
const parkData = {
name: `Test Park ${i + 1}`,
slug: `test-park-${i + 1}`,
description: 'Test park description',
park_type: ['theme_park', 'amusement_park', 'water_park'][Math.floor(Math.random() * 3)],
status: 'operating',
opening_date: '2000-01-01'
};
const options = {
escalated: includeEscalated && Math.random() < 0.1,
expiredLock: includeExpiredLocks && Math.random() < 0.1
};
await createSubmission(user.id, 'park', parkData, options);
createdParks.push(`test-park-${i + 1}`);
summary.parks++;
}
}
// Create companies
const companyTypes = ['manufacturer', 'operator', 'designer', 'property_owner'];
for (const compType of companyTypes) {
if (entityTypes.includes(compType)) {
const count = Math.floor(plan.companies / 4);
for (let i = 0; i < count; i++) {
const companyData = {
name: `Test ${compType} ${i + 1}`,
slug: `test-${compType}-${i + 1}`,
description: `Test ${compType} description`,
company_type: compType,
person_type: 'company',
founded_year: 2000
};
await createSubmission(user.id, compType, companyData);
createdCompanies[compType].push(`test-${compType}-${i + 1}`);
summary.companies++;
}
}
}
// Create rides (with dependencies if enabled)
if (entityTypes.includes('rides') && includeDependencies && createdParks.length > 0) {
for (let i = 0; i < plan.rides; i++) {
// Get random park ID from database
const parkSlug = createdParks[Math.floor(Math.random() * createdParks.length)];
const { data: parkData } = await supabase
.from('parks')
.select('id')
.eq('slug', parkSlug)
.maybeSingle();
const rideData = {
name: `Test Ride ${i + 1}`,
slug: `test-ride-${i + 1}`,
description: 'Test ride description',
category: ['roller_coaster', 'flat_ride', 'water_ride'][Math.floor(Math.random() * 3)],
status: 'operating',
park_id: parkData?.id || null,
opening_date: '2010-01-01'
};
await createSubmission(user.id, 'ride', rideData);
summary.rides++;
}
}
// Create ride models
if (entityTypes.includes('ride_models') && includeDependencies && createdCompanies.manufacturer.length > 0) {
for (let i = 0; i < plan.rideModels; i++) {
const mfgSlug = createdCompanies.manufacturer[Math.floor(Math.random() * createdCompanies.manufacturer.length)];
const { data: mfgData } = await supabase
.from('companies')
.select('id')
.eq('slug', mfgSlug)
.maybeSingle();
const modelData = {
name: `Test Model ${i + 1}`,
slug: `test-model-${i + 1}`,
manufacturer_id: mfgData?.id || null,
category: 'roller_coaster',
ride_type: 'steel',
description: 'Test ride model'
};
await createSubmission(user.id, 'ride_model', modelData);
summary.rideModels++;
}
}
const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
return new Response(
JSON.stringify({
success: true,
summary,
time: elapsedTime
}),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error('Seed error:', error);
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
});