#!/usr/bin/env node // Generate cached thumbnails (40px and 200px) for every uploaded product image. // Pre-generates thumbnails in each app's public/uploads/thumbs// directory // so Next.js serves them as static files. import { readdir, stat, mkdir, writeFile, readFile } from 'node:fs/promises'; import { dirname, extname, join } from 'node:path'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const sharp = require(process.env.SHARP_PATH || '/Users/chattie/git/mercadodevida/project/apps/admin/node_modules/sharp'); const PROJECT_DIR = join(import.meta.dirname, '..'); const SIZES = [40, 200]; const APP_DIRS = [ join(PROJECT_DIR, 'apps', 'admin'), join(PROJECT_DIR, 'frontend'), join(PROJECT_DIR, 'storefront'), ]; const SRC_DIR = join(PROJECT_DIR, 'apps', 'admin', 'public', 'uploads'); const VALID_EXTS = new Set(['.jpg', '.jpeg', '.png', '.webp', '.avif', '.gif']); async function main() { let files; try { files = await readdir(SRC_DIR); } catch (err) { if (err.code === 'ENOENT') { console.log('[INFO] No uploads dir yet, nothing to do'); return; } throw err; } const images = files.filter((f) => VALID_EXTS.has(extname(f).toLowerCase())); console.log(`[INFO] Found ${images.length} source images`); let generated = 0; for (const filename of images) { const src = join(SRC_DIR, filename); const srcStat = await stat(src); if (!srcStat.isFile()) continue; for (const appDir of APP_DIRS) { // Ensure peer upload exists so the thumbnail is reachable const peerPath = join(appDir, 'public', 'uploads', filename); try { await stat(peerPath); } catch { // Copy from source if missing (mirror admin's uploads) await mkdir(dirname(peerPath), { recursive: true }); await writeFile(peerPath, await import('node:fs/promises').then((fs) => fs.readFile(src))); } for (const size of SIZES) { // Store as /public/uploads// so Next.js serves // them as plain static files via URLs like /uploads/40/foo.jpg const thumbDir = join(appDir, 'public', 'uploads', String(size)); const thumbPath = join(thumbDir, filename); // Skip if already cached try { const s = await stat(thumbPath); if (s.isFile() && s.size > 0) continue; } catch {} await mkdir(thumbDir, { recursive: true }); await sharp(src) .resize({ width: size, withoutEnlargement: true }) .jpeg({ quality: 80 }) .toFile(thumbPath); generated += 1; } } } console.log(`[OK] Processed ${images.length} images, generated ${generated} new thumbnails`); } main().catch((err) => { console.error('[FAIL]', err.message); process.exit(1); });