# Dokumentasi API — Admin Panel Informasi (Belanta)

Base URL:
```
https://backend.belanta.my.id/informasi/admin.php
```

API ini **pure JSON REST**, tanpa halaman login/HTML. Semua request & response berupa JSON.

---

## 1. Autentikasi

Setiap request **wajib** menyertakan API Key. Tidak ada endpoint publik/tanpa key di API ini.

Key: `-`

Kirim via **salah satu** dari 2 header ini:

**Cara 1 — X-API-Key**
```
X-API-Key: ADITdev
```

**Cara 2 — Authorization Bearer**
```
Authorization: Bearer ADITdev
```

Kalau key salah/kosong → `401 Unauthorized`.
Kalau salah berkali-kali (5x dalam 15 menit) → IP dikunci otomatis 15 menit (`429`), lalu bisa diblokir lebih lanjut jika terus mencoba (`403`).

⚠️ **Penting soal keamanan key ini** — baca bagian [Catatan Keamanan](#catatan-keamanan-wajib-dibaca) di bawah sebelum pakai di production untuk data sensitif.

---

## 2. Format Umum

- Semua response: `Content-Type: application/json`
- Body request (untuk POST) wajib: `Content-Type: application/json`, kalau tidak → `415`
- Ukuran body maksimal ~20KB → lebih dari itu `413`
- Field yang tidak dikenal di body akan ditolak → `422`
- Semua field divalidasi ketat (panjang, karakter berbahaya, encoding, dll)

### Response sukses (contoh)
```json
{ "status": "ok", "message": "...", "data": [...] }
```

### Response error (contoh)
```json
{ "status": "error", "message": "Validasi gagal", "errors": { "judul": "Judul wajib 3-150 karakter, tanpa karakter berbahaya" } }
```

---

## 3. Endpoint

### 3.1 List Data — `GET`

```
GET /informasi/admin.php?aksi=list&limit=10&offset=0&urutan=desc
```

| Param    | Wajib | Default | Keterangan                        |
|----------|-------|---------|------------------------------------|
| aksi     | ya    | -       | `list`                             |
| limit    | tidak | 10      | 1–100                              |
| offset   | tidak | 0       | ≥0                                  |
| urutan   | tidak | desc    | `asc` atau `desc`                  |

**Contoh cURL:**
```bash
curl -X GET "https://backend.belanta.my.id/informasi/admin.php?aksi=list&limit=20&urutan=desc" \
  -H "X-API-Key: ADITdev"
```

**Response 200:**
```json
{
  "status": "ok",
  "data": [
    { "id": 1, "judul": "Judul Contoh", "deskripsi": "...", "tanggal": "2026-08-01 00:00:00", "link": "https://...", "gambar": "https://..." }
  ]
}
```

---

### 3.2 Detail Data — `GET`

```
GET /informasi/admin.php?aksi=detail&id=1
```

**Contoh cURL:**
```bash
curl -X GET "https://backend.belanta.my.id/informasi/admin.php?aksi=detail&id=1" \
  -H "Authorization: Bearer ADITdev"
```

**Response 200:**
```json
{ "status": "ok", "data": { "id": 1, "judul": "...", "deskripsi": "...", "tanggal": "...", "link": "...", "gambar": "..." } }
```

**Response 404** jika ID tidak ditemukan.

---

### 3.3 Tambah Data — `POST` (`aksi: create`)

```
POST /informasi/admin.php
Content-Type: application/json
X-API-Key: ADITdev
```

```json
{
  "aksi": "create",
  "judul": "Judul Informasi",
  "deskripsi": "Isi deskripsi lengkap di sini",
  "tanggal": "2026-08-10",
  "link": "https://example.com",
  "gambar": "https://example.com/gambar.jpg"
}
```

| Field       | Wajib | Aturan                                                |
|-------------|-------|--------------------------------------------------------|
| judul       | ya    | 3–150 karakter                                         |
| deskripsi   | ya    | 3–5000 karakter                                        |
| tanggal     | ya    | `YYYY-MM-DD` atau `YYYY-MM-DD HH:MM:SS`                 |
| link        | tidak | URL `http/https` valid, atau kosong `""`                |
| gambar      | tidak | URL `http/https`, atau path lokal relatif aman, atau `""` |

**Contoh cURL:**
```bash
curl -X POST "https://backend.belanta.my.id/informasi/admin.php" \
  -H "X-API-Key: ADITdev" \
  -H "Content-Type: application/json" \
  -d '{"aksi":"create","judul":"Promo Agustus","deskripsi":"Diskon spesial bulan ini","tanggal":"2026-08-10","link":"https://belanta.my.id/promo","gambar":""}'
```

**Response 201:**
```json
{ "status": "ok", "message": "Data berhasil disimpan", "id": 12 }
```

**Response 422** (validasi gagal, contoh):
```json
{ "status": "error", "message": "Validasi gagal", "errors": { "link": "Link harus URL http/https yang valid" } }
```

---

### 3.4 Update Data — `POST` (`aksi: update`)

```json
{
  "aksi": "update",
  "id": 12,
  "judul": "Judul Baru",
  "deskripsi": "Deskripsi diperbarui",
  "tanggal": "2026-08-11",
  "link": "https://example.com",
  "gambar": ""
}
```

Semua field **wajib dikirim ulang** (bukan partial update) — sama seperti aksi `create`, plus `id`.

**Contoh cURL:**
```bash
curl -X POST "https://backend.belanta.my.id/informasi/admin.php" \
  -H "X-API-Key: ADITdev" \
  -H "Content-Type: application/json" \
  -d '{"aksi":"update","id":12,"judul":"Promo Agustus (Update)","deskripsi":"Diskon diperpanjang","tanggal":"2026-08-15","link":"","gambar":""}'
```

**Response 200:**
```json
{ "status": "ok", "message": "Data berhasil diupdate" }
```

**Response 404** jika `id` tidak ditemukan.

---

### 3.5 Hapus Data — `POST` (`aksi: delete`)

```json
{ "aksi": "delete", "id": 12 }
```

**Contoh cURL:**
```bash
curl -X POST "https://backend.belanta.my.id/informasi/admin.php" \
  -H "X-API-Key: ADITdev" \
  -H "Content-Type: application/json" \
  -d '{"aksi":"delete","id":12}'
```

**Response 200:**
```json
{ "status": "ok", "message": "Data berhasil dihapus" }
```

---

## 4. Kode Status HTTP

| Kode | Arti                                                        |
|------|--------------------------------------------------------------|
| 200  | Sukses (list/detail/update/delete)                            |
| 201  | Sukses membuat data baru                                      |
| 400  | Request tidak valid (format JSON rusak, ID bukan angka, dll)  |
| 401  | API key kosong/salah                                          |
| 403  | IP diblokir sementara                                          |
| 404  | Data tidak ditemukan                                            |
| 405  | Method tidak diizinkan (selain GET/POST)                       |
| 413  | Body request terlalu besar (>20KB)                              |
| 415  | Content-Type bukan `application/json` (untuk POST)              |
| 422  | Validasi field gagal / ada field tak dikenal                    |
| 429  | Kena rate limit / lockout percobaan key salah                   |
| 500  | Error server internal                                            |

---

## 5. Contoh Integrasi JavaScript (fetch)

```js
const BASE_URL = "https://backend.belanta.my.id/informasi/admin.php";
const API_KEY = "ADITdev";

async function listInformasi(limit = 10, offset = 0, urutan = "desc") {
  const qs = new URLSearchParams({ aksi: "list", limit, offset, urutan });
  const res = await fetch(`${BASE_URL}?${qs}`, {
    method: "GET",
    headers: { "X-API-Key": API_KEY }
  });
  return res.json();
}

async function createInformasi(payload) {
  const res = await fetch(BASE_URL, {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ aksi: "create", ...payload })
  });
  return res.json();
}
```

> ⚠️ Jangan panggil endpoint ini langsung dari **browser/frontend publik** dengan API key ter-embed di JS — key akan terlihat oleh siapa pun yang buka DevTools. Panggil dari **server/backend lain** (server-to-server), bukan dari client-side JS yang publik.

---

## 6. Rate Limit & Lockout

- Maks **60 request/menit per IP** untuk seluruh endpoint (di luar percobaan key salah).
- Maks **5x percobaan API key salah dalam 15 menit per IP** → IP dikunci 15 menit, lalu bisa diblokir otomatis.
- Semua percobaan gagal & IP yang diblokir dicatat ke log (lihat fungsi `log_event()` di `aditdev.php`).

---

## Catatan Keamanan (WAJIB DIBACA)

API key `ADITdev` yang diminta di request ini **pendek dan mudah ditebak** dibanding standar token API (idealnya ≥256-bit random, misalnya hasil `bin2hex(random_bytes(32))`). Untuk mengurangi risiko:

1. Key **tidak disimpan plain text** di server — server hanya menyimpan hash SHA-256-nya (`API_KEY_HASH` di `admin.php`).
2. Brute force diperlambat dengan rate limit + lockout otomatis per IP.
3. **Sangat disarankan**: setelah testing selesai, ganti ke key random panjang. Caranya:
   ```php
   // Generate key baru (jalankan sekali di CLI/lokal):
   echo bin2hex(random_bytes(32));
   // Lalu update baris di admin.php:
   define('API_KEY_HASH', hash('sha256', '<KEY_BARU_DISINI>'));
   ```
4. Jangan commit key ke repo publik / jangan taruh di kode frontend yang bisa dibuka orang lain.
5. Karena ini **key statis tunggal**, siapa pun yang tahu key dan lolos rate-limit punya akses penuh CRUD. Kalau butuh multi-client dengan akses berbeda-beda, sistem ini perlu di-upgrade ke API key per-client + scope/permission (bukan 1 key untuk semua).

---

## Checklist Keamanan yang Diterapkan di File Ini

| Kategori | Status | Catatan |
|---|---|---|
| A. API & Backend | ✅ | Semua data dari server (bukan trust client), POST wajib utk mutasi, validasi Content-Type & size, reject unknown fields, waktu pakai `time()`/`date()` server |
| B. Authentication | ⚠️ Parsial | API key (bukan user login) — key di-hash di server, tidak ada "password" user. Rotasi/expiry token per-session **tidak relevan** karena bukan sistem login user; lihat catatan keamanan di atas |
| C. Anti Cheat | N/A | Tidak relevan — ini API CRUD informasi, bukan game/skor |
| D. Rate Limit | ✅ | Per IP per endpoint (global) + lockout khusus percobaan key salah |
| E. Input Validation | ✅ | Regex, UTF-8 check, null byte, control char, CRLF, path traversal, XSS-safe (data disimpan mentah lalu di-escape saat ditampilkan di sisi client), SQLi aman (PDO prepared statement) |
| F. JSON Database | N/A | Data utama di MySQL (PDO), bukan flat-file JSON. File JSON hanya dipakai internal untuk rate-limit/lockout — sudah pakai `flock()` + atomic write |
| G. Header Security | ✅ | X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, CSP, HSTS (jika HTTPS), Cache-Control |
| H. Server Hardening | ⚠️ Parsial | `display_errors` off, error log ke file — **directory listing, blokir `.json/.env/.git/.htaccess`, hide server signature harus diatur di `.htaccess`/vhost**, lihat bagian 7 di bawah |
| I. Session | N/A | Tidak ada session — auth stateless via API key per-request |
| J. Anti Bot | ✅ (parsial) | Lockout otomatis setelah 5x key salah + delay progresif. **CAPTCHA tidak relevan** karena ini API server-to-server, bukan form yang diisi manusia |
| K. Logging | ✅ | Log setiap create/update/delete/list/detail + percobaan key gagal + IP block, via `log_event()`/`log_error()` |
| L. Monitoring | ✅ (parsial) | Auto-block IP saat terdeteksi brute force key. Deteksi spam/multi-akun/bot **tidak relevan** (tidak ada konsep akun user di API ini) |

---

## 7. Yang Harus Ditambahkan di `.htaccess` (di folder `/informasi/`)

Bagian ini **tidak bisa diatur dari PHP**, harus ditaruh di `.htaccess` (Apache/LiteSpeed):

```apache
# Disable directory listing
Options -Indexes

# Blokir akses langsung ke file sensitif
<FilesMatch "\.(json|env|git|htaccess|bak|sql|log)$">
    Require all denied
</FilesMatch>

# Sembunyikan signature server (kalau diizinkan hosting)
ServerSignature Off

# Hide PHP version (idealnya di php.ini: expose_php = Off)
```

> Kalau hosting shared (cPanel/LiteSpeed) tidak mengizinkan `ServerSignature`/`expose_php` diubah lewat `.htaccess`, minta admin hosting untuk set `expose_php = Off` di `php.ini`, atau tambahkan di level vhost.