JMG Storage Admin Panel
Dokumentasi API v1

JMG Central Storage

Storage terpusat untuk semua aplikasi Java Media Grup. Upload sekali, akses dari mana saja — dengan isolasi per client dan siap upgrade ke S3.

Base URL: https://storage.jmg.net.id

Memulai

Ikuti langkah berikut untuk mulai menggunakan JMG Storage dari aplikasi Anda.

1

Minta akses ke admin

Hubungi admin JMG untuk mendaftarkan aplikasi Anda sebagai client baru. Admin akan membuatkan token API khusus untuk aplikasi Anda.

2

Simpan token di .env

Pasang token yang diberikan admin ke file .env aplikasi Anda. Jangan commit token ke repository.

3

Kirim request

Gunakan token sebagai Bearer Token di header Authorization setiap request ke API.

Autentikasi

Semua endpoint API membutuhkan header Authorization dengan Bearer Token.

.env aplikasi pemanggil bash
CENTRAL_STORAGE_URL=https://storage.jmg.net.id
CENTRAL_STORAGE_TOKEN=sm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
⚠ Keamanan: Jangan expose token di frontend/JavaScript publik. Token hanya untuk komunikasi backend-to-backend.

Upload File

Upload file gambar dengan multipart/form-data.

POST /api/v1/files
Field Tipe Keterangan
fileFile *wajibjpg, jpeg, png, webp, gif. Maks sesuai konfigurasi client.
visibilityStringpublic (default) atau private
collectionStringGrup file. Huruf kecil, angka, _-. Contoh: avatars, posts
cURL bash
curl -X POST 'https://storage.jmg.net.id/api/v1/files' \
  -H 'Authorization: Bearer $CENTRAL_STORAGE_TOKEN' \
  -H 'Accept: application/json' \
  -F 'file=@/path/to/photo.jpg' \
  -F 'visibility=public' \
  -F 'collection=avatars'
Response 201 json
{
  "data": {
    "id": "019756ab-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "visibility": "public",
    "collection": "avatars",
    "original_name": "photo.jpg",
    "mime_type": "image/jpeg",
    "size_bytes": 124550,
    "checksum_sha256": "a3f1...",
    "metadata": { "width": 800, "height": 800 },
    "url": "https://storage.jmg.net.id/files/019756ab-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "created_at": "2026-06-29T10:00:00+00:00"
  }
}

List & Get File

Ambil daftar file milik client (paginated) atau metadata satu file.

GET /api/v1/files

Query params: collection, visibility, per_page (maks 100, default 20), page

cURL — List bash
curl 'https://storage.jmg.net.id/api/v1/files?collection=avatars&per_page=10' \
  -H 'Authorization: Bearer $CENTRAL_STORAGE_TOKEN'
GET /api/v1/files/{uuid}
cURL — Get satu file bash
curl 'https://storage.jmg.net.id/api/v1/files/019756ab-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Authorization: Bearer $CENTRAL_STORAGE_TOKEN'

Hapus File

DELETE /api/v1/files/{uuid}
cURL bash
curl -X DELETE 'https://storage.jmg.net.id/api/v1/files/019756ab-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Authorization: Bearer $CENTRAL_STORAGE_TOKEN'

Response: 204 No Content. File dihapus dari disk dan metadata di-soft-delete.

File Private & Signed URL

File private tidak punya URL permanen. Gunakan endpoint ini untuk membuat signed URL yang valid selama 15 menit.

POST /api/v1/files/{uuid}/temporary-url
cURL — Minta Signed URL bash
curl -X POST 'https://storage.jmg.net.id/api/v1/files/019756ab-xxxx/temporary-url' \
  -H 'Authorization: Bearer $CENTRAL_STORAGE_TOKEN'
Response json
{
  "id": "019756ab-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "url": "https://storage.jmg.net.id/private-files/019756ab-xxxx?signature=...",
  "expires_in_minutes": 15
}

Berikan URL ini ke browser pengguna — URL otomatis kedaluwarsa setelah 15 menit.

Integrasi Laravel

Buat service class di aplikasi pemanggil untuk enkapsulasi komunikasi ke storage API.

app/Services/CentralStorageClient.php php
<?php

namespace App\Services;

use Illuminate\Http\Client\Response;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Http;

class CentralStorageClient
{
    private string $baseUrl;
    private string $token;

    public function __construct()
    {
        $this->baseUrl = rtrim(config('services.central_storage.url'), '/');
        $this->token   = config('services.central_storage.token');
    }

    /** Upload file, kembalikan array metadata dari storage. */
    public function upload(
        UploadedFile $file,
        string $visibility = 'public',
        string $collection = 'images'
    ): array {
        $response = Http::withToken($this->token)
            ->attach('file', $file->getContent(), $file->getClientOriginalName())
            ->post("{$this->baseUrl}/api/v1/files", [
                'visibility' => $visibility,
                'collection' => $collection,
            ]);

        $response->throw(); // lempar exception jika gagal

        return $response->json('data');
    }

    /** Ambil metadata satu file. */
    public function get(string $uuid): array
    {
        return Http::withToken($this->token)
            ->get("{$this->baseUrl}/api/v1/files/{$uuid}")
            ->throw()
            ->json('data');
    }

    /** Minta signed URL untuk file private (valid 15 menit). */
    public function temporaryUrl(string $uuid): string
    {
        return Http::withToken($this->token)
            ->post("{$this->baseUrl}/api/v1/files/{$uuid}/temporary-url")
            ->throw()
            ->json('url');
    }

    /** Hapus file. */
    public function delete(string $uuid): void
    {
        Http::withToken($this->token)
            ->delete("{$this->baseUrl}/api/v1/files/{$uuid}")
            ->throw();
    }
}
config/services.php — tambahkan bagian ini php
'central_storage' => [
    'url'   => env('CENTRAL_STORAGE_URL'),
    'token' => env('CENTRAL_STORAGE_TOKEN'),
],
.env php
# .env aplikasi pemanggil
CENTRAL_STORAGE_URL=https://storage.jmg.net.id
CENTRAL_STORAGE_TOKEN=sm_live_TOKEN_ANDA
Contoh di Controller php
public function store(Request $request, CentralStorageClient $storage): JsonResponse
{
    $request->validate(['avatar' => ['required', 'image', 'max:5120']]);

    // Upload ke storage
    $media = $storage->upload(
        file:       $request->file('avatar'),
        visibility: 'public',
        collection: 'avatars'
    );

    // Simpan UUID dan URL ke database
    $user->update([
        'avatar_storage_id' => $media['id'],
        'avatar_url'        => $media['url'],
    ]);

    return response()->json($media, 201);
}

JavaScript (dari Backend)

Jangan panggil API storage langsung dari browser — token akan bocor. Selalu lewatkan backend aplikasi Anda.

Node.js / server-side fetch javascript
const formData = new FormData();
formData.append('file', fs.createReadStream('/path/to/photo.jpg'));
formData.append('visibility', 'public');
formData.append('collection', 'avatars');

const response = await fetch(`${process.env.CENTRAL_STORAGE_URL}/api/v1/files`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.CENTRAL_STORAGE_TOKEN}`,
    'Accept': 'application/json',
    ...formData.getHeaders(),
  },
  body: formData,
});

const { data } = await response.json();
console.log(data.url); // URL file public

cURL — Referensi Lengkap

Upload public bash
curl -X POST 'https://storage.jmg.net.id/api/v1/files' \
  -H 'Authorization: Bearer $TOKEN' \
  -F 'file=@photo.jpg' \
  -F 'visibility=public' \
  -F 'collection=avatars'
Upload private bash
curl -X POST 'https://storage.jmg.net.id/api/v1/files' \
  -H 'Authorization: Bearer $TOKEN' \
  -F 'file=@dokumen.png' \
  -F 'visibility=private' \
  -F 'collection=documents'
List semua file bash
curl 'https://storage.jmg.net.id/api/v1/files' \
  -H 'Authorization: Bearer $TOKEN'
Filter by collection & visibility bash
curl 'https://storage.jmg.net.id/api/v1/files?collection=avatars&visibility=public&per_page=20&page=2' \
  -H 'Authorization: Bearer $TOKEN'
Get metadata satu file bash
curl 'https://storage.jmg.net.id/api/v1/files/{uuid}' \
  -H 'Authorization: Bearer $TOKEN'
Minta signed URL (private) bash
curl -X POST 'https://storage.jmg.net.id/api/v1/files/{uuid}/temporary-url' \
  -H 'Authorization: Bearer $TOKEN'
Hapus file bash
curl -X DELETE 'https://storage.jmg.net.id/api/v1/files/{uuid}' \
  -H 'Authorization: Bearer $TOKEN'

Upgrade ke S3-Compatible Storage

Ketika trafik bertambah, Anda bisa pindah ke object storage (AWS S3, Cloudflare R2, MinIO) tanpa mengubah kontrak API — cukup ubah konfigurasi .env.

  1. 1

    Install Flysystem S3 adapter

    composer require league/flysystem-aws-s3-v3
  2. 2

    Update .env storage server

    .env (storage server) bash
    # Ganti disk ke S3
    MEDIA_PUBLIC_DISK=media_public_s3
    MEDIA_PRIVATE_DISK=media_private_s3
    MEDIA_PUBLIC_DELIVERY=disk
    
    # Kredensial S3 / R2 / MinIO
    AWS_ACCESS_KEY_ID=your-key
    AWS_SECRET_ACCESS_KEY=your-secret
    AWS_DEFAULT_REGION=auto
    AWS_BUCKET=jmg-media-public
    AWS_PRIVATE_BUCKET=jmg-media-private
    AWS_ENDPOINT=https://xxxx.r2.cloudflarestorage.com
    AWS_URL=https://cdn.jmg.net.id
  3. 3

    Clear cache & test

    php artisan config:cache
    php artisan route:cache

    File baru langsung masuk ke S3. File lama di local disk tetap bisa dilayani karena kolom disk di database menyimpan lokasi per-file.

  4. 4

    Migrasi file lama (opsional)

    Buat job Laravel untuk copy file dari local disk ke S3 secara bertahap, update kolom disk setelah berhasil.

Error Codes

Status Penyebab
401 Token tidak ada, salah, atau sudah kedaluwarsa
403 Origin browser tidak diizinkan (jika ada header Origin)
404 File tidak ditemukan atau milik client lain
422 Validasi gagal — format file tidak didukung, ukuran melebihi batas, atau field salah
429 Rate limit terlampaui (upload: 30/menit, API: 120/menit per client)
500 File gagal disimpan ke disk — cek quota storage