Files
mercadodevida/work/artifacts/F-055/implementer.md
2026-08-19 13:48:32 +02:00

4.1 KiB

F-055 — Implementer Report

Feature

Image thumbnails: server-rendered cache + CSS max-size

Approach

Originally planned on-demand resize via route handler (?w=40 / ?w=200). Discovered that Next.js 16 prioritizes static files in public/ over the app/uploads/[filename]/route.ts handler, making the query-param approach unworkable. Switched to pre-generating thumbnails to disk under public/uploads/<width>/ and using static URLs (/uploads/40/foo.jpg, /uploads/200/foo.jpg) which Next.js serves as plain immutable assets.

Changes Made

1. Thumbnail pre-generation script

New files:

  • project/scripts/generate-thumbnails.sh — wrapper that locates sharp
  • project/scripts/generate-thumbnails.mjs — node script using sharp

The script reads source images from apps/admin/public/uploads/ and generates 40px and 200px thumbnails in each app's public/uploads/40/ and public/uploads/200/ directories. Existing thumbnails are skipped, so re-runs are idempotent and fast.

2. Sync uploads mirror thumbnails too

File: project/scripts/monolith.sh

sync_uploads() now mirrors find -maxdepth 2 (covers <app>/public/uploads/<size>/<filename>) to all three apps and calls generate-thumbnails.sh --quiet after syncing.

3. CSS max-height on product detail pages

Files:

  • project/storefront/src/app/productos/[slug]/page.tsxmax-h-[600px]
  • project/frontend/src/app/products/[slug]/page.tsxmax-h-[500px] to image container

4. UI uses static thumbnail URLs

Files:

  • project/apps/admin/src/features/products/components/sections/ImagesSection.tsximg.url.replace('/uploads/', '/uploads/200/')
  • project/apps/admin/src/app/(dashboard)/products/page.tsxp.imageUrl.replace('/uploads/', '/uploads/40/')

5. Old dynamic route handlers removed

Removed: app/uploads/[filename]/route.ts in admin, frontend, and storefront.

Reason: Next.js 16 prioritizes static files in public/ over route handlers, so the ?w= approach silently served the original full-size image regardless of query params. Static URLs /uploads/40/... avoid the collision.

Tradeoff: Newly uploaded images (after next start) won't be served immediately because Next.js caches the public/ listing at build/start. This was already the case before this change; thumbnails don't make it worse.

6. Dependencies

sharp was installed in:

  • project/apps/admin/package.json
  • project/frontend/package.json
  • project/storefront/package.json

Verification

# Generate thumbnails for existing images
./project/scripts/generate-thumbnails.sh

# Original (admin)
curl -sI "http://localhost:3004/uploads/fa7a31db...jpg" | grep -i length
# Content-Length: 129711  (600x800)

# 40px thumbnail
curl -sI "http://localhost:3004/uploads/40/fa7a31db...jpg" | grep -i length
# Content-Length: 1155  (40x53)

# 200px thumbnail
curl -sI "http://localhost:3004/uploads/200/fa7a31db...jpg" | grep -i length
# Content-Length: 12164  (200x267)

# Same in frontend (3003) and storefront (3005): identical sizes

Files Changed

  • project/scripts/monolith.sh — sync uploads mirrors thumbnails
  • project/scripts/generate-thumbnails.sh — new
  • project/scripts/generate-thumbnails.mjs — new
  • project/storefront/src/app/productos/[slug]/page.tsx — max-h-[600px]
  • project/frontend/src/app/products/[slug]/page.tsx — max-h-[500px]
  • project/apps/admin/src/app/(dashboard)/products/page.tsx — /uploads/40/
  • project/apps/admin/src/features/products/components/sections/ImagesSection.tsx — /uploads/200/
  • project/frontend/src/app/uploads/[filename]/route.ts — DELETED
  • project/storefront/src/app/uploads/[filename]/route.ts — DELETED
  • project/apps/admin/src/app/uploads/[filename]/route.ts — DELETED

Note on Subsequent Uploads

After uploading a new image through admin:

  1. Image is mirrored to frontend/storefront by api/upload/route.ts
  2. Run ./project/scripts/generate-thumbnails.sh to create the thumbnails
  3. Restart the affected app if you want the new image served immediately (otherwise it appears after the next build/start)