# Dropbox Integration Guide

## Overview
This guide explains how the application uses Dropbox for file storage instead of storing images directly in MongoDB. All image uploads (website screenshots, profile photos, company logos, etc.) are now stored in Dropbox, and only the public URLs are saved in the database.

## Why Dropbox Instead of MongoDB?

### Benefits:
1. **Database Performance**: Storing large binary files in MongoDB increases database size and slows down queries
2. **Cost Efficiency**: File storage services like Dropbox are more cost-effective for storing files
3. **Scalability**: Easier to scale file storage independently from database storage
4. **CDN Integration**: Dropbox provides fast content delivery globally
5. **Better Separation**: Clear separation between data (MongoDB) and assets (Dropbox)

## Setup Instructions

### 1. Get Dropbox Access Token

1. Go to [Dropbox App Console](https://www.dropbox.com/developers/apps)
2. Click "Create app"
3. Choose:
   - **API**: Scoped access
   - **Access type**: Full Dropbox
   - **App name**: Your app name (e.g., "CSSAwards File Storage")
4. Click "Create app"
5. In the app settings page:
   - Go to "Permissions" tab
   - Enable these permissions:
     - `files.content.write`
     - `files.content.read`
     - `sharing.write`
     - `sharing.read`
   - Click "Submit"
6. Go to "Settings" tab
7. Under "OAuth 2", find "Generated access token"
8. Click "Generate" button to create your access token
9. Copy the token (it will look like: `sl.xxxxxxxxxxxxxxxxxxxxxxxx`)

### 2. Update Environment Variables

Add your Dropbox access token to the `.env` file:

```env
DROPBOX_ACCESS_TOKEN=your_dropbox_access_token_here
```

Replace `your_dropbox_access_token_here` with the token you generated in step 1.

### 3. Verify Installation

The Dropbox package is already installed. If you need to reinstall:

```bash
npm install dropbox
```

## How It Works

### Architecture

```
Frontend (Base64 Image) 
    ↓
Backend Route Handler
    ↓
Dropbox Service (Upload to Dropbox)
    ↓
Get Public URL
    ↓
Save URL to MongoDB
    ↓
Return Response to Frontend
```

### File Upload Process

1. **Frontend sends base64 image**: The frontend sends the image as base64 encoded data
2. **Backend receives data**: The route handler extracts the base64 image
3. **Upload to Dropbox**: The `dropboxService.js` uploads the file to Dropbox
4. **Generate public URL**: Dropbox returns a shared link, which is converted to a direct URL
5. **Save URL to database**: Only the URL string is saved in MongoDB
6. **Return URL**: The backend responds with the Dropbox URL

### Folder Structure in Dropbox

Files are organized in the following folders:

- `/website-submissions/` - Website submission screenshots
- `/directory-submissions/` - Directory submission screenshots and company logos
- `/ai-website-submissions/` - AI website submission screenshots
- `/job-postings/` - Job posting company logos
- `/profile-photos/` - User profile photos

## Code Implementation

### Dropbox Service (`utils/dropboxService.js`)

The service provides these functions:

#### `uploadToDropbox(base64Data, filename, folder)`
Uploads a single file to Dropbox and returns the public URL.

**Parameters:**
- `base64Data` (string): Base64 encoded image (with or without data URL prefix)
- `filename` (string): Name for the file in Dropbox
- `folder` (string): Dropbox folder path (default: '/uploads')

**Returns:** Promise<string> - Public URL of the uploaded file

**Example:**
```javascript
const url = await uploadToDropbox(base64Image, 'website_123.jpg', '/website-submissions');
```

#### `uploadMultipleToDropbox(files, folder)`
Uploads multiple files at once.

**Parameters:**
- `files` (Array): Array of objects with `data` and `filename` properties
- `folder` (string): Dropbox folder path

**Returns:** Promise<Array<string>> - Array of public URLs

#### `generateUniqueFilename(originalName, prefix)`
Generates a unique filename with timestamp and random string.

**Parameters:**
- `originalName` (string): Original filename
- `prefix` (string): Prefix for the filename (e.g., 'website', 'profile')

**Returns:** string - Unique filename

**Example:**
```javascript
const filename = generateUniqueFilename('screenshot.jpg', 'website');
// Returns: website_1699876543210_abc123.jpg
```

#### `getFileExtensionFromBase64(base64Data)`
Extracts file extension from base64 data URL.

**Parameters:**
- `base64Data` (string): Base64 data URL

**Returns:** string - File extension (e.g., 'jpg', 'png')

#### `deleteFromDropbox(path)`
Deletes a file from Dropbox (for future cleanup needs).

**Parameters:**
- `path` (string): Full path of the file in Dropbox

**Returns:** Promise<boolean> - True if deleted successfully

## Updated Routes

### 1. Website Submission (`routes/website.route.js`)

**Before:**
```javascript
const payload = {
  Img: base64ImageData, // Stored directly in MongoDB
  // ... other fields
};
```

**After:**
```javascript
// Upload to Dropbox
const extension = getFileExtensionFromBase64(Img);
const filename = generateUniqueFilename(`${Title}.${extension}`, 'website');
const imageUrl = await uploadToDropbox(Img, filename, '/website-submissions');

const payload = {
  Img: imageUrl, // Store only the URL
  // ... other fields
};
```

### 2. Directory Submission (`routes/submitDirectory.js`)

Uploads two files:
- Screenshot: `screenshot`
- Company Logo: `companyLogo`

**Implementation:**
```javascript
// Upload screenshot
const screenshotExt = getFileExtensionFromBase64(screenshot);
const screenshotFilename = generateUniqueFilename(`${title}-screenshot.${screenshotExt}`, 'directory');
const screenshotUrl = await uploadToDropbox(screenshot, screenshotFilename, '/directory-submissions');

// Upload company logo
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,
  companyLogo: companyLogoUrl,
  // ... other fields
});
```

### 3. AI Website Submission (`routes/submitAiWebsite.js`)

**Implementation:**
```javascript
const extension = getFileExtensionFromBase64(screenshot);
const filename = generateUniqueFilename(`${title}-screenshot.${extension}`, 'ai-website');
const screenshotUrl = await uploadToDropbox(screenshot, filename, '/ai-website-submissions');

const newAiWebsite = new SubmitAiWebsite({
  screenshot: screenshotUrl,
  // ... other fields
});
```

### 4. Job Posting (`routes/postJob.js`)

**Implementation:**
```javascript
const extension = getFileExtensionFromBase64(companyLogo);
const filename = generateUniqueFilename(`${companyName}-logo.${extension}`, 'job-posting');
const companyLogoUrl = await uploadToDropbox(companyLogo, filename, '/job-postings');

const newPost = new PostJob({
  companyLogo: companyLogoUrl,
  // ... other fields
});
```

### 5. Profile Settings (`routes/settingProfile.js`)

**Implementation:**
```javascript
// Only upload if it's a new base64 image (not an existing URL)
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,
  // ... other fields
};
```

## Database Schema Changes

**No schema changes required!** The database fields remain the same (type: String), they just now store URLs instead of base64 data.

### Before:
```javascript
Img: "data:image/png;base64,iVBORw0KGgoAAAANSUhEU..." // ~50KB-500KB in database
```

### After:
```javascript
Img: "https://www.dropbox.com/s/abc123/website_123.jpg?raw=1" // ~80 bytes in database
```

## Error Handling

The service includes comprehensive error handling:

```javascript
try {
  const imageUrl = await uploadToDropbox(base64Data, filename, folder);
  // Continue with database operations
} catch (error) {
  console.error('❌ Error uploading to Dropbox:', error);
  return res.status(500).json({
    ok: false,
    error: "FILE_UPLOAD_FAILED",
    message: "Failed to upload file to Dropbox"
  });
}
```

## Testing

### Test File Upload

Create a test script (`test-dropbox-upload.js`):

```javascript
import { uploadToDropbox, generateUniqueFilename } from './utils/dropboxService.js';
import fs from 'fs';

async function testUpload() {
  try {
    // Read a test image
    const imageBuffer = fs.readFileSync('./test-image.jpg');
    const base64Image = imageBuffer.toString('base64');
    
    // Upload to Dropbox
    const filename = generateUniqueFilename('test-image.jpg', 'test');
    const url = await uploadToDropbox(base64Image, filename, '/test-uploads');
    
    console.log('✅ Upload successful!');
    console.log('📎 URL:', url);
  } catch (error) {
    console.error('❌ Upload failed:', error);
  }
}

testUpload();
```

Run the test:
```bash
node test-dropbox-upload.js
```

## Migration from MongoDB Storage

If you have existing data with base64 images in MongoDB, you'll need to migrate them:

### Migration Script Example

```javascript
import { uploadToDropbox, generateUniqueFilename, getFileExtensionFromBase64 } from './utils/dropboxService.js';
import WebsiteSubmission from './models/websiteSubmission.js';

async function migrateImages() {
  const submissions = await WebsiteSubmission.find({
    Img: { $regex: /^data:image/ } // Find base64 images
  });

  console.log(`Found ${submissions.length} submissions to migrate`);

  for (const submission of submissions) {
    try {
      // Upload to Dropbox
      const extension = getFileExtensionFromBase64(submission.Img);
      const filename = generateUniqueFilename(`${submission.Title}.${extension}`, 'website');
      const imageUrl = await uploadToDropbox(submission.Img, filename, '/website-submissions');

      // Update database
      submission.Img = imageUrl;
      await submission.save();

      console.log(`✅ Migrated: ${submission.Title}`);
    } catch (error) {
      console.error(`❌ Failed to migrate ${submission.Title}:`, error);
    }
  }

  console.log('Migration complete!');
}

migrateImages();
```

## Troubleshooting

### Error: "Access token is required"
- **Solution**: Make sure `DROPBOX_ACCESS_TOKEN` is set in your `.env` file

### Error: "Failed to upload file to Dropbox"
- **Check**: Your Dropbox access token has the correct permissions
- **Check**: The token hasn't expired (tokens don't expire but apps can be deactivated)
- **Check**: Your Dropbox account has sufficient storage space

### Error: "Invalid base64 string"
- **Solution**: Verify the frontend is sending properly encoded base64 data
- **Check**: The data URL prefix is correct (e.g., `data:image/jpeg;base64,`)

### Files not visible in Dropbox
- **Note**: Files are uploaded to the app folder if you selected "App folder" access
- **Path**: `/Apps/YourAppName/` in your Dropbox

### Shared links not working
- **Solution**: Make sure the link ends with `?raw=1` for direct access
- **Check**: The file visibility is set to "public"

## Best Practices

1. **Filename Generation**: Always use `generateUniqueFilename()` to avoid conflicts
2. **Error Handling**: Wrap all Dropbox operations in try-catch blocks
3. **Validation**: Validate image size and format before uploading
4. **Logging**: Log all upload operations for debugging
5. **Cleanup**: Consider implementing periodic cleanup of unused files
6. **Rate Limits**: Be aware of Dropbox API rate limits (currently 1,000 requests/hour for most operations)

## Security Considerations

1. **Access Token**: Never expose your Dropbox access token in client-side code
2. **File Validation**: Always validate file type and size on the backend
3. **Permissions**: Use scoped access tokens with minimum required permissions
4. **User Data**: Ensure profile photos and submissions are properly isolated

## Performance Optimization

1. **Parallel Uploads**: Use `uploadMultipleToDropbox()` for multiple files
2. **Async Operations**: All operations are async for better performance
3. **CDN**: Dropbox URLs are CDN-backed for fast global delivery
4. **Caching**: Consider implementing cache headers for frequently accessed files

## Monitoring

### Track Upload Metrics

```javascript
// Add to your logging/monitoring service
console.log('📊 Upload Metrics:', {
  fileSize: (fileBuffer.length / 1024).toFixed(2) + ' KB',
  folder: folder,
  filename: filename,
  uploadTime: Date.now() - startTime + 'ms'
});
```

## Future Enhancements

1. **Image Optimization**: Compress images before uploading
2. **Thumbnail Generation**: Create and store thumbnail versions
3. **File Cleanup**: Implement automatic cleanup of orphaned files
4. **Multiple Storage Options**: Add support for AWS S3, Google Cloud Storage as alternatives
5. **Direct Upload**: Implement frontend direct upload to Dropbox for larger files

## Support

For issues or questions:
1. Check this documentation
2. Review the Dropbox API documentation: https://www.dropbox.com/developers/documentation
3. Check the console logs for detailed error messages
4. Verify your environment variables are correctly set

## Version History

- **v1.0.0** (2024-11-12): Initial implementation with support for all file upload routes
