strapi-r2-storage
TypeScript icon, indicating that this package has built-in type declarations

1.0.2 • Public • Published

strapi-r2-storage

A robust and feature-rich Cloudflare R2 storage provider for Strapi v4 & v5 with advanced file naming, validation, compression, and retry mechanisms.

Features

  • 🚀 Strapi v4 & v5 Compatible - Works with both Strapi v4 and v5
  • 📁 Simple File Naming - Two reliable strategies (hash, UUID)
  • 🔄 Smart File Renaming - Automatic file renaming with original name preservation
  • 🗂️ Smart Folder Structure - Organize files by date or custom logic
  • 🔒 File Validation - Size limits, MIME type filtering
  • 📦 Compression Support - Automatic compression for compatible file types
  • 🔄 Retry Logic - Configurable retry with exponential backoff
  • 🌐 CDN Support - Works with Cloudflare CDN and custom domains
  • 📊 Health Checks - Built-in provider health monitoring
  • 🪵 Logging - Comprehensive logging for debugging
  • TypeScript - Full TypeScript support with type definitions

Installation

npm install strapi-r2-storage

Configuration

Add the provider configuration to your Strapi project:

Strapi Media Library Security Setup

To enable proper thumbnail display in Strapi's Media Library when using external storage providers like R2, you need to configure the Content Security Policy (CSP) to allow loading images from your R2 domain.

Update your config/middlewares.js file:

// config/middlewares.js
module.exports = ({ env }) => [
  'strapi::errors',
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::logger',
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            // Add your R2 domain here
            env('CLOUDFLARE_CDN_URL') ? new URL(env('CLOUDFLARE_CDN_URL')).hostname : null,
          ].filter(Boolean),
          'media-src': [
            "'self'",
            'data:',
            'blob:', 
            // Add your R2 domain here
            env('CLOUDFLARE_CDN_URL') ? new URL(env('CLOUDFLARE_CDN_URL')).hostname : null,
          ].filter(Boolean),
          upgradeInsecureRequests: null,
        },
      },
    },
  },
];

Important: Replace CLOUDFLARE_CDN_URL with your actual environment variable name that contains your R2 public URL.

Basic Configuration

// config/plugins.js
module.exports = {
  upload: {
    config: {
      provider: 'strapi-r2-storage',
      providerOptions: {
        accountId: process.env.CLOUDFLARE_ACCOUNT_ID,
        accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
        secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
        bucket: process.env.CLOUDFLARE_BUCKET,
        cdnUrl: process.env.CLOUDFLARE_CDN_URL, // optional
      },
    },
  },
};

Advanced Configuration

// config/plugins.js
module.exports = {
  upload: {
    config: {
      provider: 'strapi-r2-storage',
      providerOptions: {
        // Required settings
        accountId: process.env.CLOUDFLARE_ACCOUNT_ID,
        accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
        secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
        bucket: process.env.CLOUDFLARE_BUCKET,
        
        // Optional settings
        region: 'auto', // default: 'auto'
        baseUrl: process.env.CLOUDFLARE_BASE_URL, // custom domain
        cdnUrl: process.env.CLOUDFLARE_CDN_URL, // CDN URL
        publicDomain: process.env.CLOUDFLARE_PUBLIC_DOMAIN, // R2 public domain
        
        // File naming and organization
        naming: {
          strategy: 'hash', // 'hash' | 'uuid'
          preserveExtension: true,
          folderStructure: 'year-month', // 'flat' | 'year' | 'year-month' | 'year-month-day'
        },
        
        // File validation
        maxFileSize: 50 * 1024 * 1024, // 50MB
        allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'],
        blockedMimeTypes: ['application/x-executable'],
        
        // Performance and caching
        enableCompression: true,
        cacheControl: 'public, max-age=31536000', // 1 year
        enablePublicRead: true,
        
        // Additional metadata
        metadata: {
          'uploaded-by': 'strapi',
          'environment': process.env.NODE_ENV,
        },
        
        // Retry configuration
        retryOptions: {
          maxRetries: 3,
          retryDelay: 1000, // ms
        },
        
        // Debugging
        enableLogging: process.env.NODE_ENV === 'development',
      },
    },
  },
};

Environment Variables

Create a .env file in your Strapi project root:

# Cloudflare R2 Configuration
CLOUDFLARE_ACCOUNT_ID=your_account_id_here
CLOUDFLARE_ACCESS_KEY_ID=your_access_key_here
CLOUDFLARE_SECRET_ACCESS_KEY=your_secret_key_here
CLOUDFLARE_BUCKET=your_bucket_name

# Optional: CDN/Custom Domain
CLOUDFLARE_CDN_URL=https://your-cdn-domain.com
CLOUDFLARE_BASE_URL=https://your-custom-domain.com

Configuration Options

File Naming Strategies

Strategy Description Example Output Best For
hash Uses Strapi's generated hash (default) a1b2c3d4e5f6.jpg Standard usage, consistent with Strapi defaults
uuid Generated UUID v4 550e8400-e29b-41d4-a716-446655440000.jpg Complete uniqueness, no hash conflicts

Recommendation: Use uuid for maximum reliability and to avoid any potential file naming issues.

Folder Structures

Structure Description Example Path
flat All files in root (default) file.jpg
year Organized by year 2024/file.jpg
year-month Year and month 2024/03/file.jpg
year-month-day Full date 2024/03/15/file.jpg

File Validation

// Example: Only allow images under 10MB
naming: {
  maxFileSize: 10 * 1024 * 1024, // 10MB
  allowedMimeTypes: [
    'image/jpeg',
    'image/png',
    'image/webp',
    'image/gif'
  ],
}

Strapi v5 Compatibility

This provider is fully compatible with both Strapi v4 and v5. The API includes the new customParams parameter required for v5:

// The provider automatically handles both v4 and v5 API signatures
// v4: upload(file)
// v5: upload(file, customParams)

Usage Examples

Development vs Production

// Different settings per environment
const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
  upload: {
    config: {
      provider: 'strapi-r2-storage',
      providerOptions: {
        // ... other config
        enableLogging: isDevelopment,
        enableCompression: !isDevelopment, // Disable compression in dev for faster uploads
        cacheControl: isDevelopment 
          ? 'no-cache' 
          : 'public, max-age=31536000',
        naming: {
          strategy: isDevelopment ? 'hash' : 'uuid',
        },
      },
    },
  },
};

Health Checks

The provider includes a built-in health check method:

// In your Strapi application
const uploadProvider = strapi.plugins.upload.provider;
const healthCheck = await uploadProvider.checkHealth();

console.log(healthCheck);
// { status: 'ok' } or { status: 'error', message: 'Error details' }

Troubleshooting

Common Issues

  1. Authentication Errors

    • Verify your Cloudflare credentials
    • Ensure the API token has R2 permissions
  2. Upload Failures

    • Check file size limits
    • Verify MIME type restrictions
    • Review bucket permissions
  3. URL Generation Issues

    • Confirm CDN URL configuration
    • Check bucket public access settings
  4. Files Not Accessible (Cannot Copy/Download)

    • Problem: Generated URLs return errors or are not accessible
    • Solution: Configure proper public access
    // Option 1: Use custom domain (Recommended)
    providerOptions: {
      cdnUrl: 'https://your-domain.com',
      enablePublicRead: true,
    }
    
    // Option 2: Use R2 public domain
    providerOptions: {
      publicDomain: 'https://pub-abc123.r2.dev', // Your actual R2 public domain
      enablePublicRead: true,
    }
    
    // Option 3: For private files, ensure signed URLs work
    providerOptions: {
      enablePublicRead: false, // Will generate signed URLs
    }
  5. R2 Bucket Configuration

    • In Cloudflare dashboard, ensure your R2 bucket has public access enabled if using enablePublicRead: true
    • Set up custom domain in R2 settings for better URL structure

Debug Mode

Enable logging to troubleshoot issues:

providerOptions: {
  // ... other config
  enableLogging: true,
}

This will output detailed logs for all operations including uploads, deletions, and retries.

Performance Tips

  1. Enable Compression: Reduces bandwidth for text-based files
  2. Use CDN URLs: Faster content delivery worldwide
  3. Configure Caching: Set appropriate cache headers
  4. Optimize Retry Settings: Balance reliability with performance

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE file for details.

Related

Package Sidebar

Install

npm i strapi-r2-storage

Weekly Downloads

6

Version

1.0.2

License

MIT

Unpacked Size

47.1 kB

Total Files

15

Last publish

Collaborators

  • alanisme