# DigitalOcean Spaces Setup for MapleFile

**Audience**: Junior DevOps Engineers, Infrastructure Team
**Time to Complete**: 20-30 minutes
**Prerequisites**:
- Completed guide 04.5_spaces.md (DigitalOcean Spaces basics)
- AWS CLI configured with DigitalOcean profile
- DigitalOcean Spaces API keys

---

## Overview

This guide configures a **separate DigitalOcean Space for MapleFile** with the required CORS settings for browser-based file uploads.

**What You'll Build:**
- New DigitalOcean Space for MapleFile file storage
- CORS configuration to allow browser uploads from frontend
- Docker Swarm secrets for MapleFile backend
- Verified upload/download functionality

**Why a Separate Space?**
- MapleFile stores encrypted user files (different from MaplePress uploads)
- Different CORS requirements (frontend uploads directly to Spaces)
- Separate billing and storage tracking
- Independent lifecycle management

---

## Table of Contents

1. [Create MapleFile Space](#step-1-create-maplefile-space)
2. [Configure CORS for Browser Uploads](#step-2-configure-cors-for-browser-uploads)
3. [Create Docker Secrets](#step-3-create-docker-secrets)
4. [Verify Configuration](#step-4-verify-configuration)
5. [Troubleshooting](#troubleshooting)

---

## Step 1: Create MapleFile Space

### 1.1 Create Space via Dashboard

1. Log into DigitalOcean dashboard: https://cloud.digitalocean.com
2. Click **Manage** → **Spaces Object Storage** in left sidebar
3. Click **Create a Space**
4. Configure:
   - **Choose a datacenter region**: Same as your droplets (e.g., `tor1` - Toronto)
   - **Enable CDN**: ✅ Yes (recommended)
   - **Choose a unique name**: `maplefile` (must be globally unique)
   - **Select a project**: Your project (e.g., "mapleopentech Production")
5. Click **Create a Space**

**Expected output:**
- Space created successfully
- Space URL: `https://maplefile.tor1.digitaloceanspaces.com`

### 1.2 Record Space Information

**Save these values:**

```bash
# Space Name
SPACE_NAME=maplefile

# Endpoint (without https://)
SPACE_ENDPOINT=tor1.digitaloceanspaces.com

# Region code
SPACE_REGION=tor1

# Full URL
SPACE_URL=https://maplefile.tor1.digitaloceanspaces.com
```

**✅ Checkpoint:** Space created and URL recorded

---

## Step 2: Configure CORS for Browser Uploads

**CRITICAL**: This step is required for browser-based file uploads. Without CORS configuration, users will get errors when trying to upload files from the MapleFile frontend.

### 2.1 Create CORS Configuration File

**On your local machine:**

```bash
# Create CORS configuration
cat > /tmp/maplefile-cors.json << 'EOF'
{
  "CORSRules": [
    {
      "AllowedOrigins": [
        "http://localhost:5173",
        "http://localhost:3000",
        "https://maplefile.ca",
        "https://www.maplefile.ca"
      ],
      "AllowedMethods": [
        "GET",
        "PUT",
        "HEAD",
        "DELETE"
      ],
      "AllowedHeaders": [
        "*"
      ],
      "MaxAgeSeconds": 3600
    }
  ]
}
EOF
```

**Note:** Update `AllowedOrigins` to include:
- Your development URLs (`http://localhost:5173`)
- Your production domain(s) (`https://maplefile.ca`)

### 2.2 Apply CORS Configuration

```bash
# Apply CORS to MapleFile space
aws s3api put-bucket-cors \
  --bucket maplefile \
  --cors-configuration file:///tmp/maplefile-cors.json \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Should return no output (success)
```

### 2.3 Verify CORS Configuration

```bash
# Check current CORS settings
aws s3api get-bucket-cors \
  --bucket maplefile \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean
```

**Expected output:**

```json
{
    "CORSRules": [
        {
            "AllowedHeaders": ["*"],
            "AllowedMethods": ["GET", "PUT", "HEAD", "DELETE"],
            "AllowedOrigins": [
                "http://localhost:5173",
                "http://localhost:3000",
                "https://maplefile.ca",
                "https://www.maplefile.ca"
            ],
            "MaxAgeSeconds": 3600
        }
    ]
}
```

### 2.4 Test CORS with Preflight Request

```bash
# Test OPTIONS preflight request
curl -I -X OPTIONS \
  -H "Origin: http://localhost:5173" \
  -H "Access-Control-Request-Method: PUT" \
  "https://maplefile.tor1.digitaloceanspaces.com/test"

# Should return headers like:
# access-control-allow-origin: http://localhost:5173
# access-control-allow-methods: GET, PUT, HEAD, DELETE
```

**✅ Checkpoint:** CORS configuration applied and verified

---

## Step 3: Create Docker Secrets

If you're using separate API keys for MapleFile (recommended), create new secrets.

**On manager node:**

```bash
ssh dockeradmin@<manager-public-ip>
```

### 3.1 Create MapleFile Spaces Secrets

```bash
# Create secret for access key
echo -n "YOUR_ACCESS_KEY" | docker secret create maplefile_spaces_access_key -

# Create secret for secret key
echo -n "YOUR_SECRET_KEY" | docker secret create maplefile_spaces_secret_key -

# Verify
docker secret ls | grep maplefile_spaces
```

**If using same API keys as MaplePress**, you can reuse the existing secrets:
- `spaces_access_key`
- `spaces_secret_key`

### 3.2 Verify All MapleFile Secrets

```bash
docker secret ls | grep -E "maplefile|spaces"
```

**You should see:**

```
ID                          NAME                              CREATED
abc123...                   maplefile_jwt_secret             from 09_maplefile_backend.md
def456...                   maplefile_ip_encryption_key      from 09_maplefile_backend.md
ghi789...                   maplefile_spaces_access_key      NEW!
jkl012...                   maplefile_spaces_secret_key      NEW!
```

**✅ Checkpoint:** Docker secrets created

---

## Step 4: Verify Configuration

### 4.1 Test File Upload

```bash
# Create test file
echo "MapleFile test upload" > /tmp/maplefile-test.txt

# Upload to space
aws s3 cp /tmp/maplefile-test.txt s3://maplefile/test/test-file.txt \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Should show: upload: /tmp/maplefile-test.txt to s3://maplefile/test/test-file.txt
```

### 4.2 Test File Download

```bash
# Download from space
aws s3 cp s3://maplefile/test/test-file.txt /tmp/downloaded-test.txt \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Verify content
cat /tmp/downloaded-test.txt
# Should show: MapleFile test upload
```

### 4.3 Test Presigned URL Generation

The backend will generate presigned URLs for secure uploads. Test manually:

```bash
# Generate presigned upload URL (valid for 1 hour)
aws s3 presign s3://maplefile/test/presigned-test.txt \
  --expires-in 3600 \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Returns a URL like:
# https://maplefile.tor1.digitaloceanspaces.com/test/presigned-test.txt?X-Amz-Algorithm=...
```

### 4.4 Clean Up Test Files

```bash
# Delete test files
aws s3 rm s3://maplefile/test/ --recursive \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Clean up local files
rm /tmp/maplefile-test.txt /tmp/downloaded-test.txt
```

**✅ Checkpoint:** Upload, download, and presigned URLs working

---

## Troubleshooting

### Problem: CORS Error on File Upload

**Symptom:** Browser console shows:
```
Access to fetch at 'https://maplefile.tor1.digitaloceanspaces.com/...' from origin 'http://localhost:5173' has been blocked by CORS policy
```

**Causes and Solutions:**

1. **CORS not configured:**
   ```bash
   # Check CORS settings
   aws s3api get-bucket-cors \
     --bucket maplefile \
     --endpoint-url https://tor1.digitaloceanspaces.com \
     --profile digitalocean

   # If empty or error, apply CORS configuration (Step 2)
   ```

2. **Origin not in AllowedOrigins:**
   ```bash
   # Update CORS to include your frontend URL
   # Edit /tmp/maplefile-cors.json and re-apply
   ```

3. **Missing HTTP method:**
   - Ensure `PUT` is in `AllowedMethods` (required for presigned URL uploads)

### Problem: "AccessDenied" on Upload

**Symptom:** Presigned URL returns 403 Forbidden

**Causes:**

1. **Presigned URL expired:**
   - URLs have expiration time (default: 15 minutes)
   - Generate new URL and retry

2. **Wrong bucket in URL:**
   - Verify bucket name matches exactly

3. **Incorrect content type:**
   - Ensure Content-Type header matches what was signed

### Problem: "SignatureDoesNotMatch" Error

**Symptom:** Upload fails with signature error

**Causes:**

1. **Modified request headers:**
   - Don't add extra headers not in the signed request

2. **Wrong region in endpoint:**
   - Ensure endpoint matches bucket region

3. **Clock skew:**
   - Ensure system clock is synchronized

### Problem: Files Upload but Can't Download

**Symptom:** Upload succeeds but download returns 403

**Causes:**

1. **ACL not set:**
   - For public files, ensure ACL is set correctly
   - MapleFile uses private files with presigned download URLs

2. **Wrong presigned URL:**
   - Generate download URL, not upload URL

### Problem: CORS Works in Dev but Not Production

**Symptom:** Uploads work locally but fail in production

**Solution:**

```bash
# Add production domain to CORS
# Edit /tmp/maplefile-cors.json:
"AllowedOrigins": [
  "http://localhost:5173",
  "https://maplefile.ca",
  "https://www.maplefile.ca",
  "https://app.maplefile.ca"  # Add your production URLs
]

# Re-apply CORS
aws s3api put-bucket-cors \
  --bucket maplefile \
  --cors-configuration file:///tmp/maplefile-cors.json \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean
```

---

## Backend Configuration

The MapleFile backend uses these environment variables:

```yaml
# In maplefile-stack.yml
environment:
  - S3_ENDPOINT=https://tor1.digitaloceanspaces.com
  - S3_PUBLIC_ENDPOINT=https://maplefile.tor1.digitaloceanspaces.com
  - S3_BUCKET=maplefile
  - S3_REGION=tor1
  - S3_USE_SSL=true

secrets:
  - maplefile_spaces_access_key
  - maplefile_spaces_secret_key
```

---

## Next Steps

**You now have:**
- DigitalOcean Space for MapleFile
- CORS configured for browser uploads
- Docker secrets created
- Verified upload/download functionality

**Next guide:**
- Continue with **09_maplefile_backend.md** to deploy the backend

**Useful Commands:**

```bash
# List all files in MapleFile space
aws s3 ls s3://maplefile --recursive \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Get total space usage
aws s3 ls s3://maplefile --recursive --summarize --human-readable \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# Update CORS (after editing JSON)
aws s3api put-bucket-cors \
  --bucket maplefile \
  --cors-configuration file:///tmp/maplefile-cors.json \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean

# View current CORS
aws s3api get-bucket-cors \
  --bucket maplefile \
  --endpoint-url https://tor1.digitaloceanspaces.com \
  --profile digitalocean
```

---

**Last Updated**: November 2025
**Maintained By**: Infrastructure Team

**Changelog:**
- November 2025: Initial MapleFile Spaces setup guide with CORS configuration for browser uploads
