# Spec: Image Processing Refactor ## Context Current image handling uses on-demand resize via `/uploads-resized/` route. Admin uploads save originals as-is (jpg/png/webp), and resize happens on first request. User uploads (reviews, 2MB limit) also use on-demand resize. ## Goals 1. **User images (reviews, ≤2MB):** Improve size error messages to be user-friendly 2. **Admin images (products, ≤20MB):** Eager processing at upload time - Generate all resize widths (320, 640, 1024, 1600) in AVIF + WebP - Convert original to WebP (delete source file) - Full-screen viewer shows original in WebP (no width limit) - Thumbnails use resized versions from cache ## Architecture ### Server Changes #### 1. `server/src/lib/upload-images.js` - Add `eager` parameter to `persistMultipartImages` - When `eager: true`, after saving each file: 1. Call `generateAllSizes(uuid, subdir, fullPath)` — generates all sizes from original 2. Call `convertOriginalToWebp(uuid, subdir)` — converts original to WebP, deletes source 3. Update URL to use `.webp` extension (replace original extension) #### 2. `server/src/lib/image-resize.js` - Add `generateAllSizes(uuid, subdir, originalPath)`: - For each width in [320, 640, 1024, 1600]: - Generate AVIF and WebP in `.cache//` - Uses original file path (before conversion to WebP) - Add `convertOriginalToWebp(uuid, subdir)`: - Find original file (jpg/png) - Convert to WebP (quality 80) at same location with `.webp` extension - Delete original jpg/png file - Return new `.webp` path #### 3. `server/src/routes/api/admin-products.js` - Pass `eager: true` to `persistMultipartImages` #### 4. `server/src/routes/api/public-reviews.js` - Improve error message for file too large (413) ### Client Changes #### 1. `client/src/entities/product/api/product-api.ts` - Add pre-upload size check for review images - Clear error message: "Файл «» слишком большой (максимум 2 МБ)" #### 2. `client/src/shared/ui/OptimizedImage.tsx` - Update `buildSrcSet` to use cached AVIF/WebP directly - Full-screen viewer: use original `.webp` URL (no `?w=`) - Remove fallback to original format for upload URLs #### 3. `client/src/features/product-review/ui/ReviewDialog.tsx` - Show user-friendly error message for oversized files ## Data Flow ### Admin Upload (Eager) 1. Client sends FormData to `POST /api/admin/uploads` 2. Server saves original (e.g., `uuid.jpg`) 3. Server generates all sizes in `.cache/` from original 4. Server converts original to WebP (`uuid.webp`), deletes `uuid.jpg` 5. Returns URLs with `.webp` extension (e.g., `/uploads/.webp`) 6. Client displays using OptimizedImage with srcset from cache ### User Upload (Reviews) 1. Client validates file size ≤2MB before upload 2. Server validates and saves original 3. On-demand resize still works (existing flow) 4. Clear error messages at both client and server ## Error Handling ### User Upload Size Error - **Client:** Pre-upload check with message "Файл «» слишком большой (максимум 2 МБ)" - **Server:** 413 with "Файл слишком большой (максимум 2 МБ)" ### Admin Upload Processing Error - If sharp fails: return 500 with "Ошибка обработки изображения" - If file not found after save: return 500 with "Внутренняя ошибка сервера" ## Testing ### Server Tests - Test `generateAllSizes` creates all width+format combinations - Test `convertOriginalToWebp` converts and deletes original - Test `persistMultipartImages` with `eager: true` - Test error messages for oversized files ### Client Tests - Test pre-upload size validation for reviews - Test OptimizedImage srcset generation for WebP originals - Test error message display in ReviewDialog