# File Upload Migration: MongoDB → Dropbox

## Summary of Changes

This document provides a before/after comparison of how file uploads are handled in the application.

## Overview

**Previous Approach**: Store base64-encoded images directly in MongoDB  
**New Approach**: Upload images to Dropbox and store only URLs in MongoDB

---

## Before & After Comparison

### 1. Website Submission (`routes/website.route.js`)

#### Before:
```javascript
import { Router } from "express";
import WebsiteSubmission from "../models/websiteSubmission.js";

router.post("/", authenticate, async (req, res) => {
  const { Img, Title, Description } = req.body;
  
  // Img contains base64 data (50KB - 500KB)
  const payload = {
    Img: Img, // Directly store base64 in MongoDB
    Title,
    Description,
    // ...
  };
  
  await WebsiteSubmission.create(payload);
});
```

#### After:
```javascript
import { Router } from "express";
import WebsiteSubmission from "../models/websiteSubmission.js";
import { uploadToDropbox, generateUniqueFilename, getFileExtensionFromBase64 } from "../utils/dropboxService.js";

router.post("/", authenticate, async (req, res) => {
  const { Img, Title, Description } = req.body;
  
  // Upload to Dropbox first
  const extension = getFileExtensionFromBase64(Img);
  const filename = generateUniqueFilename(`${Title}.${extension}`, 'website');
  const imageUrl = await uploadToDropbox(Img, filename, '/website-submissions');
  
  // Store only the URL (~80 bytes)
  const payload = {
    Img: imageUrl, // Dropbox URL instead of base64
    Title,
    Description,
    // ...
  };
  
  await WebsiteSubmission.create(payload);
});
```

---

### 2. Directory Submission (`routes/submitDirectory.js`)

#### Before:
```javascript
router.post('/submit', authenticate, async (req, res) => {
  const { screenshot, companyLogo, title } = req.body;
  
  const newDirectory = new SubmitDirectory({
    screenshot: screenshot, // Base64 data in MongoDB
    companyLogo: companyLogo, // Base64 data in MongoDB
    title,
    // ...
  });
  
  await newDirectory.save();
});
```

#### After:
```javascript
import { uploadToDropbox, generateUniqueFilename, getFileExtensionFromBase64 } from '../utils/dropboxService.js';

router.post('/submit', authenticate, async (req, res) => {
  const { screenshot, companyLogo, title } = req.body;
  
  // Upload both files to Dropbox
  const screenshotExt = getFileExtensionFromBase64(screenshot);
  const screenshotFilename = generateUniqueFilename(`${title}-screenshot.${screenshotExt}`, 'directory');
  const screenshotUrl = await uploadToDropbox(screenshot, screenshotFilename, '/directory-submissions');
  
  const logoExt = getFileExtensionFromBase64(companyLogo);
  const logoFilename = generateUniqueFilename(`${title}-logo.${logoExt}`, 'directory');
  const companyLogoUrl = await uploadToDropbox(companyLogo, logoFilename, '/directory-submissions');
  
  const newDirectory = new SubmitDirectory({
    screenshot: screenshotUrl, // Dropbox URL
    companyLogo: companyLogoUrl, // Dropbox URL
    title,
    // ...
  });
  
  await newDirectory.save();
});
```

---

### 3. Profile Settings (`routes/settingProfile.js`)

#### Before:
```javascript
router.post("/", authenticate, async (req, res) => {
  const { ProfilePhoto, Username } = req.body;
  
  const profileData = {
    profilePhoto: ProfilePhoto, // Base64 data
    username: Username,
    // ...
  };
  
  await SettingProfile.create(profileData);
});
```

#### After:
```javascript
import { uploadToDropbox, generateUniqueFilename, getFileExtensionFromBase64 } from "../utils/dropboxService.js";

router.post("/", authenticate, async (req, res) => {
  const { ProfilePhoto, Username } = req.body;
  
  // Upload only if it's a new base64 image
  let profilePhotoUrl = ProfilePhoto;
  if (ProfilePhoto && ProfilePhoto.startsWith('data:image')) {
    const extension = getFileExtensionFromBase64(ProfilePhoto);
    const filename = generateUniqueFilename(`${Username}-profile.${extension}`, 'profile');
    profilePhotoUrl = await uploadToDropbox(ProfilePhoto, filename, '/profile-photos');
  }
  
  const profileData = {
    profilePhoto: profilePhotoUrl, // Dropbox URL
    username: Username,
    // ...
  };
  
  await SettingProfile.create(profileData);
});
```

---

## Database Content Comparison

### MongoDB Document - Before:
```json
{
  "_id": "507f1f77bcf86cd799439011",
  "Title": "Beautiful Portfolio Website",
  "Img": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEB...[50,000 more characters]",
  "Description": "A stunning portfolio",
  "createdAt": "2024-11-12T10:00:00.000Z"
}
```
**Size**: ~52 KB per document

### MongoDB Document - After:
```json
{
  "_id": "507f1f77bcf86cd799439011",
  "Title": "Beautiful Portfolio Website",
  "Img": "https://www.dropbox.com/s/abc123def456/website_1699876543210_xyz789.jpg?raw=1",
  "Description": "A stunning portfolio",
  "createdAt": "2024-11-12T10:00:00.000Z"
}
```
**Size**: ~0.5 KB per document (99% reduction!)

---

## Performance Impact

### Database Query Speed

#### Before:
- Large documents slow down queries
- Indexes are less effective with large binary data
- Memory consumption increases with each document loaded

#### After:
- Fast queries with small document sizes
- Efficient indexing
- Minimal memory footprint

### Storage Costs

#### Before:
- 1,000 images × 200 KB average = **200 MB** in MongoDB
- MongoDB charges premium for database storage

#### After:
- 1,000 images × 80 bytes average = **78 KB** in MongoDB
- Actual images stored in Dropbox (cheaper storage)

---

## File Upload Flow

### Before:
```
Frontend
    ↓ (Base64 image ~200KB)
Backend
    ↓ (Store in MongoDB)
Database (Large document)
```

### After:
```
Frontend
    ↓ (Base64 image ~200KB)
Backend
    ↓ (Upload to Dropbox)
Dropbox (Store file, return URL)
    ↓ (URL ~80 bytes)
Backend
    ↓ (Store URL in MongoDB)
Database (Small document)
```

---

## New Utility Service: `dropboxService.js`

### Key Functions:

```javascript
// Upload a file and get URL back
uploadToDropbox(base64Data, filename, folder)
// Returns: "https://www.dropbox.com/s/abc123/file.jpg?raw=1"

// Generate unique filename with timestamp
generateUniqueFilename(originalName, prefix)
// Returns: "profile_1699876543210_xyz789.jpg"

// Extract file extension from base64
getFileExtensionFromBase64(base64Data)
// Returns: "jpg", "png", "gif", etc.

// Upload multiple files at once
uploadMultipleToDropbox(files, folder)
// Returns: ["url1", "url2", "url3"]

// Delete file from Dropbox
deleteFromDropbox(path)
// Returns: true/false
```

---

## Environment Configuration

### New .env Variable:
```env
# Dropbox Configuration
DROPBOX_ACCESS_TOKEN=sl.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

**How to get token**: See `DROPBOX_SETUP_SUMMARY.md`

---

## Folder Organization in Dropbox

```
/Apps/CSSAwards-FileStorage/
├── website-submissions/
│   ├── website_1699876543210_xyz789.jpg
│   ├── website_1699876544320_abc123.png
│   └── ...
├── directory-submissions/
│   ├── acme-corp-screenshot_1699876545430_def456.jpg
│   ├── acme-corp-logo_1699876545431_ghi789.png
│   └── ...
├── ai-website-submissions/
│   ├── ai-tool-screenshot_1699876546540_jkl012.jpg
│   └── ...
├── job-postings/
│   ├── company-logo_1699876547650_mno345.jpg
│   └── ...
└── profile-photos/
    ├── john-profile_1699876548760_pqr678.jpg
    └── ...
```

---

## Error Handling

### Before:
```javascript
try {
  await WebsiteSubmission.create({ Img: base64Data });
} catch (error) {
  res.status(500).json({ error: "Database error" });
}
```

### After:
```javascript
try {
  // Upload to Dropbox
  const imageUrl = await uploadToDropbox(base64Data, filename, folder);
  
  // Save to database
  await WebsiteSubmission.create({ Img: imageUrl });
} catch (error) {
  if (error.message.includes('Dropbox')) {
    res.status(500).json({ error: "File upload failed" });
  } else {
    res.status(500).json({ error: "Database error" });
  }
}
```

---

## Testing

### Test Connection:
```bash
node test-dropbox-connection.js
```

This script will:
1. ✅ Check if access token is configured
2. ✅ Upload a test image to Dropbox
3. ✅ Verify the URL is accessible
4. ✅ Confirm integration is working

---

## Migration Checklist

- [x] Install Dropbox SDK package
- [x] Create Dropbox service utility
- [x] Update environment variables
- [x] Modify website submission route
- [x] Modify directory submission route
- [x] Modify AI website submission route
- [x] Modify job posting route
- [x] Modify profile settings route
- [x] Create documentation
- [x] Create test script
- [ ] **Get Dropbox access token** ⚠️ REQUIRED
- [ ] **Add token to .env file** ⚠️ REQUIRED
- [ ] **Test file uploads** ⚠️ REQUIRED

---

## Benefits Summary

| Aspect | Before (MongoDB) | After (Dropbox) | Improvement |
|--------|------------------|-----------------|-------------|
| **Document Size** | ~50 KB | ~0.5 KB | 99% smaller |
| **Query Speed** | Slow | Fast | Much faster |
| **Storage Cost** | High | Low | Lower cost |
| **Scalability** | Limited | Excellent | Highly scalable |
| **CDN Support** | No | Yes | Global delivery |
| **File Management** | Difficult | Easy | Better organized |

---

## Important Notes

1. **No Frontend Changes**: Frontend continues to send base64 images as before
2. **No Schema Changes**: Database models remain the same (String type)
3. **Backward Compatible**: Existing URLs in database continue to work
4. **Token Required**: App won't work without `DROPBOX_ACCESS_TOKEN` set

---

## Next Steps

1. **Get Dropbox Token**: Follow steps in `DROPBOX_SETUP_SUMMARY.md`
2. **Update .env**: Add your token to the environment file
3. **Test Setup**: Run `node test-dropbox-connection.js`
4. **Deploy**: Deploy the updated code
5. **Monitor**: Watch for any upload issues in production
6. **Migrate Old Data**: Optionally migrate existing base64 images (see `DROPBOX_INTEGRATION_GUIDE.md`)

---

**Status**: ✅ Code Complete - Configuration Required  
**Date**: November 12, 2025  
**Impact**: High - All file uploads use Dropbox  
**Risk**: Low - Backward compatible, no breaking changes
