253 lines
8.9 KiB
Markdown
253 lines
8.9 KiB
Markdown
# DETAIL TEKNIS: Cara Proxy Memfilter Data per Site (SIAB vs Office)
|
|
|
|
Dokumen ini menjelaskan **secara kode** bagaimana data dipisahkan per site.
|
|
Ada **3 lapis filter** yang bekerja dari Netify API sampai ke tampilan dashboard.
|
|
|
|
---
|
|
|
|
## LAPIS 1 — Saat Minta Data ke Netify API
|
|
### File: `proxy/netifyClientCore.js`
|
|
|
|
```
|
|
NETIFY_SITE_UUIDS = "6681452d_....(SIAB), 1959bb55_....(Office)"
|
|
|
|
|
proxy loop satu per satu:
|
|
┌─────────────────────────┐
|
|
│ for SIAB UUID: │
|
|
│ kirim request ke │
|
|
│ Netify dengan header │
|
|
│ x-net-site: SIAB-UUID│
|
|
└─────────────────────────┘
|
|
┌─────────────────────────┐
|
|
│ for Office UUID: │
|
|
│ kirim request ke │
|
|
│ Netify dengan header │
|
|
│ x-net-site: OFFICE-UUID│
|
|
└─────────────────────────┘
|
|
```
|
|
|
|
**KODE ASLI — cara header dikirim:**
|
|
```javascript
|
|
// proxy/netifyClientCore.js baris 14-18
|
|
function getHeaders(siteUuid) {
|
|
const headers = {
|
|
'x-api-key': process.env.NETIFY_API_KEY,
|
|
'Accept': 'application/json'
|
|
};
|
|
|
|
if (siteUuid) headers['x-net-site'] = siteUuid;
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
// Ini yang memfilter data di sisi Netify!
|
|
// Netify API hanya kembalikan data untuk site ini saja.
|
|
|
|
return headers;
|
|
}
|
|
|
|
async function netifyFetch(endpoint, params = {}, agentUuid, siteUuid) {
|
|
const res = await axios.get(`${BASE_URL}${endpoint}`, {
|
|
headers: getHeaders(siteUuid), // <--- siteUuid dikirim ke Netify
|
|
params,
|
|
timeout: 30000,
|
|
});
|
|
}
|
|
```
|
|
|
|
**Artinya:** Netify API sendiri yang memfilter. Kalau kita kirim header
|
|
`x-net-site: SIAB-UUID`, Netify HANYA kembalikan data milik SIAB.
|
|
Kita tidak perlu filter manual — Netify sudah filter dari sumbernya.
|
|
|
|
---
|
|
|
|
## LAPIS 2 — Saat Simpan ke MongoDB
|
|
### File: `proxy/collector.js` (loop utama)
|
|
|
|
Setelah data dari Netify masuk, setiap dokumen diberi **stempel `site_uuid`**
|
|
sebelum disimpan ke MongoDB.
|
|
|
|
**KODE ASLI — loop per site di collector.js:**
|
|
```javascript
|
|
// proxy/collector.js baris 123-222
|
|
|
|
// SITE_UUIDS diambil dari env:
|
|
// NETIFY_SITE_UUIDS="6681452d_..., 1959bb55_..."
|
|
const SITE_UUIDS = SITE_UUIDS_STR.split(','); // ["SIAB-UUID", "OFFICE-UUID"]
|
|
|
|
for (const siteUuid of SITE_UUIDS) {
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
// Loop: pertama SIAB, lalu Office (satu per satu)
|
|
|
|
console.log(`Fetching agents for Site: ${siteUuid}`);
|
|
const agents = await netify.fetchAgents(siteUuid);
|
|
// ^^^^^^^^^
|
|
// fetchAgents pakai siteUuid → Netify hanya beri agent milik site ini
|
|
|
|
// --- PENTING: Anti-duplikat antar site ---
|
|
// Kadang Netify bisa kembalikan agent yang sama untuk 2 site.
|
|
// Di sini kita cegah agar 1 agent hanya masuk 1 site.
|
|
const agents = rawAgents.filter(a => {
|
|
if (processedAgentUuids.has(a.uuid)) {
|
|
console.log(`Skipping ${a.uuid} — already assigned to another site.`);
|
|
return false; // lewati agent yang sudah diproses site lain
|
|
}
|
|
return true;
|
|
});
|
|
for (const agent of agents) processedAgentUuids.add(agent.uuid);
|
|
|
|
// Simpan agent ke MongoDB dengan site_uuid
|
|
await AgentRegistry.findOneAndUpdate(
|
|
{ uuid: agent.uuid },
|
|
{ $set: {
|
|
uuid: agent.uuid,
|
|
site_uuid: siteUuid, // <--- stempel site di sini!
|
|
...
|
|
}},
|
|
{ upsert: true }
|
|
);
|
|
|
|
// Kumpulkan data untuk setiap agent di site ini
|
|
for (const agent of agents) {
|
|
await collectForAgent(agent.uuid, timestamp, siteUuid);
|
|
// ^^^^^^^^^
|
|
// siteUuid terus dibawa ke setiap fungsi collect
|
|
}
|
|
}
|
|
```
|
|
|
|
**KODE ASLI — cara flows disimpan dengan site_uuid:**
|
|
```javascript
|
|
// proxy/collectorHelperDpi2.js baris 88-98
|
|
|
|
const flowDocs = flows.map(f => ({
|
|
timestamp,
|
|
agent_uuid: agentUuid, // siapa agent-nya
|
|
site_uuid: SITE_UUID, // <--- data ini milik site mana! (SIAB atau Office)
|
|
flow_id: f.flow_id,
|
|
src_ip: f.src_ip,
|
|
dst_ip: f.dst_ip,
|
|
download: f.download,
|
|
upload: f.upload,
|
|
// ...
|
|
}));
|
|
|
|
// Upsert ke MongoDB (tidak duplikat berdasarkan flow_id + agent_uuid)
|
|
await Flow.bulkWrite(flowDocs.map(f => ({
|
|
updateOne: {
|
|
filter: { flow_id: f.flow_id, agent_uuid: f.agent_uuid },
|
|
update: { $set: f },
|
|
upsert: true,
|
|
}
|
|
})));
|
|
```
|
|
|
|
**Hasilnya di MongoDB — data terpisah per site:**
|
|
```
|
|
Collection: flows
|
|
┌────────────────────┬────────────────────────────────────────────────────┬────────┬──────────┐
|
|
│ flow_id │ site_uuid │ src_ip │ download │
|
|
├────────────────────┼────────────────────────────────────────────────────┼────────┼──────────┤
|
|
│ flow-001 │ 6681452d_9cae_4ff4_8ae8_0d504774265e (SIAB) │ 10.0.x │ 1234 │
|
|
│ flow-002 │ 6681452d_9cae_4ff4_8ae8_0d504774265e (SIAB) │ 10.0.x │ 5678 │
|
|
│ flow-003 │ 1959bb55_045b_47c7_bbdd_f33b7db197b9 (Office) │ 192.168.x │ 9012 │
|
|
│ flow-004 │ 1959bb55_045b_47c7_bbdd_f33b7db197b9 (Office) │ 192.168.x │ 3456 │
|
|
└────────────────────┴────────────────────────────────────────────────────┴────────┴──────────┘
|
|
^^^^^^^^^^ Field ini yang memisahkan data ^^^^^^^^^^
|
|
```
|
|
|
|
**Semua collection lain juga sama:**
|
|
- `devices` → tiap dokumen ada `site_uuid`
|
|
- `threats` → tiap dokumen ada `site_uuid`
|
|
- `events` → tiap dokumen ada `site_uuid`
|
|
- `summaries` → tiap dokumen ada `site_uuid`
|
|
- `telemetry` → tiap dokumen ada `site_uuid`
|
|
|
|
---
|
|
|
|
## LAPIS 3 — Saat Dashboard Baca dari MongoDB
|
|
### File: `backend/routes/dashboard/flows.js` (contoh)
|
|
|
|
Ketika user login sebagai admin SIAB dan buka halaman Flows,
|
|
backend hanya query dokumen dengan `site_uuid` yang sesuai:
|
|
|
|
```javascript
|
|
// backend/routes/dashboard/flows.js (contoh query)
|
|
const userSiteUuid = req.user.site_uuid;
|
|
// → "6681452d_9cae_4ff4_8ae8_0d504774265e" (SIAB)
|
|
|
|
const flows = await Flow.find({
|
|
site_uuid: userSiteUuid, // <--- hanya ambil data site ini!
|
|
// ...filter waktu, pagination, dsb
|
|
}).limit(50);
|
|
```
|
|
|
|
Admin Office login → `site_uuid = 1959bb55_...` → hanya lihat data Office.
|
|
Admin SIAB login → `site_uuid = 6681452d_...` → hanya lihat data SIAB.
|
|
Super Admin → bisa pilih site mana yang ingin dilihat.
|
|
|
|
---
|
|
|
|
## RINGKASAN — Alur Lengkap Filter Data
|
|
|
|
```
|
|
Netify API
|
|
|
|
|
|-- Lapis 1: Header x-net-site dikirim ke Netify
|
|
| Netify hanya kirim data milik site tersebut
|
|
|
|
|
v
|
|
Proxy Server (setiap 5 menit)
|
|
|
|
|
|-- Lapis 2: Setiap dokumen diberi stempel site_uuid
|
|
| - SIAB data → { site_uuid: "6681452d_..." }
|
|
| - Office data → { site_uuid: "1959bb55_..." }
|
|
| - Anti-duplikat: 1 agent hanya masuk 1 site
|
|
|
|
|
v
|
|
MongoDB (semua data tercampur tapi ter-tag per site)
|
|
|
|
|
|-- Lapis 3: Backend query MongoDB dengan filter site_uuid
|
|
| - Admin SIAB login → WHERE site_uuid = SIAB-UUID
|
|
| - Admin Office login → WHERE site_uuid = OFFICE-UUID
|
|
|
|
|
v
|
|
Web Dashboard (tampil hanya data site yang sesuai)
|
|
```
|
|
|
|
---
|
|
|
|
## Skenario Konkret
|
|
|
|
**Skenario:** Network agent "F6-2V-DT-8A" ada di SIAB. Network agent "23-TE-6L-I2" ada di Office.
|
|
|
|
### Langkah 1 — Proxy request ke Netify
|
|
```
|
|
[Iter 1] siteUuid = "6681452d..." (SIAB)
|
|
→ GET /data/flows
|
|
Header: x-net-site: 6681452d...
|
|
→ Netify kembalikan: flows dari F6-2V-DT-8A (agent SIAB)
|
|
→ Simpan ke MongoDB: { site_uuid: "6681452d...", agent_uuid: "F6-2V-DT-8A", flow_id: ... }
|
|
|
|
[Iter 2] siteUuid = "1959bb55..." (Office)
|
|
→ GET /data/flows
|
|
Header: x-net-site: 1959bb55...
|
|
→ Netify kembalikan: flows dari 23-TE-6L-I2 (agent Office)
|
|
→ Simpan ke MongoDB: { site_uuid: "1959bb55...", agent_uuid: "23-TE-6L-I2", flow_id: ... }
|
|
```
|
|
|
|
### Langkah 2 — Dashboard tampilkan
|
|
```
|
|
User siab login:
|
|
req.user.site_uuid = "6681452d..."
|
|
DB query: Flow.find({ site_uuid: "6681452d..." })
|
|
Hasil: hanya flow dari F6-2V-DT-8A ✓
|
|
|
|
User office login:
|
|
req.user.site_uuid = "1959bb55..."
|
|
DB query: Flow.find({ site_uuid: "1959bb55..." })
|
|
Hasil: hanya flow dari 23-TE-6L-I2 ✓
|
|
```
|
|
|
|
**Data tidak pernah tercampur** karena ada 3 lapis isolasi ini.
|
|
|
|
---
|
|
*Dokumentasi teknis Source 2 — 29 Juli 2026*
|