Files
orion/docs/operations/image-storage.md
Samir Boulahtit e9253fbd84 refactor: rename Wizamart to Orion across entire codebase
Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart
with Orion/orion/ORION across 184 files. This includes database
identifiers, email addresses, domain references, R2 bucket names,
DNS prefixes, encryption salt, Celery app name, config defaults,
Docker configs, CI configs, documentation, seed data, and templates.

Renames homepage-wizamart.html template to homepage-orion.html.
Fixes duplicate file_pattern key in api.yaml architecture rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:46:56 +01:00

5.7 KiB
Raw Permalink Blame History

Image Storage System

Documentation for the platform's image storage and management system.

Overview

The Orion platform uses a self-hosted image storage system with:

  • Sharded directory structure for filesystem performance
  • Automatic WebP conversion for optimization
  • Multiple size variants for different use cases
  • CDN-ready architecture for scaling

Storage Architecture

Directory Structure

Images are stored in a sharded directory structure to prevent filesystem performance degradation:

/static/uploads/
  └── products/
      ├── 00/                    # First 2 chars of hash
      │   ├── 1a/               # Next 2 chars
      │   │   ├── 001a2b3c_original.webp
      │   │   ├── 001a2b3c_800.webp
      │   │   └── 001a2b3c_200.webp
      │   └── 2b/
      │       └── ...
      ├── 01/
      │   └── ...
      └── ff/
          └── ...

Hash Generation

The file hash is generated from:

hash = md5(f"{store_id}:{product_id}:{timestamp}:{original_filename}")[:8]

This ensures:

  • Unique file paths
  • Even distribution across directories
  • Predictable file locations

Image Variants

Each uploaded image generates multiple variants:

Variant Max Dimensions Format Use Case
original As uploaded (max 2000px) WebP Detail view, zoom
800 800×800 WebP Product cards
200 200×200 WebP Thumbnails, grids

Size Estimates

Original Size After Processing Storage per Image
2MB JPEG ~200KB (original) + 80KB (800) + 15KB (200) ~295KB
500KB JPEG ~150KB (original) + 60KB (800) + 12KB (200) ~222KB
100KB JPEG ~80KB (original) + 40KB (800) + 10KB (200) ~130KB

Average: ~200KB per image (all variants)

Upload Process

API Endpoint

POST /api/v1/admin/images/upload
Content-Type: multipart/form-data

file: <binary>
store_id: 123
product_id: 456 (optional, for product images)
type: product|category|banner

Response

{
  "success": true,
  "image": {
    "id": "001a2b3c",
    "urls": {
      "original": "/uploads/products/00/1a/001a2b3c_original.webp",
      "medium": "/uploads/products/00/1a/001a2b3c_800.webp",
      "thumb": "/uploads/products/00/1a/001a2b3c_200.webp"
    },
    "size_bytes": 295000,
    "dimensions": {
      "width": 1200,
      "height": 1200
    }
  }
}

Configuration

Environment Variables

# Image storage configuration
IMAGE_UPLOAD_DIR=/var/www/uploads
IMAGE_MAX_SIZE_MB=10
IMAGE_ALLOWED_TYPES=jpg,jpeg,png,gif,webp
IMAGE_QUALITY=85
IMAGE_MAX_DIMENSION=2000

Python Configuration

# app/core/config.py
class ImageSettings:
    UPLOAD_DIR: str = "/static/uploads"
    MAX_SIZE_MB: int = 10
    ALLOWED_TYPES: list = ["jpg", "jpeg", "png", "gif", "webp"]
    QUALITY: int = 85
    MAX_DIMENSION: int = 2000

    # Generated sizes
    SIZES: dict = {
        "original": None,  # No resize, just optimize
        "medium": 800,
        "thumb": 200,
    }

Performance Guidelines

Filesystem Limits

Files per Directory Status Action
< 10,000 OK None needed
10,000 - 50,000 Monitor Plan migration
50,000 - 100,000 Warning Increase sharding depth
> 100,000 Critical Migrate to object storage

Capacity Planning

Products Images (5/product) Total Files (3 sizes) Storage
10,000 50,000 150,000 30 GB
50,000 250,000 750,000 150 GB
100,000 500,000 1,500,000 300 GB
500,000 2,500,000 7,500,000 1.5 TB

CDN Integration

For production deployments, configure a CDN for image delivery:

  1. Set up Cloudflare for your domain
  2. Configure page rules for /uploads/*:
    • Cache Level: Cache Everything
    • Edge Cache TTL: 1 month
    • Browser Cache TTL: 1 week

nginx Configuration

location /uploads/ {
    alias /var/www/uploads/;
    expires 30d;
    add_header Cache-Control "public, immutable";
    add_header X-Content-Type-Options nosniff;

    # WebP fallback for older browsers
    location ~ \.(jpg|jpeg|png)$ {
        try_files $uri$webp_suffix $uri =404;
    }
}

Maintenance

Cleanup Orphaned Images

Remove images not referenced by any product:

# Run via admin CLI
python -m scripts.cleanup_orphaned_images --dry-run
python -m scripts.cleanup_orphaned_images --execute

Regenerate Variants

If image quality settings change:

# Regenerate all variants for a store
python -m scripts.regenerate_images --store-id 123

# Regenerate all variants (use with caution)
python -m scripts.regenerate_images --all

Monitoring

Metrics to Track

  • Total file count
  • Storage used (GB)
  • Files per directory (max)
  • Upload success rate
  • Average processing time

Health Checks

The platform health page includes image storage metrics:

  • Current file count
  • Storage usage
  • Directory distribution
  • Processing queue status

Troubleshooting

Common Issues

Upload fails with "File too large"

  • Check IMAGE_MAX_SIZE_MB setting
  • Verify nginx client_max_body_size

Images not displaying

  • Check file permissions (should be readable by web server)
  • Verify URL paths match actual file locations

Slow uploads

  • Check disk I/O performance
  • Consider async processing queue