195 lines
7.4 KiB
JavaScript
195 lines
7.4 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { CustomAgentLocation, Summary, DeviceStat, Flow } = require('../../models/Schemas');
|
|
const { getTimeFilter } = require('./helpers');
|
|
|
|
// ─── 1. GET /api/dashboard/agent-locations ──────────────────────────────────────
|
|
router.get('/agent-locations', async (req, res) => {
|
|
try {
|
|
const isGlobalUser = req.user?.role === 'SUPER_ADMIN' ||
|
|
((!req.user?.site_uuid || req.user?.site_uuid === 'default') && ['SOC_ANALYST', 'ENGINEER'].includes(req.user?.role));
|
|
|
|
let query = {};
|
|
if (!isGlobalUser && req.user?.site_uuid) {
|
|
query.site_uuid = req.user.site_uuid;
|
|
}
|
|
if (req.user?.role === 'AGENT_VIEWER' && req.user?.agent_uuid) {
|
|
query.agent_uuid = req.user.agent_uuid;
|
|
}
|
|
|
|
const locations = await CustomAgentLocation.find(query).lean();
|
|
res.json({ ok: true, data: locations });
|
|
} catch (err) {
|
|
console.error('[GET /agent-locations]', err.message);
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// ─── 2. POST /api/dashboard/agent-locations ─────────────────────────────────────
|
|
router.post('/agent-locations', async (req, res) => {
|
|
try {
|
|
if (req.user?.role !== 'SUPER_ADMIN' && req.user?.role !== 'TENANT_ADMIN') {
|
|
return res.status(403).json({ ok: false, error: 'Only administrators can configure agent geolocations.' });
|
|
}
|
|
const { agent_uuid, latitude, longitude, label } = req.body;
|
|
if (!agent_uuid || latitude === undefined || longitude === undefined) {
|
|
return res.status(400).json({ ok: false, error: 'agent_uuid, latitude, and longitude are required' });
|
|
}
|
|
|
|
// Determine site_uuid
|
|
let siteUuid = null;
|
|
const isGlobalUser = req.user?.role === 'SUPER_ADMIN' ||
|
|
((!req.user?.site_uuid || req.user?.site_uuid === 'default') && ['SOC_ANALYST', 'ENGINEER'].includes(req.user?.role));
|
|
|
|
if (!isGlobalUser && req.user?.site_uuid) {
|
|
const agentBelongs = await Summary.findOne({ agent_uuid, site_uuid: req.user.site_uuid });
|
|
if (!agentBelongs) {
|
|
return res.status(403).json({ ok: false, error: 'Unauthorized: This agent does not belong to your tenant.' });
|
|
}
|
|
siteUuid = req.user.site_uuid;
|
|
} else {
|
|
// Find the site_uuid from Summary collection for this agent
|
|
const summaryDoc = await Summary.findOne({ agent_uuid });
|
|
if (summaryDoc) {
|
|
siteUuid = summaryDoc.site_uuid;
|
|
} else {
|
|
// Fallback or use standard env site_uuid
|
|
siteUuid = process.env.BACKONE_SITE_UUID || process.env.NETIFY_SITE_UUID || '6681452d_9cae_4ff4_8ae8_0d504774265e';
|
|
}
|
|
}
|
|
|
|
const findQuery = { agent_uuid };
|
|
if (!isGlobalUser && req.user?.site_uuid) {
|
|
findQuery.site_uuid = req.user.site_uuid;
|
|
}
|
|
|
|
const upserted = await CustomAgentLocation.findOneAndUpdate(
|
|
findQuery,
|
|
{
|
|
agent_uuid,
|
|
site_uuid: siteUuid,
|
|
latitude: parseFloat(latitude),
|
|
longitude: parseFloat(longitude),
|
|
label: label || ''
|
|
},
|
|
{ new: true, upsert: true }
|
|
);
|
|
|
|
res.json({ ok: true, data: upserted });
|
|
} catch (err) {
|
|
console.error('[POST /agent-locations]', err.message);
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// ─── 3. DELETE /api/dashboard/agent-locations/:agent_uuid ────────────────────────
|
|
router.delete('/agent-locations/:agent_uuid', async (req, res) => {
|
|
try {
|
|
if (req.user?.role !== 'SUPER_ADMIN' && req.user?.role !== 'TENANT_ADMIN') {
|
|
return res.status(403).json({ ok: false, error: 'Only administrators can delete agent geolocations.' });
|
|
}
|
|
const { agent_uuid } = req.params;
|
|
|
|
let query = { agent_uuid };
|
|
const isGlobalUser = req.user?.role === 'SUPER_ADMIN' ||
|
|
((!req.user?.site_uuid || req.user?.site_uuid === 'default') && ['SOC_ANALYST', 'ENGINEER'].includes(req.user?.role));
|
|
|
|
if (!isGlobalUser && req.user?.site_uuid) {
|
|
query.site_uuid = req.user.site_uuid;
|
|
}
|
|
|
|
const resDelete = await CustomAgentLocation.deleteOne(query);
|
|
res.json({ ok: true, deleted: resDelete.deletedCount > 0 });
|
|
} catch (err) {
|
|
console.error('[DELETE /agent-locations]', err.message);
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// ─── 4. GET /api/dashboard/agent-flows ──────────────────────────────────────────
|
|
router.get('/agent-flows', async (req, res) => {
|
|
try {
|
|
const requestedSiteUuid = req.headers['x-backone-site-uuid'];
|
|
const isGlobalUser = req.user?.role === 'SUPER_ADMIN' ||
|
|
req.user?.role === 'EXECUTIVE' ||
|
|
((!req.user?.site_uuid || req.user?.site_uuid === 'default') && ['SOC_ANALYST', 'ENGINEER'].includes(req.user?.role));
|
|
|
|
let siteUuid = null;
|
|
if (isGlobalUser && requestedSiteUuid && requestedSiteUuid !== 'all') {
|
|
siteUuid = requestedSiteUuid;
|
|
} else if (!isGlobalUser && req.user?.site_uuid) {
|
|
siteUuid = req.user.site_uuid;
|
|
} else {
|
|
siteUuid = '6681452d_9cae_4ff4_8ae8_0d504774265e';
|
|
}
|
|
|
|
const timeFilter = getTimeFilter(req);
|
|
|
|
// Build IP-to-Agent mapping from DeviceStat
|
|
const deviceQuery = { site_uuid: { $in: [siteUuid, 'global'] } };
|
|
if (req.user?.role === 'AGENT_VIEWER' && req.user?.agent_uuid) {
|
|
deviceQuery.agent_uuid = req.user.agent_uuid;
|
|
}
|
|
const devices = await DeviceStat.find(deviceQuery).select('ip_address agent_uuid').lean();
|
|
const deviceIpToAgent = {};
|
|
for (const dev of devices) {
|
|
if (dev.ip_address && dev.agent_uuid) {
|
|
deviceIpToAgent[dev.ip_address] = dev.agent_uuid;
|
|
}
|
|
}
|
|
|
|
// Query flows
|
|
const flowsQuery = { site_uuid: { $in: [siteUuid, 'global'] } };
|
|
if (timeFilter) flowsQuery.timestamp = timeFilter;
|
|
if (req.user?.role === 'AGENT_VIEWER' && req.user?.agent_uuid) {
|
|
flowsQuery.agent_uuid = req.user.agent_uuid;
|
|
}
|
|
|
|
const flows = await Flow.find(flowsQuery)
|
|
.select('agent_uuid src_ip dst_ip download upload app_label')
|
|
.sort({ timestamp: -1 })
|
|
.limit(5000)
|
|
.lean();
|
|
|
|
const flowMap = {};
|
|
for (const flow of flows) {
|
|
const srcAgent = flow.agent_uuid;
|
|
const dstAgent = deviceIpToAgent[flow.dst_ip];
|
|
|
|
if (srcAgent && dstAgent && srcAgent !== dstAgent) {
|
|
const key = `${srcAgent}->${dstAgent}`;
|
|
if (!flowMap[key]) {
|
|
flowMap[key] = {
|
|
source: srcAgent,
|
|
target: dstAgent,
|
|
bytes: 0,
|
|
flowsCount: 0,
|
|
details: []
|
|
};
|
|
}
|
|
const bytes = ((flow.download || 0) + (flow.upload || 0));
|
|
flowMap[key].bytes += bytes;
|
|
flowMap[key].flowsCount += 1;
|
|
flowMap[key].details.push({
|
|
src_ip: flow.src_ip,
|
|
dst_ip: flow.dst_ip,
|
|
app: flow.app_label || 'Unclassified',
|
|
bytes: bytes
|
|
});
|
|
}
|
|
}
|
|
|
|
const result = Object.values(flowMap);
|
|
for (const f of result) {
|
|
f.details.sort((a, b) => b.bytes - a.bytes);
|
|
f.details = f.details.slice(0, 5); // top 5 sub-flows
|
|
}
|
|
res.json({ ok: true, data: result });
|
|
} catch (err) {
|
|
console.error('[GET /agent-flows]', err.message);
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|