105 lines
4.1 KiB
Markdown
105 lines
4.1 KiB
Markdown
# 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.tsx` → `max-h-[600px]`
|
|
- `project/frontend/src/app/products/[slug]/page.tsx` → `max-h-[500px]` to image container
|
|
|
|
### 4. UI uses static thumbnail URLs
|
|
|
|
**Files:**
|
|
- `project/apps/admin/src/features/products/components/sections/ImagesSection.tsx`
|
|
→ `img.url.replace('/uploads/', '/uploads/200/')`
|
|
- `project/apps/admin/src/app/(dashboard)/products/page.tsx`
|
|
→ `p.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
|
|
|
|
```bash
|
|
# 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) |