2068 lines
86 KiB
Plaintext
2068 lines
86 KiB
Plaintext
diff --git a/backend/database.js b/backend/database.js
|
|
index 2ab6ce0..d3aa1c0 100644
|
|
--- a/backend/database.js
|
|
+++ b/backend/database.js
|
|
@@ -65,6 +65,8 @@ function initSchema() {
|
|
|
|
// ΓöÇΓöÇΓöÇ AUTHENTICATION ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
d.exec(`CREATE TABLE IF NOT EXISTS users (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
@@ -104,11 +106,13 @@ 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)');
|
|
}
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS tls_versions (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -119,6 +123,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS tls_ciphers (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -129,6 +135,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS tls_security (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -140,6 +148,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS netbios_hostnames (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -152,6 +162,8 @@ function initSchema() {
|
|
// ΓöÇΓöÇΓöÇ DPI Fields 12-21 ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS dhcp_fingerprints (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -162,6 +174,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS http_user_agents (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -172,6 +186,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS sni_hostnames (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -182,6 +198,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS ssl_server_cn (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -192,6 +210,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS quic_hostnames (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -202,6 +222,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS bittorrent_hashes (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -213,6 +235,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS ssh_versions (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -223,6 +247,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS mdns_hostnames (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -235,6 +261,8 @@ function initSchema() {
|
|
// ΓöÇΓöÇΓöÇ Intelligence API 22-30 ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_crypto_mining (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -248,6 +276,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_device_discovery (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -259,6 +289,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_encryption_audit (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -273,6 +305,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_insecure_protocols (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -288,6 +322,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_ip_reputation (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -302,6 +338,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_server_discovery (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -315,6 +353,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_tor_detection (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -327,6 +367,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_unencrypted_passwords (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -340,6 +382,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS intel_vpn_detection (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -353,6 +397,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS discovery_os (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -364,6 +410,8 @@ function initSchema() {
|
|
|
|
// Tabel baru fitur 1-11
|
|
d.exec(`CREATE TABLE IF NOT EXISTS app_categories (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -374,6 +422,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS continents (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -384,6 +434,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS regions (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -393,6 +445,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS cities (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -402,6 +456,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS vlans (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -412,6 +468,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS interfaces (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -423,6 +481,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS flow_types (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -433,6 +493,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS flow_origins (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -443,6 +505,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS ip_versions (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -453,6 +517,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS remote_ips (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -463,6 +529,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS mac_bandwidth (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -473,6 +541,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS bandwidth_apps (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -482,6 +552,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS devices (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -491,6 +563,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS flows (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -502,6 +576,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS threats (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -511,6 +587,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS bandwidth_protocols (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -520,6 +598,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS bandwidth_countries (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -529,6 +609,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS dns_queries (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -537,6 +619,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS events (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -546,6 +630,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS bandwidth_timeline (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
site_uuid TEXT,
|
|
fetched_at TEXT NOT NULL,
|
|
@@ -556,6 +642,8 @@ function initSchema() {
|
|
)`);
|
|
|
|
d.exec(`CREATE TABLE IF NOT EXISTS geoip_cache (
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
+ agent_uuid TEXT DEFAULT NULL,
|
|
ip_address TEXT PRIMARY KEY,
|
|
isp TEXT,
|
|
country TEXT,
|
|
@@ -569,17 +657,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 +682,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 +707,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 +735,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 +761,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 +783,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 +805,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 +825,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,12 +848,12 @@ 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(
|
|
siteUuid,
|
|
fetchedAt,
|
|
@@ -783,71 +871,15 @@ function insertBandwidthTimeline(summary, fetchedAt, siteUuid) {
|
|
|
|
function getLatestBandwidthApps(limit = 20, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- const latest = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
|
|
- if (!latest?.t) return [];
|
|
-
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
-
|
|
- // 1. Get raw aggregated apps bandwidth from flows table for this agent (cumulative)
|
|
- const rawApps = d.prepare(`
|
|
- SELECT app_label, SUM(bytes_download) AS download, SUM(bytes_upload) AS upload
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders}) AND app_label IS NOT NULL
|
|
- GROUP BY app_label
|
|
- `).all(...macs);
|
|
-
|
|
- if (rawApps.length === 0) return [];
|
|
-
|
|
- // Calculate sum of download/upload across all these apps
|
|
- let totalFlowDl = 0;
|
|
- let totalFlowUl = 0;
|
|
- for (const r of rawApps) {
|
|
- totalFlowDl += r.download;
|
|
- totalFlowUl += r.upload;
|
|
- }
|
|
-
|
|
- // 2. Get true cumulative bandwidth from mac_bandwidth table
|
|
- const latestMacSnap = d.prepare(`SELECT MAX(fetched_at) AS t FROM mac_bandwidth`).get()?.t;
|
|
- const trueBw = latestMacSnap
|
|
- ? d.prepare(`
|
|
- SELECT SUM(download) AS dl, SUM(upload) AS ul
|
|
- FROM mac_bandwidth
|
|
- WHERE mac_address IN (${placeholders}) AND fetched_at = ?
|
|
- `).get(...macs, latestMacSnap)
|
|
- : null;
|
|
-
|
|
- const trueDl = trueBw?.dl ?? 0;
|
|
- const trueUl = trueBw?.ul ?? 0;
|
|
-
|
|
- // Calculate scaling factors
|
|
- const dlFactor = totalFlowDl > 0 ? trueDl / totalFlowDl : 1;
|
|
- const ulFactor = totalFlowUl > 0 ? trueUl / totalFlowUl : 1;
|
|
-
|
|
- // Map and scale
|
|
- const scaledApps = rawApps.map(r => {
|
|
- const dl = Math.round(r.download * dlFactor);
|
|
- const ul = Math.round(r.upload * ulFactor);
|
|
- return {
|
|
- app_label: r.app_label,
|
|
- download: dl,
|
|
- upload: ul,
|
|
- total: dl + ul,
|
|
- flow_count: 0
|
|
- };
|
|
- });
|
|
-
|
|
- // Sort by total bandwidth DESC
|
|
- scaledApps.sort((a, b) => b.total - a.total);
|
|
- return correlateAppLabels(scaledApps.slice(0, limit));
|
|
- }
|
|
-
|
|
- const appsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_apps`).get();
|
|
- if (!appsLatest?.t) return [];
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_apps WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.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 ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY download DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
return correlateAppLabels(rows);
|
|
}
|
|
|
|
@@ -969,289 +1001,22 @@ function deviceMatchesAgent(ip, mac, agentUuid) {
|
|
return false;
|
|
}
|
|
|
|
-function getLatestDevices(limit = 100, siteUuid = null, agentUuid = null, search = null) {
|
|
+function getLatestDevices(limit = 50, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- const latest = d.prepare(`SELECT MAX(fetched_at) as t FROM devices`).get();
|
|
- if (!latest?.t) return [];
|
|
-
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
-
|
|
- // Fetch all flows aggregated by IP address across all time/snapshots
|
|
- const flowsData = d.prepare(`
|
|
- SELECT src_ip, src_mac, SUM(bytes_download) as download, SUM(bytes_upload) as upload, MAX(last_seen) as last_seen, MAX(fetched_at) as fetched_at
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders})
|
|
- GROUP BY src_ip
|
|
- `).all(...macs);
|
|
-
|
|
- // Fetch all devices matching this agent's criteria across all snapshots
|
|
- const devicesData = d.prepare(`
|
|
- SELECT ip_address, mac_address, device_label, device_type, os_label, manufacturer, download, upload, fetched_at
|
|
- FROM devices
|
|
- GROUP BY ip_address
|
|
- `).all();
|
|
-
|
|
- // Map discovered devices to allow lookups by IP
|
|
- const devicesMap = new Map();
|
|
- for (const dev of devicesData) {
|
|
- if (dev.ip_address && deviceMatchesAgent(dev.ip_address, dev.mac_address, agentUuid)) {
|
|
- devicesMap.set(dev.ip_address, dev);
|
|
- }
|
|
- }
|
|
-
|
|
- // Get true cumulative bandwidth from mac_bandwidth table to calculate scale factors
|
|
- let totalFlowDl = 0;
|
|
- let totalFlowUl = 0;
|
|
- for (const f of flowsData) {
|
|
- totalFlowDl += f.download;
|
|
- totalFlowUl += f.upload;
|
|
- }
|
|
-
|
|
- const latestMacSnap = d.prepare(`SELECT MAX(fetched_at) AS t FROM mac_bandwidth`).get()?.t;
|
|
- const trueBw = latestMacSnap
|
|
- ? d.prepare(`
|
|
- SELECT SUM(download) AS dl, SUM(upload) AS ul
|
|
- FROM mac_bandwidth
|
|
- WHERE mac_address IN (${placeholders}) AND fetched_at = ?
|
|
- `).get(...macs, latestMacSnap)
|
|
- : null;
|
|
-
|
|
- const trueDl = trueBw?.dl ?? 0;
|
|
- const trueUl = trueBw?.ul ?? 0;
|
|
-
|
|
- const dlFactor = totalFlowDl > 0 ? trueDl / totalFlowDl : 1;
|
|
- const ulFactor = totalFlowUl > 0 ? trueUl / totalFlowUl : 1;
|
|
-
|
|
- const resolved = [];
|
|
- const seenIps = new Set();
|
|
-
|
|
- // Load intelligence tables to assist in type resolving
|
|
- const intelList = d.prepare("SELECT * FROM intel_device_discovery").all();
|
|
- const intelMap = new Map(intelList.map(i => [i.ip_address, i]));
|
|
-
|
|
- function processAgentDevice(ip, mac, dbDev, download, upload, lastSeen) {
|
|
- const intelInfo = intelMap.get(ip);
|
|
- const dbLabel = dbDev ? dbDev.device_label : (intelInfo ? intelInfo.device_label : null);
|
|
- const dbMan = dbDev ? dbDev.manufacturer : (intelInfo ? intelInfo.manufacturer : null);
|
|
- const dbType = intelInfo ? intelInfo.device_type : null;
|
|
-
|
|
- const meta = resolveDeviceMetadata(ip, mac, dbLabel, dbMan, dbType);
|
|
- const isRouted = mac === '04:f4:1c:ce:c2:e6' && ip !== '10.6.50.25' && ip !== '10.6.12.242';
|
|
-
|
|
- // Scale download and upload
|
|
- const scaledDl = Math.round((download || 0) * dlFactor);
|
|
- const scaledUl = Math.round((upload || 0) * ulFactor);
|
|
-
|
|
- return {
|
|
- id: dbDev ? dbDev.id : null,
|
|
- site_uuid: dbDev ? dbDev.site_uuid : siteUuid,
|
|
- fetched_at: lastSeen || latest.t,
|
|
- mac_address: mac,
|
|
- ip_address: ip,
|
|
- device_label: meta.label,
|
|
- device_type: meta.type,
|
|
- os_label: meta.os,
|
|
- manufacturer: meta.manufacturer,
|
|
- download: scaledDl || 0,
|
|
- upload: scaledUl || 0,
|
|
- total: (scaledDl || 0) + (scaledUl || 0),
|
|
- is_gateway_routed: isRouted ? 1 : 0
|
|
- };
|
|
- }
|
|
-
|
|
- // 1. Process flowsData
|
|
- for (const f of flowsData) {
|
|
- if (!f.src_ip || seenIps.has(f.src_ip)) continue;
|
|
- if (deviceMatchesAgent(f.src_ip, f.src_mac, agentUuid)) {
|
|
- seenIps.add(f.src_ip);
|
|
- const dbDev = devicesMap.get(f.src_ip);
|
|
- resolved.push(processAgentDevice(f.src_ip, f.src_mac, dbDev, f.download, f.upload, f.last_seen || f.fetched_at));
|
|
- }
|
|
- }
|
|
-
|
|
- // 2. Process devicesData that were not in flowsData but match agent
|
|
- for (const dev of devicesData) {
|
|
- if (!dev.ip_address || seenIps.has(dev.ip_address)) continue;
|
|
- if (deviceMatchesAgent(dev.ip_address, dev.mac_address, agentUuid)) {
|
|
- seenIps.add(dev.ip_address);
|
|
- resolved.push(processAgentDevice(dev.ip_address, dev.mac_address, dev, dev.download, dev.upload, dev.fetched_at));
|
|
- }
|
|
- }
|
|
-
|
|
- resolved.sort((a, b) => b.download - a.download);
|
|
- return resolved.slice(0, limit);
|
|
- }
|
|
-
|
|
- // Admin View Search Logic
|
|
- if (search) {
|
|
- const q = `%${search}%`;
|
|
-
|
|
- // 1. Fetch matching historical devices from devices table (grouped by IP address)
|
|
- const devicesData = d.prepare(`
|
|
- 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 (
|
|
- ip_address LIKE @q OR
|
|
- mac_address LIKE @q OR
|
|
- device_label LIKE @q OR
|
|
- manufacturer LIKE @q OR
|
|
- device_type LIKE @q OR
|
|
- os_label LIKE @q
|
|
- )
|
|
- GROUP BY ip_address
|
|
- `).all({ siteUuid, q });
|
|
-
|
|
- // 2. Fetch matching historical flows from flows table (grouped by IP address)
|
|
- const flowsData = d.prepare(`
|
|
- SELECT src_ip as ip_address, src_mac as mac_address,
|
|
- 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 (
|
|
- src_ip LIKE @q OR
|
|
- src_mac LIKE @q OR
|
|
- app_label LIKE @q OR
|
|
- domain LIKE @q
|
|
- )
|
|
- GROUP BY src_ip
|
|
- `).all({ siteUuid, q });
|
|
-
|
|
- const resolvedMap = new Map();
|
|
- const intelList = d.prepare("SELECT * FROM intel_device_discovery").all();
|
|
- const intelMap = new Map(intelList.map(i => [i.ip_address, i]));
|
|
-
|
|
- const processSearchDevice = (ip, mac, dbDev, download, upload, lastSeen) => {
|
|
- const intelInfo = intelMap.get(ip);
|
|
- const dbLabel = dbDev ? dbDev.device_label : (intelInfo ? intelInfo.device_label : null);
|
|
- const dbMan = dbDev ? dbDev.manufacturer : (intelInfo ? intelInfo.manufacturer : null);
|
|
- const dbType = intelInfo ? intelInfo.device_type : null;
|
|
-
|
|
- const meta = resolveDeviceMetadata(ip, mac, dbLabel, dbMan, dbType);
|
|
- const isRouted = mac === '04:f4:1c:ce:c2:e6' && ip !== '10.6.50.25' && ip !== '10.6.12.242';
|
|
-
|
|
- return {
|
|
- id: dbDev ? dbDev.id : null,
|
|
- site_uuid: dbDev ? dbDev.site_uuid : siteUuid,
|
|
- fetched_at: lastSeen || latest.t,
|
|
- mac_address: mac,
|
|
- ip_address: ip,
|
|
- device_label: meta.label,
|
|
- device_type: meta.type,
|
|
- os_label: meta.os,
|
|
- manufacturer: meta.manufacturer,
|
|
- download: download || 0,
|
|
- upload: upload || 0,
|
|
- total: (download || 0) + (upload || 0),
|
|
- is_gateway_routed: isRouted ? 1 : 0
|
|
- };
|
|
- };
|
|
-
|
|
- // Add from devicesData
|
|
- for (const dev of devicesData) {
|
|
- if (!dev.ip_address) continue;
|
|
- resolvedMap.set(dev.ip_address, processSearchDevice(dev.ip_address, dev.mac_address, dev, dev.download, dev.upload, dev.fetched_at));
|
|
- }
|
|
-
|
|
- // Add/Merge from flowsData
|
|
- for (const f of flowsData) {
|
|
- if (!f.ip_address) continue;
|
|
- const existing = resolvedMap.get(f.ip_address);
|
|
- if (existing) {
|
|
- existing.download = Math.max(existing.download, f.download || 0);
|
|
- existing.upload = Math.max(existing.upload, f.upload || 0);
|
|
- existing.total = existing.download + existing.upload;
|
|
- } else {
|
|
- resolvedMap.set(f.ip_address, processSearchDevice(f.ip_address, f.mac_address, null, f.download, f.upload, f.fetched_at));
|
|
- }
|
|
- }
|
|
-
|
|
- const resolved = Array.from(resolvedMap.values());
|
|
- resolved.sort((a, b) => b.download - a.download);
|
|
- return resolved.slice(0, limit);
|
|
- }
|
|
-
|
|
- // Load intelligence tables to assist in type resolving
|
|
- const intelList = d.prepare("SELECT * FROM intel_device_discovery").all();
|
|
- const intelMap = new Map(intelList.map(i => [i.ip_address, i]));
|
|
-
|
|
- // Get all devices in latest snapshot from devices table
|
|
- const devices = d.prepare(`SELECT * FROM devices WHERE fetched_at = ?`).all(latest.t);
|
|
-
|
|
- // Get active flow bandwidth in latest flows snapshot
|
|
- const latestFlowFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
|
|
- let flowsBandwidth = [];
|
|
- if (latestFlowFetch?.t) {
|
|
- flowsBandwidth = d.prepare(`
|
|
- SELECT src_ip, src_mac, SUM(bytes_download) as flow_download, SUM(bytes_upload) as flow_upload
|
|
- FROM flows
|
|
- WHERE fetched_at = ?
|
|
- GROUP BY src_ip
|
|
- `).all(latestFlowFetch.t);
|
|
- }
|
|
- const flowMap = new Map(flowsBandwidth.map(f => [f.src_ip, f]));
|
|
-
|
|
- const resolved = [];
|
|
- const seenIps = new Set();
|
|
-
|
|
- function processDevice(ip, mac, dbDev, flowInfo) {
|
|
- const intelInfo = intelMap.get(ip);
|
|
- const dbLabel = dbDev ? dbDev.device_label : (intelInfo ? intelInfo.device_label : null);
|
|
- const dbMan = dbDev ? dbDev.manufacturer : (intelInfo ? intelInfo.manufacturer : null);
|
|
- const dbType = intelInfo ? intelInfo.device_type : null;
|
|
-
|
|
- const meta = resolveDeviceMetadata(ip, mac, dbLabel, dbMan, dbType);
|
|
- const isRouted = mac === '04:f4:1c:ce:c2:e6' && ip !== '10.6.50.25' && ip !== '10.6.12.242';
|
|
-
|
|
- const download = flowInfo ? flowInfo.flow_download : (dbDev ? dbDev.download : 0);
|
|
- const upload = flowInfo ? flowInfo.flow_upload : (dbDev ? dbDev.upload : 0);
|
|
-
|
|
- return {
|
|
- id: dbDev ? dbDev.id : null,
|
|
- site_uuid: dbDev ? dbDev.site_uuid : siteUuid,
|
|
- fetched_at: latest.t,
|
|
- mac_address: mac,
|
|
- ip_address: ip,
|
|
- device_label: meta.label,
|
|
- device_type: meta.type,
|
|
- os_label: meta.os,
|
|
- manufacturer: meta.manufacturer,
|
|
- download: download || 0,
|
|
- upload: upload || 0,
|
|
- total: (download || 0) + (upload || 0),
|
|
- is_gateway_routed: isRouted ? 1 : 0
|
|
- };
|
|
- }
|
|
-
|
|
- // 1. Process all devices in the devices table
|
|
- for (const dev of devices) {
|
|
- if (!dev.ip_address) continue;
|
|
- if (seenIps.has(dev.ip_address)) continue;
|
|
- seenIps.add(dev.ip_address);
|
|
- const flowInfo = flowMap.get(dev.ip_address);
|
|
- resolved.push(processDevice(dev.ip_address, dev.mac_address, dev, flowInfo));
|
|
- }
|
|
-
|
|
- // 2. Process any active flow IPs that are NOT present in the devices table (e.g. dynamic client IPs)
|
|
- for (const flow of flowsBandwidth) {
|
|
- if (!flow.src_ip || seenIps.has(flow.src_ip)) continue;
|
|
- seenIps.add(flow.src_ip);
|
|
- resolved.push(processDevice(flow.src_ip, flow.src_mac, null, flow));
|
|
- }
|
|
-
|
|
- // 3. Filter list based on role (Admin vs Agent)
|
|
- let filtered = resolved;
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- filtered = resolved.filter(dev => deviceMatchesAgent(dev.ip_address, dev.mac_address, agentUuid));
|
|
- } else if (siteUuid) {
|
|
- filtered = resolved.filter(dev => dev.site_uuid === siteUuid);
|
|
- }
|
|
-
|
|
- // 4. Sort by download DESC
|
|
- filtered.sort((a, b) => b.download - a.download);
|
|
- return filtered.slice(0, limit);
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM devices WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.t) return [];
|
|
+ const rows = d.prepare(`
|
|
+ SELECT * FROM devices
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY download DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
+
|
|
+ return rows.map(r => {
|
|
+ const meta = resolveDeviceMetadata(r.ip_address, r.mac_address, r.device_label, r.manufacturer, r.device_type);
|
|
+ return { ...r, ...meta, total: (r.download||0) + (r.upload||0) };
|
|
+ });
|
|
}
|
|
|
|
function correlateFlows(flows) {
|
|
@@ -1527,812 +1292,518 @@ function correlateAppLabels(apps) {
|
|
});
|
|
}
|
|
|
|
-function getLatestFlows(limit = 100, siteUuid = null, agentUuid = null) {
|
|
+function getLatestFlows(limit = 50, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- const latest = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
|
|
- if (!latest?.t) return [];
|
|
-
|
|
- let rows = [];
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- rows = d.prepare(`
|
|
- SELECT * FROM flows
|
|
- WHERE src_mac IN (${placeholders})
|
|
- ORDER BY last_seen DESC, fetched_at DESC
|
|
- LIMIT ?
|
|
- `).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 });
|
|
- }
|
|
-
|
|
- const mapped = rows.map(r => ({
|
|
- ...r,
|
|
- download: r.bytes_download ?? 0,
|
|
- upload: r.bytes_upload ?? 0
|
|
- }));
|
|
-
|
|
- return correlateFlows(mapped);
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM flows WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.t) return [];
|
|
+ return d.prepare(`
|
|
+ SELECT * FROM flows
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY bytes_download DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
-function getLatestThreats(limit = 50, siteUuid = null, agentUuid = null) {
|
|
+function getLatestThreats(limit = 20, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- return d.prepare(`
|
|
- SELECT * FROM threats
|
|
- WHERE mac_address IN (${placeholders}) OR ip_address IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
|
|
- ORDER BY fetched_at DESC
|
|
- 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 });
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM threats WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.t) return [];
|
|
+ return d.prepare(`
|
|
+ SELECT * FROM threats
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY id DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
function getLatestProtocols(limit = 20, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- const latest = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
|
|
- if (!latest?.t) return [];
|
|
-
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- return d.prepare(`
|
|
- SELECT protocol AS protocol_label, SUM(bytes_download) AS download, SUM(bytes_upload) AS upload, COUNT(*) AS flow_count
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders}) AND fetched_at = ?
|
|
- GROUP BY protocol
|
|
- ORDER BY download DESC
|
|
- LIMIT ?
|
|
- `).all(...macs, latest.t, limit);
|
|
- }
|
|
-
|
|
- const protoLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_protocols`).get();
|
|
- if (!protoLatest?.t) return [];
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_protocols WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.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 ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY download DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
function getLatestCountries(limit = 15, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
-
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
-
|
|
- const rows = d.prepare(`
|
|
- SELECT g.country AS country_name,
|
|
- SUM(f.bytes_download) AS download,
|
|
- SUM(f.bytes_upload) AS upload,
|
|
- COUNT(*) AS flow_count
|
|
- FROM (
|
|
- SELECT dst_ip, bytes_download, bytes_upload
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders})
|
|
- ORDER BY last_seen DESC, fetched_at DESC
|
|
- LIMIT 500
|
|
- ) f
|
|
- JOIN geoip_cache g ON f.dst_ip = g.ip_address
|
|
- WHERE g.country IS NOT NULL
|
|
- AND g.country != 'Local'
|
|
- GROUP BY g.country
|
|
- ORDER BY download DESC
|
|
- LIMIT ?
|
|
- `).all(...macs, limit);
|
|
-
|
|
- // Build dynamic name-to-code mapping from bandwidth_countries
|
|
- const nameToCodeMap = {};
|
|
- try {
|
|
- const mappingRows = d.prepare("SELECT DISTINCT country_code, country_name FROM bandwidth_countries WHERE country_code IS NOT NULL").all();
|
|
- for (const r of mappingRows) {
|
|
- if (r.country_name) {
|
|
- nameToCodeMap[r.country_name.toLowerCase().trim()] = r.country_code;
|
|
- }
|
|
- }
|
|
- } catch (err) {
|
|
- console.error('[DB] Failed to build country code mapping:', err);
|
|
- }
|
|
-
|
|
- // Fallback/standard mapping
|
|
- const fallbackMap = {
|
|
- 'indonesia': 'ID',
|
|
- 'united states': 'US',
|
|
- 'united kingdom': 'GB',
|
|
- 'great britain': 'GB',
|
|
- 'singapore': 'SG',
|
|
- 'hong kong': 'HK',
|
|
- 'japan': 'JP',
|
|
- 'canada': 'CA',
|
|
- 'australia': 'AU',
|
|
- 'germany': 'DE',
|
|
- 'france': 'FR',
|
|
- 'india': 'IN',
|
|
- 'china': 'CN',
|
|
- 'south korea': 'KR',
|
|
- 'russia': 'RU',
|
|
- 'russian federation': 'RU',
|
|
- 'brazil': 'BR',
|
|
- 'italy': 'IT',
|
|
- 'spain': 'ES',
|
|
- 'netherlands': 'NL',
|
|
- 'the netherlands': 'NL',
|
|
- 'switzerland': 'CH',
|
|
- 'sweden': 'SE',
|
|
- 'norway': 'NO',
|
|
- 'finland': 'FI',
|
|
- 'denmark': 'DK',
|
|
- 'ireland': 'IE',
|
|
- 'belgium': 'BE',
|
|
- 'austria': 'AT',
|
|
- 'malaysia': 'MY',
|
|
- 'thailand': 'TH',
|
|
- 'vietnam': 'VN',
|
|
- 'philippines': 'PH',
|
|
- 'taiwan': 'TW',
|
|
- 'new zealand': 'NZ',
|
|
- 'south africa': 'ZA',
|
|
- 'mexico': 'MX',
|
|
- 'argentina': 'AR',
|
|
- 'chile': 'CL',
|
|
- 'colombia': 'CO',
|
|
- 'turkey': 'TR',
|
|
- 'saudi arabia': 'SA',
|
|
- 'united arab emirates': 'AE',
|
|
- 'egypt': 'EG',
|
|
- 'israel': 'IL',
|
|
- 'ukraine': 'UA',
|
|
- 'poland': 'PL',
|
|
- 'romania': 'RO',
|
|
- 'greece': 'GR',
|
|
- 'hungary': 'HU',
|
|
- 'bangladesh': 'BD',
|
|
- 'seychelles': 'SC',
|
|
- 'luxembourg': 'LU',
|
|
- 'pakistan': 'PK'
|
|
- };
|
|
-
|
|
- return rows.map(r => {
|
|
- const normalizedName = r.country_name.toLowerCase().trim();
|
|
- const code = nameToCodeMap[normalizedName] || fallbackMap[normalizedName] || null;
|
|
- return {
|
|
- country_code: code,
|
|
- country_name: r.country_name,
|
|
- download: r.download ?? 0,
|
|
- upload: r.upload ?? 0,
|
|
- flow_count: r.flow_count ?? 0
|
|
- };
|
|
- });
|
|
- }
|
|
-
|
|
- const countryLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_countries`).get();
|
|
- if (!countryLatest?.t) return [];
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM bandwidth_countries WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.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 ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY download DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
function getLatestDNS(limit = 20, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- const latest = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get();
|
|
- if (!latest?.t) return [];
|
|
-
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- return d.prepare(`
|
|
- SELECT domain, COUNT(*) AS query_count, app_label, 'Web' AS category
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders}) AND domain IS NOT NULL AND fetched_at = ?
|
|
- GROUP BY domain
|
|
- ORDER BY query_count DESC
|
|
- LIMIT ?
|
|
- `).all(...macs, latest.t, limit);
|
|
- }
|
|
-
|
|
- const dnsLatest = d.prepare(`SELECT MAX(fetched_at) as t FROM dns_queries`).get();
|
|
- if (!dnsLatest?.t) return [];
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM dns_queries WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.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 ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY query_count DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
function getLatestEvents(limit = 50, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- return d.prepare(`
|
|
- SELECT * FROM events
|
|
- WHERE mac_address IN (${placeholders}) OR ip_address IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
|
|
- ORDER BY fetched_at DESC
|
|
- 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 });
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM events WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.t) return [];
|
|
+ return d.prepare(`
|
|
+ SELECT * FROM events
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ ORDER BY event_at DESC LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
-function getBandwidthTimeline(points = 60, siteUuid = null, agentUuid = null) {
|
|
+function getBandwidthTimeline(limit = 12, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- return d.prepare(`
|
|
- SELECT mb.fetched_at,
|
|
- SUM(mb.download) AS total_download,
|
|
- SUM(mb.upload) AS total_upload,
|
|
- COALESCE(fl.flow_count, 0) AS total_flows,
|
|
- COALESCE(fl.device_count, 0) AS active_devices
|
|
- FROM mac_bandwidth mb
|
|
- LEFT JOIN (
|
|
- SELECT fetched_at, COUNT(*) AS flow_count, COUNT(DISTINCT src_ip) AS device_count
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders})
|
|
- GROUP BY fetched_at
|
|
- ) fl ON fl.fetched_at = mb.fetched_at
|
|
- WHERE mb.mac_address IN (${placeholders})
|
|
- GROUP BY mb.fetched_at
|
|
- ORDER BY mb.fetched_at DESC
|
|
- LIMIT ?
|
|
- `).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
|
|
- `).all({ limit: points, siteUuid }).reverse();
|
|
+ SELECT * FROM bandwidth_timeline
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ ORDER BY fetched_at DESC LIMIT @limit
|
|
+ `).all({ limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
function getStats(siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
+
|
|
+ const devRows = d.prepare(`SELECT MAX(fetched_at) as t FROM devices WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ const devT = devRows?.t;
|
|
+ const totalDevices = devT ? d.prepare(`SELECT count(*) as c FROM devices WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND ${buildAgentWhere(agentUuid)} AND fetched_at = @t`).get({siteUuid, t: devT, agentUuid}).c : 0;
|
|
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
-
|
|
- // Count cumulative unique client devices for this agent
|
|
- const flowsIPs = d.prepare(`
|
|
- SELECT DISTINCT src_ip, src_mac FROM flows
|
|
- WHERE src_mac IN (${placeholders})
|
|
- `).all(...macs);
|
|
-
|
|
- const devicesIPs = d.prepare(`
|
|
- SELECT DISTINCT ip_address, mac_address FROM devices
|
|
- `).all();
|
|
-
|
|
- const uniqueDevs = new Set();
|
|
- const addDev = (ip, mac) => {
|
|
- if (!ip) return;
|
|
- if (deviceMatchesAgent(ip, mac, agentUuid)) {
|
|
- uniqueDevs.add(ip);
|
|
- }
|
|
- };
|
|
- for (const f of flowsIPs) addDev(f.src_ip, f.src_mac);
|
|
- for (const dev of devicesIPs) addDev(dev.ip_address, dev.mac_address);
|
|
- const totalDevices = uniqueDevs.size;
|
|
-
|
|
- // Count cumulative flows for this agent
|
|
- const activeFlows = d.prepare(`
|
|
- SELECT COUNT(*) as n FROM flows
|
|
- WHERE src_mac IN (${placeholders})
|
|
- `).get(...macs)?.n ?? 0;
|
|
-
|
|
- // Count threats for this agent
|
|
- const totalThreats = d.prepare(`
|
|
- SELECT COUNT(*) as n FROM threats
|
|
- WHERE mac_address IN (${placeholders}) OR ip_address IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
|
|
- `).get(...macs, ...macs)?.n ?? 0;
|
|
-
|
|
- // Count events for this agent
|
|
- const totalEvents = d.prepare(`
|
|
- SELECT COUNT(*) as n FROM events
|
|
- WHERE mac_address IN (${placeholders}) OR ip_address IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
|
|
- `).get(...macs, ...macs)?.n ?? 0;
|
|
-
|
|
- const lastFetch = d.prepare(`SELECT MAX(fetched_at) as t FROM flows`).get()?.t ?? null;
|
|
-
|
|
- // Construct latest bandwidth timeline summary for this agent
|
|
- const latestMacSnap = d.prepare(`SELECT MAX(fetched_at) AS t FROM mac_bandwidth`).get()?.t;
|
|
- const latestBw = latestMacSnap
|
|
- ? d.prepare(`
|
|
- SELECT SUM(download) AS total_download, SUM(upload) AS total_upload,
|
|
- ? AS total_flows, ? AS active_devices, ? as fetched_at
|
|
- FROM mac_bandwidth
|
|
- WHERE mac_address IN (${placeholders}) AND fetched_at = ?
|
|
- `).get(activeFlows, totalDevices, latestMacSnap, ...macs, latestMacSnap)
|
|
- : {};
|
|
-
|
|
- return { totalDevices, activeFlows, totalThreats, totalEvents, lastFetch, latestBw };
|
|
- }
|
|
+ const flowRows = d.prepare(`SELECT MAX(fetched_at) as t FROM flows WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ const flowT = flowRows?.t;
|
|
+ const activeFlows = flowT ? d.prepare(`SELECT count(*) as c FROM flows WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND ${buildAgentWhere(agentUuid)} AND fetched_at = @t`).get({siteUuid, t: flowT, agentUuid}).c : 0;
|
|
|
|
- // 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)
|
|
- : 0;
|
|
+ const threatRows = d.prepare(`SELECT count(*) as c FROM threats WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND ${buildAgentWhere(agentUuid)}`).get({siteUuid, agentUuid});
|
|
+ const totalThreats = threatRows.c;
|
|
|
|
- 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)
|
|
- : 0;
|
|
+ const eventRows = d.prepare(`SELECT count(*) as c FROM events WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid) AND ${buildAgentWhere(agentUuid)}`).get({siteUuid, agentUuid});
|
|
+ const totalEvents = eventRows.c;
|
|
|
|
- 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 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 });
|
|
+ let lastFetch = devT || flowT || null;
|
|
|
|
- return { totalDevices, activeFlows, totalThreats, totalEvents, lastFetch, latestBw };
|
|
+ return { totalDevices, activeFlows, totalThreats, totalEvents, lastFetch };
|
|
}
|
|
|
|
// ΓöÇΓöÇΓöÇ 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);
|
|
}
|
|
|
|
// ΓöÇΓöÇΓöÇ QUERY FITUR BARU ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
-function getLatest(table, orderBy = 'download', limit = 50, siteUuid = null, agentUuid = null) {
|
|
+function getLatest(table, limit = 20, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- const latest = d.prepare(`SELECT MAX(fetched_at) as t FROM ${table}`).get();
|
|
- if (!latest?.t) return [];
|
|
-
|
|
- let rows = [];
|
|
-
|
|
- // If agentUuid is provided, handle direct MAC or interface filtering
|
|
- const macs = agentUuid ? AGENT_MAC_MAP[agentUuid] : null;
|
|
- if (agentUuid && macs) {
|
|
- if (table === 'mac_bandwidth') {
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- rows = d.prepare(`SELECT * FROM mac_bandwidth WHERE fetched_at = ? AND mac_address IN (${placeholders}) ORDER BY ${orderBy} DESC LIMIT ?`).all(latest.t, ...macs, limit);
|
|
- return rows;
|
|
- }
|
|
- if (table === 'interfaces') {
|
|
- const numericIds = AGENT_NUMERIC_IDS[agentUuid] || [];
|
|
- if (numericIds.length > 0) {
|
|
- const placeholders = numericIds.map(() => '?').join(',');
|
|
- rows = d.prepare(`SELECT * FROM interfaces WHERE fetched_at = ? AND agent_id IN (${placeholders}) ORDER BY ${orderBy} DESC LIMIT ?`).all(latest.t, ...numericIds, limit);
|
|
- return rows;
|
|
- }
|
|
- }
|
|
- if (table === 'remote_ips') {
|
|
- const latestFlowsFetch = d.prepare("SELECT MAX(fetched_at) as t FROM flows").get();
|
|
- if (latestFlowsFetch?.t) {
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- rows = d.prepare(`
|
|
- SELECT dst_ip AS remote_ip,
|
|
- CASE WHEN dst_ip LIKE '%:%' THEN 6 ELSE 4 END AS ip_version,
|
|
- SUM(bytes_download) AS download,
|
|
- SUM(bytes_upload) AS upload,
|
|
- SUM(bytes_download + bytes_upload) AS total
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders}) AND fetched_at = ?
|
|
- GROUP BY dst_ip
|
|
- ORDER BY download DESC
|
|
- LIMIT ?
|
|
- `).all(...macs, latestFlowsFetch.t, limit);
|
|
- return rows;
|
|
- }
|
|
- }
|
|
- if (table === 'dns_queries') {
|
|
- const latestFlowsFetch = d.prepare("SELECT MAX(fetched_at) as t FROM flows").get();
|
|
- if (latestFlowsFetch?.t) {
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
- rows = d.prepare(`
|
|
- SELECT domain, COUNT(*) AS query_count
|
|
- FROM flows
|
|
- WHERE src_mac IN (${placeholders}) AND fetched_at = ? AND domain IS NOT NULL
|
|
- GROUP BY domain
|
|
- ORDER BY query_count DESC
|
|
- LIMIT ?
|
|
- `).all(...macs, latestFlowsFetch.t, limit);
|
|
- return rows;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // 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 });
|
|
-
|
|
- // If agentUuid is provided, scale down numerical values by traffic ratio to match the agent's footprint
|
|
- if (agentUuid && !['mac_bandwidth', 'interfaces'].includes(table)) {
|
|
- const ratio = getAgentTrafficRatio(agentUuid);
|
|
- return rows.map(r => ({
|
|
- ...r,
|
|
- download: Math.round((r.download || 0) * ratio),
|
|
- upload: Math.round((r.upload || 0) * ratio),
|
|
- total: Math.round((r.total || 0) * ratio),
|
|
- query_count: Math.round((r.query_count || 0) * ratio),
|
|
- flow_count: Math.round((r.flow_count || 0) * ratio),
|
|
- }));
|
|
- }
|
|
-
|
|
- return rows;
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM ${table} WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.t) return [];
|
|
+ return d.prepare(`
|
|
+ SELECT * FROM ${table}
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
-// 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
|
|
);
|
|
@@ -2341,23 +1812,25 @@ function insertVPNDetection(rows, fetchedAt, siteUuid) {
|
|
|
|
// ΓöÇΓöÇΓöÇ QUERY Intelligence ΓÇö ambil semua row tanpa batasan fetched_at ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
|
|
// (karena event ini tidak diposting ulang tiap menit, simpan kumulatif)
|
|
-function getIntelData(table, limit = 100, siteUuid = null, agentUuid = null) {
|
|
+function getIntelData(table, limit = 50, siteUuid = null, agentUuid = null) {
|
|
const d = getDB();
|
|
- if (agentUuid && AGENT_MAC_MAP[agentUuid]) {
|
|
- const macs = AGENT_MAC_MAP[agentUuid];
|
|
- const placeholders = macs.map(() => '?').join(',');
|
|
-
|
|
- // For reputation, the column is local_ip, not ip_address
|
|
- const ipField = table === 'intel_ip_reputation' ? 'local_ip' : 'ip_address';
|
|
-
|
|
- return d.prepare(`
|
|
- SELECT * FROM ${table}
|
|
- WHERE mac_address IN (${placeholders}) OR ${ipField} IN (SELECT DISTINCT src_ip FROM flows WHERE src_mac IN (${placeholders}))
|
|
- ORDER BY fetched_at DESC
|
|
- 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 });
|
|
+ const allowed = [
|
|
+ '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'
|
|
+ ];
|
|
+ if (!allowed.includes(table)) return [];
|
|
+
|
|
+ const tRows = d.prepare(`SELECT MAX(fetched_at) as t FROM ${table} WHERE ${buildAgentWhere(agentUuid)}`).get({agentUuid});
|
|
+ if (!tRows?.t) return [];
|
|
+
|
|
+ return d.prepare(`
|
|
+ SELECT * FROM ${table}
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ AND fetched_at = @fetched_at
|
|
+ LIMIT @limit
|
|
+ `).all({ fetched_at: tRows.t, limit, siteUuid, agentUuid });
|
|
}
|
|
|
|
function getIntelStats(siteUuid = null, agentUuid = null) {
|
|
@@ -2365,27 +1838,18 @@ function getIntelStats(siteUuid = null, agentUuid = null) {
|
|
const tables = [
|
|
'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',
|
|
+ 'intel_tor_detection', 'intel_unencrypted_passwords', 'intel_vpn_detection'
|
|
];
|
|
- const counts = {};
|
|
-
|
|
- const macs = agentUuid ? AGENT_MAC_MAP[agentUuid] : null;
|
|
- const placeholders = macs ? macs.map(() => '?').join(',') : '';
|
|
-
|
|
+ const stats = {};
|
|
for (const t of tables) {
|
|
- try {
|
|
- if (agentUuid && macs) {
|
|
- const ipField = t === 'intel_ip_reputation' ? 'local_ip' : 'ip_address';
|
|
- counts[t] = d.prepare(`
|
|
- SELECT COUNT(*) as n FROM ${t}
|
|
- 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;
|
|
- }
|
|
- } catch { counts[t] = 0; }
|
|
+ const r = d.prepare(`
|
|
+ SELECT count(*) as c FROM ${t}
|
|
+ WHERE (@siteUuid IS NULL OR site_uuid = @siteUuid)
|
|
+ AND ${buildAgentWhere(agentUuid)}
|
|
+ `).get({siteUuid, agentUuid});
|
|
+ stats[t] = r.c;
|
|
}
|
|
- return counts;
|
|
+ return stats;
|
|
}
|
|
|
|
function getDataInterval() {
|
|
@@ -2468,7 +1932,7 @@ function syncAgentUsers(agents) {
|
|
for (const agent of agents) {
|
|
const uuid = agent.uuid;
|
|
if (!uuid) continue;
|
|
- const label = AGENT_LABELS[uuid] || agent.label || uuid;
|
|
+ const label = agent.label || uuid;
|
|
|
|
// Create username = lowercase uuid (e.g. '1r-79-j9-ye')
|
|
const usernameUuidLower = uuid.toLowerCase();
|