115 lines
3.4 KiB
JavaScript
115 lines
3.4 KiB
JavaScript
/**
|
|
* 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: (() => {
|
|
const p = process.env.DB_PASSWORD;
|
|
if (!p) throw new Error('DB_PASSWORD env var is required');
|
|
return p;
|
|
})(),
|
|
});
|
|
|
|
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); |