# Supabase Avatars Storage Bucket Setup Guide

This guide provides complete steps for setting up a secure avatars storage bucket in Supabase with proper RLS policies and configuration.

## 1. Creating the Storage Bucket in Supabase Dashboard

### Step-by-Step Instructions:

1. **Navigate to Storage:**
   - Log into your Supabase Dashboard
   - Select your project
   - Click on "Storage" in the left sidebar

2. **Create New Bucket:**
   - Click the "New bucket" button (top right)
   - Enter bucket name: `avatars`
   - Set bucket as **PUBLIC** (toggle ON)
   - Click "Create bucket"

3. **Configure Bucket Settings:**
   - Click on your new `avatars` bucket
   - Go to "Settings" tab
   - Set the following configurations:
     - **File size limit:** 10485760 (10MB in bytes)
     - **Allowed MIME types:** See section 3 below

## 2. File Size Limits Configuration

### Recommended Settings:
- **Maximum file size:** 10MB (10,485,760 bytes)
- **Recommended dimensions:** 512x512px to 1024x1024px
- **Format optimization:** Automatically compress on client side before upload

### Size Breakdown:
```
Small avatars: 100x100px (50-100KB)
Medium avatars: 256x256px (100-300KB)
Large avatars: 512x512px (300-800KB)
Full resolution: 1024x1024px (1-3MB)
```

## 3. Allowed MIME Types

### Image Formats (Recommended):
```
image/jpeg
image/jpg
image/png
image/webp
image/gif
```

### Configuration in Supabase:
1. In bucket settings, find "Allowed MIME types"
2. Add each MIME type on a new line
3. Use exact format (no wildcards)

### Client-Side Validation:
```javascript
const ALLOWED_TYPES = [
  'image/jpeg',
  'image/jpg', 
  'image/png',
  'image/webp',
  'image/gif'
];

const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
```

## 4. Row Level Security (RLS) Policies

### Essential Policies for Avatars Bucket:

#### Policy 1: Allow Public Read Access
```sql
-- Policy name: Allow public read access
-- Target roles: anon, authenticated
-- Operation: SELECT
-- Using expression: bucket_id = 'avatars'
```

#### Policy 2: Allow Users to Upload Their Own Avatar
```sql
-- Policy name: Allow users to upload own avatar
-- Target roles: authenticated
-- Operation: INSERT
-- With check expression: 
--   bucket_id = 'avatars' AND 
--   (storage.foldername(name))[1] = auth.uid()::text
```

#### Policy 3: Allow Users to Update Their Own Avatar
```sql
-- Policy name: Allow users to update own avatar
-- Target roles: authenticated
-- Operation: UPDATE
-- Using expression:
--   bucket_id = 'avatars' AND 
--   (storage.foldername(name))[1] = auth.uid()::text
-- With check expression: Same as above
```

#### Policy 4: Allow Users to Delete Their Own Avatar
```sql
-- Policy name: Allow users to delete own avatar
-- Target roles: authenticated
-- Operation: DELETE
-- Using expression:
--   bucket_id = 'avatars' AND 
--   (storage.foldername(name))[1] = auth.uid()::text
```

### Creating Policies in Dashboard:
1. Go to "Storage" → "Policies"
2. Click "New policy" for the avatars bucket
3. Select the appropriate template or create custom
4. Copy the expressions above
5. Save each policy

## 5. Testing Your Setup

### Test 1: Upload Functionality
```javascript
// Test upload with proper path structure
const userId = 'user-123';
const file = selectedFile; // File from input
const filePath = `${userId}/avatar-${Date.now()}.jpg`;

const { data, error } = await supabase.storage
  .from('avatars')
  .upload(filePath, file, {
    cacheControl: '3600',
    upsert: false
  });

if (error) console.error('Upload failed:', error);
else console.log('Upload successful:', data);
```

### Test 2: Public Access
```javascript
// Test public URL generation
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user-123/avatar-123.jpg');

console.log('Public URL:', data.publicUrl);
// Should be accessible in browser
```

### Test 3: RLS Policies
```javascript
// Test with different user (should fail)
const otherUserPath = 'other-user/avatar-456.jpg';
const { error } = await supabase.storage
  .from('avatars')
  .download(otherUserPath);

// Should get permission error
console.log('Access denied (expected):', error);
```

### Test 4: File Size Validation
```javascript
// Test with oversized file (>10MB)
const largeFile = new File([new ArrayBuffer(11 * 1024 * 1024)], 'large.jpg');
const { error } = await supabase.storage
  .from('avatars')
  .upload(`${userId}/large-avatar.jpg`, largeFile);

// Should get size limit error
console.log('Size limit error (expected):', error);
```

## 6. Common Gotchas and Issues

### Issue 1: CORS Errors
**Problem:** Browser blocks requests to storage URLs
**Solution:** 
- Ensure bucket is set to PUBLIC
- Check CORS settings in Supabase project settings
- Use proper public URL format

### Issue 2: RLS Policy Not Working
**Problem:** Users can access other users' avatars
**Solution:**
- Double-check policy expressions use correct column names
- Verify folder structure matches policy logic
- Test with different authenticated users

### Issue 3: Upload Failures
**Problem:** Files fail to upload
**Common causes:**
- File size exceeds limit
- MIME type not in allowed list
- Network timeout for large files
- Invalid file path format

### Issue 4: Image Not Displaying
**Problem:** Uploaded image shows broken link
**Solution:**
- Verify public URL generation
- Check if file actually uploaded successfully
- Ensure proper file extension in path
- Test direct URL in browser

### Issue 5: Cache Issues
**Problem:** Updated avatar doesn't show new image
**Solution:**
- Add cache-busting parameter to URL
- Use `upsert: true` when uploading updates
- Set appropriate cache-control headers

### Best Practices:

1. **File Naming:** Use timestamps or UUIDs to avoid conflicts
2. **Folder Structure:** Organize by user ID for easy management
3. **Client Validation:** Always validate before upload
4. **Error Handling:** Provide clear user feedback
5. **Image Optimization:** Resize/compress before upload
6. **Backup Strategy:** Consider bucket backup procedures

### Example Implementation Pattern:
```javascript
// Complete avatar upload flow
async function uploadAvatar(userId, file) {
  try {
    // Validate file
    if (!validateFile(file)) {
      throw new Error('Invalid file type or size');
    }
    
    // Generate unique filename
    const timestamp = Date.now();
    const fileExt = file.name.split('.').pop();
    const filePath = `${userId}/avatar-${timestamp}.${fileExt}`;
    
    // Upload to storage
    const { data, error } = await supabase.storage
      .from('avatars')
      .upload(filePath, file, {
        cacheControl: '3600',
        upsert: true // Allow updates
      });
    
    if (error) throw error;
    
    // Get public URL
    const { data: urlData } = supabase.storage
      .from('avatars')
      .getPublicUrl(filePath);
    
    // Update user profile with new avatar URL
    await supabase
      .from('profiles')
      .update({ avatar_url: urlData.publicUrl })
      .eq('id', userId);
    
    return { success: true, url: urlData.publicUrl };
    
  } catch (error) {
    console.error('Avatar upload failed:', error);
    return { success: false, error: error.message };
  }
}
```

## Quick Reference Checklist

- [ ] Bucket created with name `avatars`
- [ ] Bucket set to PUBLIC access
- [ ] File size limit set to 10MB (10485760 bytes)
- [ ] Allowed MIME types configured
- [ ] RLS policies created for upload/view/update/delete
- [ ] Folder structure organized by user ID
- [ ] Client-side validation implemented
- [ ] Upload functionality tested
- [ ] Public URL access verified
- [ ] RLS policies tested with different users
- [ ] Error handling implemented
- [ ] Cache strategy defined

This setup provides a secure, scalable foundation for user avatar storage in your Supabase application.