UNPKG

supa-seed

Version:

A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support

202 lines 7.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MediaSeeder = void 0; const types_1 = require("../../../core/types/types"); const image_utils_1 = require("../../../core/utils/image-utils"); class MediaSeeder extends types_1.SeedModule { constructor() { super(...arguments); this.bucketName = 'setup-images'; } async seed() { const setups = this.context.cache.get('setups'); if (!setups?.length) { console.log('⚠️ No setups found in cache, skipping media seeding'); return; } for (const setup of setups) { await this.seedSetupImages(setup); } } async seedSetupImages(setup) { const { config } = this.context; const imageCount = config.imagesPerSetup; for (let i = 0; i < imageCount; i++) { try { const image = await this.generateSetupImage(setup, i); const mediaAttachment = await this.uploadAndSaveImage(setup, image, i); if (mediaAttachment) { this.context.stats.imagesUploaded++; } } catch (error) { console.error(`Failed to create image ${i + 1} for setup ${setup.id}:`, error); } } } async generateSetupImage(setup, index) { const { faker, config } = this.context; if (config.enableRealImages) { return await this.generateRealisticImage(setup, index); } else { return await this.generatePlaceholderImage(setup, index); } } async generateRealisticImage(setup, index) { const { faker } = this.context; // Define search terms based on setup category and type const searchTerms = this.getImageSearchTerms(setup); const searchTerm = faker.helpers.arrayElement(searchTerms); // Try to download from Unsplash (requires API key) if (process.env.UNSPLASH_ACCESS_KEY) { try { const unsplashImage = await (0, image_utils_1.downloadUnsplashImage)(searchTerm, { width: 1200, height: 800, category: this.mapCategoryToImageCategory(setup.category), }); if (unsplashImage) { return unsplashImage; } } catch (error) { console.log(`Failed to download Unsplash image: ${error}`); // Fall back to placeholder } } // Generate AI placeholder with category-specific styling return await this.generatePlaceholderImage(setup, index); } async generatePlaceholderImage(setup, index) { const { faker } = this.context; const options = { width: 1200, height: 800, category: this.mapCategoryToImageCategory(setup.category), style: 'realistic', }; // Generate contextual placeholder return await (0, image_utils_1.generatePlaceholderImage)(options); } async uploadAndSaveImage(setup, image, index) { const { client } = this.context; // Generate storage path const storagePath = `${setup.id}/${image.filename}`; try { // Upload to Supabase Storage const { data: uploadData, error: uploadError } = await client.storage .from(this.bucketName) .upload(storagePath, image.buffer, { contentType: image.mimetype, cacheControl: '3600', upsert: false, }); if (uploadError) { throw new Error(`Storage upload failed: ${uploadError.message}`); } // Get public URL const { data: urlData } = client.storage .from(this.bucketName) .getPublicUrl(storagePath); if (!urlData?.publicUrl) { throw new Error('Failed to get public URL for uploaded image'); } // Save media attachment record const { data: mediaRecord, error: dbError } = await client .from('media_attachments') .insert({ setup_id: setup.id, file_name: image.filename, file_size: image.size, file_type: image.mimetype, storage_path: storagePath, public_url: urlData.publicUrl, alt_text: this.generateAltText(setup, index), created_by: setup.userId, updated_by: setup.userId, }) .select() .single(); if (dbError) { // Clean up uploaded file on database error await client.storage .from(this.bucketName) .remove([storagePath]); throw new Error(`Database insert failed: ${dbError.message}`); } return mediaRecord; } catch (error) { console.error(`Failed to upload and save image:`, error); return null; } } getImageSearchTerms(setup) { const { category } = setup; const termsByCategory = { 'overlanding': [ 'overlanding truck camping', '4x4 camping setup', 'truck bed camping', 'off-road camping gear', 'overland vehicle setup', 'camping truck interior', ], 'van-life': [ 'van life interior', 'camper van setup', 'van conversion', 'mobile home interior', 'van camping setup', 'nomad van life', ], 'car-camping': [ 'car camping setup', 'tent camping car', 'camping gear organization', 'camp site setup', 'outdoor camping equipment', ], 'backpacking': [ 'backpacking gear', 'ultralight backpacking', 'hiking backpack setup', 'trail camping gear', 'wilderness backpacking', ], 'ultralight': [ 'ultralight hiking gear', 'lightweight backpacking', 'minimalist camping', 'ultralight tent setup', 'gram weenie gear', ], }; return termsByCategory[category] || [ 'outdoor camping gear', 'camping equipment', 'outdoor adventure', ]; } mapCategoryToImageCategory(category) { if (category.includes('van') || category.includes('overlanding')) { return 'vehicle'; } if (category.includes('gear') || category.includes('ultralight')) { return 'gear'; } return 'outdoor'; } generateAltText(setup, index) { const { faker } = this.context; const templates = [ `${setup.title} setup - Image ${index + 1}`, `${setup.category} gear setup showing ${faker.helpers.arrayElement(['camping equipment', 'outdoor gear', 'adventure setup'])}`, `Detailed view of ${setup.title} ${faker.helpers.arrayElement(['configuration', 'setup', 'layout'])}`, ]; return faker.helpers.arrayElement(templates); } } exports.MediaSeeder = MediaSeeder; //# sourceMappingURL=media-seeder.js.map