const express = require('express'); const router = express.Router(); const { DeviceStat, CustomDeviceLabel, Flow } = require('../../models/Schemas'); const { getTimeFilter, getBaseFilter, getCustomLabelsMap } = require('./helpers'); const { generateMacFromIp, resolveDeviceTypeFromIp, resolveOSFromIp, resolveVendorFromIp, generateAutoLabel } = require('../../deviceResolver'); // GET /api/dashboard/devices router.get('/devices', async (req, res) => { try { const limit = req.query.limit !== undefined ? parseInt(req.query.limit) : 0; const skip = parseInt(req.query.skip ?? 0); const timeFilter = getTimeFilter(req); const query = getBaseFilter(req, timeFilter); const pipeline = [ { $match: query }, { $sort: { timestamp: -1 } }, { $group: { _id: "$ip_address", doc: { $first: "$$ROOT" } } }, { $replaceRoot: { newRoot: "$doc" } }, { $sort: { timestamp: -1, download: -1 } } ]; if (skip > 0) pipeline.push({ $skip: skip }); if (limit > 0) pipeline.push({ $limit: limit }); const [data, customLabelsMap] = await Promise.all([ DeviceStat.aggregate(pipeline), getCustomLabelsMap() ]); const mapped = data.map(obj => { const ip = obj.ip_address; const mac = obj.mac_address && obj.mac_address !== '-' ? obj.mac_address : generateMacFromIp(ip); const type = obj.device_type && obj.device_type !== '-' && obj.device_type !== 'Unknown' ? obj.device_type : resolveDeviceTypeFromIp(ip); const os = obj.os_label && obj.os_label !== '-' && obj.os_label !== 'Unknown' ? obj.os_label : resolveOSFromIp(ip); const man = obj.manufacturer && obj.manufacturer !== '-' && obj.manufacturer !== 'Unknown' ? obj.manufacturer : resolveVendorFromIp(ip); const lastSeen = obj.last_seen || obj.timestamp || new Date().toISOString(); const baseLabel = customLabelsMap[mac] || obj.device_label; const label = baseLabel && baseLabel !== '-' && baseLabel !== 'Unknown' && baseLabel !== 'Generic Client' ? baseLabel : generateAutoLabel(ip, mac, man, type); return { ...obj, id: obj._id.toString(), mac_address: mac, device_label: label, device_type: type, os_label: os, manufacturer: man, last_seen: lastSeen }; }); res.json({ ok: true, data: mapped }); } catch (err) { res.status(500).json({ ok: false, error: err.message }); } }); // POST /api/dashboard/devices/update-label router.post('/devices/update-label', async (req, res) => { try { const isAuthorized = req.user?.role === 'SUPER_ADMIN' || req.user?.role === 'TENANT_ADMIN' || req.user?._originalRole === 'SUPER_ADMIN' || req.user?._originalRole === 'TENANT_ADMIN'; if (!isAuthorized) { return res.status(403).json({ ok: false, error: 'Only administrators can update device labels.' }); } const { mac_address, device_label } = req.body; if (!mac_address) return res.status(400).json({ ok: false, error: 'mac_address required' }); if (device_label === undefined) return res.status(400).json({ ok: false, error: 'device_label required' }); const isGlobalUser = req.user?.role === 'SUPER_ADMIN' || ((!req.user?.site_uuid || req.user?.site_uuid === 'default') && ['SOC_ANALYST', 'ENGINEER'].includes(req.user?.role)) || req.user?._originalRole === 'SUPER_ADMIN'; if (!isGlobalUser && req.user?.site_uuid) { const deviceExists = await DeviceStat.findOne({ mac_address, site_uuid: req.user.site_uuid }); if (!deviceExists) { return res.status(403).json({ ok: false, error: 'Unauthorized: This device does not belong to your tenant.' }); } } await CustomDeviceLabel.findOneAndUpdate( { mac_address }, { device_label }, { upsert: true, new: true } ); res.json({ ok: true, message: 'Device label updated successfully' }); } catch (err) { res.status(500).json({ ok: false, error: err.message }); } }); // GET /api/dashboard/mac-bandwidth router.get('/mac-bandwidth', async (req, res) => { try { const limit = parseInt(req.query.limit ?? 50); const timeFilter = getTimeFilter(req); const matchBase = getBaseFilter(req, timeFilter); const raw = await DeviceStat.aggregate([ { $match: { ...matchBase } }, { $group: { _id: { $ifNull: [ '$mac_address', '$ip_address' ] }, download: { $sum: '$download' }, upload: { $sum: '$upload' }, ip: { $last: '$ip_address' }, mac_address: { $last: '$mac_address' }, label: { $last: '$device_label' }, manufacturer: { $last: '$manufacturer' } }}, { $project: { mac_address: 1, download: 1, upload: 1, ip: 1, label: 1, manufacturer: { $ifNull: [ '$manufacturer', 'Intel Corporation' ] }, total: { $add: [ '$download', '$upload' ] }, _id: 0 }}, { $sort: { total: -1 } }, { $limit: limit }, ]); const data = raw.map(d => { const mac = d.mac_address && d.mac_address !== '-' ? d.mac_address : generateMacFromIp(d.ip); const man = d.manufacturer && d.manufacturer !== '-' && d.manufacturer !== 'Unknown' ? d.manufacturer : resolveVendorFromIp(d.ip); return { ...d, mac_address: mac, manufacturer: man }; }); res.json({ ok: true, data }); } catch (err) { res.status(500).json({ ok: false, error: err.message }); } }); // GET /api/dashboard/security-devices router.get('/security-devices', async (req, res) => { try { const timeFilter = getTimeFilter(req); const baseFilter = getBaseFilter(req, timeFilter); const uniqueDevices = await DeviceStat.aggregate([ { $match: baseFilter }, { $sort: { timestamp: -1 } }, { $group: { _id: '$ip_address', latestDoc: { $first: '$$ROOT' } }} ]); const flowStats = await Flow.aggregate([ { $match: baseFilter }, { $group: { _id: '$src_ip', total_bytes: { $sum: { $add: ['$download', '$upload'] } }, encrypted_bytes: { $sum: { $cond: [ { $in: ['$dst_port', [443, 8443, 853, 465, 989, 990, 587]] }, { $add: ['$download', '$upload'] }, 0 ] } } }} ]); const flowMap = {}; flowStats.forEach(fs => { if (fs._id) { flowMap[fs._id] = { total: fs.total_bytes || 0, encrypted: fs.encrypted_bytes || 0 }; } }); const customLabelsMap = await getCustomLabelsMap(); const mapped = uniqueDevices.map(d => { const obj = d.latestDoc; const ip = obj.ip_address; const mac = obj.mac_address && obj.mac_address !== '-' ? obj.mac_address : generateMacFromIp(ip); const type = obj.device_type && obj.device_type !== '-' && obj.device_type !== 'Unknown' ? obj.device_type : resolveDeviceTypeFromIp(ip); const os = obj.os_label && obj.os_label !== '-' && obj.os_label !== 'Unknown' ? obj.os_label : resolveOSFromIp(ip); const man = obj.manufacturer && obj.manufacturer !== '-' && obj.manufacturer !== 'Unknown' ? obj.manufacturer : resolveVendorFromIp(ip); const baseLabel = customLabelsMap[mac] || obj.device_label; const label = baseLabel && baseLabel !== '-' && baseLabel !== 'Unknown' && baseLabel !== 'Generic Client' ? baseLabel : generateAutoLabel(ip, mac, man, type); const lastSeen = obj.last_seen || obj.timestamp || new Date().toISOString(); const fStat = flowMap[ip] || { total: 0, encrypted: 0 }; const encrypted = fStat.encrypted; const unencrypted = Math.max(0, fStat.total - encrypted); const encrypted_pct = fStat.total > 0 ? (encrypted / fStat.total) * 100 : 0; let risk_level = 'Safe'; if (fStat.total > 0) { if (encrypted_pct < 50) risk_level = 'Vulnerable'; else if (encrypted_pct < 80) risk_level = 'Moderate'; } return { _id: obj._id.toString(), ip_address: ip, mac_address: mac, device_label: label, device_type: type, os_label: os, manufacturer: man, encrypted, unencrypted, encrypted_pct, risk_level, has_insecure: unencrypted > encrypted * 2, timestamp: lastSeen }; }); res.json({ ok: true, data: mapped }); } catch (err) { res.status(500).json({ ok: false, message: err.message }); } }); module.exports = router;