# R2 Custom Domain Setup Guide

This guide walks through setting up a production-ready custom domain for the Sojorn R2 bucket.

## Why Custom Domain?

The R2 public development URL (`https://pub-*.r2.dev`) has significant limitations:
- ⚠️ Rate limited - not suitable for production traffic
- ⚠️ No Cloudflare features (Access, Caching, Analytics)
- ⚠️ No custom SSL certificates
- ⚠️ Not recommended by Cloudflare for production

A custom domain provides:
- ✅ Unlimited bandwidth and requests
- ✅ Full Cloudflare features (caching, CDN, DDoS protection)
- ✅ Custom SSL certificates
- ✅ Professional appearance
- ✅ Better SEO and branding

## Recommended Domain Structure

If your main domain is `sojorn.com`, use a subdomain for media:
- `media.sojorn.com` - Professional, clear purpose
- `cdn.sojorn.com` - Common CDN pattern
- `images.sojorn.com` - Descriptive alternative

**Recommended**: `media.sojorn.com`

## Setup Steps

### Step 1: Connect Domain to R2 Bucket

1. **Go to Cloudflare Dashboard** → **R2** → **`sojorn-media`** bucket
2. Click **"Settings"** tab
3. Under **"Public Access"**, click **"Connect Domain"**
4. Enter your chosen subdomain: `media.sojorn.com`
5. Click **"Connect Domain"**

Cloudflare will automatically:
- Create a DNS CNAME record pointing to R2
- Provision an SSL certificate
- Enable CDN caching

### Step 2: Verify Domain Configuration

Wait 1-2 minutes for DNS propagation, then test:

```bash
# Check DNS record
nslookup media.sojorn.com

# Test direct access (upload a test file first)
curl -I https://media.sojorn.com/test-image.jpg
# Should return: HTTP/2 200
```

### Step 3: Configure Environment Variable

Set the public URL in Supabase secrets:

```bash
# Set the R2 public URL secret
npx supabase secrets set R2_PUBLIC_URL=https://media.sojorn.com --project-ref zwkihedetedlatyvplyz

# Verify all R2 secrets are set
npx supabase secrets list --project-ref zwkihedetedlatyvplyz
```

Expected secrets:
- `R2_ACCOUNT_ID` - Your Cloudflare account ID
- `R2_ACCESS_KEY` - R2 API token access key
- `R2_SECRET_KEY` - R2 API token secret key
- `R2_PUBLIC_URL` - Your custom domain URL (e.g., https://media.sojorn.com)

### Step 4: Deploy Updated Edge Function

The edge function now uses the `R2_PUBLIC_URL` environment variable:

```bash
# Deploy the updated edge function
npx supabase functions deploy upload-image --project-ref zwkihedetedlatyvplyz

# Verify deployment
npx supabase functions list --project-ref zwkihedetedlatyvplyz
```

### Step 5: Test End-to-End

1. **Upload a test image** through the app
2. **Check the database** for the generated URL:
   ```sql
   SELECT id, image_url, created_at
   FROM posts
   WHERE image_url IS NOT NULL
   ORDER BY created_at DESC
   LIMIT 1;
   ```
3. **Verify URL format**: Should be `https://media.sojorn.com/{uuid}.{ext}`
4. **Test in browser**: Open the URL directly - image should load
5. **Check in app**: Image should display in the feed

## Troubleshooting

### Domain Not Connecting

**Error**: "Failed to connect domain"
**Solution**:
- Verify domain is managed by Cloudflare (same account)
- Check domain isn't already connected to another R2 bucket
- Ensure subdomain doesn't have conflicting DNS records

### SSL Certificate Issues

**Error**: "SSL handshake failed" or "NET::ERR_CERT_COMMON_NAME_INVALID"
**Solution**:
- Wait 5-10 minutes for SSL certificate provisioning
- Verify domain shows "Active" status in R2 bucket settings
- Check Cloudflare SSL/TLS mode is set to "Full" or "Full (strict)"

### Images Return 404

**Error**: Images uploaded but return 404 on custom domain
**Solution**:
- Verify domain connection is "Active" in R2 settings
- Check file actually exists: `curl -I https://{ACCOUNT_ID}.r2.cloudflarestorage.com/sojorn-media/{filename}`
- Verify bucket name matches in edge function (should be `sojorn-media`)

### Old Dev URLs Still Used

**Problem**: New uploads use dev URL instead of custom domain
**Solution**:
- Verify `R2_PUBLIC_URL` secret is set: `npx supabase secrets list`
- Redeploy edge function: `npx supabase functions deploy upload-image`
- Check edge function logs for errors: `npx supabase functions logs upload-image`

## Cloudflare Caching Configuration

After connecting the domain, optimize caching in Cloudflare Dashboard:

1. **Go to** your domain in Cloudflare Dashboard
2. **Navigate to** Rules → Page Rules
3. **Create a rule** for `media.sojorn.com/*`:
   - Cache Level: Standard
   - Edge Cache TTL: 1 month
   - Browser Cache TTL: 1 hour

This ensures images are cached at Cloudflare's edge for fast global delivery.

## Security Considerations

### CORS Configuration

The R2 bucket CORS is already configured for all origins (`*`):
```json
{
  "Allowed Origins": "*",
  "Allowed Methods": ["GET", "HEAD", "PUT"],
  "Allowed Headers": "*"
}
```

For production, consider restricting origins:
```json
{
  "Allowed Origins": ["https://sojorn.com", "https://app.sojorn.com"],
  "Allowed Methods": ["GET", "HEAD"],
  "Allowed Headers": ["*"]
}
```

### Access Control

Images are publicly readable by design. To restrict access:
1. Use signed URLs (requires code changes)
2. Implement Cloudflare Access rules
3. Add authentication checks before generating upload URLs

## Cost Considerations

### R2 Pricing (as of 2026)
- **Storage**: $0.015/GB per month
- **Class A Operations** (writes): $4.50 per million requests
- **Class B Operations** (reads): $0.36 per million requests
- **Data Transfer**: FREE (no egress fees)

### Custom Domain Benefits
- **Cloudflare CDN**: Free caching reduces R2 read operations
- **No Egress Fees**: Unlike AWS S3, R2 doesn't charge for bandwidth
- **Edge Caching**: Reduces origin requests by 95%+

## Monitoring

Track R2 usage in Cloudflare Dashboard:
1. **Go to** R2 → `sojorn-media` bucket
2. **Check** Metrics tab for:
   - Storage size
   - Request count
   - Bandwidth usage

Set up alerts for:
- Storage exceeding threshold (e.g., 10GB)
- Unusual request spikes
- Error rate increases

---

## Quick Reference

### Required Supabase Secrets
```bash
R2_ACCOUNT_ID=<your-cloudflare-account-id>
R2_ACCESS_KEY=<your-r2-access-key>
R2_SECRET_KEY=<your-r2-secret-key>
R2_PUBLIC_URL=https://media.sojorn.com
```

### Deploy Commands
```bash
# Set secret
npx supabase secrets set R2_PUBLIC_URL=https://media.sojorn.com --project-ref zwkihedetedlatyvplyz

# Deploy function
npx supabase functions deploy upload-image --project-ref zwkihedetedlatyvplyz

# View logs
npx supabase functions logs upload-image --project-ref zwkihedetedlatyvplyz
```

### Test Commands
```bash
# Check DNS
nslookup media.sojorn.com

# Test HTTPS
curl -I https://media.sojorn.com/

# Upload test file
curl -X PUT "https://<account-id>.r2.cloudflarestorage.com/sojorn-media/test.txt" \
  -H "Authorization: Bearer <token>" \
  --data "test content"

# Verify via custom domain
curl -I https://media.sojorn.com/test.txt
```

---

**Next Steps**: After completing this setup, proceed to the main [IMAGE_UPLOAD_IMPLEMENTATION.md](./IMAGE_UPLOAD_IMPLEMENTATION.md) guide for testing the full upload flow.
