# Implementer — F-052 (iteración 2)

## Cambios (completos)

### Código
- `apps/admin/src/app/api/upload/route.ts`: tras `writeFile`, copia a `frontend/public/uploads/` y `storefront/public/uploads/` con `Promise.allSettled`. La copia no bloquea el upload principal.
- `project/scripts/monolith.sh`: nueva función `sync_uploads()` que se invoca al final de `build_prod()` y antes de `spawn_service` en `start_all()` para cubrir el caso "arranque en frío sin uploads aún".
- **`frontend/src/app/uploads/[filename]/route.ts`** y **`storefront/src/app/uploads/[filename]/route.ts`**: handlers dinámicos (idénticos al del admin) que leen cada petición de `public/uploads/<filename>` directamente del disco. Imprescindible: Next.js sirve los estáticos de `public/` cacheados al build time, por lo que cualquier archivo subido después devolvería 404 sin esta ruta.

### Assets
- `frontend/public/uploads/` y `storefront/public/uploads/` creados.
- Archivos sincronizados manualmente para evitar esperar al próximo restart.

### Servicios
- `next build` re-ejecutado para admin, frontend y storefront.
- Servicios reiniciados.

## Verificación end-to-end

### Imágenes existentes (en build)
```
GET 3004 /uploads/fa7a31db-...jpg → 200 image/jpeg 129711
GET 3003 /uploads/fa7a31db-...jpg → 200 image/jpeg 129711
GET 3005 /uploads/fa7a31db-...jpg → 200 image/jpeg 129711
```

### Imagen subida en caliente (sin rebuild)
```
POST 3004 /api/upload (admin) → 201 {url: /uploads/a7e3aa30-...jpg}
GET 3004 /uploads/a7e3aa30-...jpg → 200 image/jpeg
GET 3003 /uploads/a7e3aa30-...jpg → 200 image/jpeg
GET 3005 /uploads/a7e3aa30-...jpg → 200 image/jpeg
```

### Favicons siguen 200
```
GET 3004 /favicon.ico → 200
GET 3005 /favicon.ico → 200
```

## Por qué fue necesaria la 2ª iteración
El `mirrorToPeers` del admin copia el archivo al disco de frontend/storefront, pero los servicios en producción cachean el listado de `public/` al build. Sin una ruta dinámica `/uploads/[filename]/`, los archivos nuevos solo se sirven si existían al hacer `next build`. La ruta dinámica los lee frescos del disco en cada request, igual que hace el admin desde F-050.
