Files
mercadodevida/work/artifacts/F-056/implementer.md
2026-08-19 15:10:09 +02:00

87 lines
3.9 KiB
Markdown

# F-056 — Implementer evidence
## Scope delivered
Two distinct defects were addressed in this branch:
1. **Uploads 404 / 502 on freshly uploaded files.** Next.js caches the
`public/` directory listing at server start, so any file written to
`public/uploads/` after `next start` returns 404 from the static handler and
`next/image` answers 502 because its upstream fetch fails.
2. **Product detail image overflows its container** because the wrapper had
no positioning context for `next/image fill`.
## Changes
### 1. Dynamic uploads route (per app)
Three new route handlers were added — one per Next.js frontend
(`apps/admin`, `frontend`, `storefront`). Each reads uploads from the local
`public/uploads/` and the canonical `apps/admin/public/uploads/` directory on
every request, so a freshly uploaded file resolves without restart or rebuild:
- `project/apps/admin/src/app/uploads/[...path]/route.ts`
- `project/frontend/src/app/uploads/[...path]/route.ts`
- `project/storefront/src/app/uploads/[...path]/route.ts`
Each handler:
- Sets `dynamic = 'force-dynamic'` so Next.js does not cache the route at build.
- Validates the path against `SAFE_SEGMENT = /^[A-Za-z0-9._-]+$/` to block traversal.
- Maps a known extension to a content type and serves the file with
`Cache-Control: public, max-age=31536000, immutable`.
- Generates the `40` and `200` thumbnails on demand via `sharp` and caches them
on disk, so `/uploads/40/<file>` and `/uploads/200/<file>` resolve without
requiring `generate-thumbnails.sh`.
### 2. Inline thumbnail generation on upload
`project/apps/admin/src/app/api/upload/route.ts` now calls `generateThumbnails`
immediately after `mirrorToPeers`, producing the 40 and 200 px variants in every
mirror location. Failures are non-fatal: the dynamic handler above regenerates
the missing thumbnail on the next request.
### 3. Product detail image container
`project/frontend/src/app/products/[slug]/page.tsx` adds the `relative` class
to the image wrapper so `next/image fill` is positioned correctly inside the
`max-h-[500px] overflow-hidden` container. The image stays inside its box on
all viewports.
## Acceptance traceability
| Acceptance criterion | How it is met |
| -------------------- | ------------- |
| New upload file loads 200 without server restart | Dynamic `/uploads/[...path]/route.ts` reads from disk per request. Verified with curl on a freshly uploaded file. |
| Thumbs 40 and 200 exist right after upload | `generateThumbnails` runs inline in `/api/upload` after `mirrorToPeers`. |
| `next/image` no more 502 for fresh uploads | Static 404 (which became 502 upstream) is replaced by the dynamic handler that returns 200 from disk. |
| Product detail image stays inside its box | `relative` class added so `next/image fill` is contained by `aspect-square max-h-[500px] overflow-hidden`. |
## Commands run during build
- `pnpm --filter ./project/apps/admin exec tsc --noEmit` — typecheck pass.
- `pnpm --filter ./project/frontend exec tsc --noEmit` — typecheck pass.
- `pnpm --filter ./project/storefront exec tsc --noEmit` — typecheck pass.
- `./scripts/verify.sh` — exit code 0.
- Manual curl: `/uploads/40/8223b962-642c-4c95-bc2e-37c2d2fad4ac.jpg`
HTTP 200, 568 bytes.
## Files touched
```
project/apps/admin/src/app/api/upload/route.ts (modified)
project/apps/admin/src/app/uploads/[...path]/route.ts (new)
project/frontend/src/app/uploads/[...path]/route.ts (new)
project/frontend/src/app/products/[slug]/page.tsx (modified)
project/storefront/src/app/uploads/[...path]/route.ts (new)
```
## Artifacts produced
- `project/apps/admin/public/uploads/40/<file>.jpg` — generated inline by upload route.
- `project/frontend/public/uploads/40/<file>.jpg` — generated inline by upload route.
- `project/storefront/public/uploads/40/<file>.jpg` — generated inline by upload route.
## Status
Build completed. Ready for reviewer/security/qa gates.