- Add Apollo Client for Saleor GraphQL API - Create GraphQL fragments (Product, Variant, Checkout) - Create GraphQL queries (Products, Checkout) - Create GraphQL mutations (Checkout operations) - Add TypeScript types for Saleor entities - Add product helper functions - Install @apollo/client and graphql dependencies Part of WordPress/WooCommerce → Saleor migration
12 KiB
12 KiB
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
# 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:
mc mb local/saleor
Step 2: Image Migration Strategies
Option A: Copy Images from WordPress to Saleor Bucket
Best for: Clean separation, full control
# 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:
# 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
- Keep WordPress images in
manoon-media - New Saleor uploads go to
saleorbucket - Use URL mapping for old images
// 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 <img src={mappedUrl} />;
};
Step 3: Add Images to Saleor Products
Saleor Product Media Structure
Saleor stores media in product_productmedia table:
-- Check table structure
\d product_productmedia
-- Columns:
-- id, product_id, image (file path), alt, sort_order, type
Migration Script
-- 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:
- Go to https://dashboard.manoonoils.com
- Catalog → Products → Select product
- Media tab → Upload images
- Set alt text, sort order
Using GraphQL API (Programmatic)
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:
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:
# 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:
// Storefront config
const ASSETS_URL = process.env.NEXT_PUBLIC_ASSETS_URL ||
'https://minio-api.nodecrew.me/manoon-media/assets/';
// Usage
<img src={`${ASSETS_URL}logo.png`} alt="Logo" />
Step 5: Storefront Image Component
Handle both old and new image URLs:
// 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 <div className="image-placeholder">No Image</div>;
}
return (
<img
src={mappedUrl}
alt={alt}
className={className}
onError={() => setError(true)}
loading="lazy"
/>
);
}
Step 6: Image Optimization
Saleor Thumbnails
Saleor automatically generates thumbnails:
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
import Image from 'next/image';
// Optimized image component
export function OptimizedProductImage({ media }) {
return (
<Image
src={media.thumbnail?.url || media.url}
alt={media.alt}
width={400}
height={400}
quality={80}
placeholder="blur"
blurDataURL={media.thumbnail?.url}
/>
);
}
Step 7: Bulk Image Migration Script
#!/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
# 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
- Check image exists in bucket:
mc ls local/saleor/products/2024/01/
- Check image path in database:
SELECT * FROM product_productmedia WHERE product_id = 1;
- Verify MEDIA_URL configuration:
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
- Create
saleorbucket in existing MinIO - Copy all product images from
manoon-mediatosaleor - Upload logos to
saleor/assets/or Next.js public folder - Run SQL to insert image records into
product_productmedia - Update storefront to handle both old and new URLs during transition
- Test all images load correctly