# Media & Image Migration Guide ## Current Setup ### WordPress/WooCommerce (Current) - **Storage:** MinIO - **Bucket:** `manoon-media` - **Plugin:** Advanced Media Offloader (ADVMO) - **Endpoint:** `http://minio:9000` - **Public URL:** `https://minio-api.nodecrew.me/manoon-media/` ### Saleor (New) - **Storage:** MinIO (same instance) - **Bucket:** `saleor` - **Endpoint:** `http://minio.manoonoils:9000` - **Media URL:** `/media/` (served via Saleor API) - **PVC:** `saleor-media-pvc` (5GB local cache) ## Architecture ``` ┌─────────────────┐ ┌─────────────────┐ │ WordPress │ │ Saleor │ │ │ │ │ │ WooCommerce │ │ API/Dashboard│ │ │ │ │ └────────┬────────┘ └────────┬────────┘ │ │ │ ADVMO Plugin │ django-storages │ (S3-compatible) │ (S3-compatible) │ │ └───────────┬───────────────┘ │ ┌───────────┴───────────┐ │ MinIO │ │ (S3-compatible │ │ object storage) │ └───────────┬───────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ┌────▼────┐ ┌────▼────┐ ┌─────▼─────┐ │ manoon- │ │ saleor │ │ other │ │ media │ │ bucket │ │ buckets │ │ (WP) │ │(Saleor) │ │ │ └─────────┘ └─────────┘ └───────────┘ ``` ## Step 1: Verify Buckets ```bash # Access MinIO container kubectl exec -ti deployment/minio -n manoonoils -- /bin/sh # List all buckets mc alias set local http://localhost:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD mc ls local # Expected output: # [bucket] manoon-media (WordPress) # [bucket] saleor (Saleor) # [bucket] other... (if any) ``` If `saleor` bucket doesn't exist, create it: ```bash mc mb local/saleor ``` ## Step 2: Image Migration Strategies ### Option A: Copy Images from WordPress to Saleor Bucket **Best for:** Clean separation, full control ```bash # Copy all images from WordPress bucket to Saleor bucket kubectl exec -ti deployment/minio -n manoonoils -- \ mc cp --recursive local/manoon-media/wp-content/uploads/ local/saleor/ # Or sync (faster for subsequent runs) kubectl exec -ti deployment/minio -n manoonoils -- \ mc mirror local/manoon-media/wp-content/uploads/ local/saleor/products/ ``` **After copy, images will be at:** - `http://minio-api.nodecrew.me/saleor/products/2024/01/image.jpg` ### Option B: Share Bucket (Keep WordPress Images in Place) **Best for:** Quick migration, no duplication Configure Saleor to read from `manoon-media` bucket: ```yaml # Update deployment to use WordPress bucket temporarily env: - name: AWS_MEDIA_BUCKET_NAME value: "manoon-media" # Instead of "saleor" - name: MEDIA_URL value: "https://minio-api.nodecrew.me/manoon-media/" ``` **Pros:** No copying needed **Cons:** WordPress and Saleor share bucket (risk of conflicts) ### Option C: Keep Separate + URL Mapping **Best for:** Gradual migration 1. Keep WordPress images in `manoon-media` 2. New Saleor uploads go to `saleor` bucket 3. Use URL mapping for old images ```typescript // Storefront image component const ProductImage = ({ imageUrl }) => { // If image is from old WordPress, rewrite URL const mappedUrl = imageUrl.includes('manoon-media') ? imageUrl.replace('manoon-media', 'saleor') : imageUrl; return ; }; ``` ## Step 3: Add Images to Saleor Products ### Saleor Product Media Structure Saleor stores media in `product_productmedia` table: ```sql -- Check table structure \d product_productmedia -- Columns: -- id, product_id, image (file path), alt, sort_order, type ``` ### Migration Script ```sql -- Create temporary mapping table CREATE TEMP TABLE wp_image_mapping ( wp_product_id INTEGER, saleor_product_id INTEGER, wp_image_url VARCHAR(500), saleor_image_path VARCHAR(500) ); -- After copying images to saleor bucket, insert media records INSERT INTO product_productmedia (product_id, image, alt, sort_order, type) SELECT p.id as product_id, 'products/' || SPLIT_PART(m.saleor_image_path, '/', -1) as image, p.name as alt, 0 as sort_order, 'IMAGE' as type FROM temp_woocommerce_import t JOIN product_product p ON p.slug = t.slug JOIN wp_image_mapping m ON m.wp_product_id = t.wc_id; ``` ### Using Saleor Dashboard (Manual) For small catalogs, use the Saleor Dashboard: 1. Go to https://dashboard.manoonoils.com 2. Catalog → Products → Select product 3. Media tab → Upload images 4. Set alt text, sort order ### Using GraphQL API (Programmatic) ```graphql mutation ProductMediaCreate($product: ID!, $image: Upload!, $alt: String) { productMediaCreate(input: {product: $product, image: $image, alt: $alt}) { media { id url } errors { field message } } } ``` Python script example: ```python import requests from saleor.graphql import Client # Upload image to Saleor def upload_product_image(product_id, image_path, alt_text): url = "https://api.manoonoils.com/graphql/" query = """ mutation ProductMediaCreate($product: ID!, $image: Upload!, $alt: String) { productMediaCreate(input: {product: $product, image: $image, alt: $alt}) { media { id url } errors { field message } } } """ operations = { "query": query, "variables": { "product": product_id, "alt": alt_text } } map_data = {"0": ["variables.image"]} with open(image_path, 'rb') as f: files = { 'operations': (None, json.dumps(operations)), 'map': (None, json.dumps(map_data)), '0': (image_path, f, 'image/jpeg') } response = requests.post(url, files=files) return response.json() ``` ## Step 4: Handle Logos & Assets ### Option 1: Store in Saleor (Recommended) Upload logos to Saleor as product media for a "Store" product, or serve via CDN: ```bash # Upload logo to MinIO saleor bucket mc cp logo.png local/saleor/assets/ mc cp favicon.ico local/saleor/assets/ ``` **Access URLs:** - Logo: `https://minio-api.nodecrew.me/saleor/assets/logo.png` - Favicon: `https://minio-api.nodecrew.me/saleor/assets/favicon.ico` ### Option 2: Store in Next.js Public Folder For storefront-specific assets: ``` storefront/ ├── public/ │ ├── logo.png │ ├── favicon.ico │ └── images/ │ └── hero-banner.jpg ``` Access: `https://dev.manoonoils.com/logo.png` ### Option 3: Keep in WordPress (Transition Period) Continue serving assets from WordPress during migration: ```typescript // Storefront config const ASSETS_URL = process.env.NEXT_PUBLIC_ASSETS_URL || 'https://minio-api.nodecrew.me/manoon-media/assets/'; // Usage Logo ``` ## Step 5: Storefront Image Component Handle both old and new image URLs: ```typescript // components/ProductImage.tsx import { useState } from 'react'; interface ProductImageProps { url: string; alt: string; className?: string; } export function ProductImage({ url, alt, className }: ProductImageProps) { const [error, setError] = useState(false); // Map old WordPress URLs to new Saleor URLs const mappedUrl = url?.includes('manoon-media') ? url.replace('manoon-media', 'saleor') : url; if (error) { return
No Image
; } return ( {alt} setError(true)} loading="lazy" /> ); } ``` ## Step 6: Image Optimization ### Saleor Thumbnails Saleor automatically generates thumbnails: ```graphql query ProductImages { product(slug: "organsko-maslinovo-ulje", channel: "default-channel") { media { id url alt type # Thumbnails thumbnail(size: 255) { url } thumbnail(size: 510) { url } thumbnail(size: 1020) { url } } } } ``` ### Next.js Image Optimization ```typescript import Image from 'next/image'; // Optimized image component export function OptimizedProductImage({ media }) { return ( {media.alt} ); } ``` ## Step 7: Bulk Image Migration Script ```bash #!/bin/bash # migrate-images.sh # 1. Export WooCommerce product images list kubectl exec deployment/wordpress -n manoonoils -- \ wp db query "SELECT p.ID, p.post_title, pm.meta_value as image_url FROM wp_posts p JOIN wp_postmeta pm ON p.ID = pm.post_id WHERE p.post_type = 'product' AND pm.meta_key = '_wp_attached_file'" \ > /tmp/wp-images.csv # 2. Copy images to Saleor bucket while IFS=',' read -r product_id title image_path; do echo "Copying: $image_path" kubectl exec deployment/minio -n manoonoils -- \ mc cp "local/manoon-media/$image_path" "local/saleor/products/" done < /tmp/wp-images.csv # 3. Update Saleor database with image paths # (Run SQL script to insert into product_productmedia) ``` ## Step 8: Verification Checklist - [ ] All products have at least one image - [ ] Images load correctly in Saleor Dashboard - [ ] Images display in storefront - [ ] Thumbnails generate properly - [ ] Alt text is set for SEO - [ ] Logo loads correctly - [ ] Favicon works - [ ] No broken image links ## Troubleshooting ### Images not showing in Saleor Dashboard ```bash # Check if Saleor can access MinIO kubectl exec deployment/saleor-api -n saleor -- \ curl -I http://minio.manoonoils:9000/saleor/ # Check bucket permissions kubectl exec deployment/minio -n manoonoils -- \ mc policy get local/saleor # Set bucket to public (if needed) kubectl exec deployment/minio -n manoonoils -- \ mc policy set public local/saleor ``` ### Image URLs returning 404 1. Check image exists in bucket: ```bash mc ls local/saleor/products/2024/01/ ``` 2. Check image path in database: ```sql SELECT * FROM product_productmedia WHERE product_id = 1; ``` 3. Verify MEDIA_URL configuration: ```bash kubectl get deployment saleor-api -n saleor -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="MEDIA_URL")].value}' ``` ## Summary | Component | Current (WP) | Target (Saleor) | Action | |-----------|--------------|-----------------|--------| | **Product Images** | MinIO: `manoon-media` | MinIO: `saleor` | Copy or share bucket | | **Logo** | WP media | MinIO: `saleor/assets/` or Next.js public | Upload to new location | | **Favicon** | WP root | Next.js public or MinIO | Move to storefront | | **Thumbnails** | WP generates | Saleor generates | Automatic | | **CDN** | MinIO direct | MinIO direct or Cloudflare | Optional upgrade | ## Recommended Approach 1. **Create `saleor` bucket** in existing MinIO 2. **Copy** all product images from `manoon-media` to `saleor` 3. **Upload logos** to `saleor/assets/` or Next.js public folder 4. **Run SQL** to insert image records into `product_productmedia` 5. **Update storefront** to handle both old and new URLs during transition 6. **Test** all images load correctly