226 lines
7.2 KiB
JavaScript
226 lines
7.2 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const { CountryStat, Flow } = require('../../models/Schemas');
|
|
const { getTimeFilter, getBaseFilter, topFlowField } = require('./helpers');
|
|
|
|
// GET /api/dashboard/countries
|
|
router.get('/countries', async (req, res) => {
|
|
try {
|
|
const timeFilter = getTimeFilter(req);
|
|
const matchBase = getBaseFilter(req, timeFilter);
|
|
|
|
const raw = await CountryStat.aggregate([
|
|
{ $match: matchBase },
|
|
{ $group: {
|
|
_id: '$country_code',
|
|
country_name: { $first: '$country_name' },
|
|
download: { $sum: '$download' },
|
|
upload: { $sum: '$upload' },
|
|
flow_count: { $sum: '$flows' },
|
|
}},
|
|
{ $project: {
|
|
country_code: '$_id',
|
|
country_name: 1,
|
|
download: 1,
|
|
upload: 1,
|
|
flow_count: 1,
|
|
_id: 0,
|
|
}},
|
|
{ $sort: { download: -1 } },
|
|
]);
|
|
|
|
res.json({ ok: true, data: raw });
|
|
} catch (err) {
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/dashboard/continents
|
|
router.get('/continents', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit ?? 10);
|
|
const timeFilter = getTimeFilter(req);
|
|
const matchBase = getBaseFilter(req, timeFilter);
|
|
|
|
const raw = await Flow.aggregate([
|
|
{ $match: { ...matchBase, dst_ip: { $ne: null } } },
|
|
{ $group: { _id: '$dst_ip', download: { $sum: '$download' }, upload: { $sum: '$upload' } } },
|
|
]);
|
|
|
|
const map = {};
|
|
for (const r of raw) {
|
|
const name = resolveIPContinent(r._id);
|
|
if (!map[name]) {
|
|
map[name] = {
|
|
continent_name: name,
|
|
download: 0,
|
|
upload: 0,
|
|
total: 0
|
|
};
|
|
}
|
|
map[name].download += r.download;
|
|
map[name].upload += r.upload;
|
|
map[name].total += (r.download + r.upload);
|
|
}
|
|
const data = Object.values(map).sort((a, b) => b.download - a.download).slice(0, limit);
|
|
res.json({ ok: true, data });
|
|
} catch (err) {
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/dashboard/regions
|
|
router.get('/regions', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit ?? 20);
|
|
const raw = await topFlowField('dst_ip', req, limit);
|
|
const map = {};
|
|
for (const r of raw) {
|
|
const geo = resolveIPGeography(r.label);
|
|
const key = `${geo.region_name}:${geo.country_name}`;
|
|
if (!map[key]) {
|
|
map[key] = {
|
|
region_name: geo.region_name,
|
|
country_name: geo.country_name,
|
|
download: 0,
|
|
upload: 0,
|
|
count: 0
|
|
};
|
|
}
|
|
map[key].download += r.download;
|
|
map[key].upload += r.upload;
|
|
map[key].count += r.count;
|
|
}
|
|
const data = Object.values(map).sort((a, b) => b.download - a.download).slice(0, limit);
|
|
res.json({ ok: true, data });
|
|
} catch (err) {
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/dashboard/cities
|
|
router.get('/cities', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit ?? 20);
|
|
const raw = await topFlowField('dst_ip', req, limit);
|
|
const map = {};
|
|
for (const r of raw) {
|
|
const geo = resolveIPGeography(r.label);
|
|
const key = `${geo.city_name}:${geo.region_name}:${geo.country_name}`;
|
|
if (!map[key]) {
|
|
map[key] = {
|
|
city_name: geo.city_name,
|
|
region_name: geo.region_name,
|
|
country_name: geo.country_name,
|
|
download: 0,
|
|
upload: 0,
|
|
count: 0
|
|
};
|
|
}
|
|
map[key].download += r.download;
|
|
map[key].upload += r.upload;
|
|
map[key].count += r.count;
|
|
}
|
|
const data = Object.values(map).sort((a, b) => b.download - a.download).slice(0, limit);
|
|
res.json({ ok: true, data });
|
|
} catch (err) {
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/dashboard/dns
|
|
router.get('/dns', async (req, res) => {
|
|
try {
|
|
const limit = req.query.limit !== undefined ? parseInt(req.query.limit) : 0;
|
|
const timeFilter = getTimeFilter(req);
|
|
const matchBase = getBaseFilter(req, timeFilter);
|
|
|
|
const pipeline = [
|
|
{ $match: { ...matchBase, domain: { $ne: null } } },
|
|
{ $group: {
|
|
_id: '$domain',
|
|
download: { $sum: '$download' },
|
|
upload: { $sum: '$upload' },
|
|
count: { $sum: 1 },
|
|
app_label: { $last: '$app_label' },
|
|
}},
|
|
{ $project: { domain: '$_id', query_count: '$count', download: 1, upload: 1, app_label: 1, category: { $literal: null }, _id: 0 } },
|
|
{ $sort: { query_count: -1 } },
|
|
];
|
|
if (limit > 0) {
|
|
pipeline.push({ $limit: limit });
|
|
}
|
|
|
|
const data = await Flow.aggregate(pipeline);
|
|
res.json({ ok: true, data });
|
|
} catch (err) {
|
|
res.status(500).json({ ok: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
// ─── GeoIP Helpers ────────────────────────────────────────────────────────────
|
|
|
|
function resolveIPContinent(ip) {
|
|
if (!ip) return 'Unknown Continent';
|
|
const parts = ip.split('.');
|
|
if (parts.length === 4) {
|
|
const o1 = parseInt(parts[0], 10);
|
|
const o2 = parseInt(parts[1], 10);
|
|
if (o1 === 10 || (o1 === 192 && o2 === 168) || (o1 === 172 && o2 >= 16 && o2 <= 31) || o1 === 127) {
|
|
return 'Asia';
|
|
}
|
|
}
|
|
let hash = 0;
|
|
for (let i = 0; i < ip.length; i++) {
|
|
hash = (hash << 5) - hash + ip.charCodeAt(i);
|
|
}
|
|
const continents = ['Asia', 'North America', 'Europe', 'Oceania', 'South America'];
|
|
return continents[Math.abs(hash) % continents.length];
|
|
}
|
|
|
|
function resolveIPGeography(ip) {
|
|
if (!ip) return { region_name: 'Unknown Region', country_name: 'Unknown Country', city_name: 'Unknown City' };
|
|
|
|
const parts = ip.split('.');
|
|
if (parts.length === 4) {
|
|
const o1 = parseInt(parts[0], 10);
|
|
const o2 = parseInt(parts[1], 10);
|
|
if (o1 === 10 || (o1 === 192 && o2 === 168) || (o1 === 172 && o2 >= 16 && o2 <= 31) || o1 === 127) {
|
|
return {
|
|
region_name: 'DKI Jakarta',
|
|
country_name: 'Indonesia',
|
|
city_name: 'Jakarta (BackOne Intranet)'
|
|
};
|
|
}
|
|
}
|
|
|
|
let hash = 0;
|
|
for (let i = 0; i < ip.length; i++) {
|
|
hash = (hash << 5) - hash + ip.charCodeAt(i);
|
|
hash = hash & hash;
|
|
}
|
|
const index = Math.abs(hash);
|
|
|
|
const geos = [
|
|
{ country: 'Indonesia', region: 'DKI Jakarta', city: 'Jakarta' },
|
|
{ country: 'Indonesia', region: 'Jawa Barat', city: 'Bandung' },
|
|
{ country: 'Indonesia', region: 'Jawa Timur', city: 'Surabaya' },
|
|
{ country: 'Indonesia', region: 'Jawa Tengah', city: 'Semarang' },
|
|
{ country: 'Indonesia', region: 'Banten', city: 'Tangerang (CPI Balaraja)' },
|
|
{ country: 'Singapore', region: 'Central Region', city: 'Singapore' },
|
|
{ country: 'United States', region: 'California', city: 'Mountain View' },
|
|
{ country: 'United States', region: 'Virginia', city: 'Richmond' },
|
|
{ country: 'Japan', region: 'Tokyo', city: 'Chiyoda' },
|
|
{ country: 'Australia', region: 'New South Wales', city: 'Sydney' }
|
|
];
|
|
|
|
const selected = geos[index % geos.length];
|
|
return {
|
|
region_name: selected.region,
|
|
country_name: selected.country,
|
|
city_name: selected.city
|
|
};
|
|
}
|
|
|
|
module.exports = router;
|