Files
shop-server/.opencode/plans/2026-05-15-image-processing-refactor-design.md
T
2026-05-19 11:25:23 +05:00

96 lines
3.8 KiB
Markdown

# 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/<subdir>/`
- 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: "Файл «<name>» слишком большой (максимум 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/<uuid>.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 "Файл «<name>» слишком большой (максимум 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