# File Manager

# Uploading New File
Sebelum hit API, FE ambil data per file:
- `name` (contoh: `spidey.png`)
- `content_type` (contoh: `image/png`)
- `size_bytes` (contoh: `42170`)

## Step 1 - FE minta upload intent ke BE, lalu upload langsung ke storage

## A. Minta Upload Intent ke Backend

### Endpoint
- `POST /api/v1/file-manager/files/upload`

### FE kirim request ini
- Single File

```json
{
  "data": [
    {
      "type": "file_manager_file_upload_intent",
      "attributes": {
        "name": "image.png",
        "content_type": "image/png",
        "size_bytes": 123
      }
    }
  ]
}
```

- Bulk File

```json
{
  "data": [
    {
      "type": "file_manager_file_upload_intent",
      "attributes": {
        "name": "image1.png",
        "content_type": "image/png",
        "size_bytes": 123
      }
    },
    {
      "type": "file_manager_file_upload_intent",
      "attributes": {
        "name": "image2.png",
        "content_type": "image/png",
        "size_bytes": 456
      }
    }
  ]
}
```

### Backend akan balas per file
- `upload_intent_uuid`
- `upload_url`
- `method` (`PUT`)
- `required_headers` 
- `object.bucket`
- `object.key`
- `expires_at`
- `expires_in_seconds`

Contoh response:

```json
{
  "data": [
    {
      "index": 0,
      "success": true,
      "resource": {
        "type": "file_manager_file_upload_intent",
        "id": "019d8215-c622-7576-86bd-3ddeecaf4867",
        "attributes": {
          "upload_intent_uuid": "019d8215-c622-7576-86bd-3ddeecaf4867",
          "upload_url": "http://localhost:4566/...",
          "method": "PUT",
          "required_headers": {
            "content-type": "image/png"
          },
          "object": {
            "bucket": "wucher-file-dev",
            "key": "fm/objects/files/2026/04/12/019d8215c6227a1aa20fc21f99f36584.png"
          },
          "file": {
            "name": "spidey.png",
            "mime_type": "image/png",
            "size_bytes": 42170
          },
          "expires_at": "2026-04-12T14:40:58Z",
          "expires_in_seconds": 899
        }
      }
    }
  ],
  "meta": {
    "total": 1,
    "success": 1,
    "failed": 0,
    "partial_success": false
  }
}
```

## B. FE Handle langsung Upload Binary Langsung ke S3

Untuk setiap item `success: true` dari step 1:
1. Ambil `upload_url`
2. Ambil `method`
3. Ambil `required_headers` (jika ada)
4. Lakukan request upload file ke S3

Contoh yang akan dilakukan FE:

```js
  await fetch(upload_url, {
    method: method, // "PUT"
    headers: { "Content-Type": file.type },
    body: file,
  });
```

atau pseudo
```js
  const intents = await api.requestUploadIntents(files) // bulk
  const readyToUpload = intents.data.filter(i => i.success)

  const uploadResults = await parallelLimit(readyToUpload, 4, putToS3) // paralel
  const succeeded = uploadResults.filter(r => r.success)

  if (succeeded.length > 0) {
    await api.createFilesBulk(succeeded.map(toCreatePayload))
  }
```


Catatan penting:
- **1 file = 1 PUT ke S3**
- boleh paralel (disarankan pakai concurrency limit)
- file yang gagal PUT **jangan** dikirim ke step create


## Step 2 - Upload Cancel (opsional)

### Endpoint 
- `DELETE /api/v1/file-manager/files/cancel-upload`

### FE kirim request ini

```json
{
  "data": [
    {
      "type": "file_manager_file_cancel_upload",
      "attributes": {
        "upload_intent_uuid": "019d8215-c622-7576-86bd-3ddeecaf4867"
      }
    }
  ]
}
```

### Kapan dipakai
- user sudah request upload intent
- tapi user batal sebelum create
- FE bisa cancel upload intent supaya flow lebih bersih

### Contoh response

```json
{
  "data": [
    {
      "index": 0,
      "success": true,
      "resource": {
        "type": "file_manager_file_cancel_upload_result",
        "id": "019d8215-c622-7576-86bd-3ddeecaf4867",
        "attributes": {
          "status": "canceled"
        }
      }
    }
  ],
  "meta": {
    "total": 1,
    "success": 1,
    "failed": 0,
    "partial_success": false
  }
}
```

## Step 3 - FE Panggil Create ke Backend (Finalisasi)

Setelah PUT S3 sukses, FE gunakan:

### Endpoint

- `POST /api/v1/file-manager/files/create`

### FE kirim request ini

```json
{
  "data": [
    {
      "type": "file_manager_file_create",
      "attributes": {
        "upload_intent_uuid": "019d8215-c622-7576-86bd-3ddeecaf4867",
        "name": "spidey.png",
        "folder_id": null
      }
    }
  ]
}
```

Aturan:

- `upload_intent_uuid`: wajib
- `name`: wajib
- `folder_id`: opsional (kalau mau dimasukkan ke folder)
- `null` atau tidak dikirim = root

## Step 4 - Process Create (BE)

Untuk setiap item:
1. Ambil upload intent dari `upload_intent_uuid`
2. Verify object benar-benar ada di storage
3. Verify metadata object (size/content type) sesuai intent
4. Simpan metadata file ke DB
5. Simpan outbox durable
6. Set status awal file menjadi `uploaded`

## Step 5 - Proses Async Lanjutan (Backend)

Setelah status `uploaded`, worker akan lanjut:

- `uploaded -> processing -> validated -> ready`
- atau `uploaded -> processing -> failed`

## Step 6 - FE Handle Response Create

Response create adalah bulk per item.

- Kalau semua sukses: HTTP `201`
- Kalau campuran sukses + gagal: HTTP `207` dengan `partial_success: true`

Contoh sukses:

```json
{
  "data": [
    {
      "index": 0,
      "success": true,
      "message": "created",
      "resource": {
        "type": "file_manager_file",
        "id": "019d628a-8d92-7949-b355-95de506da3b7",
        "attributes": {
          "folder_id": "019d4309-4dc8-78be-bb0c-3ebb8f3056ba",
          "name": "spidey01.png",
          "extension": "png",
          "size_bytes": 42170,
          "mime_type": "image/png",
          "status": "uploaded",
          "created_at": "2026-04-06T11:25:40Z",
          "updated_at": "2026-04-06T11:25:40Z"
        }
      }
    }
  ],
  "meta": {
    "total": 1,
    "success": 1,
    "failed": 0,
    "partial_success": false
  }
}
```
