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.
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
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.
| 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 |
| 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 |
| 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 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
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
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
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
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.
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.
Informasi teknis internal tidak lagi muncul di endpoint publik manapun. Endpoint /health dan /stats hanya menampilkan informasi yang relevan bagi pengguna.
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.
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
};| 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.
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. β‘