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
JavaScript
;
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