init push
This commit is contained in:
288
docs/file-manager-upload.md
Normal file
288
docs/file-manager-upload.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# 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
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user