feat(site): implement dynamic site selection dropdown in sidebar and enable multi-site monitoring

This commit is contained in:
Rafif-Riqullah-Siregar committed 2026-07-13 16:57:45 +07:00
1 parent be6bc14190
commit 4882108068
125 files changed
+5666 -4305

No files matched your search

+172 -48
View File
@@ -1,6 +1,6 @@
const express = require('express');
const router = express.Router();
const { Threat, Event, Flow } = require('../../models/Schemas');
const { Threat, Event, Flow, DeviceStat } = require('../../models/Schemas');
const { getTimeFilter, getBaseFilter } = require('./helpers');
const { generateMacFromIp, resolveDeviceTypeFromIp, resolveOSFromIp, resolveVendorFromIp } = require('../../deviceResolver');
@@ -118,25 +118,56 @@ router.get('/intelligence/stats', async (req, res) => {
const timeFilter = getTimeFilter(req);
const base = getBaseFilter(req, timeFilter);
const list = await Threat.find(base).lean();
const total = list.length;
const high = list.filter(t => (t.severity || 'medium').toLowerCase() === 'high' || (t.severity || 'medium').toLowerCase() === 'critical').length;
const medium = list.filter(t => (t.severity || 'medium').toLowerCase() === 'medium' || (t.severity || 'medium').toLowerCase() === 'warning').length;
const low = list.filter(t => (t.severity || 'medium').toLowerCase() === 'low' || (t.severity || 'medium').toLowerCase() === 'info').length;
// Get real counts for all 9 categories
const [
intel_crypto_mining,
intel_tor_detection,
intel_vpn_detection,
intel_ip_reputation,
intel_insecure_protocols,
intel_unencrypted_passwords,
rawDevices,
intel_server_discovery
] = await Promise.all([
Threat.countDocuments({ ...base, threat_type: /mining/i }),
Threat.countDocuments({ ...base, threat_type: /tor/i }),
Threat.countDocuments({ ...base, threat_type: /vpn/i }),
Threat.countDocuments({ ...base, threat_type: /reputation/i }),
Threat.countDocuments({ ...base, threat_type: /insecure/i, $nor: [{ threat_type: /password/i }] }),
Threat.countDocuments({ ...base, threat_type: /password/i }),
DeviceStat.distinct('ip_address', base),
Event.countDocuments({ ...base, event_type: 'server.discovery' })
]);
res.json({ ok: true, data: { total, high, medium, low } });
const intel_device_discovery = rawDevices.length;
const intel_encryption_audit = rawDevices.length; // Same as devices for now, as each device is audited
res.json({
ok: true,
data: {
intel_crypto_mining,
intel_tor_detection,
intel_vpn_detection,
intel_ip_reputation,
intel_insecure_protocols,
intel_unencrypted_passwords,
intel_encryption_audit,
intel_device_discovery,
intel_server_discovery
}
});
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
// Helper for detail threat intelligence tables
async function getIntelData(req, threatTypeFilter = null, limit = 0) {
async function getIntelData(req, threatTypeRegex = null, limit = 0) {
const timeFilter = getTimeFilter(req);
const query = getBaseFilter(req, timeFilter);
if (threatTypeFilter) {
query.threat_type = { $regex: threatTypeFilter, $options: 'i' };
if (threatTypeRegex) {
query.threat_type = { $regex: threatTypeRegex, $options: 'i' };
}
let dbQuery = Threat.find(query).sort({ detected_at: -1, timestamp: -1 });
@@ -144,60 +175,153 @@ async function getIntelData(req, threatTypeFilter = null, limit = 0) {
const list = await dbQuery.lean();
return list.map((t, index) => {
const ip = t.ip_address || t.src_ip || '10.6.10.44';
const mac = t.mac_address || t.src_mac || generateMacFromIp(ip);
return list.map((t) => {
const ip = t.ip_address || t.src_ip;
const mac = t.mac_address || t.src_mac;
const eTime = t.detected_at || t.timestamp?.toISOString() || new Date().toISOString();
return {
id: t._id?.toString(),
detected_at: eTime,
ip_address: ip,
mac_address: mac,
pool_host: t.domain || 'stratum.antpool.com',
pool_ip: t.dst_ip || '172.217.194.100',
pool_host: t.domain || null,
pool_ip: t.dst_ip || null,
protocol: t.protocol || 'TCP',
app_label: t.app_label || 'Stratum Protocol',
confidence: 95.5,
download: t.download || 12450,
upload: t.upload || 8450,
exit_node: t.dst_ip || '185.220.101.5',
circuit_id: 'circ_' + Math.abs(index * 1337),
country: 'Germany',
vpn_type: t.app_label?.includes('WireGuard') ? 'WireGuard' : 'OpenVPN',
remote_ip: t.dst_ip || '198.51.100.44',
device_label: t.ip_address || ip,
device_type: resolveDeviceTypeFromIp(ip),
os_label: resolveOSFromIp(ip),
manufacturer: resolveVendorFromIp(ip),
is_new: 1,
encrypted_pct: 85.0,
unencrypted: 150000,
encrypted: 850000,
total: 1000000,
risk_level: 'Low',
app_label: t.app_label || 'Unknown',
confidence: t.severity === 'Critical' ? 99 : (t.severity === 'High' ? 90 : 75),
download: t.download || 0,
upload: t.upload || 0,
exit_node: t.dst_ip || null,
circuit_id: t.flow_id || null,
country: 'Unknown', // Geo IP not in Threat schema yet
vpn_type: t.app_label || 'Unknown VPN',
remote_ip: t.dst_ip || null,
device_label: ip,
device_type: 'Unknown',
os_label: 'Unknown',
manufacturer: 'Unknown',
risk_level: t.severity || 'Medium',
risk: t.severity || 'Medium',
source: 'DPI Scanner',
reputation: t.threat_type || 'Malicious IP',
score: 8.5,
local_ip: ip,
blacklisted: 1,
server_type: 'Database Server',
hostname: t.domain || 'db-01.local',
port: t.dst_port || 3306,
username: 'admin_backone',
severity: t.severity || 'Critical'
severity: t.severity || 'Warning'
};
});
}
router.get('/intelligence/crypto-mining', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'mining', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/device-discovery', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, null, 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/encryption-audit', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, null, 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/insecure-protocols', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'Insecure', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/ip-reputation', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'Reputation', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/server-discovery', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, null, 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/tor', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'tor', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/unencrypted-passwords', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'Insecure Plaintext Password', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/vpn', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'vpn|VPN', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/unencrypted-passwords', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'password', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
router.get('/intelligence/vpn', async (req, res) => { try { res.json({ ok: true, data: await getIntelData(req, 'vpn', 0) }); } catch(e){ res.status(500).json({ok:false,error:e.message}); } });
// Specialized Intelligence Data
router.get('/intelligence/device-discovery', async (req, res) => {
try {
const timeFilter = getTimeFilter(req);
const query = getBaseFilter(req, timeFilter);
const devices = await require('../../models/Schemas').DeviceStat.find(query).sort({ timestamp: -1 }).lean();
const uniqueMap = new Map();
devices.forEach(d => {
if (!uniqueMap.has(d.ip_address)) {
uniqueMap.set(d.ip_address, {
id: d._id?.toString(),
ip_address: d.ip_address,
mac_address: d.mac_address || '-',
device_type: d.device_type || 'Unknown',
os_label: d.os_label || 'Unknown',
manufacturer: d.manufacturer || 'Unknown',
download: d.download || 0,
upload: d.upload || 0,
last_seen: d.timestamp || new Date()
});
}
});
res.json({ ok: true, data: Array.from(uniqueMap.values()) });
} catch(e) { res.status(500).json({ ok: false, error: e.message }); }
});
router.get('/intelligence/encryption-audit', async (req, res) => {
try {
const timeFilter = getTimeFilter(req);
const query = getBaseFilter(req, timeFilter);
const devices = await require('../../models/Schemas').DeviceStat.find(query).sort({ timestamp: -1 }).lean();
const uniqueMap = new Map();
devices.forEach(d => {
if (!uniqueMap.has(d.ip_address)) {
const download = d.download || 0;
const upload = d.upload || 0;
uniqueMap.set(d.ip_address, {
id: d._id?.toString(),
ip_address: d.ip_address,
mac_address: d.mac_address || '-',
device_label: d.device_label || d.ip_address,
encrypted_pct: 85, // Default for now as per DPI capability
unencrypted: Math.floor(download * 0.15),
encrypted: Math.floor(download * 0.85),
total: download + upload,
risk_level: download > 1024 * 1024 * 1024 ? 'medium' : 'safe',
last_seen: d.last_seen || d.timestamp || new Date().toISOString()
});
}
});
res.json({ ok: true, data: Array.from(uniqueMap.values()) });
} catch(e) { res.status(500).json({ ok: false, error: e.message }); }
});
router.get('/intelligence/server-discovery', async (req, res) => {
try {
const timeFilter = getTimeFilter(req);
const query = getBaseFilter(req, timeFilter);
query.event_type = 'server.discovery';
const events = await Event.find(query).sort({ timestamp: -1 }).lean();
// Resolve IPs using DeviceStat
const macs = events.map(e => e.mac_address).filter(Boolean);
const devices = await DeviceStat.find({ mac_address: { $in: macs } }).lean();
const macMap = {};
devices.forEach(d => {
macMap[d.mac_address] = d;
});
const data = events.map(e => {
let serverType = e.category_label || 'Local Server';
let osLabel = 'Unknown';
let port = 0;
// Parse description: "Detected DHCP server on External Gateway"
const match = e.description?.match(/Detected (.*?) server on (.*)/i);
if (match) {
serverType = match[1].trim();
osLabel = match[2].trim();
}
// Infer Port
const sTypeUpper = serverType.toUpperCase();
if (sTypeUpper.includes('DHCP')) port = 67;
else if (sTypeUpper.includes('DNS')) port = 53;
else if (sTypeUpper.includes('SSH')) port = 22;
else if (sTypeUpper.includes('HTTP')) port = 80;
else if (sTypeUpper.includes('HTTPS')) port = 443;
else if (sTypeUpper.includes('FTP')) port = 21;
const device = macMap[e.mac_address] || {};
return {
id: e._id?.toString(),
ip_address: e.ip_address || device.ip_address || null,
mac_address: e.mac_address,
server_type: serverType,
port: port,
os_label: osLabel !== 'Unknown' ? osLabel : (device.os_label || 'Unknown'),
last_seen: e.event_at || e.timestamp || device.last_seen || device.timestamp || new Date().toISOString()
};
});
res.json({ ok: true, data });
} catch(e) { res.status(500).json({ ok: false, error: e.message }); }
});
module.exports = router;