82 lines
2.8 KiB
JavaScript
Executable File
82 lines
2.8 KiB
JavaScript
Executable File
#!/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/<size>/ 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 <app>/public/uploads/<size>/<filename> 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);
|
|
}); |