# MyCloudVault Backup Upload API

## Overview

The Backup Upload API enables Android devices to upload backup files to MyCloudVault servers. It supports both single-file uploads and chunked uploads for large files, with progress tracking and automatic file organization.

---

## Storage Architecture

### File Organization

```
/private/backups/
├── {deviceId}/
│   ├── {backupId}_{timestamp}.backup
│   ├── {backupId}_{timestamp}.zip
│   └── ...
└── temp/
    └── {deviceId}/
        └── {backupId}/
            ├── chunk_0
            ├── chunk_1
            └── ...
```

- **Final backups**: `/private/backups/{deviceId}/`
- **Temporary chunks**: `/private/backups/temp/{deviceId}/{backupId}/`
- **Automatic cleanup**: Temp files deleted after merge

---

## API Endpoints

### Base URL
```
https://vm6tgfyizy.preview.c24.airoapp.ai
```

---

## 1. Single File Upload

**Endpoint:** `POST /api/backup/upload`

**Purpose:** Upload a complete backup file in a single request.

**Best for:** Files under 100 MB

**Content-Type:** `multipart/form-data`

**Form Fields:**
- `file` (required): The backup file
- `deviceId` (required): Device ID
- `backupId` (optional): Backup ID (auto-generated if not provided)
- `backupType` (optional): 'full' | 'incremental' | 'differential' (default: 'full')
- `metadata` (optional): JSON string with additional backup info

**Example Request (cURL):**
```bash
curl -X POST https://vm6tgfyizy.preview.c24.airoapp.ai/api/backup/upload \
  -F "file=@/path/to/backup.zip" \
  -F "deviceId=android-123" \
  -F "backupType=full" \
  -F 'metadata={"appVersion":"1.0.0","androidVersion":"14"}'
```

**Response:**
```json
{
  "success": true,
  "backup": {
    "id": 1,
    "backupId": "a1b2c3d4e5f6...",
    "deviceId": "android-123",
    "backupType": "full",
    "status": "completed",
    "size": 1250,
    "filePath": "/private/backups/android-123/a1b2c3d4e5f6_1702411200000.zip",
    "startedAt": "2025-12-12T21:00:00.000Z",
    "completedAt": "2025-12-12T21:00:30.000Z"
  },
  "message": "Backup uploaded successfully"
}
```

**Allowed File Types:**
- `.backup`
- `.bak`
- `.zip`
- `.tar`
- `.gz`
- `.7z`
- No extension (treated as `.backup`)

**File Size Limit:** 10 GB

---

## 2. Chunked Upload

**Endpoint:** `POST /api/backup/upload-chunk`

**Purpose:** Upload large files in chunks for reliability.

**Best for:** Files over 100 MB

**Content-Type:** `multipart/form-data`

**Form Fields:**
- `chunk` (required): The file chunk
- `deviceId` (required): Device ID
- `backupId` (required): Backup ID (must be same for all chunks)
- `chunkIndex` (required): Current chunk index (0-based)
- `totalChunks` (required): Total number of chunks
- `fileName` (required): Original file name
- `backupType` (optional): 'full' | 'incremental' | 'differential' (default: 'full')
- `metadata` (optional): JSON string (only needed on last chunk)

**Example Request (cURL):**
```bash
# Upload chunk 0
curl -X POST https://vm6tgfyizy.preview.c24.airoapp.ai/api/backup/upload-chunk \
  -F "chunk=@/path/to/chunk_0" \
  -F "deviceId=android-123" \
  -F "backupId=backup-456" \
  -F "chunkIndex=0" \
  -F "totalChunks=5" \
  -F "fileName=backup.zip" \
  -F "backupType=full"

# Upload chunk 1
curl -X POST https://vm6tgfyizy.preview.c24.airoapp.ai/api/backup/upload-chunk \
  -F "chunk=@/path/to/chunk_1" \
  -F "deviceId=android-123" \
  -F "backupId=backup-456" \
  -F "chunkIndex=1" \
  -F "totalChunks=5" \
  -F "fileName=backup.zip"

# ... continue for all chunks
```

**Response (Intermediate Chunk):**
```json
{
  "success": true,
  "chunkIndex": 0,
  "received": true,
  "complete": false,
  "receivedChunks": 1,
  "totalChunks": 5
}
```

**Response (Final Chunk):**
```json
{
  "success": true,
  "chunkIndex": 4,
  "received": true,
  "complete": true,
  "backup": {
    "id": 2,
    "backupId": "backup-456",
    "deviceId": "android-123",
    "backupType": "full",
    "status": "completed",
    "size": 5120,
    "filePath": "/private/backups/android-123/backup-456_1702411800000.zip",
    "startedAt": "2025-12-12T21:10:00.000Z",
    "completedAt": "2025-12-12T21:15:00.000Z"
  },
  "message": "Backup uploaded successfully"
}
```

**Chunk Size Limit:** 100 MB per chunk

**Recommended Chunk Size:** 50 MB for optimal performance

---

## 3. Upload Progress

**Endpoint:** `GET /api/backup/progress/:backupId`

**Purpose:** Check upload progress for chunked uploads.

**Query Parameters:**
- `deviceId` (required): Device ID

**Example Request:**
```
GET /api/backup/progress/backup-456?deviceId=android-123
```

**Response (Uploading):**
```json
{
  "success": true,
  "progress": {
    "backupId": "backup-456",
    "status": "uploading",
    "chunksReceived": 3,
    "totalChunks": 5,
    "percentComplete": 60
  }
}
```

**Response (Completed):**
```json
{
  "success": true,
  "progress": {
    "backupId": "backup-456",
    "status": "completed",
    "percentComplete": 100,
    "backup": {
      "id": 2,
      "backupId": "backup-456",
      "deviceId": "android-123",
      "status": "completed",
      "size": 5120
    }
  }
}
```

---

## Implementation Examples

### Android (Kotlin) - Single File Upload

```kotlin
import okhttp3.*
import java.io.File

class BackupUploader(private val deviceId: String) {
    private val client = OkHttpClient()
    private val baseUrl = "https://vm6tgfyizy.preview.c24.airoapp.ai"

    fun uploadBackup(file: File, backupType: String = "full"): Response {
        val requestBody = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("file", file.name,
                RequestBody.create(MediaType.parse("application/octet-stream"), file)
            )
            .addFormDataPart("deviceId", deviceId)
            .addFormDataPart("backupType", backupType)
            .addFormDataPart("metadata", """
                {
                    "appVersion": "1.0.0",
                    "androidVersion": "${Build.VERSION.RELEASE}",
                    "deviceModel": "${Build.MODEL}"
                }
            """.trimIndent())
            .build()

        val request = Request.Builder()
            .url("$baseUrl/api/backup/upload")
            .post(requestBody)
            .build()

        return client.newCall(request).execute()
    }
}

// Usage
val uploader = BackupUploader("android-123")
val backupFile = File("/path/to/backup.zip")
val response = uploader.uploadBackup(backupFile)

if (response.isSuccessful) {
    println("Backup uploaded successfully")
} else {
    println("Upload failed: ${response.message}")
}
```

### Android (Kotlin) - Chunked Upload

```kotlin
import okhttp3.*
import java.io.File
import java.io.FileInputStream
import java.util.UUID

class ChunkedBackupUploader(private val deviceId: String) {
    private val client = OkHttpClient()
    private val baseUrl = "https://vm6tgfyizy.preview.c24.airoapp.ai"
    private val chunkSize = 50 * 1024 * 1024 // 50 MB

    fun uploadBackupChunked(
        file: File,
        backupType: String = "full",
        onProgress: (Int, Int) -> Unit
    ): Boolean {
        val backupId = UUID.randomUUID().toString()
        val totalChunks = (file.length() / chunkSize).toInt() + 1

        FileInputStream(file).use { inputStream ->
            val buffer = ByteArray(chunkSize)
            var chunkIndex = 0

            while (true) {
                val bytesRead = inputStream.read(buffer)
                if (bytesRead == -1) break

                val chunkData = buffer.copyOf(bytesRead)
                val success = uploadChunk(
                    chunkData,
                    backupId,
                    chunkIndex,
                    totalChunks,
                    file.name,
                    backupType
                )

                if (!success) {
                    return false
                }

                chunkIndex++
                onProgress(chunkIndex, totalChunks)
            }
        }

        return true
    }

    private fun uploadChunk(
        chunkData: ByteArray,
        backupId: String,
        chunkIndex: Int,
        totalChunks: Int,
        fileName: String,
        backupType: String
    ): Boolean {
        val requestBody = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("chunk", "chunk_$chunkIndex",
                RequestBody.create(MediaType.parse("application/octet-stream"), chunkData)
            )
            .addFormDataPart("deviceId", deviceId)
            .addFormDataPart("backupId", backupId)
            .addFormDataPart("chunkIndex", chunkIndex.toString())
            .addFormDataPart("totalChunks", totalChunks.toString())
            .addFormDataPart("fileName", fileName)
            .addFormDataPart("backupType", backupType)
            .build()

        val request = Request.Builder()
            .url("$baseUrl/api/backup/upload-chunk")
            .post(requestBody)
            .build()

        val response = client.newCall(request).execute()
        return response.isSuccessful
    }
}

// Usage
val uploader = ChunkedBackupUploader("android-123")
val backupFile = File("/path/to/large-backup.zip")

val success = uploader.uploadBackupChunked(backupFile) { current, total ->
    val progress = (current.toFloat() / total * 100).toInt()
    println("Upload progress: $progress%")
}

if (success) {
    println("Backup uploaded successfully")
} else {
    println("Upload failed")
}
```

### Android (Kotlin) - Progress Tracking

```kotlin
import okhttp3.*
import org.json.JSONObject

class ProgressTracker(private val deviceId: String) {
    private val client = OkHttpClient()
    private val baseUrl = "https://vm6tgfyizy.preview.c24.airoapp.ai"

    fun checkProgress(backupId: String): UploadProgress? {
        val request = Request.Builder()
            .url("$baseUrl/api/backup/progress/$backupId?deviceId=$deviceId")
            .get()
            .build()

        val response = client.newCall(request).execute()
        if (!response.isSuccessful) return null

        val json = JSONObject(response.body()?.string())
        val progress = json.getJSONObject("progress")

        return UploadProgress(
            backupId = progress.getString("backupId"),
            status = progress.getString("status"),
            chunksReceived = progress.optInt("chunksReceived", 0),
            totalChunks = progress.optInt("totalChunks", 0),
            percentComplete = progress.optInt("percentComplete", 0)
        )
    }
}

data class UploadProgress(
    val backupId: String,
    val status: String,
    val chunksReceived: Int,
    val totalChunks: Int,
    val percentComplete: Int
)

// Usage
val tracker = ProgressTracker("android-123")
val progress = tracker.checkProgress("backup-456")

progress?.let {
    println("Status: ${it.status}")
    println("Progress: ${it.percentComplete}%")
    println("Chunks: ${it.chunksReceived}/${it.totalChunks}")
}
```

---

## Best Practices

### 1. Choose Upload Method
- **Single upload**: Files < 100 MB
- **Chunked upload**: Files > 100 MB
- **Chunk size**: 50 MB recommended

### 2. Error Handling
- Implement retry logic with exponential backoff
- Resume failed chunked uploads from last successful chunk
- Validate file integrity after upload

### 3. Progress Tracking
- Poll progress endpoint every 5-10 seconds during upload
- Display progress to user
- Handle network interruptions gracefully

### 4. Metadata
- Include device info (model, OS version, app version)
- Add backup timestamp and type
- Store file checksums for verification

### 5. Security
- Always use HTTPS
- Validate file types before upload
- Implement authentication (device pairing)
- Consider encrypting backup files

---

## Error Codes

| Status Code | Error | Description |
|-------------|-------|-------------|
| 400 | Bad Request | Missing required fields or invalid file type |
| 413 | Payload Too Large | File or chunk exceeds size limit |
| 500 | Internal Server Error | Server-side processing error |

---

## Troubleshooting

### Upload Fails
- Check file size limits
- Verify file type is allowed
- Ensure device ID is correct
- Check network connectivity

### Chunked Upload Incomplete
- Verify all chunks were sent
- Check chunk indices are sequential
- Ensure backupId is same for all chunks
- Check temp directory for partial uploads

### Progress Not Updating
- Verify backupId is correct
- Check deviceId matches
- Ensure chunks are being received
- Check server logs for errors

---

## Support

For issues or questions, refer to the main MyCloudVault documentation.
