Skip to content

Instantly share code, notes, and snippets.

@siputzx
Last active February 26, 2026 05:40
Show Gist options
  • Select an option

  • Save siputzx/072e0491e724bfed9d204da0a467007a to your computer and use it in GitHub Desktop.

Select an option

Save siputzx/072e0491e724bfed9d204da0a467007a to your computer and use it in GitHub Desktop.
detail update api

πŸš€ API Siputzx β€” Major Update: Hybrid Architecture

API Siputzx telah mengalami perombakan besar-besaran dalam hal arsitektur, keamanan, dan performa. Dokumen ini menjelaskan secara transparan apa yang berubah dan mengapa ini membuat API jauh lebih cepat, stabil, dan aman dari sebelumnya.


πŸ—οΈ Dari Satu Server Menjadi Dua Layer

Sebelumnya β€” Semua di Satu Tempat

Client β†’ [Node.js + Hono] β†’ Response

Seluruh sistem β€” autentikasi, rate limit, proteksi, proses request, hingga WebSocket β€” berjalan di satu proses Node.js. Artinya:

  • Kalau server restart, semua data rate limit & counter hilang
  • Kalau traffic melonjak, satu server menanggung semuanya
  • Kalau server down, API mati total
  • WebSocket monitoring ikut mati kalau server restart

Sekarang β€” Hybrid Rust Γ— Bun

Client β†’ [Gateway Rust] β†’ [Node 1: Bun/Elysia]
                       β†˜ [Node 2: Bun/Elysia]

Sistem kini terdiri dari dua layer yang benar-benar terpisah:

Layer Teknologi Tanggung Jawab
Gateway Rust + Axum Rate limit, routing, load balancing, anti-spam, health check
API Server Bun + Elysia Proses request, generate konten, logika endpoint

Gateway Rust berdiri di garis terdepan. Ia yang memutuskan apakah sebuah request boleh lanjut atau tidak β€” sebelum API server bahkan tahu ada request masuk.


πŸ“Š Perbandingan Langsung

Ketersediaan & Stabilitas

Sebelum Sekarang
Node server 1 Multi-node
Server mati API mati total Traffic otomatis ke node lain
Restart server Semua data counter hilang Data tetap aman di Redis
WebSocket monitoring Ikut mati saat restart Selalu hidup di gateway
Health check ❌ Tidak ada βœ… Setiap 10 detik otomatis
Pemulihan node Manual Otomatis setelah 2x health check sukses

Performa

Sebelum Sekarang
Request concurrent Dibatasi event loop Node.js Gateway Rust handle jutaan concurrent
Rate limit storage In-memory, hilang saat restart Redis β€” persisten & atomic
Operasi blocking per request Ya, sequential Tidak β€” sebagian diproses background
Distribusi load ❌ Semua ke satu server βœ… Dibagi berdasarkan beban & latency

Keamanan

Sebelum Sekarang
Validasi URL parameter ❌ Tidak ada βœ… Validasi ketat format & protokol
Potensi SSRF Ada Dihilangkan
Rate limit anonymous In-memory, reset tiap restart Redis sliding window, persisten
Cooldown massal Ada β€” semua user terdampak Dihapus β€” hanya requester yang ditolak
Info infrastruktur di response Terekspos Disembunyikan dari semua response publik

πŸ›‘οΈ Sistem Anti-Spam & Rate Limit

Masalah di Sistem Lama

Sistem lama menggunakan mekanisme endpoint cooldown global: kalau satu endpoint menerima β‰₯15 request/detik, endpoint tersebut masuk mode cooldown dan semua user β€” termasuk yang normal β€” tidak bisa mengaksesnya sampai traffic turun dan stabil selama beberapa detik.

Ini bermasalah karena:

  • Satu orang spam = semua orang kena dampak
  • Cooldown bisa berlangsung lama dan tidak terprediksi
  • Data cooldown hilang setiap server restart, jadi proteksi tidak konsisten
  • Counter disimpan di memory JavaScript biasa β€” tidak atomic, rentan race condition saat traffic tinggi

Sistem Baru

Rate limit sekarang diproses di gateway Rust menggunakan Redis sebagai penyimpanan terpusat.

Setiap request ke endpoint API dihitung dalam jendela waktu berjalan (sliding window) per endpoint. Counter disimpan di Redis menggunakan Lua script atomic β€” tidak ada kondisi race, tidak ada data yang tumpang tindih, dan hasilnya selalu konsisten walaupun ada ratusan request datang bersamaan.

Kalau sebuah request melebihi batas, gateway langsung menolak dengan 429 Too Many Requests sebelum request menyentuh API server sama sekali. Server API tidak terbebani oleh traffic spam.

Bedanya dengan sistem lama:

  • Batas berlaku per request individual, bukan per endpoint secara global
  • User yang spam hanya memblokir dirinya sendiri, tidak mempengaruhi user lain
  • Data counter tidak hilang walau gateway di-restart
  • Penolakan terjadi di level gateway β€” API server tidak pernah tahu ada request spam

Alur Setiap Request

flowchart TD
    A([πŸ“₯ Request Masuk]) --> B{Endpoint publik?\n/health Β· /stats Β· /ws Β· /}
    B -- Ya --> C([βœ… Langsung Diproses\nTanpa pengecekan apapun])
    B -- Tidak --> D{Ada API Key?}

    D -- Ada & dikenal --> E{Tier unlimited?}
    E -- Ya --> G([βœ… Lanjut ke API Server])
    E -- Tidak --> F{Masih dalam batas?}
    F -- Ya --> G
    F -- Tidak --> RL([❌ 429 Too Many Requests\nHeader X-RateLimit-* dikembalikan])

    D -- Tidak ada --> AN{Cek batas anonymous\nper endpoint}
    AN -- Dalam batas --> G
    AN -- Melebihi --> RL

    D -- Key tidak dikenal --> INV([❌ 429 β€” Key Tidak Valid])

    G --> LB{Pilih node terbaik\nBeban rendah + latency rendah}
    LB --> FWD{Forward ke node}
    FWD -- Berhasil --> R([βœ… Response ke Client])
    FWD -- Gagal --> FB{Ada node lain?}
    FB -- Ya --> LB
    FB -- Tidak --> ERR([❌ 503 β€” Semua Node Tidak Tersedia])

    style A fill:#1a1a2e,stroke:#7c6af7,color:#e2e2f0
    style C fill:#0d2a1a,stroke:#06d6a0,color:#06d6a0
    style G fill:#0d2a1a,stroke:#06d6a0,color:#06d6a0
    style R fill:#0d2a1a,stroke:#06d6a0,color:#06d6a0
    style RL fill:#2a0d1a,stroke:#f72585,color:#f72585
    style INV fill:#2a0d1a,stroke:#f72585,color:#f72585
    style ERR fill:#2a1a0d,stroke:#ffd166,color:#ffd166
Loading

⚑ Load Balancing & Failover Otomatis

Sebelumnya tidak ada load balancing β€” semua request ke satu server, dan kalau server itu down, tidak ada fallback apapun.

Sekarang gateway mendistribusikan traffic ke beberapa node secara cerdas. Algoritma pemilihan node mempertimbangkan dua hal: jumlah koneksi aktif di setiap node dan latency rata-rata node tersebut berdasarkan health check terakhir. Node yang lebih ringan dan lebih cepat mendapat lebih banyak traffic.

flowchart LR
    C([πŸ‘€ Client]) --> GW

    subgraph GW["πŸ›‘οΈ Gateway Rust β€” Selalu Online"]
        RL[Rate Limit]
        LB[Load Balancer\nLeast-conn + Latency]
        HC[Health Checker\nSetiap 10 detik]
    end

    subgraph API["⚑ API Servers β€” Bun/Elysia"]
        N1[Node 1 🟒]
        N2[Node 2 🟒]
    end

    RL --> LB
    LB -->|Traffic normal| N1
    LB -.->|Failover otomatis| N2
    HC -->|Monitor| N1
    HC -->|Monitor| N2
    N1 --> C
    N2 -.-> C

    style GW fill:#0f0f1a,stroke:#7c6af7,color:#e2e2f0
    style API fill:#0a1a0a,stroke:#06d6a0,color:#e2e2f0
Loading

Kalau satu node tidak merespons, gateway otomatis mencoba node berikutnya dalam request yang sama β€” client tidak perlu retry, tidak ada error yang terlihat dari sisi pengguna.

Node baru juga tidak langsung menerima traffic setelah restart. Ia harus lulus 2 health check berturut-turut sebelum dianggap siap β€” mencegah node yang belum stabil dibanjiri request.


πŸ”’ Peningkatan Keamanan

Validasi URL

Di versi lama, parameter URL yang dikirim user langsung digunakan untuk fetch ke server eksternal tanpa pengecekan apapun. Ini membuka celah di mana URL yang diarahkan ke jaringan internal bisa dieksekusi oleh server tanpa disadari.

Sekarang setiap URL yang dikirim sebagai parameter divalidasi format dan protokolnya terlebih dahulu. URL yang tidak sesuai langsung ditolak dengan pesan error yang jelas sebelum ada request keluar dari server.

Keamanan Response Publik

Informasi teknis internal tidak lagi muncul di endpoint publik manapun. Endpoint /health dan /stats hanya menampilkan informasi yang relevan bagi pengguna.

Statistik Serangan Persisten

Setiap request yang ditolak karena spam atau pelanggaran batas dicatat secara persisten. Data ini tidak hilang walau gateway di-restart dan bisa dipantau kapanpun melalui endpoint /stats.


πŸ“‘ WebSocket Monitoring Realtime

Di versi lama, WebSocket dijalankan di dalam proses yang sama dengan API server. Kalau server restart, semua koneksi WebSocket putus dan client perlu reconnect manual.

Sekarang WebSocket dihandle langsung oleh gateway Rust dan berjalan terpisah dari siklus hidup API server. Koneksi jauh lebih stabil dan data statistik dikirim otomatis setiap 2 detik ke semua client yang terhubung.

const ws = new WebSocket('wss://api.siputzx.my.id/ws');

ws.onmessage = ({ data }) => {
  const payload = JSON.parse(data);
  // payload.data.metrics  β†’ total request, histori harian
  // payload.data.attacks  β†’ total serangan, histori harian
  // payload.nodes         β†’ status semua node realtime
  // payload.healthy_nodes β†’ jumlah node aktif
};

πŸ”„ Persistensi Data

Data Sebelum Sekarang
Counter request Hilang saat restart Tersimpan di Redis
Statistik serangan Hilang saat restart Tersimpan di Redis
Backup ke Firebase Setiap 60 detik Setiap 60 detik + saat shutdown
Restore saat boot Ada, tapi bisa gagal total Ada, dengan retry otomatis 3x

Data metrik dan statistik serangan di-backup ke Firebase secara berkala dan di-restore otomatis setiap kali sistem start ulang. Histori tidak pernah hilang.


πŸ“Š Pantau Status & Uptime

Status semua node, latency, dan histori uptime tersedia secara publik dan realtime:

πŸ”— status.siputzx.my.id

Atau langsung via API:

GET https://api.siputzx.my.id/health  β†’ Status semua node
GET https://api.siputzx.my.id/stats   β†’ Statistik lengkap
GET https://api.siputzx.my.id/ws      β†’ Stream realtime via WebSocket

API Siputzx dibangun untuk tetap cepat, aman, dan online β€” bahkan ketika infrastruktur di belakangnya sedang berubah. ⚑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment