# Blackberry Farm Server API Setup

## Overview
This document outlines the server-side setup required for plant image storage on blackberries.homesteadingoutlaws.com.

## 1. Database Setup

First, create the plant_images table in your MySQL database:

```sql
CREATE TABLE plant_images (
    id INT PRIMARY KEY AUTO_INCREMENT,
    plant_id VARCHAR(255) NOT NULL,
    filename VARCHAR(255) NOT NULL,
    original_filename VARCHAR(255),
    file_path VARCHAR(500) NOT NULL,
    file_size INT,
    notes TEXT,
    growth_stage VARCHAR(100),
    captured_at TIMESTAMP NULL,
    uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_plant_id (plant_id),
    INDEX idx_uploaded_at (uploaded_at)
);
```

## 2. Directory Structure

Create the following directory structure on your server:

```
/public_html/api/v1/
├── plant-images.php          # Main endpoint handler
├── config/
│   └── database.php          # Database configuration
├── includes/
│   ├── cors.php              # CORS headers
│   └── auth.php              # Authentication (optional)
└── uploads/
    └── plant-images/         # Image storage directory (755 permissions)
```

## 3. File Contents

### /api/v1/config/database.php
```php
<?php
// Database configuration
$servername = "localhost"; // or your database server
$username = "your_db_username";
$password = "your_db_password";
$dbname = "your_database_name";

try {
    $pdo = new PDO("mysql:host=$servername;dbname=$dbname;charset=utf8mb4", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch(PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Database connection failed']);
    exit();
}
?>
```

### /api/v1/includes/cors.php
```php
<?php
// Set CORS headers to allow requests from your Flutter app
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
header('Content-Type: application/json');

// Handle preflight OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit();
}
?>
```

### /api/v1/plant-images.php
```php
<?php
require_once 'includes/cors.php';
require_once 'config/database.php';

// Get request method
$method = $_SERVER['REQUEST_METHOD'];

switch($method) {
    case 'POST':
        handleImageUpload();
        break;
    case 'GET':
        handleGetImages();
        break;
    case 'DELETE':
        handleDeleteImage();
        break;
    default:
        http_response_code(405);
        echo json_encode(['error' => 'Method not allowed']);
        break;
}

function handleImageUpload() {
    global $pdo;
    
    try {
        // Check if image file was uploaded
        if (!isset($_FILES['image'])) {
            throw new Exception('No image file provided');
        }
        
        $image = $_FILES['image'];
        $plantId = $_POST['plant_id'] ?? '';
        $notes = $_POST['notes'] ?? '';
        $growthStage = $_POST['growth_stage'] ?? '';
        $capturedAt = $_POST['captured_at'] ?? date('Y-m-d H:i:s');
        
        if (empty($plantId)) {
            throw new Exception('Plant ID is required');
        }
        
        // Validate file
        $allowedTypes = ['image/jpeg', 'image/jpg', 'image/png'];
        $maxFileSize = 10 * 1024 * 1024; // 10MB
        
        if (!in_array($image['type'], $allowedTypes)) {
            throw new Exception('Invalid file type. Only JPEG and PNG allowed.');
        }
        
        if ($image['size'] > $maxFileSize) {
            throw new Exception('File size too large. Maximum 10MB allowed.');
        }
        
        // Create upload directory if it doesn't exist
        $uploadDir = __DIR__ . '/uploads/plant-images/';
        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }
        
        // Generate unique filename
        $fileExtension = pathinfo($image['name'], PATHINFO_EXTENSION);
        $filename = 'plant_' . $plantId . '_' . time() . '_' . uniqid() . '.' . $fileExtension;
        $filePath = $uploadDir . $filename;
        $relativePath = 'uploads/plant-images/' . $filename;
        
        // Move uploaded file
        if (!move_uploaded_file($image['tmp_name'], $filePath)) {
            throw new Exception('Failed to save image file');
        }
        
        // Save to database
        $stmt = $pdo->prepare("
            INSERT INTO plant_images (plant_id, filename, original_filename, file_path, file_size, notes, growth_stage, captured_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ");
        
        $stmt->execute([
            $plantId,
            $filename,
            $image['name'],
            $relativePath,
            $image['size'],
            $notes,
            $growthStage,
            $capturedAt
        ]);
        
        $imageId = $pdo->lastInsertId();
        
        // Return success response
        echo json_encode([
            'success' => true,
            'message' => 'Image uploaded successfully',
            'data' => [
                'id' => $imageId,
                'plant_id' => $plantId,
                'filename' => $filename,
                'file_path' => $relativePath,
                'file_size' => $image['size'],
                'notes' => $notes,
                'growth_stage' => $growthStage,
                'captured_at' => $capturedAt,
                'uploaded_at' => date('Y-m-d H:i:s')
            ]
        ]);
        
    } catch (Exception $e) {
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'error' => $e->getMessage()
        ]);
    }
}

function handleGetImages() {
    global $pdo;
    
    try {
        $plantId = $_GET['plant_id'] ?? '';
        
        if (empty($plantId)) {
            // Get all images
            $stmt = $pdo->prepare("SELECT * FROM plant_images ORDER BY uploaded_at DESC");
            $stmt->execute();
        } else {
            // Get images for specific plant
            $stmt = $pdo->prepare("SELECT * FROM plant_images WHERE plant_id = ? ORDER BY uploaded_at DESC");
            $stmt->execute([$plantId]);
        }
        
        $images = $stmt->fetchAll();
        
        // Add full URL to file paths
        $baseUrl = 'https://blackberries.homesteadingoutlaws.com/api/v1/';
        foreach ($images as &$image) {
            $image['file_url'] = $baseUrl . $image['file_path'];
        }
        
        echo json_encode([
            'success' => true,
            'data' => $images,
            'count' => count($images)
        ]);
        
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'success' => false,
            'error' => $e->getMessage()
        ]);
    }
}

function handleDeleteImage() {
    global $pdo;
    
    try {
        // Get image ID from URL path
        $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        $pathParts = explode('/', trim($path, '/'));
        $imageId = end($pathParts);
        
        if (!is_numeric($imageId)) {
            throw new Exception('Invalid image ID');
        }
        
        // Get image details
        $stmt = $pdo->prepare("SELECT * FROM plant_images WHERE id = ?");
        $stmt->execute([$imageId]);
        $image = $stmt->fetch();
        
        if (!$image) {
            throw new Exception('Image not found');
        }
        
        // Delete file
        $filePath = __DIR__ . '/' . $image['file_path'];
        if (file_exists($filePath)) {
            unlink($filePath);
        }
        
        // Delete from database
        $stmt = $pdo->prepare("DELETE FROM plant_images WHERE id = ?");
        $stmt->execute([$imageId]);
        
        echo json_encode([
            'success' => true,
            'message' => 'Image deleted successfully'
        ]);
        
    } catch (Exception $e) {
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'error' => $e->getMessage()
        ]);
    }
}
?>
```

## 4. .htaccess Configuration

Create `/api/v1/.htaccess` to handle URL routing:

```apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# Route plant-images requests
RewriteRule ^plant-images/?(.*)$ plant-images.php [QSA,L]
```

## 5. Security Considerations

### File Upload Security
- Validate file types (JPEG, PNG only)
- Limit file sizes (10MB max)
- Generate unique filenames
- Store uploads outside web root if possible
- Scan uploaded files for malware

### Access Control
- Add authentication if needed
- Implement rate limiting
- Validate CSRF tokens
- Use HTTPS only

### Database Security
- Use prepared statements (already implemented)
- Validate all input data
- Implement proper error handling

## 6. Testing the API

After setup, test the endpoints:

```bash
# Upload image
curl -X POST https://blackberries.homesteadingoutlaws.com/api/v1/plant-images \
  -F "image=@test-image.jpg" \
  -F "plant_id=BB-001" \
  -F "notes=Test upload" \
  -F "growth_stage=flowering"

# Get images for plant
curl "https://blackberries.homesteadingoutlaws.com/api/v1/plant-images?plant_id=BB-001"

# Get all images
curl "https://blackberries.homesteadingoutlaws.com/api/v1/plant-images"

# Delete image
curl -X DELETE "https://blackberries.homesteadingoutlaws.com/api/v1/plant-images/1"
```

## 7. Deployment Steps

1. **Upload files to server** in the correct directory structure
2. **Create database table** using the SQL above
3. **Update database.php** with your actual database credentials
4. **Set directory permissions**:
   ```bash
   chmod 755 /api/v1/uploads/plant-images/
   chown www-data:www-data /api/v1/uploads/plant-images/
   ```
5. **Test endpoints** using curl or Postman
6. **Update mobile app** to use the new endpoints
7. **Monitor server logs** for any issues

## 8. Mobile App Updates

Once server is set up, update the Flutter app's `PlantImageService` to use:
- `POST /api/v1/plant-images` for uploads
- `GET /api/v1/plant-images?plant_id=X` for fetching images
- `DELETE /api/v1/plant-images/{id}` for deletion

The existing local storage and sync functionality will automatically work with these endpoints.