/** * Migration script to convert existing JPEG/PNG images to WebP * Run from the k8s cluster directly */ const { Pool } = require('pg'); const sharp = require('sharp'); const pool = new Pool({ host: 'postgres.lahuasca', port: 5432, database: 'lahuasca', user: 'lahuasca', password: process.env.DB_PASSWORD || 'lahuasca123', }); async function convertToWebP(base64Data) { const matches = base64Data.match(/^data:image\/(\w+);base64,(.+)$/); if (!matches) { return base64Data; } const buffer = Buffer.from(matches[2], 'base64'); const webpBuffer = await sharp(buffer) .webp({ quality: 90 }) .toBuffer(); return `data:image/webp;base64,${webpBuffer.toString('base64')}`; } async function main() { console.log('Starting image migration to WebP...\n'); const client = await pool.connect(); try { const { rows: images } = await client.query( 'SELECT id, filename, data, thumbnail, medium FROM images' ); console.log(`Found ${images.length} images to process\n`); let converted = 0; let skipped = 0; let errors = 0; let totalSaved = 0; for (const img of images) { console.log(`Processing image ${img.id}: ${img.filename}`); try { let needsUpdate = false; const updates = {}; if (img.data && !img.data.includes('image/webp')) { console.log(' Converting full image...'); updates.data = await convertToWebP(img.data); needsUpdate = true; } if (img.thumbnail && !img.thumbnail.includes('image/webp')) { console.log(' Converting thumbnail...'); updates.thumbnail = await convertToWebP(img.thumbnail); needsUpdate = true; } if (img.medium && !img.medium.includes('image/webp')) { console.log(' Converting medium...'); updates.medium = await convertToWebP(img.medium); needsUpdate = true; } if (needsUpdate) { const oldSize = (img.data?.length || 0) + (img.thumbnail?.length || 0) + (img.medium?.length || 0); const newSize = (updates.data?.length || img.data?.length || 0) + (updates.thumbnail?.length || img.thumbnail?.length || 0) + (updates.medium?.length || img.medium?.length || 0); const savedKB = Math.round((oldSize - newSize) / 1024); totalSaved += savedKB; console.log(` Saved: ${savedKB} KB`); await client.query( 'UPDATE images SET data = $1, thumbnail = $2, medium = $3 WHERE id = $4', [updates.data || img.data, updates.thumbnail || img.thumbnail, updates.medium || img.medium, img.id] ); converted++; } else { console.log(' Already WebP, skipping'); skipped++; } } catch (err) { console.error(` Error: ${err.message}`); errors++; } } console.log('\n========================================'); console.log(`Converted: ${converted}`); console.log(`Skipped: ${skipped}`); console.log(`Errors: ${errors}`); console.log(`Total saved: ${Math.round(totalSaved / 1024)} MB`); console.log('========================================'); } finally { client.release(); await pool.end(); } } main().catch(console.error);