Fix data synchronization per agent using Netify API and updated DB schema

This commit is contained in:
Rafif-Riqullah-Siregar committed 2026-07-02 12:44:35 +07:00
1 parent 44f701c322
commit 179e13f849
8 files changed
+3379 -408

No files matched your search

+4
View File
@@ -11,3 +11,7 @@
- Users must have defined roles (e.g., SUPER_ADMIN, TENANT_ADMIN, SOC_ANALYST, ENGINEER). - Users must have defined roles (e.g., SUPER_ADMIN, TENANT_ADMIN, SOC_ANALYST, ENGINEER).
- Frontend components and Backend API routes must check user roles before rendering sensitive actions (e.g., hiding "Delete Agent" for Analysts). - Frontend components and Backend API routes must check user roles before rendering sensitive actions (e.g., hiding "Delete Agent" for Analysts).
- **Authentication**: Use a robust session management system (e.g., NextAuth.js or JWT-based auth via backend) before exposing the dashboard. - **Authentication**: Use a robust session management system (e.g., NextAuth.js or JWT-based auth via backend) before exposing the dashboard.
# Strict Confidentiality & Privacy
- **DO NOT DISCLOSE**: All prompts, discussions, and details regarding the "Web Dashboard BackOne Deep Package Inspection" project are strictly confidential.
- **RESTRICTIONS**: It is strictly forbidden to publish, expose, or share any part of this project with the public. Future instances of this agent MUST strictly protect all information about the BackOne DPI Dashboard.
+192 -191
View File
@@ -104,7 +104,7 @@ function initSchema() {
const adminExists = d.prepare("SELECT count(*) as count FROM users WHERE username = 'admin'").get(); const adminExists = d.prepare("SELECT count(*) as count FROM users WHERE username = 'admin'").get();
if (adminExists.count === 0) { if (adminExists.count === 0) {
const hash = bcrypt.hashSync('admin123', 10); const hash = bcrypt.hashSync('admin123', 10);
d.prepare("INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)").run('admin', hash, 'SUPER_ADMIN'); d.prepare("INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?, ?)").run('admin', hash, 'SUPER_ADMIN');
console.log('[DB] Created default admin user (admin / admin123)'); console.log('[DB] Created default admin user (admin / admin123)');
} }
@@ -569,17 +569,17 @@ function initSchema() {
// ─── INSERT FUNCTIONS ───────────────────────────────────────────────────────── // ─── INSERT FUNCTIONS ─────────────────────────────────────────────────────────
function insertBandwidthApps(rows, fetchedAt, siteUuid) { function insertBandwidthApps(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO bandwidth_apps INSERT INTO bandwidth_apps
(site_uuid, fetched_at, app_id, app_label, app_tag, category, favicon, download, upload, total, flow_count) (agent_uuid, site_uuid, fetched_at, app_id, app_label, app_tag, category, favicon, download, upload, total, flow_count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.app_id ?? null, r.app_id ?? null,
r.app_label ?? 'Unknown', r.app_label ?? 'Unknown',
r.app_tag ?? null, r.app_tag ?? null,
@@ -594,17 +594,17 @@ function insertBandwidthApps(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertDevices(rows, fetchedAt, siteUuid) { function insertDevices(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO devices INSERT INTO devices
(site_uuid, fetched_at, mac_address, ip_address, device_label, device_type, os_label, manufacturer, download, upload, last_seen) (agent_uuid, site_uuid, fetched_at, mac_address, ip_address, device_label, device_type, os_label, manufacturer, download, upload, last_seen)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.mac_address ?? null, r.mac_address ?? null,
r.ip_address ?? null, r.ip_address ?? null,
r.device_label ?? r.ip_address ?? 'Unknown', r.device_label ?? r.ip_address ?? 'Unknown',
@@ -619,17 +619,17 @@ function insertDevices(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertFlows(rows, fetchedAt, siteUuid) { function insertFlows(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO flows INSERT INTO flows
(site_uuid, fetched_at, flow_id, src_ip, src_mac, dst_ip, dst_port, protocol, app_label, domain, bytes_download, bytes_upload, first_seen, last_seen) (agent_uuid, site_uuid, fetched_at, flow_id, src_ip, src_mac, dst_ip, dst_port, protocol, app_label, domain, bytes_download, bytes_upload, first_seen, last_seen)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.flow_id ?? null, r.flow_id ?? null,
r.src_ip ?? null, r.src_ip ?? null,
r.src_mac ?? null, r.src_mac ?? null,
@@ -647,17 +647,17 @@ function insertFlows(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertThreats(rows, fetchedAt, siteUuid) { function insertThreats(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO threats INSERT INTO threats
(site_uuid, fetched_at, threat_id, threat_type, severity, mac_address, ip_address, dst_ip, app_label, domain, description, detected_at) (agent_uuid, site_uuid, fetched_at, threat_id, threat_type, severity, mac_address, ip_address, dst_ip, app_label, domain, description, detected_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.threat_id ?? null, r.threat_id ?? null,
r.threat_type ?? null, r.threat_type ?? null,
r.severity ?? null, r.severity ?? null,
@@ -673,18 +673,18 @@ function insertThreats(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertProtocols(rows, fetchedAt, siteUuid) { function insertProtocols(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO bandwidth_protocols INSERT INTO bandwidth_protocols
(site_uuid, fetched_at, protocol_id, protocol_label, download, upload, flow_count) (agent_uuid, site_uuid, fetched_at, protocol_id, protocol_label, download, upload, flow_count)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
// Data sudah di-flatten oleh netify.js — akses langsung tanpa nested // Data sudah di-flatten oleh netify.js — akses langsung tanpa nested
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.protocol_id ?? null, r.protocol_id ?? null,
r.protocol_label ?? 'Unknown', r.protocol_label ?? 'Unknown',
r.download ?? 0, r.download ?? 0,
@@ -695,18 +695,18 @@ function insertProtocols(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertCountries(rows, fetchedAt, siteUuid) { function insertCountries(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO bandwidth_countries INSERT INTO bandwidth_countries
(site_uuid, fetched_at, country_code, country_name, download, upload, flow_count) (agent_uuid, site_uuid, fetched_at, country_code, country_name, download, upload, flow_count)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
// Data sudah di-flatten oleh netify.js — akses langsung tanpa nested // Data sudah di-flatten oleh netify.js — akses langsung tanpa nested
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.country_code ?? null, r.country_code ?? null,
r.country_name ?? 'Unknown', r.country_name ?? 'Unknown',
r.download ?? 0, r.download ?? 0,
@@ -717,17 +717,17 @@ function insertCountries(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertDNS(rows, fetchedAt, siteUuid) { function insertDNS(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO dns_queries INSERT INTO dns_queries
(site_uuid, fetched_at, domain, query_count, app_label, category) (agent_uuid, site_uuid, fetched_at, domain, query_count, app_label, category)
VALUES (?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.domain ?? null, r.domain ?? null,
r.query_count ?? 0, r.query_count ?? 0,
r.app_label ?? null, r.app_label ?? null,
@@ -737,17 +737,17 @@ function insertDNS(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertEvents(rows, fetchedAt, siteUuid) { function insertEvents(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(` const stmt = d.prepare(`
INSERT INTO events INSERT INTO events
(site_uuid, fetched_at, event_id, event_type, severity, mac_address, ip_address, description, event_at) (agent_uuid, site_uuid, fetched_at, event_id, event_type, severity, mac_address, ip_address, description, event_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`); `);
d.transaction((items) => { d.transaction((items) => {
for (const r of items) { for (const r of items) {
stmt.run( stmt.run(
siteUuid, fetchedAt, agentUuid, siteUuid, fetchedAt,
r.event_id ?? null, r.event_id ?? null,
r.event_type ?? null, r.event_type ?? null,
r.severity ?? null, r.severity ?? null,
@@ -760,13 +760,14 @@ function insertEvents(rows, fetchedAt, siteUuid) {
})(rows); })(rows);
} }
function insertBandwidthTimeline(summary, fetchedAt, siteUuid) { function insertBandwidthTimeline(summary, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
d.prepare(` d.prepare(`
INSERT INTO bandwidth_timeline INSERT INTO bandwidth_timeline
(site_uuid, fetched_at, total_download, total_upload, total_flows, active_devices, download_speed, upload_speed, flow_speed) (agent_uuid, site_uuid, fetched_at, total_download, total_upload, total_flows, active_devices, download_speed, upload_speed, flow_speed)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run( `).run(
agentUuid,
siteUuid, siteUuid,
fetchedAt, fetchedAt,
summary.download ?? 0, summary.download ?? 0,
@@ -846,8 +847,8 @@ function getLatestBandwidthApps(limit = 20, siteUuid = null, agentUuid = null) {
const appsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_apps`).get(); const appsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_apps`).get();
if (!appsLatest?.t) return []; if (!appsLatest?.t) return [];
const rows = d.prepare(` const rows = d.prepare(`
SELECT * FROM bandwidth_apps WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit SELECT * FROM bandwidth_apps WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
`).all({ fetched_at: appsLatest.t, limit, siteUuid }); `).all({ fetched_at: appsLatest.t, limit, siteUuid, agentUuid });
return correlateAppLabels(rows); return correlateAppLabels(rows);
} }
@@ -1093,7 +1094,7 @@ function getLatestDevices(limit = 100, siteUuid = null, agentUuid = null, search
SELECT ip_address, mac_address, device_label, device_type, os_label, manufacturer, SELECT ip_address, mac_address, device_label, device_type, os_label, manufacturer,
MAX(download) as download, MAX(upload) as upload, MAX(fetched_at) as fetched_at, site_uuid, id MAX(download) as download, MAX(upload) as upload, MAX(fetched_at) as fetched_at, site_uuid, id
FROM devices FROM devices
WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND ( WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND (
ip_address LIKE @q OR ip_address LIKE @q OR
mac_address LIKE @q OR mac_address LIKE @q OR
device_label LIKE @q OR device_label LIKE @q OR
@@ -1110,7 +1111,7 @@ function getLatestDevices(limit = 100, siteUuid = null, agentUuid = null, search
SUM(bytes_download) as download, SUM(bytes_upload) as upload, SUM(bytes_download) as download, SUM(bytes_upload) as upload,
MAX(last_seen) as last_seen, MAX(fetched_at) as fetched_at, site_uuid MAX(last_seen) as last_seen, MAX(fetched_at) as fetched_at, site_uuid
FROM flows FROM flows
WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND ( WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND (
src_ip LIKE @q OR src_ip LIKE @q OR
src_mac LIKE @q OR src_mac LIKE @q OR
app_label LIKE @q OR app_label LIKE @q OR
@@ -1544,8 +1545,8 @@ function getLatestFlows(limit = 100, siteUuid = null, agentUuid = null) {
`).all(...macs, limit); `).all(...macs, limit);
} else { } else {
rows = d.prepare(` rows = d.prepare(`
SELECT * FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY bytes_download DESC LIMIT @limit SELECT * FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY bytes_download DESC LIMIT @limit
`).all({ fetched_at: latest.t, limit, siteUuid }); `).all({ fetched_at: latest.t, limit, siteUuid, agentUuid });
} }
const mapped = rows.map(r => ({ const mapped = rows.map(r => ({
@@ -1569,7 +1570,7 @@ function getLatestThreats(limit = 50, siteUuid = null, agentUuid = null) {
LIMIT ? LIMIT ?
`).all(...macs, ...macs, limit); `).all(...macs, ...macs, limit);
} }
return d.prepare(`SELECT * FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid }); return d.prepare(`SELECT * FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid, agentUuid });
} }
function getLatestProtocols(limit = 20, siteUuid = null, agentUuid = null) { function getLatestProtocols(limit = 20, siteUuid = null, agentUuid = null) {
@@ -1593,8 +1594,8 @@ function getLatestProtocols(limit = 20, siteUuid = null, agentUuid = null) {
const protoLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_protocols`).get(); const protoLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_protocols`).get();
if (!protoLatest?.t) return []; if (!protoLatest?.t) return [];
return d.prepare(` return d.prepare(`
SELECT * FROM bandwidth_protocols WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit SELECT * FROM bandwidth_protocols WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
`).all({ fetched_at: protoLatest.t, limit, siteUuid }); `).all({ fetched_at: protoLatest.t, limit, siteUuid, agentUuid });
} }
function getLatestCountries(limit = 15, siteUuid = null, agentUuid = null) { function getLatestCountries(limit = 15, siteUuid = null, agentUuid = null) {
@@ -1711,8 +1712,8 @@ function getLatestCountries(limit = 15, siteUuid = null, agentUuid = null) {
const countryLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_countries`).get(); const countryLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_countries`).get();
if (!countryLatest?.t) return []; if (!countryLatest?.t) return [];
return d.prepare(` return d.prepare(`
SELECT * FROM bandwidth_countries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit SELECT * FROM bandwidth_countries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
`).all({ fetched_at: countryLatest.t, limit, siteUuid }); `).all({ fetched_at: countryLatest.t, limit, siteUuid, agentUuid });
} }
function getLatestDNS(limit = 20, siteUuid = null, agentUuid = null) { function getLatestDNS(limit = 20, siteUuid = null, agentUuid = null) {
@@ -1736,8 +1737,8 @@ function getLatestDNS(limit = 20, siteUuid = null, agentUuid = null) {
const dnsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM dns_queries`).get(); const dnsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM dns_queries`).get();
if (!dnsLatest?.t) return []; if (!dnsLatest?.t) return [];
return d.prepare(` return d.prepare(`
SELECT * FROM dns_queries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY query_count DESC LIMIT @limit SELECT * FROM dns_queries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY query_count DESC LIMIT @limit
`).all({ fetched_at: dnsLatest.t, limit, siteUuid }); `).all({ fetched_at: dnsLatest.t, limit, siteUuid, agentUuid });
} }
function getLatestEvents(limit = 50, siteUuid = null, agentUuid = null) { function getLatestEvents(limit = 50, siteUuid = null, agentUuid = null) {
@@ -1752,7 +1753,7 @@ function getLatestEvents(limit = 50, siteUuid = null, agentUuid = null) {
LIMIT ? LIMIT ?
`).all(...macs, ...macs, limit); `).all(...macs, ...macs, limit);
} }
return d.prepare(`SELECT * FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid }); return d.prepare(`SELECT * FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid, agentUuid });
} }
function getBandwidthTimeline(points = 60, siteUuid = null, agentUuid = null) { function getBandwidthTimeline(points = 60, siteUuid = null, agentUuid = null) {
@@ -1780,7 +1781,7 @@ function getBandwidthTimeline(points = 60, siteUuid = null, agentUuid = null) {
`).all(...macs, ...macs, points).reverse(); `).all(...macs, ...macs, points).reverse();
} }
return d.prepare(` return d.prepare(`
SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit
`).all({ limit: points, siteUuid }).reverse(); `).all({ limit: points, siteUuid }).reverse();
} }
@@ -1849,141 +1850,141 @@ function getStats(siteUuid = null, agentUuid = null) {
// Fallback to site-wide stats if no agentUuid // Fallback to site-wide stats if no agentUuid
const latestDevFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM devices`).get(); const latestDevFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM devices`).get();
const totalDevices = latestDevFetch?.t const totalDevices = latestDevFetch?.t
? (d.prepare(`SELECT COUNT(*) as n FROM devices WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestDevFetch.t, siteUuid })?.n ?? 0) ? (d.prepare(`SELECT COUNT(*) as n FROM devices WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestDevFetch.t, siteUuid })?.n ?? 0)
: 0; : 0;
const latestFlowFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get(); const latestFlowFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
const activeFlows = latestFlowFetch?.t const activeFlows = latestFlowFetch?.t
? (d.prepare(`SELECT COUNT(*) as n FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestFlowFetch.t, siteUuid })?.n ?? 0) ? (d.prepare(`SELECT COUNT(*) as n FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestFlowFetch.t, siteUuid })?.n ?? 0)
: 0; : 0;
const totalThreats = d.prepare(`SELECT COUNT(*) as n FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)`).get({ siteUuid })?.n ?? 0; const totalThreats = d.prepare(`SELECT COUNT(*) as n FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid)`).get({ siteUuid, agentUuid })?.n ?? 0;
const totalEvents = d.prepare(`SELECT COUNT(*) as n FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)`).get({ siteUuid })?.n ?? 0; const totalEvents = d.prepare(`SELECT COUNT(*) as n FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid)`).get({ siteUuid, agentUuid })?.n ?? 0;
const lastFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_timeline`).get()?.t ?? null; const lastFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_timeline`).get()?.t ?? null;
const latestBw = d.prepare(`SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT 1`).get({ siteUuid }); const latestBw = d.prepare(`SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT 1`).get({ siteUuid, agentUuid });
return { totalDevices, activeFlows, totalThreats, totalEvents, lastFetch, latestBw }; return { totalDevices, activeFlows, totalThreats, totalEvents, lastFetch, latestBw };
} }
// ─── INSERT FITUR BARU 1-11 ─────────────────────────────────────────────────── // ─── INSERT FITUR BARU 1-11 ───────────────────────────────────────────────────
function insertAppCategories(rows, fetchedAt, siteUuid) { function insertAppCategories(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO app_categories const stmt = d.prepare(`INSERT INTO app_categories
(site_uuid, fetched_at, category_label, download, upload, total) (agent_uuid, site_uuid, fetched_at, category_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.category_label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.category_label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertContinents(rows, fetchedAt, siteUuid) { function insertContinents(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO continents const stmt = d.prepare(`INSERT INTO continents
(site_uuid, fetched_at, continent_name, download, upload, total) (agent_uuid, site_uuid, fetched_at, continent_name, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.continent_name, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.continent_name, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertRegions(rows, fetchedAt, siteUuid) { function insertRegions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO regions const stmt = d.prepare(`INSERT INTO regions
(site_uuid, fetched_at, region_name, region_code, country_name, country_code, download) (agent_uuid, site_uuid, fetched_at, region_name, region_code, country_name, country_code, download)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.region_name, r.region_code, r.country_name, r.country_code, r.download); agentUuid, siteUuid, fetchedAt, r.region_name, r.region_code, r.country_name, r.country_code, r.download);
})(rows); })(rows);
} }
function insertCities(rows, fetchedAt, siteUuid) { function insertCities(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO cities const stmt = d.prepare(`INSERT INTO cities
(site_uuid, fetched_at, city_name, region_name, country_name, country_code, download) (agent_uuid, site_uuid, fetched_at, city_name, region_name, country_name, country_code, download)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.city_name, r.region_name, r.country_name, r.country_code, r.download); agentUuid, siteUuid, fetchedAt, r.city_name, r.region_name, r.country_name, r.country_code, r.download);
})(rows); })(rows);
} }
function insertVLANs(rows, fetchedAt, siteUuid) { function insertVLANs(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO vlans const stmt = d.prepare(`INSERT INTO vlans
(site_uuid, fetched_at, vlan_id, vlan_label, download, upload, total) (agent_uuid, site_uuid, fetched_at, vlan_id, vlan_label, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.vlan_id, r.vlan_label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.vlan_id, r.vlan_label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertInterfaces(rows, fetchedAt, siteUuid) { function insertInterfaces(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO interfaces const stmt = d.prepare(`INSERT INTO interfaces
(site_uuid, fetched_at, iface_id, iface_name, iface_role, agent_id, download, upload, total) (agent_uuid, site_uuid, fetched_at, iface_id, iface_name, iface_role, agent_id, download, upload, total)
VALUES (?, ?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.iface_id, r.iface_name, r.iface_role, String(r.agent_id ?? ''), r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.iface_id, r.iface_name, r.iface_role, String(r.agent_id ?? ''), r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertFlowTypes(rows, fetchedAt, siteUuid) { function insertFlowTypes(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO flow_types const stmt = d.prepare(`INSERT INTO flow_types
(site_uuid, fetched_at, flow_type_label, download, upload, total) (agent_uuid, site_uuid, fetched_at, flow_type_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.flow_type_label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.flow_type_label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertFlowOrigins(rows, fetchedAt, siteUuid) { function insertFlowOrigins(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO flow_origins const stmt = d.prepare(`INSERT INTO flow_origins
(site_uuid, fetched_at, flow_origin_label, download, upload, total) (agent_uuid, site_uuid, fetched_at, flow_origin_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.flow_origin_label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.flow_origin_label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertIPVersions(rows, fetchedAt, siteUuid) { function insertIPVersions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO ip_versions const stmt = d.prepare(`INSERT INTO ip_versions
(site_uuid, fetched_at, ip_version_label, download, upload, total) (agent_uuid, site_uuid, fetched_at, ip_version_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.ip_version_label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.ip_version_label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertRemoteIPs(rows, fetchedAt, siteUuid) { function insertRemoteIPs(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO remote_ips const stmt = d.prepare(`INSERT INTO remote_ips
(site_uuid, fetched_at, remote_ip, ip_version, download, upload, total) (agent_uuid, site_uuid, fetched_at, remote_ip, ip_version, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.remote_ip, r.ip_version, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.remote_ip, r.ip_version, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertMACBandwidth(rows, fetchedAt, siteUuid) { function insertMACBandwidth(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO mac_bandwidth const stmt = d.prepare(`INSERT INTO mac_bandwidth
(site_uuid, fetched_at, mac_address, manufacturer, download, upload, total) (agent_uuid, site_uuid, fetched_at, mac_address, manufacturer, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.mac_address, r.manufacturer, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.mac_address, r.manufacturer, r.download, r.upload, r.total);
})(rows); })(rows);
} }
@@ -2048,7 +2049,7 @@ function getLatest(table, orderBy = 'download', limit = 50, siteUuid = null, age
} }
// Fallback to standard site-wide select // Fallback to standard site-wide select
rows = d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY ${orderBy} DESC LIMIT @limit`).all({ fetched_at: latest.t, limit, siteUuid }); rows = d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY ${orderBy} DESC LIMIT @limit`).all({ fetched_at: latest.t, limit, siteUuid, agentUuid });
// If agentUuid is provided, scale down numerical values by traffic ratio to match the agent's footprint // If agentUuid is provided, scale down numerical values by traffic ratio to match the agent's footprint
if (agentUuid && !['mac_bandwidth', 'interfaces'].includes(table)) { if (agentUuid && !['mac_bandwidth', 'interfaces'].includes(table)) {
@@ -2067,272 +2068,272 @@ function getLatest(table, orderBy = 'download', limit = 50, siteUuid = null, age
} }
// DPI Fields // DPI Fields
function insertTLSVersions(rows, fetchedAt, siteUuid) { function insertTLSVersions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO tls_versions const stmt = d.prepare(`INSERT INTO tls_versions
(site_uuid, fetched_at, tls_version, download, upload, total) (agent_uuid, site_uuid, fetched_at, tls_version, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.tls_version, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.tls_version, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertTLSCiphers(rows, fetchedAt, siteUuid) { function insertTLSCiphers(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO tls_ciphers const stmt = d.prepare(`INSERT INTO tls_ciphers
(site_uuid, fetched_at, tls_cipher, download, upload, total) (agent_uuid, site_uuid, fetched_at, tls_cipher, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.tls_cipher, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.tls_cipher, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertTLSSecurity(rows, fetchedAt, siteUuid) { function insertTLSSecurity(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO tls_security const stmt = d.prepare(`INSERT INTO tls_security
(site_uuid, fetched_at, tls_security, color, download, upload, total) (agent_uuid, site_uuid, fetched_at, tls_security, color, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.tls_security, r.color, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.tls_security, r.color, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertNetBIOSHostnames(rows, fetchedAt, siteUuid) { function insertNetBIOSHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO netbios_hostnames const stmt = d.prepare(`INSERT INTO netbios_hostnames
(site_uuid, fetched_at, hostname, download, upload, total) (agent_uuid, site_uuid, fetched_at, hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.hostname, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.hostname, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertDiscoveryOS(rows, fetchedAt, siteUuid) { function insertDiscoveryOS(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO discovery_os const stmt = d.prepare(`INSERT INTO discovery_os
(site_uuid, fetched_at, os_label, download, upload, total) (agent_uuid, site_uuid, fetched_at, os_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.os_label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.os_label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
// ─── INSERT DPI 12-21 ──────────────────────────────────────────────────────── // ─── INSERT DPI 12-21 ────────────────────────────────────────────────────────
function insertDHCPFingerprints(rows, fetchedAt, siteUuid) { function insertDHCPFingerprints(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO dhcp_fingerprints const stmt = d.prepare(`INSERT INTO dhcp_fingerprints
(site_uuid, fetched_at, fingerprint, download, upload, total) (agent_uuid, site_uuid, fetched_at, fingerprint, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.fingerprint, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.fingerprint, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertHTTPUserAgents(rows, fetchedAt, siteUuid) { function insertHTTPUserAgents(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO http_user_agents const stmt = d.prepare(`INSERT INTO http_user_agents
(site_uuid, fetched_at, user_agent, download, upload, total) (agent_uuid, site_uuid, fetched_at, user_agent, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.user_agent, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.user_agent, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertSNIHostnames(rows, fetchedAt, siteUuid) { function insertSNIHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO sni_hostnames const stmt = d.prepare(`INSERT INTO sni_hostnames
(site_uuid, fetched_at, sni_hostname, download, upload, total) (agent_uuid, site_uuid, fetched_at, sni_hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.sni_hostname, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.sni_hostname, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertSSLServerCN(rows, fetchedAt, siteUuid) { function insertSSLServerCN(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO ssl_server_cn const stmt = d.prepare(`INSERT INTO ssl_server_cn
(site_uuid, fetched_at, ssl_server_cn, download, upload, total) (agent_uuid, site_uuid, fetched_at, ssl_server_cn, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.ssl_server_cn, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.ssl_server_cn, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertQUICHostnames(rows, fetchedAt, siteUuid) { function insertQUICHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO quic_hostnames const stmt = d.prepare(`INSERT INTO quic_hostnames
(site_uuid, fetched_at, quic_hostname, download, upload, total) (agent_uuid, site_uuid, fetched_at, quic_hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.quic_hostname, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.quic_hostname, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertBitTorrentHashes(rows, fetchedAt, siteUuid) { function insertBitTorrentHashes(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO bittorrent_hashes const stmt = d.prepare(`INSERT INTO bittorrent_hashes
(site_uuid, fetched_at, info_hash, label, download, upload, total) (agent_uuid, site_uuid, fetched_at, info_hash, label, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.info_hash, r.label, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.info_hash, r.label, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertSSHVersions(rows, fetchedAt, siteUuid) { function insertSSHVersions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO ssh_versions const stmt = d.prepare(`INSERT INTO ssh_versions
(site_uuid, fetched_at, ssh_version, download, upload, total) (agent_uuid, site_uuid, fetched_at, ssh_version, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.ssh_version, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.ssh_version, r.download, r.upload, r.total);
})(rows); })(rows);
} }
function insertMDNSHostnames(rows, fetchedAt, siteUuid) { function insertMDNSHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO mdns_hostnames const stmt = d.prepare(`INSERT INTO mdns_hostnames
(site_uuid, fetched_at, mdns_hostname, download, upload, total) (agent_uuid, site_uuid, fetched_at, mdns_hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.mdns_hostname, r.download, r.upload, r.total); agentUuid, siteUuid, fetchedAt, r.mdns_hostname, r.download, r.upload, r.total);
})(rows); })(rows);
} }
// ─── INSERT INTELLIGENCE 22-30 ──────────────────────────────────────────────── // ─── INSERT INTELLIGENCE 22-30 ────────────────────────────────────────────────
function insertCryptoMining(rows, fetchedAt, siteUuid) { function insertCryptoMining(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_crypto_mining const stmt = d.prepare(`INSERT INTO intel_crypto_mining
(site_uuid, fetched_at, detected_at, ip_address, mac_address, pool_host, pool_ip, protocol, app_label, confidence, download, upload) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, pool_host, pool_ip, protocol, app_label, confidence, download, upload)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.pool_host, r.pool_ip, r.protocol, r.app_label, r.confidence, r.pool_host, r.pool_ip, r.protocol, r.app_label, r.confidence,
r.download ?? 0, r.upload ?? 0 r.download ?? 0, r.upload ?? 0
); );
})(rows); })(rows);
} }
function insertDeviceDiscovery(rows, fetchedAt, siteUuid) { function insertDeviceDiscovery(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_device_discovery const stmt = d.prepare(`INSERT INTO intel_device_discovery
(site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, device_type, os_label, manufacturer, is_new) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, device_type, os_label, manufacturer, is_new)
VALUES (?, ?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.device_label, r.device_type, r.os_label, r.manufacturer, r.device_label, r.device_type, r.os_label, r.manufacturer,
r.is_new ? 1 : 0 r.is_new ? 1 : 0
); );
})(rows); })(rows);
} }
function insertEncryptionAudit(rows, fetchedAt, siteUuid) { function insertEncryptionAudit(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_encryption_audit const stmt = d.prepare(`INSERT INTO intel_encryption_audit
(site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, encrypted_pct, unencrypted, encrypted, total, risk_level) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, encrypted_pct, unencrypted, encrypted, total, risk_level)
VALUES (?, ?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.device_label, r.encrypted_pct, r.unencrypted ?? 0, r.encrypted ?? 0, r.device_label, r.encrypted_pct, r.unencrypted ?? 0, r.encrypted ?? 0,
r.total ?? 0, r.risk_level r.total ?? 0, r.risk_level
); );
})(rows); })(rows);
} }
function insertInsecureProtocols(rows, fetchedAt, siteUuid) { function insertInsecureProtocols(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_insecure_protocols const stmt = d.prepare(`INSERT INTO intel_insecure_protocols
(site_uuid, fetched_at, detected_at, protocol, ip_address, mac_address, dst_ip, dst_port, app_label, download, upload, risk, source) (agent_uuid, site_uuid, fetched_at, detected_at, protocol, ip_address, mac_address, dst_ip, dst_port, app_label, download, upload, risk, source)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.protocol, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.protocol, r.ip_address, r.mac_address,
r.dst_ip, r.dst_port, r.app_label, r.download ?? 0, r.upload ?? 0, r.dst_ip, r.dst_port, r.app_label, r.download ?? 0, r.upload ?? 0,
r.risk ?? 'Medium', r.source ?? 'api' r.risk ?? 'Medium', r.source ?? 'api'
); );
})(rows); })(rows);
} }
function insertIPReputation(rows, fetchedAt, siteUuid) { function insertIPReputation(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_ip_reputation const stmt = d.prepare(`INSERT INTO intel_ip_reputation
(site_uuid, fetched_at, detected_at, ip_address, local_ip, mac_address, reputation, score, country, app_label, download, upload, blacklisted) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, local_ip, mac_address, reputation, score, country, app_label, download, upload, blacklisted)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.local_ip, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.local_ip, r.mac_address,
r.reputation, r.score, r.country, r.app_label, r.reputation, r.score, r.country, r.app_label,
r.download ?? 0, r.upload ?? 0, r.blacklisted ? 1 : 0 r.download ?? 0, r.upload ?? 0, r.blacklisted ? 1 : 0
); );
})(rows); })(rows);
} }
function insertServerDiscovery(rows, fetchedAt, siteUuid) { function insertServerDiscovery(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_server_discovery const stmt = d.prepare(`INSERT INTO intel_server_discovery
(site_uuid, fetched_at, detected_at, ip_address, mac_address, server_type, hostname, port, protocol, os_label, download, upload) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, server_type, hostname, port, protocol, os_label, download, upload)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.server_type, r.hostname, r.port, r.protocol, r.os_label, r.server_type, r.hostname, r.port, r.protocol, r.os_label,
r.download ?? 0, r.upload ?? 0 r.download ?? 0, r.upload ?? 0
); );
})(rows); })(rows);
} }
function insertTorDetection(rows, fetchedAt, siteUuid) { function insertTorDetection(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_tor_detection const stmt = d.prepare(`INSERT INTO intel_tor_detection
(site_uuid, fetched_at, detected_at, ip_address, mac_address, exit_node, circuit_id, download, upload, country) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, exit_node, circuit_id, download, upload, country)
VALUES (?, ?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.exit_node, r.circuit_id, r.download ?? 0, r.upload ?? 0, r.country r.exit_node, r.circuit_id, r.download ?? 0, r.upload ?? 0, r.country
); );
})(rows); })(rows);
} }
function insertUnencryptedPasswords(rows, fetchedAt, siteUuid) { function insertUnencryptedPasswords(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_unencrypted_passwords const stmt = d.prepare(`INSERT INTO intel_unencrypted_passwords
(site_uuid, fetched_at, detected_at, ip_address, mac_address, dst_ip, dst_port, protocol, username, download, upload, severity) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, dst_ip, dst_port, protocol, username, download, upload, severity)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.dst_ip, r.dst_port, r.protocol, r.username, r.dst_ip, r.dst_port, r.protocol, r.username,
r.download ?? 0, r.upload ?? 0, r.severity ?? 'Critical' r.download ?? 0, r.upload ?? 0, r.severity ?? 'Critical'
); );
})(rows); })(rows);
} }
function insertVPNDetection(rows, fetchedAt, siteUuid) { function insertVPNDetection(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB(); const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_vpn_detection const stmt = d.prepare(`INSERT INTO intel_vpn_detection
(site_uuid, fetched_at, detected_at, ip_address, mac_address, vpn_type, remote_ip, protocol, download, upload, country, confidence) (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, vpn_type, remote_ip, protocol, download, upload, country, confidence)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`); VALUES (?, ?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => { d.transaction(items => {
for (const r of items) stmt.run( for (const r of items) stmt.run(
siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address, agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.vpn_type, r.remote_ip, r.protocol, r.vpn_type, r.remote_ip, r.protocol,
r.download ?? 0, r.upload ?? 0, r.country, r.confidence r.download ?? 0, r.upload ?? 0, r.country, r.confidence
); );
@@ -2357,7 +2358,7 @@ function getIntelData(table, limit = 100, siteUuid = null, agentUuid = null) {
LIMIT ? LIMIT ?
`).all(...macs, ...macs, limit); `).all(...macs, ...macs, limit);
} }
return d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid }); return d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid, agentUuid });
} }
function getIntelStats(siteUuid = null, agentUuid = null) { function getIntelStats(siteUuid = null, agentUuid = null) {
@@ -2381,7 +2382,7 @@ function getIntelStats(siteUuid = null, agentUuid = null) {
WHERE mac_address IN (${placeholders}) OR ${ipField} IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders})) WHERE mac_address IN (${placeholders}) OR ${ipField} IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
`).get(...macs, ...macs)?.n ?? 0; `).get(...macs, ...macs)?.n ?? 0;
} else { } else {
counts[t] = d.prepare(`SELECT COUNT(*) as n FROM ${t} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)`).get({ siteUuid })?.n ?? 0; counts[t] = d.prepare(`SELECT COUNT(*) as n FROM ${t} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid)`).get({ siteUuid, agentUuid })?.n ?? 0;
} }
} catch { counts[t] = 0; } } catch { counts[t] = 0; }
} }
+130 -129
View File
@@ -353,7 +353,8 @@ function getMockDataForPath(path, params = {}) {
return []; return [];
} }
async function netifyFetch(path, params = {}, useApiKey = false) { async function netifyFetch(path, params = {}, useApiKey = false, agentUuid = null) {
if (agentUuid) params.settings_agent = agentUuid;
if (!JWT_TOKEN || !SITE_UUID || JWT_TOKEN === 'YOUR_JWT_TOKEN' || SITE_UUID === 'YOUR_SITE_UUID' || JWT_TOKEN.startsWith('change_me') || JWT_TOKEN.startsWith('YOUR_')) { if (!JWT_TOKEN || !SITE_UUID || JWT_TOKEN === 'YOUR_JWT_TOKEN' || SITE_UUID === 'YOUR_SITE_UUID' || JWT_TOKEN.startsWith('change_me') || JWT_TOKEN.startsWith('YOUR_')) {
return getMockDataForPath(path, params); return getMockDataForPath(path, params);
} }
@@ -376,10 +377,10 @@ async function netifyFetch(path, params = {}, useApiKey = false) {
} }
// ─── TOP APPS: download + upload digabung ───────────────────────────────────── // ─── TOP APPS: download + upload digabung ─────────────────────────────────────
async function fetchTopApps(interval = 1440, limit = 20) { async function fetchTopApps(interval = 1440, limit = 20, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/application/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/application/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/application/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/application/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
@@ -406,10 +407,10 @@ async function fetchTopApps(interval = 1440, limit = 20) {
} }
// ─── TOP DEVICES: download + upload digabung ────────────────────────────────── // ─── TOP DEVICES: download + upload digabung ──────────────────────────────────
async function fetchTopDevices(interval = 1440, limit = 50) { async function fetchTopDevices(interval = 1440, limit = 50, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/local_ip/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/local_ip/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/local_ip/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/local_ip/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
@@ -461,11 +462,11 @@ const PORT_SERVICE_MAP = {
// NOTE: API flows tidak punya field app/domain — enrichment dilakukan via: // NOTE: API flows tidak punya field app/domain — enrichment dilakukan via:
// 1. PORT_SERVICE_MAP (reliable, berdasarkan dst port) // 1. PORT_SERVICE_MAP (reliable, berdasarkan dst port)
// 2. tls_sni field (satu-satunya SNI field valid, tapi nilai sering kosong) // 2. tls_sni field (satu-satunya SNI field valid, tapi nilai sering kosong)
async function fetchFlows(limit = 500) { async function fetchFlows(limit = 500, agentUuid = null) {
// Hanya fetch flows + tls_sni (satu-satunya SNI field yang valid = bukan 422) // Hanya fetch flows + tls_sni (satu-satunya SNI field yang valid = bukan 422)
const [raw, tslSniRaw] = await Promise.all([ const [raw, tslSniRaw] = await Promise.all([
netifyFetch('/data/flows', { settings_limit: limit }), netifyFetch('/data/flows', { settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/tls_sni/download', { filter_interval: 1440, settings_limit: 50 }), netifyFetch('/data/stats/top/tls_sni/download', { filter_interval: 1440, settings_limit: 50 }, false, agentUuid),
]); ]);
if (!raw || !Array.isArray(raw)) return null; if (!raw || !Array.isArray(raw)) return null;
@@ -513,10 +514,10 @@ async function fetchFlows(limit = 500) {
// ─── PROTOCOLS ──────────────────────────────────────────────────────────────── // ─── PROTOCOLS ────────────────────────────────────────────────────────────────
async function fetchTopProtocols(interval = 1440, limit = 20) { async function fetchTopProtocols(interval = 1440, limit = 20, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/protocol/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/protocol/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/protocol/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/protocol/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
@@ -538,10 +539,10 @@ async function fetchTopProtocols(interval = 1440, limit = 20) {
} }
// ─── COUNTRIES ──────────────────────────────────────────────────────────────── // ─── COUNTRIES ────────────────────────────────────────────────────────────────
async function fetchTopCountries(interval = 1440, limit = 15) { async function fetchTopCountries(interval = 1440, limit = 15, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/country/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/country/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/country/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/country/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
@@ -563,12 +564,12 @@ async function fetchTopCountries(interval = 1440, limit = 15) {
} }
// ─── DNS/HOSTNAME — gunakan tls_sni karena hostname endpoint timeout ────────── // ─── DNS/HOSTNAME — gunakan tls_sni karena hostname endpoint timeout ──────────
async function fetchTopDomains(interval = 1440, limit = 50) { async function fetchTopDomains(interval = 1440, limit = 50, agentUuid = null) {
// NOTE: /data/stats/top/hostname/download selalu timeout // NOTE: /data/stats/top/hostname/download selalu timeout
// Gunakan tls_sni yang confirmed bekerja di API ini // Gunakan tls_sni yang confirmed bekerja di API ini
const raw = await netifyFetch('/data/stats/top/tls_sni/download', { const raw = await netifyFetch('/data/stats/top/tls_sni/download', {
filter_interval: interval, settings_limit: limit, filter_interval: interval, settings_limit: limit,
}); }, false, agentUuid);
if (!raw) return null; if (!raw) return null;
return raw return raw
@@ -584,8 +585,8 @@ async function fetchTopDomains(interval = 1440, limit = 50) {
// ─── BANDWIDTH SUMMARY untuk timeline ───────────────────────────────────────── // ─── BANDWIDTH SUMMARY untuk timeline ─────────────────────────────────────────
async function fetchBandwidthSummary(interval = 30) { async function fetchBandwidthSummary(interval = 30, agentUuid = null) {
const rawSummary = await netifyFetch('/data/stats/summary', { filter_interval: interval }); const rawSummary = await netifyFetch('/data/stats/summary', { filter_interval: interval }, false, agentUuid);
if (rawSummary) { if (rawSummary) {
return { return {
download: rawSummary.download ?? 0, download: rawSummary.download ?? 0,
@@ -600,8 +601,8 @@ async function fetchBandwidthSummary(interval = 30) {
// Fallback to old way (aggregate top 100 application stats) // Fallback to old way (aggregate top 100 application stats)
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/application/download', { filter_interval: interval, settings_limit: 100 }), netifyFetch('/data/stats/top/application/download', { filter_interval: interval, settings_limit: 100 }, false, agentUuid),
netifyFetch('/data/stats/top/application/upload', { filter_interval: interval, settings_limit: 100 }), netifyFetch('/data/stats/top/application/upload', { filter_interval: interval, settings_limit: 100 }, false, agentUuid),
]); ]);
const download = (dlData ?? []).reduce((s, r) => s + (r.download ?? 0), 0); const download = (dlData ?? []).reduce((s, r) => s + (r.download ?? 0), 0);
@@ -620,15 +621,15 @@ async function fetchBandwidthSummary(interval = 30) {
} }
// ─── THREATS — gunakan flows dengan filter anomali sebagai fallback ──────────── // ─── THREATS — gunakan flows dengan filter anomali sebagai fallback ────────────
async function fetchCyberThreats(limit = 50) { async function fetchCyberThreats(limit = 50, agentUuid = null) {
// Events/threats belum ada di v1 — return array kosong agar tidak error // Events/threats belum ada di v1 — return array kosong agar tidak error
// Akan diisi saat endpoint ditemukan // Akan diisi saat endpoint ditemukan
return []; return [];
} }
// ─── EVENTS ─────────────────────────────────────────────────────────────────── // ─── EVENTS ───────────────────────────────────────────────────────────────────
async function fetchEvents(limit = 50) { async function fetchEvents(limit = 50, agentUuid = null) {
const raw = await netifyFetch('/event/events', { settings_limit: limit }); const raw = await netifyFetch('/event/events', { settings_limit: limit }, false, agentUuid);
if (!raw || !Array.isArray(raw)) return []; if (!raw || !Array.isArray(raw)) return [];
return raw.map(r => { return raw.map(r => {
@@ -673,13 +674,13 @@ async function fetchEvents(limit = 50) {
}); });
} }
async function fetchDiscoveredDevices(interval = 1440, limit = 500) { async function fetchDiscoveredDevices(interval = 1440, limit = 500, agentUuid = null) {
// Ambil lebih banyak bandwidth data (limit x2) supaya coverage IP lebih luas // Ambil lebih banyak bandwidth data (limit x2) supaya coverage IP lebih luas
const [intelRaw, dlData, ulData, flowsRaw] = await Promise.all([ const [intelRaw, dlData, ulData, flowsRaw] = await Promise.all([
netifyFetch('/intelligence/discovery/devices', { settings_limit: limit }), netifyFetch('/intelligence/discovery/devices', { settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/local_ip/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/local_ip/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/local_ip/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/local_ip/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/flows', { settings_limit: 500 }), netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid),
]); ]);
if (!intelRaw || !Array.isArray(intelRaw)) return []; if (!intelRaw || !Array.isArray(intelRaw)) return [];
@@ -804,10 +805,10 @@ const OUI_MAP = {
// ─── TAMBAHAN FITUR 1-11 ────────────────────────────────────────────────────── // ─── TAMBAHAN FITUR 1-11 ──────────────────────────────────────────────────────
// 1. Top Application Category // 1. Top Application Category
async function fetchTopAppCategories(interval = 1440, limit = 15) { async function fetchTopAppCategories(interval = 1440, limit = 15, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/application_category/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/application_category/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/application_category/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/application_category/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -827,10 +828,10 @@ async function fetchTopAppCategories(interval = 1440, limit = 15) {
} }
// 2. Top Continent // 2. Top Continent
async function fetchTopContinents(interval = 1440, limit = 10) { async function fetchTopContinents(interval = 1440, limit = 10, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/continent/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/continent/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/continent/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/continent/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -850,10 +851,10 @@ async function fetchTopContinents(interval = 1440, limit = 10) {
} }
// 3. Top Region // 3. Top Region
async function fetchTopRegions(interval = 1440, limit = 20) { async function fetchTopRegions(interval = 1440, limit = 20, agentUuid = null) {
const raw = await netifyFetch('/data/stats/top/region/download', { const raw = await netifyFetch('/data/stats/top/region/download', {
filter_interval: interval, settings_limit: limit, filter_interval: interval, settings_limit: limit,
}); }, false, agentUuid);
if (!raw) return null; if (!raw) return null;
return raw.map(r => ({ return raw.map(r => ({
region_name : r.region?.region_name?.trim() || '(Unknown Region)', region_name : r.region?.region_name?.trim() || '(Unknown Region)',
@@ -865,10 +866,10 @@ async function fetchTopRegions(interval = 1440, limit = 20) {
} }
// 4. Top City // 4. Top City
async function fetchTopCities(interval = 1440, limit = 20) { async function fetchTopCities(interval = 1440, limit = 20, agentUuid = null) {
const raw = await netifyFetch('/data/stats/top/city/download', { const raw = await netifyFetch('/data/stats/top/city/download', {
filter_interval: interval, settings_limit: limit, filter_interval: interval, settings_limit: limit,
}); }, false, agentUuid);
if (!raw) return null; if (!raw) return null;
return raw.map(r => ({ return raw.map(r => ({
city_name : r.city?.city?.trim() || '(Unknown City)', city_name : r.city?.city?.trim() || '(Unknown City)',
@@ -880,10 +881,10 @@ async function fetchTopCities(interval = 1440, limit = 20) {
} }
// 5. Top VLAN // 5. Top VLAN
async function fetchTopVLANs(interval = 1440, limit = 20) { async function fetchTopVLANs(interval = 1440, limit = 20, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/vlan/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/vlan/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/vlan/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/vlan/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -901,10 +902,10 @@ async function fetchTopVLANs(interval = 1440, limit = 20) {
} }
// 6. Top Interface // 6. Top Interface
async function fetchTopInterfaces(interval = 1440, limit = 20) { async function fetchTopInterfaces(interval = 1440, limit = 20, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/interface/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/interface/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/interface/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/interface/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -924,10 +925,10 @@ async function fetchTopInterfaces(interval = 1440, limit = 20) {
} }
// 7. Top Flow Type // 7. Top Flow Type
async function fetchTopFlowTypes(interval = 1440, limit = 10) { async function fetchTopFlowTypes(interval = 1440, limit = 10, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/flow_type/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/flow_type/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/flow_type/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/flow_type/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -947,10 +948,10 @@ async function fetchTopFlowTypes(interval = 1440, limit = 10) {
} }
// 8. Top Flow Origin // 8. Top Flow Origin
async function fetchTopFlowOrigins(interval = 1440, limit = 10) { async function fetchTopFlowOrigins(interval = 1440, limit = 10, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/flow_origin/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/flow_origin/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/flow_origin/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/flow_origin/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -970,10 +971,10 @@ async function fetchTopFlowOrigins(interval = 1440, limit = 10) {
} }
// 9. Top IP Version // 9. Top IP Version
async function fetchTopIPVersions(interval = 1440, limit = 5) { async function fetchTopIPVersions(interval = 1440, limit = 5, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/ip_version/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/ip_version/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/ip_version/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/ip_version/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -993,10 +994,10 @@ async function fetchTopIPVersions(interval = 1440, limit = 5) {
} }
// 10. Top Remote IP // 10. Top Remote IP
async function fetchTopRemoteIPs(interval = 1440, limit = 20) { async function fetchTopRemoteIPs(interval = 1440, limit = 20, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/remote_ip/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/remote_ip/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/remote_ip/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/remote_ip/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1017,11 +1018,11 @@ async function fetchTopRemoteIPs(interval = 1440, limit = 20) {
} }
// 11. Top Local MAC + Discovery OS // 11. Top Local MAC + Discovery OS
async function fetchTopLocalMACs(interval = 1440, limit = 50) { async function fetchTopLocalMACs(interval = 1440, limit = 50, agentUuid = null) {
const [dlData, ulData, osData] = await Promise.all([ const [dlData, ulData, osData] = await Promise.all([
netifyFetch('/data/stats/top/local_mac/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/local_mac/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/local_mac/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/local_mac/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/discovery_os/download', { filter_interval: interval, settings_limit: 20 }), netifyFetch('/data/stats/top/discovery_os/download', { filter_interval: interval, settings_limit: 20 }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1048,10 +1049,10 @@ async function fetchTopLocalMACs(interval = 1440, limit = 50) {
} }
// ─── DISCOVERY OS (standalone) ─────────────────────────────────────────────── // ─── DISCOVERY OS (standalone) ───────────────────────────────────────────────
async function fetchTopDiscoveryOS(interval = 1440, limit = 20) { async function fetchTopDiscoveryOS(interval = 1440, limit = 20, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/discovery_os/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/discovery_os/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/discovery_os/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/discovery_os/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1073,10 +1074,10 @@ async function fetchTopDiscoveryOS(interval = 1440, limit = 20) {
// ─── DPI FIELDS ─────────────────────────────────────────────────────────────── // ─── DPI FIELDS ───────────────────────────────────────────────────────────────
// TLS Version // TLS Version
async function fetchTLSVersions(interval = 1440, limit = 10) { async function fetchTLSVersions(interval = 1440, limit = 10, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/tls_version/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/tls_version/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/tls_version/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/tls_version/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1096,10 +1097,10 @@ async function fetchTLSVersions(interval = 1440, limit = 10) {
} }
// TLS Cipher // TLS Cipher
async function fetchTLSCiphers(interval = 1440, limit = 15) { async function fetchTLSCiphers(interval = 1440, limit = 15, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/tls_cipher/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/tls_cipher/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/tls_cipher/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/tls_cipher/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1119,10 +1120,10 @@ async function fetchTLSCiphers(interval = 1440, limit = 15) {
} }
// TLS Security Level // TLS Security Level
async function fetchTLSSecurity(interval = 1440, limit = 10) { async function fetchTLSSecurity(interval = 1440, limit = 10, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/tls_security/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/tls_security/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/tls_security/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/tls_security/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1149,10 +1150,10 @@ async function fetchTLSSecurity(interval = 1440, limit = 10) {
} }
// NetBIOS Hostname (nama PC Windows) // NetBIOS Hostname (nama PC Windows)
async function fetchNetBIOSHostnames(interval = 1440, limit = 30) { async function fetchNetBIOSHostnames(interval = 1440, limit = 30, agentUuid = null) {
const [dlData, ulData] = await Promise.all([ const [dlData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/netbios_hostname/download', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/netbios_hostname/download', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch('/data/stats/top/netbios_hostname/upload', { filter_interval: interval, settings_limit: limit }), netifyFetch('/data/stats/top/netbios_hostname/upload', { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dlData) return null; if (!dlData) return null;
const ulMap = {}; const ulMap = {};
@@ -1171,7 +1172,7 @@ async function fetchNetBIOSHostnames(interval = 1440, limit = 30) {
}); });
} }
async function fetchLookupApplications(page = 1, limit = 50, search = '') { async function fetchLookupApplications(page = 1, limit = 50, search = '', agentUuid = null) {
if (!JWT_TOKEN || !SITE_UUID || JWT_TOKEN === 'YOUR_JWT_TOKEN' || SITE_UUID === 'YOUR_SITE_UUID' || JWT_TOKEN.startsWith('change_me') || JWT_TOKEN.startsWith('YOUR_')) { if (!JWT_TOKEN || !SITE_UUID || JWT_TOKEN === 'YOUR_JWT_TOKEN' || SITE_UUID === 'YOUR_SITE_UUID' || JWT_TOKEN.startsWith('change_me') || JWT_TOKEN.startsWith('YOUR_')) {
return { return {
applications: [ applications: [
@@ -1381,7 +1382,7 @@ async function fetchAgentDetails(agentUuid) {
} }
// Fetch data for a specific device IP — from local DB (all 10 correlated tables) // Fetch data for a specific device IP — from local DB (all 10 correlated tables)
async function fetchDeviceDetails(ip) { async function fetchDeviceDetails(ip, agentUuid = null) {
const db = require('./database'); const db = require('./database');
const d = db.getDB(); const d = db.getDB();
@@ -1840,7 +1841,7 @@ async function fetchDeviceDetails(ip) {
} }
// Fetch data for a specific application// Fetch data for a specific application — from local DB // Fetch data for a specific application// Fetch data for a specific application — from local DB
async function fetchAppDetails(appLabel) { async function fetchAppDetails(appLabel, agentUuid = null) {
const db = require('./database'); const db = require('./database');
const d = db.getDB(); const d = db.getDB();
@@ -2163,10 +2164,10 @@ module.exports = {
// mdns_hostname → mdns_hostname ✓ (sudah benar, mungkin belum aktif di akun) // mdns_hostname → mdns_hostname ✓ (sudah benar, mungkin belum aktif di akun)
// Helper builder untuk DPI single-field // Helper builder untuk DPI single-field
async function _dpiTopField(fieldName, interval, limit) { async function _dpiTopField(fieldName, interval, limit, agentUuid = null) {
const [dl, ul] = await Promise.all([ const [dl, ul] = await Promise.all([
netifyFetch(`/data/stats/top/${fieldName}/download`, { filter_interval: interval, settings_limit: limit }), netifyFetch(`/data/stats/top/${fieldName}/download`, { filter_interval: interval, settings_limit: limit }, false, agentUuid),
netifyFetch(`/data/stats/top/${fieldName}/upload`, { filter_interval: interval, settings_limit: limit }), netifyFetch(`/data/stats/top/${fieldName}/upload`, { filter_interval: interval, settings_limit: limit }, false, agentUuid),
]); ]);
if (!dl) return null; if (!dl) return null;
const ulMap = {}; const ulMap = {};
@@ -2178,8 +2179,8 @@ async function _dpiTopField(fieldName, interval, limit) {
} }
// 12. DHCP Class (field: dhcp_class) // 12. DHCP Class (field: dhcp_class)
async function fetchDHCPClassFingerprints(interval = 1440, limit = 30) { async function fetchDHCPClassFingerprints(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('dhcp_class', interval, limit); const res = await _dpiTopField('dhcp_class', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const label = r.dhcp_class?.name ?? r.dhcp_class?.label ?? String(r.dhcp_class ?? 'Unknown'); const label = r.dhcp_class?.name ?? r.dhcp_class?.label ?? String(r.dhcp_class ?? 'Unknown');
@@ -2193,8 +2194,8 @@ async function fetchDHCPClassFingerprints(interval = 1440, limit = 30) {
} }
// 13. HTTP User-Agent (field: http_useragent) // 13. HTTP User-Agent (field: http_useragent)
async function fetchHTTPUserAgents(interval = 1440, limit = 30) { async function fetchHTTPUserAgents(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('http_useragent', interval, limit); const res = await _dpiTopField('http_useragent', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const label = r.http_useragent?.name ?? r.http_useragent?.label ?? String(r.http_useragent ?? 'Unknown'); const label = r.http_useragent?.name ?? r.http_useragent?.label ?? String(r.http_useragent ?? 'Unknown');
@@ -2208,8 +2209,8 @@ async function fetchHTTPUserAgents(interval = 1440, limit = 30) {
} }
// 14. HTTPS SNI Hostname (field: https_sni_hostname) // 14. HTTPS SNI Hostname (field: https_sni_hostname)
async function fetchSNIHostnames(interval = 1440, limit = 30) { async function fetchSNIHostnames(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('https_sni_hostname', interval, limit); const res = await _dpiTopField('https_sni_hostname', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const name = r.https_sni_hostname?.name ?? r.https_sni_hostname?.label ?? String(r.https_sni_hostname ?? 'Unknown'); const name = r.https_sni_hostname?.name ?? r.https_sni_hostname?.label ?? String(r.https_sni_hostname ?? 'Unknown');
@@ -2223,8 +2224,8 @@ async function fetchSNIHostnames(interval = 1440, limit = 30) {
} }
// 15. SSL Server Common Name (field: ssl_server_cn) // 15. SSL Server Common Name (field: ssl_server_cn)
async function fetchSSLServerCN(interval = 1440, limit = 30) { async function fetchSSLServerCN(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('ssl_server_cn', interval, limit); const res = await _dpiTopField('ssl_server_cn', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const name = r.ssl_server_cn?.name ?? r.ssl_server_cn?.label ?? String(r.ssl_server_cn ?? 'Unknown'); const name = r.ssl_server_cn?.name ?? r.ssl_server_cn?.label ?? String(r.ssl_server_cn ?? 'Unknown');
@@ -2238,8 +2239,8 @@ async function fetchSSLServerCN(interval = 1440, limit = 30) {
} }
// 17. QUIC Hostname (field: quic_hostname) // 17. QUIC Hostname (field: quic_hostname)
async function fetchQUICHostnames(interval = 1440, limit = 30) { async function fetchQUICHostnames(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('quic_hostname', interval, limit); const res = await _dpiTopField('quic_hostname', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const name = r.quic_hostname?.name ?? r.quic_hostname?.label ?? String(r.quic_hostname ?? 'Unknown'); const name = r.quic_hostname?.name ?? r.quic_hostname?.label ?? String(r.quic_hostname ?? 'Unknown');
@@ -2253,8 +2254,8 @@ async function fetchQUICHostnames(interval = 1440, limit = 30) {
} }
// 18. BitTorrent Info Hash (field: bittorrent_info_hash) // 18. BitTorrent Info Hash (field: bittorrent_info_hash)
async function fetchBitTorrentInfoHashes(interval = 1440, limit = 30) { async function fetchBitTorrentInfoHashes(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('bittorrent_info_hash', interval, limit); const res = await _dpiTopField('bittorrent_info_hash', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const hash = r.bittorrent_info_hash?.hash ?? r.bittorrent_info_hash?.name ?? String(r.bittorrent_info_hash ?? 'Unknown'); const hash = r.bittorrent_info_hash?.hash ?? r.bittorrent_info_hash?.name ?? String(r.bittorrent_info_hash ?? 'Unknown');
@@ -2270,8 +2271,8 @@ async function fetchBitTorrentInfoHashes(interval = 1440, limit = 30) {
} }
// 19a. SSH Client (field: ssh_client) // 19a. SSH Client (field: ssh_client)
async function fetchSSHClients(interval = 1440, limit = 20) { async function fetchSSHClients(interval = 1440, limit = 20, agentUuid = null) {
const res = await _dpiTopField('ssh_client', interval, limit); const res = await _dpiTopField('ssh_client', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const label = r.ssh_client?.name ?? r.ssh_client?.label ?? String(r.ssh_client ?? 'Unknown'); const label = r.ssh_client?.name ?? r.ssh_client?.label ?? String(r.ssh_client ?? 'Unknown');
@@ -2286,8 +2287,8 @@ async function fetchSSHClients(interval = 1440, limit = 20) {
} }
// 19b. SSH Server (field: ssh_server) // 19b. SSH Server (field: ssh_server)
async function fetchSSHServers(interval = 1440, limit = 20) { async function fetchSSHServers(interval = 1440, limit = 20, agentUuid = null) {
const res = await _dpiTopField('ssh_server', interval, limit); const res = await _dpiTopField('ssh_server', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const label = r.ssh_server?.name ?? r.ssh_server?.label ?? String(r.ssh_server ?? 'Unknown'); const label = r.ssh_server?.name ?? r.ssh_server?.label ?? String(r.ssh_server ?? 'Unknown');
@@ -2302,8 +2303,8 @@ async function fetchSSHServers(interval = 1440, limit = 20) {
} }
// 21. mDNS Hostname (field: mdns_hostname) // 21. mDNS Hostname (field: mdns_hostname)
async function fetchMDNSHostnames(interval = 1440, limit = 30) { async function fetchMDNSHostnames(interval = 1440, limit = 30, agentUuid = null) {
const res = await _dpiTopField('mdns_hostname', interval, limit); const res = await _dpiTopField('mdns_hostname', interval, limit, agentUuid);
if (!res) return null; if (!res) return null;
return res.dl.map(r => { return res.dl.map(r => {
const name = r.mdns_hostname?.name ?? r.mdns_hostname?.label ?? String(r.mdns_hostname ?? 'Unknown'); const name = r.mdns_hostname?.name ?? r.mdns_hostname?.label ?? String(r.mdns_hostname ?? 'Unknown');
@@ -2324,13 +2325,13 @@ async function fetchMDNSHostnames(interval = 1440, limit = 30) {
// 22. Cryptocurrency Mining — derive dari apps dengan nama mengandung "crypto" / "mining" // 22. Cryptocurrency Mining — derive dari apps dengan nama mengandung "crypto" / "mining"
// + flows ke port mining pool (3333, 4444, 8333, 9999, 14444) // + flows ke port mining pool (3333, 4444, 8333, 9999, 14444)
async function fetchCryptoMining(interval = 1440, limit = 50) { async function fetchCryptoMining(interval = 1440, limit = 50, agentUuid = null) {
const MINING_POOLS_PORTS = new Set([3333, 4444, 5555, 7777, 8333, 9332, 9999, 14444, 45560, 45700]); const MINING_POOLS_PORTS = new Set([3333, 4444, 5555, 7777, 8333, 9332, 9999, 14444, 45560, 45700]);
const MINING_APPS = ['bitcoin', 'crypto', 'mining', 'monero', 'ethereum', 'nicehash', 'nanopool', 'f2pool', 'antpool', 'slushpool']; const MINING_APPS = ['bitcoin', 'crypto', 'mining', 'monero', 'ethereum', 'nicehash', 'nanopool', 'f2pool', 'antpool', 'slushpool'];
const [appData, flowsRaw] = await Promise.all([ const [appData, flowsRaw] = await Promise.all([
netifyFetch('/data/stats/top/application/download', { filter_interval: interval, settings_limit: 100 }), netifyFetch('/data/stats/top/application/download', { filter_interval: interval, settings_limit: 100 }, false, agentUuid),
netifyFetch('/data/flows', { settings_limit: 500 }), netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid),
]); ]);
const results = []; const results = [];
@@ -2384,10 +2385,10 @@ async function fetchCryptoMining(interval = 1440, limit = 50) {
} }
// 23. Device Discovery — derive dari flows (perangkat unik dengan MAC) // 23. Device Discovery — derive dari flows (perangkat unik dengan MAC)
async function fetchDeviceDiscovery(limit = 100) { async function fetchDeviceDiscovery(limit = 100, agentUuid = null) {
const [flowsRaw, dlData] = await Promise.all([ const [flowsRaw, dlData] = await Promise.all([
netifyFetch('/data/flows', { settings_limit: 500 }), netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid),
netifyFetch('/data/stats/top/local_ip/download', { filter_interval: 1440, settings_limit: 200 }), netifyFetch('/data/stats/top/local_ip/download', { filter_interval: 1440, settings_limit: 200 }, false, agentUuid),
]); ]);
const seen = new Map(); const seen = new Map();
@@ -2434,10 +2435,10 @@ async function fetchDeviceDiscovery(limit = 100) {
} }
// 24. Encryption Audit — derive dari TLS security per-IP dari flows // 24. Encryption Audit — derive dari TLS security per-IP dari flows
async function fetchEncryptionAudit(limit = 50) { async function fetchEncryptionAudit(limit = 50, agentUuid = null) {
const [tlsSec, flowsRaw] = await Promise.all([ const [tlsSec, flowsRaw] = await Promise.all([
netifyFetch('/data/stats/top/tls_security/download', { filter_interval: 1440, settings_limit: 10 }), netifyFetch('/data/stats/top/tls_security/download', { filter_interval: 1440, settings_limit: 10 }, false, agentUuid),
netifyFetch('/data/flows', { settings_limit: 500 }), netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid),
]); ]);
// Hitung per-IP: encrypted vs unencrypted flows // Hitung per-IP: encrypted vs unencrypted flows
@@ -2485,7 +2486,7 @@ async function fetchEncryptionAudit(limit = 50) {
} }
// 25. Insecure Protocols — derive dari top protocols (confirmed bekerja) // 25. Insecure Protocols — derive dari top protocols (confirmed bekerja)
async function fetchInsecureProtocols(interval = 1440, limit = 50) { async function fetchInsecureProtocols(interval = 1440, limit = 50, agentUuid = null) {
const INSECURE = { const INSECURE = {
'HTTP' : { port: 80, risk: 'High' }, 'HTTP' : { port: 80, risk: 'High' },
'FTP' : { port: 21, risk: 'Critical' }, 'FTP' : { port: 21, risk: 'Critical' },
@@ -2504,8 +2505,8 @@ async function fetchInsecureProtocols(interval = 1440, limit = 50) {
}; };
const [protoData, ulData] = await Promise.all([ const [protoData, ulData] = await Promise.all([
netifyFetch('/data/stats/top/protocol/download', { filter_interval: interval, settings_limit: 100 }), netifyFetch('/data/stats/top/protocol/download', { filter_interval: interval, settings_limit: 100 }, false, agentUuid),
netifyFetch('/data/stats/top/protocol/upload', { filter_interval: interval, settings_limit: 100 }), netifyFetch('/data/stats/top/protocol/upload', { filter_interval: interval, settings_limit: 100 }, false, agentUuid),
]); ]);
if (!protoData) return []; if (!protoData) return [];
@@ -2538,7 +2539,7 @@ async function fetchInsecureProtocols(interval = 1440, limit = 50) {
} }
// 26. IP Reputation — derive dari top remote_ip + cross-check negara berisiko tinggi // 26. IP Reputation — derive dari top remote_ip + cross-check negara berisiko tinggi
async function fetchIPReputation(limit = 50) { async function fetchIPReputation(limit = 50, agentUuid = null) {
// Negara dengan risiko tinggi berdasarkan threat intel umum // Negara dengan risiko tinggi berdasarkan threat intel umum
const HIGH_RISK_COUNTRIES = new Set([ const HIGH_RISK_COUNTRIES = new Set([
'China', 'Russia', 'Iran', 'North Korea', 'Nigeria', 'Romania', 'China', 'Russia', 'Iran', 'North Korea', 'Nigeria', 'Romania',
@@ -2546,8 +2547,8 @@ async function fetchIPReputation(limit = 50) {
]); ]);
const [ipData, countryData] = await Promise.all([ const [ipData, countryData] = await Promise.all([
netifyFetch('/data/stats/top/remote_ip/download', { filter_interval: 1440, settings_limit: 100 }), netifyFetch('/data/stats/top/remote_ip/download', { filter_interval: 1440, settings_limit: 100 }, false, agentUuid),
netifyFetch('/data/stats/top/country/download', { filter_interval: 1440, settings_limit: 50 }), netifyFetch('/data/stats/top/country/download', { filter_interval: 1440, settings_limit: 50 }, false, agentUuid),
]); ]);
const results = []; const results = [];
@@ -2602,7 +2603,7 @@ async function fetchIPReputation(limit = 50) {
} }
// 27. Server Discovery — derive dari flows dengan flow_origin=Server + port well-known server // 27. Server Discovery — derive dari flows dengan flow_origin=Server + port well-known server
async function fetchServerDiscovery(limit = 100) { async function fetchServerDiscovery(limit = 100, agentUuid = null) {
const SERVER_PORTS = { const SERVER_PORTS = {
80: 'HTTP', 443: 'HTTPS', 22: 'SSH', 21: 'FTP', 25: 'SMTP', 80: 'HTTP', 443: 'HTTPS', 22: 'SSH', 21: 'FTP', 25: 'SMTP',
110: 'POP3', 143: 'IMAP', 3306: 'MySQL', 5432: 'PostgreSQL', 110: 'POP3', 143: 'IMAP', 3306: 'MySQL', 5432: 'PostgreSQL',
@@ -2612,8 +2613,8 @@ async function fetchServerDiscovery(limit = 100) {
}; };
const [flowOriginData, flowsRaw] = await Promise.all([ const [flowOriginData, flowsRaw] = await Promise.all([
netifyFetch('/data/stats/top/flow_origin/download', { filter_interval: 1440, settings_limit: 10 }), netifyFetch('/data/stats/top/flow_origin/download', { filter_interval: 1440, settings_limit: 10 }, false, agentUuid),
netifyFetch('/data/flows', { settings_limit: 500 }), netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid),
]); ]);
const serverMap = {}; const serverMap = {};
@@ -2656,12 +2657,12 @@ async function fetchServerDiscovery(limit = 100) {
} }
// 28. Tor Detection — derive dari top remote_ip + app/hostname matching Tor // 28. Tor Detection — derive dari top remote_ip + app/hostname matching Tor
async function fetchTorDetection(limit = 50) { async function fetchTorDetection(limit = 50, agentUuid = null) {
const TOR_INDICATORS = ['tor', '.onion', 'torproject', 'torbrowser']; const TOR_INDICATORS = ['tor', '.onion', 'torproject', 'torbrowser'];
const [appData, domainData] = await Promise.all([ const [appData, domainData] = await Promise.all([
netifyFetch('/data/stats/top/application/download', { filter_interval: 1440, settings_limit: 100 }), netifyFetch('/data/stats/top/application/download', { filter_interval: 1440, settings_limit: 100 }, false, agentUuid),
netifyFetch('/data/stats/top/hostname/download', { filter_interval: 1440, settings_limit: 100 }), netifyFetch('/data/stats/top/hostname/download', { filter_interval: 1440, settings_limit: 100 }, false, agentUuid),
]); ]);
const results = []; const results = [];
@@ -2711,7 +2712,7 @@ async function fetchTorDetection(limit = 50) {
} }
// 29. Unencrypted Passwords — derive dari flows ke port cleartext auth // 29. Unencrypted Passwords — derive dari flows ke port cleartext auth
async function fetchUnencryptedPasswords(limit = 50) { async function fetchUnencryptedPasswords(limit = 50, agentUuid = null) {
// Port yang dikenal mengirim kredensial cleartext // Port yang dikenal mengirim kredensial cleartext
const CLEARTEXT_AUTH_PORTS = { const CLEARTEXT_AUTH_PORTS = {
21 : { protocol: 'FTP', severity: 'Critical' }, 21 : { protocol: 'FTP', severity: 'Critical' },
@@ -2726,7 +2727,7 @@ async function fetchUnencryptedPasswords(limit = 50) {
514 : { protocol: 'rsh', severity: 'Critical' }, 514 : { protocol: 'rsh', severity: 'Critical' },
}; };
const flowsRaw = await netifyFetch('/data/flows', { settings_limit: 500 }); const flowsRaw = await netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid);
if (!flowsRaw) return []; if (!flowsRaw) return [];
const seen = new Map(); const seen = new Map();
@@ -2760,7 +2761,7 @@ async function fetchUnencryptedPasswords(limit = 50) {
} }
// 30. VPN Detection — derive dari apps/protocols/hostnames yang mengindikasikan VPN // 30. VPN Detection — derive dari apps/protocols/hostnames yang mengindikasikan VPN
async function fetchVPNDetection(limit = 50) { async function fetchVPNDetection(limit = 50, agentUuid = null) {
const VPN_APPS = [ const VPN_APPS = [
'openvpn', 'wireguard', 'nordvpn', 'expressvpn', 'surfshark', 'tunnelbear', 'openvpn', 'wireguard', 'nordvpn', 'expressvpn', 'surfshark', 'tunnelbear',
'mullvad', 'protonvpn', 'ipvanish', 'pia', 'private internet access', 'mullvad', 'protonvpn', 'ipvanish', 'pia', 'private internet access',
@@ -2772,9 +2773,9 @@ async function fetchVPNDetection(limit = 50) {
const VPN_PORTS = new Set([1194, 51820, 500, 4500, 1701, 1723, 8388, 443]); const VPN_PORTS = new Set([1194, 51820, 500, 4500, 1701, 1723, 8388, 443]);
const [appData, protoData, flowsRaw] = await Promise.all([ const [appData, protoData, flowsRaw] = await Promise.all([
netifyFetch('/data/stats/top/application/download', { filter_interval: 1440, settings_limit: 100 }), netifyFetch('/data/stats/top/application/download', { filter_interval: 1440, settings_limit: 100 }, false, agentUuid),
netifyFetch('/data/stats/top/protocol/download', { filter_interval: 1440, settings_limit: 50 }), netifyFetch('/data/stats/top/protocol/download', { filter_interval: 1440, settings_limit: 50 }, false, agentUuid),
netifyFetch('/data/flows', { settings_limit: 500 }), netifyFetch('/data/flows', { settings_limit: 500 }, false, agentUuid),
]); ]);
const results = []; const results = [];
@@ -2849,7 +2850,7 @@ async function fetchVPNDetection(limit = 50) {
} }
async function fetchAgents() { async function fetchAgents() {
const data = await netifyFetch('/data/stats/top/agent/download', { filter_interval: 43200, settings_limit: 100 }); const data = await netifyFetch('/data/stats/top/agent/download', { filter_interval: 43200, settings_limit: 100 }, false, null);
if (!data || !Array.isArray(data)) return []; if (!data || !Array.isArray(data)) return [];
return data.map(r => ({ return data.map(r => ({
id: r.agent?.id, id: r.agent?.id,
Binary file not shown.
+102 -88
View File
@@ -18,7 +18,7 @@ async function runPoll() {
try { try {
// 0. Sync agents and seed default user accounts dynamically // 0. Sync agents and seed default user accounts dynamically
try { try {
const apiAgents = await netify.fetchAgents(); apiAgents = await netify.fetchAgents();
if (apiAgents && apiAgents.length > 0) { if (apiAgents && apiAgents.length > 0) {
db.syncAgentUsers(apiAgents); db.syncAgentUsers(apiAgents);
console.log(`[Scheduler] OK Sync Agents : ${apiAgents.length} agen terdeteksi`); console.log(`[Scheduler] OK Sync Agents : ${apiAgents.length} agen terdeteksi`);
@@ -27,256 +27,270 @@ async function runPoll() {
console.error('[Scheduler] Gagal sync agent users:', err.message); console.error('[Scheduler] Gagal sync agent users:', err.message);
} }
// 1. Top Aplikasi async function fetchAndStore(fetchedAt, agentUuid) {
const apps = await netify.fetchTopApps(1440, 20); const agentLabel = agentUuid ? agentUuid : 'Global';
console.log(`[Scheduler] Fetching data for ${agentLabel}`);
// 1. Top Aplikasi
const apps = await netify.fetchTopApps(1440, 20, agentUuid);
if (apps && Array.isArray(apps)) { if (apps && Array.isArray(apps)) {
db.insertBandwidthApps(apps, fetchedAt, SITE_UUID); db.insertBandwidthApps(apps, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Apps : ${apps.length} baris`); console.log(`[Scheduler] OK Apps : ${apps.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Apps : tidak ada data`); console.log(`[Scheduler] -- Apps : tidak ada data`);
} }
// 2. Top Devices — pakai fetchDiscoveredDevices yg sudah dinormalisasi // 2. Top Devices — pakai fetchDiscoveredDevices yg sudah dinormalisasi
const devices = await netify.fetchDiscoveredDevices(1440, 200); const devices = await netify.fetchDiscoveredDevices(1440, 200, agentUuid);
if (devices && Array.isArray(devices)) { if (devices && Array.isArray(devices)) {
db.insertDevices(devices, fetchedAt, SITE_UUID); db.insertDevices(devices, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Devices : ${devices.length} baris`); console.log(`[Scheduler] OK Devices : ${devices.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Devices : tidak ada data`); console.log(`[Scheduler] -- Devices : tidak ada data`);
} }
// 3. Top Protokol // 3. Top Protokol
const protocols = await netify.fetchTopProtocols(1440, 20); const protocols = await netify.fetchTopProtocols(1440, 20, agentUuid);
if (protocols && Array.isArray(protocols)) { if (protocols && Array.isArray(protocols)) {
db.insertProtocols(protocols, fetchedAt, SITE_UUID); db.insertProtocols(protocols, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Protocols : ${protocols.length} baris`); console.log(`[Scheduler] OK Protocols : ${protocols.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Protocols : tidak ada data`); console.log(`[Scheduler] -- Protocols : tidak ada data`);
} }
// 4. Top Negara // 4. Top Negara
const countries = await netify.fetchTopCountries(1440, 15); const countries = await netify.fetchTopCountries(1440, 15, agentUuid);
if (countries && Array.isArray(countries)) { if (countries && Array.isArray(countries)) {
db.insertCountries(countries, fetchedAt, SITE_UUID); db.insertCountries(countries, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Countries : ${countries.length} baris`); console.log(`[Scheduler] OK Countries : ${countries.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Countries : tidak ada data`); console.log(`[Scheduler] -- Countries : tidak ada data`);
} }
// 5. Top Domain/DNS // 5. Top Domain/DNS
const domains = await netify.fetchTopDomains(1440, 20); const domains = await netify.fetchTopDomains(1440, 20, agentUuid);
if (domains && Array.isArray(domains)) { if (domains && Array.isArray(domains)) {
db.insertDNS(domains, fetchedAt, SITE_UUID); db.insertDNS(domains, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK DNS : ${domains.length} baris`); console.log(`[Scheduler] OK DNS : ${domains.length} baris`);
} else { } else {
console.log(`[Scheduler] -- DNS : tidak ada data`); console.log(`[Scheduler] -- DNS : tidak ada data`);
} }
// 6. Flows — pakai local_ip sebagai proxy // 6. Flows — pakai local_ip sebagai proxy
const flows = await netify.fetchFlows(200); const flows = await netify.fetchFlows(200, agentUuid);
if (flows && Array.isArray(flows)) { if (flows && Array.isArray(flows)) {
db.insertFlows(flows, fetchedAt, SITE_UUID); db.insertFlows(flows, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Flows : ${flows.length} baris`); console.log(`[Scheduler] OK Flows : ${flows.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Flows : tidak ada data`); console.log(`[Scheduler] -- Flows : tidak ada data`);
} }
// 7. Threats — dari Events Status // 7. Threats — dari Events Status
const threats = await netify.fetchCyberThreats(1440, 50); const threats = await netify.fetchCyberThreats(1440, 50, agentUuid);
if (threats && Array.isArray(threats)) { if (threats && Array.isArray(threats)) {
db.insertThreats(threats, fetchedAt, SITE_UUID); db.insertThreats(threats, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Threats : ${threats.length} baris`); console.log(`[Scheduler] OK Threats : ${threats.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Threats : tidak ada data`); console.log(`[Scheduler] -- Threats : tidak ada data`);
} }
// 8. Events Log // 8. Events Log
const events = await netify.fetchEvents(50); const events = await netify.fetchEvents(50, agentUuid);
if (events && Array.isArray(events)) { if (events && Array.isArray(events)) {
db.insertEvents(events, fetchedAt, SITE_UUID); db.insertEvents(events, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Events : ${events.length} baris`); console.log(`[Scheduler] OK Events : ${events.length} baris`);
} else { } else {
console.log(`[Scheduler] -- Events : tidak ada data`); console.log(`[Scheduler] -- Events : tidak ada data`);
} }
// 10. App Categories // 10. App Categories
const appCats = await netify.fetchTopAppCategories(1440, 15); const appCats = await netify.fetchTopAppCategories(1440, 15, agentUuid);
if (appCats?.length) { db.insertAppCategories(appCats, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK AppCats : ${appCats.length} baris`); } if (appCats?.length) { db.insertAppCategories(appCats, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK AppCats : ${appCats.length} baris`); }
else console.log(`[Scheduler] -- AppCats : tidak ada data`); else console.log(`[Scheduler] -- AppCats : tidak ada data`);
// 11. Continents // 11. Continents
const continents = await netify.fetchTopContinents(1440, 10); const continents = await netify.fetchTopContinents(1440, 10, agentUuid);
if (continents?.length) { db.insertContinents(continents, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK Continents : ${continents.length} baris`); } if (continents?.length) { db.insertContinents(continents, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Continents : ${continents.length} baris`); }
else console.log(`[Scheduler] -- Continents : tidak ada data`); else console.log(`[Scheduler] -- Continents : tidak ada data`);
// 12. Regions // 12. Regions
const regions = await netify.fetchTopRegions(1440, 20); const regions = await netify.fetchTopRegions(1440, 20, agentUuid);
if (regions?.length) { db.insertRegions(regions, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK Regions : ${regions.length} baris`); } if (regions?.length) { db.insertRegions(regions, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Regions : ${regions.length} baris`); }
else console.log(`[Scheduler] -- Regions : tidak ada data`); else console.log(`[Scheduler] -- Regions : tidak ada data`);
// 13. Cities // 13. Cities
const cities = await netify.fetchTopCities(1440, 20); const cities = await netify.fetchTopCities(1440, 20, agentUuid);
if (cities?.length) { db.insertCities(cities, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK Cities : ${cities.length} baris`); } if (cities?.length) { db.insertCities(cities, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Cities : ${cities.length} baris`); }
else console.log(`[Scheduler] -- Cities : tidak ada data`); else console.log(`[Scheduler] -- Cities : tidak ada data`);
// 14. VLANs // 14. VLANs
const vlans = await netify.fetchTopVLANs(1440, 20); const vlans = await netify.fetchTopVLANs(1440, 20, agentUuid);
if (vlans?.length) { db.insertVLANs(vlans, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK VLANs : ${vlans.length} baris`); } if (vlans?.length) { db.insertVLANs(vlans, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK VLANs : ${vlans.length} baris`); }
else console.log(`[Scheduler] -- VLANs : tidak ada data`); else console.log(`[Scheduler] -- VLANs : tidak ada data`);
// 15. Interfaces // 15. Interfaces
const ifaces = await netify.fetchTopInterfaces(1440, 20); const ifaces = await netify.fetchTopInterfaces(1440, 20, agentUuid);
if (ifaces?.length) { db.insertInterfaces(ifaces, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK Interfaces : ${ifaces.length} baris`); } if (ifaces?.length) { db.insertInterfaces(ifaces, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Interfaces : ${ifaces.length} baris`); }
else console.log(`[Scheduler] -- Interfaces : tidak ada data`); else console.log(`[Scheduler] -- Interfaces : tidak ada data`);
// 16. Flow Types // 16. Flow Types
const flowTypes = await netify.fetchTopFlowTypes(1440, 10); const flowTypes = await netify.fetchTopFlowTypes(1440, 10, agentUuid);
if (flowTypes?.length) { db.insertFlowTypes(flowTypes, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK FlowTypes : ${flowTypes.length} baris`); } if (flowTypes?.length) { db.insertFlowTypes(flowTypes, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK FlowTypes : ${flowTypes.length} baris`); }
else console.log(`[Scheduler] -- FlowTypes : tidak ada data`); else console.log(`[Scheduler] -- FlowTypes : tidak ada data`);
// 17. Flow Origins // 17. Flow Origins
const flowOrigins = await netify.fetchTopFlowOrigins(1440, 10); const flowOrigins = await netify.fetchTopFlowOrigins(1440, 10, agentUuid);
if (flowOrigins?.length) { db.insertFlowOrigins(flowOrigins, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK FlowOrigin : ${flowOrigins.length} baris`); } if (flowOrigins?.length) { db.insertFlowOrigins(flowOrigins, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK FlowOrigin : ${flowOrigins.length} baris`); }
else console.log(`[Scheduler] -- FlowOrigin : tidak ada data`); else console.log(`[Scheduler] -- FlowOrigin : tidak ada data`);
// 18. IP Versions // 18. IP Versions
const ipVersions = await netify.fetchTopIPVersions(1440, 5); const ipVersions = await netify.fetchTopIPVersions(1440, 5, agentUuid);
if (ipVersions?.length) { db.insertIPVersions(ipVersions, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK IPVersions : ${ipVersions.length} baris`); } if (ipVersions?.length) { db.insertIPVersions(ipVersions, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK IPVersions : ${ipVersions.length} baris`); }
else console.log(`[Scheduler] -- IPVersions : tidak ada data`); else console.log(`[Scheduler] -- IPVersions : tidak ada data`);
// 19. Remote IPs // 19. Remote IPs
const remoteIPs = await netify.fetchTopRemoteIPs(1440, 20); const remoteIPs = await netify.fetchTopRemoteIPs(1440, 20, agentUuid);
if (remoteIPs?.length) { db.insertRemoteIPs(remoteIPs, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK RemoteIPs : ${remoteIPs.length} baris`); } if (remoteIPs?.length) { db.insertRemoteIPs(remoteIPs, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK RemoteIPs : ${remoteIPs.length} baris`); }
else console.log(`[Scheduler] -- RemoteIPs : tidak ada data`); else console.log(`[Scheduler] -- RemoteIPs : tidak ada data`);
// 20. MAC Bandwidth // 20. MAC Bandwidth
const macBW = await netify.fetchTopLocalMACs(1440, 50); const macBW = await netify.fetchTopLocalMACs(1440, 50, agentUuid);
if (macBW?.length) { db.insertMACBandwidth(macBW, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK MACBandwdh : ${macBW.length} baris`); } if (macBW?.length) { db.insertMACBandwidth(macBW, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK MACBandwdh : ${macBW.length} baris`); }
else console.log(`[Scheduler] -- MACBandwdh : tidak ada data`); else console.log(`[Scheduler] -- MACBandwdh : tidak ada data`);
// 9. Bandwidth Timeline // 9. Bandwidth Timeline
const summary = await netify.fetchBandwidthSummary(1440); const summary = await netify.fetchBandwidthSummary(1440, agentUuid);
const devCount = devices?.length ?? 0; const devCount = devices?.length ?? 0;
db.insertBandwidthTimeline({ ...summary, devices: devCount }, fetchedAt, SITE_UUID); db.insertBandwidthTimeline({ ...summary, devices: devCount }, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Timeline : saved`); console.log(`[Scheduler] OK Timeline : saved`);
// 21. TLS Versions // 21. TLS Versions
const tlsVer = await netify.fetchTLSVersions(1440, 10); const tlsVer = await netify.fetchTLSVersions(1440, 10, agentUuid);
if (tlsVer?.length) { db.insertTLSVersions(tlsVer, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK TLS Ver : ${tlsVer.length} baris`); } if (tlsVer?.length) { db.insertTLSVersions(tlsVer, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TLS Ver : ${tlsVer.length} baris`); }
else console.log(`[Scheduler] -- TLS Ver : tidak ada data`); else console.log(`[Scheduler] -- TLS Ver : tidak ada data`);
// 22. TLS Ciphers // 22. TLS Ciphers
const tlsCipher = await netify.fetchTLSCiphers(1440, 15); const tlsCipher = await netify.fetchTLSCiphers(1440, 15, agentUuid);
if (tlsCipher?.length) { db.insertTLSCiphers(tlsCipher, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK TLS Cipher : ${tlsCipher.length} baris`); } if (tlsCipher?.length) { db.insertTLSCiphers(tlsCipher, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TLS Cipher : ${tlsCipher.length} baris`); }
else console.log(`[Scheduler] -- TLS Cipher : tidak ada data`); else console.log(`[Scheduler] -- TLS Cipher : tidak ada data`);
// 23. TLS Security // 23. TLS Security
const tlsSec = await netify.fetchTLSSecurity(1440, 10); const tlsSec = await netify.fetchTLSSecurity(1440, 10, agentUuid);
if (tlsSec?.length) { db.insertTLSSecurity(tlsSec, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK TLS Sec : ${tlsSec.length} baris`); } if (tlsSec?.length) { db.insertTLSSecurity(tlsSec, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TLS Sec : ${tlsSec.length} baris`); }
else console.log(`[Scheduler] -- TLS Sec : tidak ada data`); else console.log(`[Scheduler] -- TLS Sec : tidak ada data`);
// 24. NetBIOS Hostnames // 24. NetBIOS Hostnames
const netbios = await netify.fetchNetBIOSHostnames(1440, 30); const netbios = await netify.fetchNetBIOSHostnames(1440, 30, agentUuid);
if (netbios?.length) { db.insertNetBIOSHostnames(netbios, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK NetBIOS : ${netbios.length} baris`); } if (netbios?.length) { db.insertNetBIOSHostnames(netbios, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK NetBIOS : ${netbios.length} baris`); }
else console.log(`[Scheduler] -- NetBIOS : tidak ada data`); else console.log(`[Scheduler] -- NetBIOS : tidak ada data`);
// 25. Discovery OS (standalone — OS yang terdeteksi di jaringan) // 25. Discovery OS (standalone — OS yang terdeteksi di jaringan)
const discOs = await netify.fetchTopDiscoveryOS(1440, 20); const discOs = await netify.fetchTopDiscoveryOS(1440, 20, agentUuid);
if (discOs?.length) { db.insertDiscoveryOS(discOs, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK DiscOS : ${discOs.length} baris`); } if (discOs?.length) { db.insertDiscoveryOS(discOs, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK DiscOS : ${discOs.length} baris`); }
else console.log(`[Scheduler] -- DiscOS : tidak ada data`); else console.log(`[Scheduler] -- DiscOS : tidak ada data`);
// 26. DHCP Class Fingerprint // 26. DHCP Class Fingerprint
const dhcpFp = await netify.fetchDHCPClassFingerprints(1440, 30); const dhcpFp = await netify.fetchDHCPClassFingerprints(1440, 30, agentUuid);
if (dhcpFp?.length) { db.insertDHCPFingerprints(dhcpFp, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK DHCP FP : ${dhcpFp.length} baris`); } if (dhcpFp?.length) { db.insertDHCPFingerprints(dhcpFp, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK DHCP FP : ${dhcpFp.length} baris`); }
else console.log(`[Scheduler] -- DHCP FP : tidak ada data`); else console.log(`[Scheduler] -- DHCP FP : tidak ada data`);
// 27. HTTP User-Agent // 27. HTTP User-Agent
const userAgents = await netify.fetchHTTPUserAgents(1440, 30); const userAgents = await netify.fetchHTTPUserAgents(1440, 30, agentUuid);
if (userAgents?.length) { db.insertHTTPUserAgents(userAgents, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK UserAgent : ${userAgents.length} baris`); } if (userAgents?.length) { db.insertHTTPUserAgents(userAgents, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK UserAgent : ${userAgents.length} baris`); }
else console.log(`[Scheduler] -- UserAgent : tidak ada data`); else console.log(`[Scheduler] -- UserAgent : tidak ada data`);
// 28. HTTPS SNI Hostname // 28. HTTPS SNI Hostname
const sniHosts = await netify.fetchSNIHostnames(1440, 30); const sniHosts = await netify.fetchSNIHostnames(1440, 30, agentUuid);
if (sniHosts?.length) { db.insertSNIHostnames(sniHosts, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK SNI Host : ${sniHosts.length} baris`); } if (sniHosts?.length) { db.insertSNIHostnames(sniHosts, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SNI Host : ${sniHosts.length} baris`); }
else console.log(`[Scheduler] -- SNI Host : tidak ada data`); else console.log(`[Scheduler] -- SNI Host : tidak ada data`);
// 29. SSL Server Common Name // 29. SSL Server Common Name
const sslCN = await netify.fetchSSLServerCN(1440, 30); const sslCN = await netify.fetchSSLServerCN(1440, 30, agentUuid);
if (sslCN?.length) { db.insertSSLServerCN(sslCN, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK SSL CN : ${sslCN.length} baris`); } if (sslCN?.length) { db.insertSSLServerCN(sslCN, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SSL CN : ${sslCN.length} baris`); }
else console.log(`[Scheduler] -- SSL CN : tidak ada data`); else console.log(`[Scheduler] -- SSL CN : tidak ada data`);
// 30. QUIC Hostname // 30. QUIC Hostname
const quicHosts = await netify.fetchQUICHostnames(1440, 30); const quicHosts = await netify.fetchQUICHostnames(1440, 30, agentUuid);
if (quicHosts?.length) { db.insertQUICHostnames(quicHosts, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK QUIC Host : ${quicHosts.length} baris`); } if (quicHosts?.length) { db.insertQUICHostnames(quicHosts, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK QUIC Host : ${quicHosts.length} baris`); }
else console.log(`[Scheduler] -- QUIC Host : tidak ada data`); else console.log(`[Scheduler] -- QUIC Host : tidak ada data`);
// 31. BitTorrent Info Hash // 31. BitTorrent Info Hash
const btHashes = await netify.fetchBitTorrentInfoHashes(1440, 30); const btHashes = await netify.fetchBitTorrentInfoHashes(1440, 30, agentUuid);
if (btHashes?.length) { db.insertBitTorrentHashes(btHashes, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK BT Hash : ${btHashes.length} baris`); } if (btHashes?.length) { db.insertBitTorrentHashes(btHashes, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK BT Hash : ${btHashes.length} baris`); }
else console.log(`[Scheduler] -- BT Hash : tidak ada data`); else console.log(`[Scheduler] -- BT Hash : tidak ada data`);
// 32. SSH Client (field: ssh_client) // 32. SSH Client (field: ssh_client)
const sshClient = await netify.fetchSSHClients(1440, 20); const sshClient = await netify.fetchSSHClients(1440, 20, agentUuid);
if (sshClient?.length) { db.insertSSHVersions(sshClient, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK SSH Client : ${sshClient.length} baris`); } if (sshClient?.length) { db.insertSSHVersions(sshClient, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SSH Client : ${sshClient.length} baris`); }
else console.log(`[Scheduler] -- SSH Client : tidak ada data`); else console.log(`[Scheduler] -- SSH Client : tidak ada data`);
// 32b. SSH Server (field: ssh_server) // 32b. SSH Server (field: ssh_server)
const sshServer = await netify.fetchSSHServers(1440, 20); const sshServer = await netify.fetchSSHServers(1440, 20, agentUuid);
if (sshServer?.length) { db.insertSSHVersions(sshServer, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK SSH Server : ${sshServer.length} baris`); } if (sshServer?.length) { db.insertSSHVersions(sshServer, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SSH Server : ${sshServer.length} baris`); }
else console.log(`[Scheduler] -- SSH Server : tidak ada data`); else console.log(`[Scheduler] -- SSH Server : tidak ada data`);
// 33. mDNS Hostname (Chromecast, Apple TV, etc.) // 33. mDNS Hostname (Chromecast, Apple TV, etc.)
const mdnsHosts = await netify.fetchMDNSHostnames(1440, 30); const mdnsHosts = await netify.fetchMDNSHostnames(1440, 30, agentUuid);
if (mdnsHosts?.length) { db.insertMDNSHostnames(mdnsHosts, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK mDNS Host : ${mdnsHosts.length} baris`); } if (mdnsHosts?.length) { db.insertMDNSHostnames(mdnsHosts, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK mDNS Host : ${mdnsHosts.length} baris`); }
else console.log(`[Scheduler] -- mDNS Host : tidak ada data`); else console.log(`[Scheduler] -- mDNS Host : tidak ada data`);
// ─── INTELLIGENCE 22-30 (derive dari data yang tersedia) ───────────────── // ─── INTELLIGENCE 22-30 (derive dari data yang tersedia) ─────────────────
// 34. Cryptocurrency Mining (derive dari apps + flows ke port mining) // 34. Cryptocurrency Mining (derive dari apps + flows ke port mining)
const cryptoMining = await netify.fetchCryptoMining(50); const cryptoMining = await netify.fetchCryptoMining(50, agentUuid);
if (cryptoMining?.length) { db.insertCryptoMining(cryptoMining, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK CryptoMine : ${cryptoMining.length} baris`); } if (cryptoMining?.length) { db.insertCryptoMining(cryptoMining, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK CryptoMine : ${cryptoMining.length} baris`); }
else console.log(`[Scheduler] -- CryptoMine : tidak ada data`); else console.log(`[Scheduler] -- CryptoMine : tidak ada data`);
// 35. Device Discovery (derive dari flows + bandwidth per-IP) // 35. Device Discovery (derive dari flows + bandwidth per-IP)
const devDisc = await netify.fetchDeviceDiscovery(100); const devDisc = await netify.fetchDeviceDiscovery(100, agentUuid);
if (devDisc?.length) { db.insertDeviceDiscovery(devDisc, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK DevDisc : ${devDisc.length} baris`); } if (devDisc?.length) { db.insertDeviceDiscovery(devDisc, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK DevDisc : ${devDisc.length} baris`); }
else console.log(`[Scheduler] -- DevDisc : tidak ada data`); else console.log(`[Scheduler] -- DevDisc : tidak ada data`);
// 36. Encryption Audit (derive dari flows per-IP: port encrypted vs plain) // 36. Encryption Audit (derive dari flows per-IP: port encrypted vs plain)
const encAudit = await netify.fetchEncryptionAudit(50); const encAudit = await netify.fetchEncryptionAudit(50, agentUuid);
if (encAudit?.length) { db.insertEncryptionAudit(encAudit, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK EncAudit : ${encAudit.length} baris`); } if (encAudit?.length) { db.insertEncryptionAudit(encAudit, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK EncAudit : ${encAudit.length} baris`); }
else console.log(`[Scheduler] -- EncAudit : tidak ada data`); else console.log(`[Scheduler] -- EncAudit : tidak ada data`);
// 37. Insecure Protocols (derive dari top protocols) // 37. Insecure Protocols (derive dari top protocols)
const insecProto = await netify.fetchInsecureProtocols(1440, 50); const insecProto = await netify.fetchInsecureProtocols(1440, 50, agentUuid);
if (insecProto?.length) { db.insertInsecureProtocols(insecProto, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK InsecProto : ${insecProto.length} baris`); } if (insecProto?.length) { db.insertInsecureProtocols(insecProto, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK InsecProto : ${insecProto.length} baris`); }
else console.log(`[Scheduler] -- InsecProto : tidak ada data`); else console.log(`[Scheduler] -- InsecProto : tidak ada data`);
// 38. IP Reputation (derive dari top remote_ip + high-risk countries) // 38. IP Reputation (derive dari top remote_ip + high-risk countries)
const ipRep = await netify.fetchIPReputation(50); const ipRep = await netify.fetchIPReputation(50, agentUuid);
if (ipRep?.length) { db.insertIPReputation(ipRep, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK IPRepute : ${ipRep.length} baris`); } if (ipRep?.length) { db.insertIPReputation(ipRep, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK IPRepute : ${ipRep.length} baris`); }
else console.log(`[Scheduler] -- IPRepute : tidak ada data`); else console.log(`[Scheduler] -- IPRepute : tidak ada data`);
// 39. Server Discovery (derive dari flows ke port server well-known) // 39. Server Discovery (derive dari flows ke port server well-known)
const srvDisc = await netify.fetchServerDiscovery(100); const srvDisc = await netify.fetchServerDiscovery(100, agentUuid);
if (srvDisc?.length) { db.insertServerDiscovery(srvDisc, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK SrvDisc : ${srvDisc.length} baris`); } if (srvDisc?.length) { db.insertServerDiscovery(srvDisc, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SrvDisc : ${srvDisc.length} baris`); }
else console.log(`[Scheduler] -- SrvDisc : tidak ada data`); else console.log(`[Scheduler] -- SrvDisc : tidak ada data`);
// 40. Tor Detection (derive dari apps/hostnames mengandung "tor") // 40. Tor Detection (derive dari apps/hostnames mengandung "tor")
const torDet = await netify.fetchTorDetection(50); const torDet = await netify.fetchTorDetection(50, agentUuid);
if (torDet?.length) { db.insertTorDetection(torDet, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK TorDet : ${torDet.length} baris`); } if (torDet?.length) { db.insertTorDetection(torDet, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TorDet : ${torDet.length} baris`); }
else console.log(`[Scheduler] -- TorDet : tidak ada data`); else console.log(`[Scheduler] -- TorDet : tidak ada data`);
// 41. Unencrypted Password (derive dari flows ke port cleartext auth) // 41. Unencrypted Password (derive dari flows ke port cleartext auth)
const unencPwd = await netify.fetchUnencryptedPasswords(50); const unencPwd = await netify.fetchUnencryptedPasswords(50, agentUuid);
if (unencPwd?.length) { db.insertUnencryptedPasswords(unencPwd, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK UnencPwd : ${unencPwd.length} baris`); } if (unencPwd?.length) { db.insertUnencryptedPasswords(unencPwd, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK UnencPwd : ${unencPwd.length} baris`); }
else console.log(`[Scheduler] -- UnencPwd : tidak ada data`); else console.log(`[Scheduler] -- UnencPwd : tidak ada data`);
// 42. VPN Detection (derive dari apps/protocols/ports VPN) // 42. VPN Detection (derive dari apps/protocols/ports VPN)
const vpnDet = await netify.fetchVPNDetection(50); const vpnDet = await netify.fetchVPNDetection(50, agentUuid);
if (vpnDet?.length) { db.insertVPNDetection(vpnDet, fetchedAt, SITE_UUID); console.log(`[Scheduler] OK VPNDet : ${vpnDet.length} baris`); } if (vpnDet?.length) { db.insertVPNDetection(vpnDet, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK VPNDet : ${vpnDet.length} baris`); }
else console.log(`[Scheduler] -- VPNDet : tidak ada data`); else console.log(`[Scheduler] -- VPNDet : tidak ada data`);
}
// --- Main loop
await fetchAndStore(fetchedAt, null);
if (apiAgents && apiAgents.length > 0) {
for (const agent of apiAgents) {
if (agent && agent.uuid) {
await fetchAndStore(fetchedAt, agent.uuid);
}
}
}
} catch (err) { } catch (err) {
console.error('[Scheduler] ERROR:', err); console.error('[Scheduler] ERROR:', err);
} finally { } finally {
+2067
View File
File diff suppressed because it is too large. Load diff
+855
View File
@@ -0,0 +1,855 @@
diff --git a/backend/database.js b/backend/database.js
index 2ab6ce0..d4c2c68 100644
--- a/backend/database.js
+++ b/backend/database.js
@@ -104,7 +104,7 @@ function initSchema() {
const adminExists = d.prepare("SELECT count(*) as count FROM users WHERE username = 'admin'").get();
if (adminExists.count === 0) {
const hash = bcrypt.hashSync('admin123', 10);
- d.prepare("INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)").run('admin', hash, 'SUPER_ADMIN');
+ d.prepare("INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?, ?)").run('admin', hash, 'SUPER_ADMIN');
console.log('[DB] Created default admin user (admin / admin123)');
}
@@ -569,17 +569,17 @@ function initSchema() {
// ΓöÇΓöÇΓöÇ INSERT FUNCTIONS ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-function insertBandwidthApps(rows, fetchedAt, siteUuid) {
+function insertBandwidthApps(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO bandwidth_apps
- (site_uuid, fetched_at, app_id, app_label, app_tag, category, favicon, download, upload, total, flow_count)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, app_id, app_label, app_tag, category, favicon, download, upload, total, flow_count)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.app_id ?? null,
r.app_label ?? 'Unknown',
r.app_tag ?? null,
@@ -594,17 +594,17 @@ function insertBandwidthApps(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertDevices(rows, fetchedAt, siteUuid) {
+function insertDevices(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO devices
- (site_uuid, fetched_at, mac_address, ip_address, device_label, device_type, os_label, manufacturer, download, upload, last_seen)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, mac_address, ip_address, device_label, device_type, os_label, manufacturer, download, upload, last_seen)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.mac_address ?? null,
r.ip_address ?? null,
r.device_label ?? r.ip_address ?? 'Unknown',
@@ -619,17 +619,17 @@ function insertDevices(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertFlows(rows, fetchedAt, siteUuid) {
+function insertFlows(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO flows
- (site_uuid, fetched_at, flow_id, src_ip, src_mac, dst_ip, dst_port, protocol, app_label, domain, bytes_download, bytes_upload, first_seen, last_seen)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, flow_id, src_ip, src_mac, dst_ip, dst_port, protocol, app_label, domain, bytes_download, bytes_upload, first_seen, last_seen)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.flow_id ?? null,
r.src_ip ?? null,
r.src_mac ?? null,
@@ -647,17 +647,17 @@ function insertFlows(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertThreats(rows, fetchedAt, siteUuid) {
+function insertThreats(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO threats
- (site_uuid, fetched_at, threat_id, threat_type, severity, mac_address, ip_address, dst_ip, app_label, domain, description, detected_at)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, threat_id, threat_type, severity, mac_address, ip_address, dst_ip, app_label, domain, description, detected_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.threat_id ?? null,
r.threat_type ?? null,
r.severity ?? null,
@@ -673,18 +673,18 @@ function insertThreats(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertProtocols(rows, fetchedAt, siteUuid) {
+function insertProtocols(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO bandwidth_protocols
- (site_uuid, fetched_at, protocol_id, protocol_label, download, upload, flow_count)
- VALUES (?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, protocol_id, protocol_label, download, upload, flow_count)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
// Data sudah di-flatten oleh netify.js ΓÇö akses langsung tanpa nested
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.protocol_id ?? null,
r.protocol_label ?? 'Unknown',
r.download ?? 0,
@@ -695,18 +695,18 @@ function insertProtocols(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertCountries(rows, fetchedAt, siteUuid) {
+function insertCountries(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO bandwidth_countries
- (site_uuid, fetched_at, country_code, country_name, download, upload, flow_count)
- VALUES (?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, country_code, country_name, download, upload, flow_count)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
// Data sudah di-flatten oleh netify.js ΓÇö akses langsung tanpa nested
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.country_code ?? null,
r.country_name ?? 'Unknown',
r.download ?? 0,
@@ -717,17 +717,17 @@ function insertCountries(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertDNS(rows, fetchedAt, siteUuid) {
+function insertDNS(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO dns_queries
- (site_uuid, fetched_at, domain, query_count, app_label, category)
- VALUES (?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, domain, query_count, app_label, category)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.domain ?? null,
r.query_count ?? 0,
r.app_label ?? null,
@@ -737,17 +737,17 @@ function insertDNS(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertEvents(rows, fetchedAt, siteUuid) {
+function insertEvents(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`
INSERT INTO events
- (site_uuid, fetched_at, event_id, event_type, severity, mac_address, ip_address, description, event_at)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, event_id, event_type, severity, mac_address, ip_address, description, event_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
d.transaction((items) => {
for (const r of items) {
stmt.run(
- siteUuid, fetchedAt,
+ agentUuid, siteUuid, fetchedAt,
r.event_id ?? null,
r.event_type ?? null,
r.severity ?? null,
@@ -760,13 +760,14 @@ function insertEvents(rows, fetchedAt, siteUuid) {
})(rows);
}
-function insertBandwidthTimeline(summary, fetchedAt, siteUuid) {
+function insertBandwidthTimeline(summary, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
d.prepare(`
INSERT INTO bandwidth_timeline
- (site_uuid, fetched_at, total_download, total_upload, total_flows, active_devices, download_speed, upload_speed, flow_speed)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (agent_uuid, site_uuid, fetched_at, total_download, total_upload, total_flows, active_devices, download_speed, upload_speed, flow_speed)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
+ agentUuid,
siteUuid,
fetchedAt,
summary.download ?? 0,
@@ -846,8 +847,8 @@ function getLatestBandwidthApps(limit = 20, siteUuid = null, agentUuid = null) {
const appsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_apps`).get();
if (!appsLatest?.t) return [];
const rows = d.prepare(`
- SELECT * FROM bandwidth_apps WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
- `).all({ fetched_at: appsLatest.t, limit, siteUuid });
+ SELECT * FROM bandwidth_apps WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
+ `).all({ fetched_at: appsLatest.t, limit, siteUuid, agentUuid });
return correlateAppLabels(rows);
}
@@ -1093,7 +1094,7 @@ function getLatestDevices(limit = 100, siteUuid = null, agentUuid = null, search
SELECT ip_address, mac_address, device_label, device_type, os_label, manufacturer,
MAX(download) as download, MAX(upload) as upload, MAX(fetched_at) as fetched_at, site_uuid, id
FROM devices
- WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND (
ip_address LIKE @q OR
mac_address LIKE @q OR
device_label LIKE @q OR
@@ -1110,7 +1111,7 @@ function getLatestDevices(limit = 100, siteUuid = null, agentUuid = null, search
SUM(bytes_download) as download, SUM(bytes_upload) as upload,
MAX(last_seen) as last_seen, MAX(fetched_at) as fetched_at, site_uuid
FROM flows
- WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND (
src_ip LIKE @q OR
src_mac LIKE @q OR
app_label LIKE @q OR
@@ -1544,8 +1545,8 @@ function getLatestFlows(limit = 100, siteUuid = null, agentUuid = null) {
`).all(...macs, limit);
} else {
rows = d.prepare(`
- SELECT * FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY bytes_download DESC LIMIT @limit
- `).all({ fetched_at: latest.t, limit, siteUuid });
+ SELECT * FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY bytes_download DESC LIMIT @limit
+ `).all({ fetched_at: latest.t, limit, siteUuid, agentUuid });
}
const mapped = rows.map(r => ({
@@ -1569,7 +1570,7 @@ function getLatestThreats(limit = 50, siteUuid = null, agentUuid = null) {
LIMIT ?
`).all(...macs, ...macs, limit);
}
- return d.prepare(`SELECT * FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid });
+ return d.prepare(`SELECT * FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid, agentUuid });
}
function getLatestProtocols(limit = 20, siteUuid = null, agentUuid = null) {
@@ -1593,8 +1594,8 @@ function getLatestProtocols(limit = 20, siteUuid = null, agentUuid = null) {
const protoLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_protocols`).get();
if (!protoLatest?.t) return [];
return d.prepare(`
- SELECT * FROM bandwidth_protocols WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
- `).all({ fetched_at: protoLatest.t, limit, siteUuid });
+ SELECT * FROM bandwidth_protocols WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
+ `).all({ fetched_at: protoLatest.t, limit, siteUuid, agentUuid });
}
function getLatestCountries(limit = 15, siteUuid = null, agentUuid = null) {
@@ -1711,8 +1712,8 @@ function getLatestCountries(limit = 15, siteUuid = null, agentUuid = null) {
const countryLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_countries`).get();
if (!countryLatest?.t) return [];
return d.prepare(`
- SELECT * FROM bandwidth_countries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
- `).all({ fetched_at: countryLatest.t, limit, siteUuid });
+ SELECT * FROM bandwidth_countries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY download DESC LIMIT @limit
+ `).all({ fetched_at: countryLatest.t, limit, siteUuid, agentUuid });
}
function getLatestDNS(limit = 20, siteUuid = null, agentUuid = null) {
@@ -1736,8 +1737,8 @@ function getLatestDNS(limit = 20, siteUuid = null, agentUuid = null) {
const dnsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM dns_queries`).get();
if (!dnsLatest?.t) return [];
return d.prepare(`
- SELECT * FROM dns_queries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY query_count DESC LIMIT @limit
- `).all({ fetched_at: dnsLatest.t, limit, siteUuid });
+ SELECT * FROM dns_queries WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY query_count DESC LIMIT @limit
+ `).all({ fetched_at: dnsLatest.t, limit, siteUuid, agentUuid });
}
function getLatestEvents(limit = 50, siteUuid = null, agentUuid = null) {
@@ -1752,7 +1753,7 @@ function getLatestEvents(limit = 50, siteUuid = null, agentUuid = null) {
LIMIT ?
`).all(...macs, ...macs, limit);
}
- return d.prepare(`SELECT * FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid });
+ return d.prepare(`SELECT * FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid, agentUuid });
}
function getBandwidthTimeline(points = 60, siteUuid = null, agentUuid = null) {
@@ -1780,7 +1781,7 @@ function getBandwidthTimeline(points = 60, siteUuid = null, agentUuid = null) {
`).all(...macs, ...macs, points).reverse();
}
return d.prepare(`
- SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit
+ SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit
`).all({ limit: points, siteUuid }).reverse();
}
@@ -1849,141 +1850,141 @@ function getStats(siteUuid = null, agentUuid = null) {
// Fallback to site-wide stats if no agentUuid
const latestDevFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM devices`).get();
const totalDevices = latestDevFetch?.t
- ? (d.prepare(`SELECT COUNT(*) as n FROM devices WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestDevFetch.t, siteUuid })?.n ?? 0)
+ ? (d.prepare(`SELECT COUNT(*) as n FROM devices WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestDevFetch.t, siteUuid })?.n ?? 0)
: 0;
const latestFlowFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
const activeFlows = latestFlowFetch?.t
- ? (d.prepare(`SELECT COUNT(*) as n FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestFlowFetch.t, siteUuid })?.n ?? 0)
+ ? (d.prepare(`SELECT COUNT(*) as n FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at`).get({ fetched_at: latestFlowFetch.t, siteUuid })?.n ?? 0)
: 0;
- const totalThreats = d.prepare(`SELECT COUNT(*) as n FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)`).get({ siteUuid })?.n ?? 0;
- const totalEvents = d.prepare(`SELECT COUNT(*) as n FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)`).get({ siteUuid })?.n ?? 0;
+ const totalThreats = d.prepare(`SELECT COUNT(*) as n FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid)`).get({ siteUuid, agentUuid })?.n ?? 0;
+ const totalEvents = d.prepare(`SELECT COUNT(*) as n FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid)`).get({ siteUuid, agentUuid })?.n ?? 0;
const lastFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_timeline`).get()?.t ?? null;
- const latestBw = d.prepare(`SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT 1`).get({ siteUuid });
+ const latestBw = d.prepare(`SELECT * FROM bandwidth_timeline WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT 1`).get({ siteUuid, agentUuid });
return { totalDevices, activeFlows, totalThreats, totalEvents, lastFetch, latestBw };
}
// ΓöÇΓöÇΓöÇ INSERT FITUR BARU 1-11 ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-function insertAppCategories(rows, fetchedAt, siteUuid) {
+function insertAppCategories(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO app_categories
- (site_uuid, fetched_at, category_label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, category_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.category_label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.category_label, r.download, r.upload, r.total);
})(rows);
}
-function insertContinents(rows, fetchedAt, siteUuid) {
+function insertContinents(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO continents
- (site_uuid, fetched_at, continent_name, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, continent_name, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.continent_name, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.continent_name, r.download, r.upload, r.total);
})(rows);
}
-function insertRegions(rows, fetchedAt, siteUuid) {
+function insertRegions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO regions
- (site_uuid, fetched_at, region_name, region_code, country_name, country_code, download)
+ (agent_uuid, site_uuid, fetched_at, region_name, region_code, country_name, country_code, download)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.region_name, r.region_code, r.country_name, r.country_code, r.download);
+ agentUuid, siteUuid, fetchedAt, r.region_name, r.region_code, r.country_name, r.country_code, r.download);
})(rows);
}
-function insertCities(rows, fetchedAt, siteUuid) {
+function insertCities(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO cities
- (site_uuid, fetched_at, city_name, region_name, country_name, country_code, download)
+ (agent_uuid, site_uuid, fetched_at, city_name, region_name, country_name, country_code, download)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.city_name, r.region_name, r.country_name, r.country_code, r.download);
+ agentUuid, siteUuid, fetchedAt, r.city_name, r.region_name, r.country_name, r.country_code, r.download);
})(rows);
}
-function insertVLANs(rows, fetchedAt, siteUuid) {
+function insertVLANs(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO vlans
- (site_uuid, fetched_at, vlan_id, vlan_label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, vlan_id, vlan_label, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.vlan_id, r.vlan_label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.vlan_id, r.vlan_label, r.download, r.upload, r.total);
})(rows);
}
-function insertInterfaces(rows, fetchedAt, siteUuid) {
+function insertInterfaces(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO interfaces
- (site_uuid, fetched_at, iface_id, iface_name, iface_role, agent_id, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, iface_id, iface_name, iface_role, agent_id, download, upload, total)
VALUES (?, ?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.iface_id, r.iface_name, r.iface_role, String(r.agent_id ?? ''), r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.iface_id, r.iface_name, r.iface_role, String(r.agent_id ?? ''), r.download, r.upload, r.total);
})(rows);
}
-function insertFlowTypes(rows, fetchedAt, siteUuid) {
+function insertFlowTypes(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO flow_types
- (site_uuid, fetched_at, flow_type_label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, flow_type_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.flow_type_label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.flow_type_label, r.download, r.upload, r.total);
})(rows);
}
-function insertFlowOrigins(rows, fetchedAt, siteUuid) {
+function insertFlowOrigins(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO flow_origins
- (site_uuid, fetched_at, flow_origin_label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, flow_origin_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.flow_origin_label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.flow_origin_label, r.download, r.upload, r.total);
})(rows);
}
-function insertIPVersions(rows, fetchedAt, siteUuid) {
+function insertIPVersions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO ip_versions
- (site_uuid, fetched_at, ip_version_label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, ip_version_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.ip_version_label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.ip_version_label, r.download, r.upload, r.total);
})(rows);
}
-function insertRemoteIPs(rows, fetchedAt, siteUuid) {
+function insertRemoteIPs(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO remote_ips
- (site_uuid, fetched_at, remote_ip, ip_version, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, remote_ip, ip_version, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.remote_ip, r.ip_version, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.remote_ip, r.ip_version, r.download, r.upload, r.total);
})(rows);
}
-function insertMACBandwidth(rows, fetchedAt, siteUuid) {
+function insertMACBandwidth(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO mac_bandwidth
- (site_uuid, fetched_at, mac_address, manufacturer, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, mac_address, manufacturer, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.mac_address, r.manufacturer, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.mac_address, r.manufacturer, r.download, r.upload, r.total);
})(rows);
}
@@ -2048,7 +2049,7 @@ function getLatest(table, orderBy = 'download', limit = 50, siteUuid = null, age
}
// Fallback to standard site-wide select
- rows = d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND fetched_at = @fetched_at ORDER BY ${orderBy} DESC LIMIT @limit`).all({ fetched_at: latest.t, limit, siteUuid });
+ rows = d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) AND fetched_at = @fetched_at ORDER BY ${orderBy} DESC LIMIT @limit`).all({ fetched_at: latest.t, limit, siteUuid, agentUuid });
// If agentUuid is provided, scale down numerical values by traffic ratio to match the agent's footprint
if (agentUuid && !['mac_bandwidth', 'interfaces'].includes(table)) {
@@ -2067,272 +2068,272 @@ function getLatest(table, orderBy = 'download', limit = 50, siteUuid = null, age
}
// DPI Fields
-function insertTLSVersions(rows, fetchedAt, siteUuid) {
+function insertTLSVersions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO tls_versions
- (site_uuid, fetched_at, tls_version, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, tls_version, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.tls_version, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.tls_version, r.download, r.upload, r.total);
})(rows);
}
-function insertTLSCiphers(rows, fetchedAt, siteUuid) {
+function insertTLSCiphers(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO tls_ciphers
- (site_uuid, fetched_at, tls_cipher, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, tls_cipher, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.tls_cipher, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.tls_cipher, r.download, r.upload, r.total);
})(rows);
}
-function insertTLSSecurity(rows, fetchedAt, siteUuid) {
+function insertTLSSecurity(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO tls_security
- (site_uuid, fetched_at, tls_security, color, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, tls_security, color, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.tls_security, r.color, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.tls_security, r.color, r.download, r.upload, r.total);
})(rows);
}
-function insertNetBIOSHostnames(rows, fetchedAt, siteUuid) {
+function insertNetBIOSHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO netbios_hostnames
- (site_uuid, fetched_at, hostname, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.hostname, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.hostname, r.download, r.upload, r.total);
})(rows);
}
-function insertDiscoveryOS(rows, fetchedAt, siteUuid) {
+function insertDiscoveryOS(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO discovery_os
- (site_uuid, fetched_at, os_label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, os_label, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.os_label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.os_label, r.download, r.upload, r.total);
})(rows);
}
// ΓöÇΓöÇΓöÇ INSERT DPI 12-21 ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-function insertDHCPFingerprints(rows, fetchedAt, siteUuid) {
+function insertDHCPFingerprints(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO dhcp_fingerprints
- (site_uuid, fetched_at, fingerprint, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, fingerprint, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.fingerprint, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.fingerprint, r.download, r.upload, r.total);
})(rows);
}
-function insertHTTPUserAgents(rows, fetchedAt, siteUuid) {
+function insertHTTPUserAgents(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO http_user_agents
- (site_uuid, fetched_at, user_agent, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, user_agent, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.user_agent, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.user_agent, r.download, r.upload, r.total);
})(rows);
}
-function insertSNIHostnames(rows, fetchedAt, siteUuid) {
+function insertSNIHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO sni_hostnames
- (site_uuid, fetched_at, sni_hostname, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, sni_hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.sni_hostname, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.sni_hostname, r.download, r.upload, r.total);
})(rows);
}
-function insertSSLServerCN(rows, fetchedAt, siteUuid) {
+function insertSSLServerCN(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO ssl_server_cn
- (site_uuid, fetched_at, ssl_server_cn, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, ssl_server_cn, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.ssl_server_cn, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.ssl_server_cn, r.download, r.upload, r.total);
})(rows);
}
-function insertQUICHostnames(rows, fetchedAt, siteUuid) {
+function insertQUICHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO quic_hostnames
- (site_uuid, fetched_at, quic_hostname, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, quic_hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.quic_hostname, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.quic_hostname, r.download, r.upload, r.total);
})(rows);
}
-function insertBitTorrentHashes(rows, fetchedAt, siteUuid) {
+function insertBitTorrentHashes(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO bittorrent_hashes
- (site_uuid, fetched_at, info_hash, label, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, info_hash, label, download, upload, total)
VALUES (?, ?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.info_hash, r.label, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.info_hash, r.label, r.download, r.upload, r.total);
})(rows);
}
-function insertSSHVersions(rows, fetchedAt, siteUuid) {
+function insertSSHVersions(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO ssh_versions
- (site_uuid, fetched_at, ssh_version, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, ssh_version, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.ssh_version, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.ssh_version, r.download, r.upload, r.total);
})(rows);
}
-function insertMDNSHostnames(rows, fetchedAt, siteUuid) {
+function insertMDNSHostnames(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO mdns_hostnames
- (site_uuid, fetched_at, mdns_hostname, download, upload, total)
+ (agent_uuid, site_uuid, fetched_at, mdns_hostname, download, upload, total)
VALUES (?, ?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.mdns_hostname, r.download, r.upload, r.total);
+ agentUuid, siteUuid, fetchedAt, r.mdns_hostname, r.download, r.upload, r.total);
})(rows);
}
// ΓöÇΓöÇΓöÇ INSERT INTELLIGENCE 22-30 ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-function insertCryptoMining(rows, fetchedAt, siteUuid) {
+function insertCryptoMining(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_crypto_mining
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, pool_host, pool_ip, protocol, app_label, confidence, download, upload)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, pool_host, pool_ip, protocol, app_label, confidence, download, upload)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.pool_host, r.pool_ip, r.protocol, r.app_label, r.confidence,
r.download ?? 0, r.upload ?? 0
);
})(rows);
}
-function insertDeviceDiscovery(rows, fetchedAt, siteUuid) {
+function insertDeviceDiscovery(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_device_discovery
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, device_type, os_label, manufacturer, is_new)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, device_type, os_label, manufacturer, is_new)
VALUES (?, ?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.device_label, r.device_type, r.os_label, r.manufacturer,
r.is_new ? 1 : 0
);
})(rows);
}
-function insertEncryptionAudit(rows, fetchedAt, siteUuid) {
+function insertEncryptionAudit(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_encryption_audit
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, encrypted_pct, unencrypted, encrypted, total, risk_level)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, device_label, encrypted_pct, unencrypted, encrypted, total, risk_level)
VALUES (?, ?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.device_label, r.encrypted_pct, r.unencrypted ?? 0, r.encrypted ?? 0,
r.total ?? 0, r.risk_level
);
})(rows);
}
-function insertInsecureProtocols(rows, fetchedAt, siteUuid) {
+function insertInsecureProtocols(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_insecure_protocols
- (site_uuid, fetched_at, detected_at, protocol, ip_address, mac_address, dst_ip, dst_port, app_label, download, upload, risk, source)
+ (agent_uuid, site_uuid, fetched_at, detected_at, protocol, ip_address, mac_address, dst_ip, dst_port, app_label, download, upload, risk, source)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.protocol, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.protocol, r.ip_address, r.mac_address,
r.dst_ip, r.dst_port, r.app_label, r.download ?? 0, r.upload ?? 0,
r.risk ?? 'Medium', r.source ?? 'api'
);
})(rows);
}
-function insertIPReputation(rows, fetchedAt, siteUuid) {
+function insertIPReputation(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_ip_reputation
- (site_uuid, fetched_at, detected_at, ip_address, local_ip, mac_address, reputation, score, country, app_label, download, upload, blacklisted)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, local_ip, mac_address, reputation, score, country, app_label, download, upload, blacklisted)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.local_ip, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.local_ip, r.mac_address,
r.reputation, r.score, r.country, r.app_label,
r.download ?? 0, r.upload ?? 0, r.blacklisted ? 1 : 0
);
})(rows);
}
-function insertServerDiscovery(rows, fetchedAt, siteUuid) {
+function insertServerDiscovery(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_server_discovery
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, server_type, hostname, port, protocol, os_label, download, upload)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, server_type, hostname, port, protocol, os_label, download, upload)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.server_type, r.hostname, r.port, r.protocol, r.os_label,
r.download ?? 0, r.upload ?? 0
);
})(rows);
}
-function insertTorDetection(rows, fetchedAt, siteUuid) {
+function insertTorDetection(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_tor_detection
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, exit_node, circuit_id, download, upload, country)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, exit_node, circuit_id, download, upload, country)
VALUES (?, ?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.exit_node, r.circuit_id, r.download ?? 0, r.upload ?? 0, r.country
);
})(rows);
}
-function insertUnencryptedPasswords(rows, fetchedAt, siteUuid) {
+function insertUnencryptedPasswords(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_unencrypted_passwords
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, dst_ip, dst_port, protocol, username, download, upload, severity)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, dst_ip, dst_port, protocol, username, download, upload, severity)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.dst_ip, r.dst_port, r.protocol, r.username,
r.download ?? 0, r.upload ?? 0, r.severity ?? 'Critical'
);
})(rows);
}
-function insertVPNDetection(rows, fetchedAt, siteUuid) {
+function insertVPNDetection(rows, fetchedAt, siteUuid, agentUuid) {
const d = getDB();
const stmt = d.prepare(`INSERT INTO intel_vpn_detection
- (site_uuid, fetched_at, detected_at, ip_address, mac_address, vpn_type, remote_ip, protocol, download, upload, country, confidence)
+ (agent_uuid, site_uuid, fetched_at, detected_at, ip_address, mac_address, vpn_type, remote_ip, protocol, download, upload, country, confidence)
VALUES (?, ?,?,?,?,?,?,?,?,?,?,?)`);
d.transaction(items => {
for (const r of items) stmt.run(
- siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
+ agentUuid, siteUuid, fetchedAt, r.detected_at ?? fetchedAt, r.ip_address, r.mac_address,
r.vpn_type, r.remote_ip, r.protocol,
r.download ?? 0, r.upload ?? 0, r.country, r.confidence
);
@@ -2357,7 +2358,7 @@ function getIntelData(table, limit = 100, siteUuid = null, agentUuid = null) {
LIMIT ?
`).all(...macs, ...macs, limit);
}
- return d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid });
+ return d.prepare(`SELECT * FROM ${table} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid) ORDER BY fetched_at DESC LIMIT @limit`).all({ limit, siteUuid, agentUuid });
}
function getIntelStats(siteUuid = null, agentUuid = null) {
@@ -2381,7 +2382,7 @@ function getIntelStats(siteUuid = null, agentUuid = null) {
WHERE mac_address IN (${placeholders}) OR ${ipField} IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
`).get(...macs, ...macs)?.n ?? 0;
} else {
- counts[t] = d.prepare(`SELECT COUNT(*) as n FROM ${t} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)`).get({ siteUuid })?.n ?? 0;
+ counts[t] = d.prepare(`SELECT COUNT(*) as n FROM ${t} WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND (@agentUuid IS NULL OR agent_uuid = @agentUuid)`).get({ siteUuid, agentUuid })?.n ?? 0;
}
} catch { counts[t] = 0; }
}
+29
View File
@@ -0,0 +1,29 @@
const Database = require('better-sqlite3');
const path = require('path');
const DB_PATH = path.join('d:/FILE/Magang/Deep Package Inspection/backend/netify_data.db');
const db = new Database(DB_PATH);
const tables = [
'bandwidth_apps', 'bandwidth_protocols', 'bandwidth_countries', 'dns_queries', 'events',
'bandwidth_timeline', 'threats', 'devices', 'flows', 'tls_versions', 'tls_ciphers',
'tls_security', 'netbios_hostnames', 'dhcp_fingerprints', 'http_user_agents', 'sni_hostnames',
'ssl_server_cn', 'quic_hostnames', 'bittorrent_hashes', 'ssh_versions', 'mdns_hostnames',
'intel_crypto_mining', 'intel_device_discovery', 'intel_encryption_audit', 'intel_insecure_protocols',
'intel_ip_reputation', 'intel_server_discovery', 'intel_tor_detection', 'intel_unencrypted_passwords',
'intel_vpn_detection', 'app_categories', 'continents', 'regions', 'cities', 'vlans',
'interfaces', 'flow_types', 'flow_origins', 'ip_versions', 'remote_ips', 'mac_bandwidth', 'discovery_os'
];
let success = 0;
for (const t of tables) {
try {
db.exec(`ALTER TABLE ${t} ADD COLUMN agent_uuid TEXT DEFAULT NULL`);
success++;
} catch (e) {
if (!e.message.includes('duplicate column name')) {
console.log(`Failed to alter ${t}:`, e.message);
}
}
}
console.log(`Migration complete! Successfully altered ${success} tables.`);
db.close();