# 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
```
## 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