# S3 Storage Configuration (DigitalOcean Spaces)

This guide explains how to configure Daylight to use DigitalOcean Spaces (S3-compatible storage) for media file storage, including CDN integration for optimal performance.

## Prerequisites

- A DigitalOcean account with Spaces enabled
- Access to your Laravel application's environment configuration
- Basic knowledge of Laravel's filesystem configuration

## Step 1: Install AWS SDK

The Laravel Flysystem S3 driver requires the AWS SDK for PHP. Install it via Composer:

```bash
composer require league/flysystem-aws-s3-v3 "^3.0"
```

## Step 2: Create a DigitalOcean Space

1. Log in to your DigitalOcean account
2. Navigate to **Spaces** in the left sidebar
3. Click **Create a Space**
4. Configure your Space:
   - **Choose a datacenter region** (e.g., `ams3` for Amsterdam)
   - **Enable CDN** (recommended for better performance)
   - **Choose a unique name** (e.g., `your-project-media`)
   - **Select a project**
5. Click **Create a Space**
6. Note your Space's details:
   - **Space name**: `your-project-media`
   - **Region**: `ams3`
   - **Endpoint**: `ams3.digitaloceanspaces.com`
   - **Edge endpoint** (CDN): `your-project-media.ams3.cdn.digitaloceanspaces.com`

## Step 3: Generate API Keys

1. In DigitalOcean, go to **API** in the left sidebar
2. Navigate to **Spaces access keys**
3. Click **Generate New Key**
4. Give it a name (e.g., "Laravel Media Storage")
5. Save both:
   - **Access Key ID** (e.g., `DO00ABCDEFGHIJKLMNOP`)
   - **Secret Access Key** (e.g., `abc123...xyz789`)

> **Important**: The secret key is only shown once. Store it securely!

## Step 4: Configure Laravel Filesystem

Edit `config/filesystems.php` and add a new disk configuration:

```php
'disks' => [
    // ... existing disks

    'spaces' => [
        'driver' => 's3',
        'key' => env('DO_SPACES_KEY'),
        'secret' => env('DO_SPACES_SECRET'),
        'region' => env('DO_SPACES_REGION', 'ams3'),
        'bucket' => env('DO_SPACES_BUCKET'),
        'endpoint' => env('DO_SPACES_ENDPOINT'),
        'url' => env('DO_SPACES_CDN_ENDPOINT'), // Use CDN endpoint for public URLs
        'use_path_style_endpoint' => false,
        'visibility' => 'public',
    ],
],
```

## Step 5: Configure Environment Variables

Add the following to your `.env` file:

```env
# DigitalOcean Spaces Configuration
DO_SPACES_KEY=DO00ABCDEFGHIJKLMNOP
DO_SPACES_SECRET=your-secret-key-here
DO_SPACES_REGION=ams3
DO_SPACES_BUCKET=your-project-media
DO_SPACES_ENDPOINT=https://ams3.digitaloceanspaces.com
DO_SPACES_CDN_ENDPOINT=https://your-project-media.ams3.cdn.digitaloceanspaces.com

# Media Library Configuration
MEDIA_DISK=spaces
```

### Configuration Breakdown

| Variable | Description | Example |
|----------|-------------|---------|
| `DO_SPACES_KEY` | Your Spaces access key ID | `DO00ABCDEFGHIJKLMNOP` |
| `DO_SPACES_SECRET` | Your Spaces secret key | `abc123...xyz789` |
| `DO_SPACES_REGION` | Datacenter region | `ams3`, `fra1`, `nyc3`, `sfo3`, `sgp1` |
| `DO_SPACES_BUCKET` | Your Space name | `your-project-media` |
| `DO_SPACES_ENDPOINT` | Region endpoint URL | `https://ams3.digitaloceanspaces.com` |
| `DO_SPACES_CDN_ENDPOINT` | CDN endpoint URL (for faster delivery) | `https://your-project-media.ams3.cdn.digitaloceanspaces.com` |
| `MEDIA_DISK` | Disk to use for media storage | `spaces` |

## Step 6: Update Media Configuration

The Daylight media library is pre-configured to respect the `MEDIA_DISK` environment variable. No changes to `config/media.php` are needed if you've set:

```env
MEDIA_DISK=spaces
```

## Step 7: Configure CORS (Optional)

If you plan to upload files directly from the browser, configure CORS settings:

1. In your DigitalOcean Space, go to **Settings**
2. Scroll to **CORS Configurations**
3. Add a new CORS configuration:

```json
{
  "AllowedOrigins": ["https://yourdomain.com"],
  "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
  "AllowedHeaders": ["*"],
  "MaxAgeSeconds": 3000
}
```

Replace `https://yourdomain.com` with your actual domain.

## Step 8: Test the Configuration

Test that your configuration works correctly:

```bash
php artisan tinker
```

Then run:

```php
Storage::disk('spaces')->put('test.txt', 'Hello DigitalOcean Spaces!');
Storage::disk('spaces')->exists('test.txt'); // Should return true
Storage::disk('spaces')->url('test.txt'); // Should return CDN URL
Storage::disk('spaces')->delete('test.txt');
```

## Step 9: Migrate Existing Media (Optional)

If you have existing media files on local storage, migrate them to Spaces:

```bash
# Create a backup first!
php artisan media:migrate-to-s3
```

> **Note**: You'll need to create this custom command or manually migrate files.

## CDN Benefits

Using the CDN endpoint (`DO_SPACES_CDN_ENDPOINT`) provides several benefits:

- ✅ **Faster delivery**: Content is cached at edge locations worldwide
- ✅ **Reduced latency**: Users download from the nearest edge location
- ✅ **Lower egress costs**: CDN bandwidth is typically cheaper
- ✅ **Better performance**: Offloads traffic from your origin server

## Directory Structure

Media files are stored with the following structure:

```
your-project-media/
├── media/
│   ├── products/
│   │   ├── electronics/
│   │   │   ├── laptop.jpg
│   │   │   └── conversions/
│   │   │       ├── laptop-thumbnail.jpg
│   │   │       ├── laptop-thumbnail-2x.jpg
│   │   │       └── laptop-webp.webp
│   │   └── furniture/
│   │       └── chair.jpg
│   └── documents/
│       └── manual.pdf
```

## Security Considerations

### File Permissions

Files are uploaded with public visibility by default. If you need private files:

1. Update the disk configuration:
```php
'visibility' => 'private',
```

2. Generate signed URLs for private access:
```php
$url = Storage::disk('spaces')->temporaryUrl(
    'private-file.pdf', 
    now()->addMinutes(5)
);
```

### Access Keys

- ✅ **Never commit** `.env` file to version control
- ✅ **Use separate keys** for development and production
- ✅ **Rotate keys regularly** for security
- ✅ **Limit key permissions** to only what's needed (read/write to specific bucket)

## Troubleshooting

### Issue: "Class 'League\Flysystem\AwsS3V3\AwsS3V3Adapter' not found"

**Solution**: Install the AWS S3 package:
```bash
composer require league/flysystem-aws-s3-v3 "^3.0"
```

### Issue: "Error executing PutObject"

**Solution**: Check your credentials and bucket name in `.env`. Verify the Space exists in the correct region.

### Issue: Images not loading (404)

**Solution**: 
1. Verify CDN endpoint is correct in `.env`
2. Check file exists: `Storage::disk('spaces')->exists('path/to/file.jpg')`
3. Ensure Space has public read permissions
4. Clear CDN cache if recently uploaded

### Issue: "Credentials could not be loaded"

**Solution**: 
1. Verify `DO_SPACES_KEY` and `DO_SPACES_SECRET` are set correctly
2. Check for extra spaces or quotes in `.env`
3. Run `php artisan config:clear` to clear cached config

### Issue: CORS errors in browser

**Solution**: Configure CORS settings in your Space (see Step 7)

## Performance Tips

1. **Enable CDN**: Always use the CDN endpoint for better performance
2. **Use image conversions**: Serve appropriately sized images (thumbnail, medium, large)
3. **Implement lazy loading**: Load images only when needed
4. **Set cache headers**: Configure browser caching for static assets
5. **Use WebP format**: Enable WebP conversions for smaller file sizes

## Cost Optimization

- **Storage**: $5/month for 250 GB
- **Bandwidth**: First 1 TB free, then $0.01/GB
- **CDN**: Included with no additional cost

To optimize costs:
- Delete unused files regularly
- Use image conversions to avoid storing unnecessarily large files
- Leverage CDN caching to reduce bandwidth usage

## Switching Between Environments

### Development (Local)
```env
MEDIA_DISK=public
```

### Staging/Production (Spaces)
```env
MEDIA_DISK=spaces
```

This allows you to use local storage during development and Spaces in production without code changes.

## Additional Resources

- [DigitalOcean Spaces Documentation](https://docs.digitalocean.com/products/spaces/)
- [Laravel Filesystem Documentation](https://laravel.com/docs/filesystem)
- [Flysystem AWS S3 Adapter](https://flysystem.thephpleague.com/docs/adapter/aws-s3/)

## Support

For issues specific to Daylight's media library, please refer to the main documentation or contact support.

