feat(source2): push all latest files - device labeling, help system, proxy docs, database isolation fix

- Added DOKUMENTASI-FILTER-PER-SITE.md (site isolation docs)
- Fixed start-with-env.js to force-load .env.production
- Fixed MONGODB_URI hostname from mongodb-netify to mongodb.prod.proit.id
- Updated .gitignore to exclude sensitive scripts and credential files
- Minor UI and labeling improvements
This commit is contained in:
rafif committed 2026-07-29 14:14:29 +07:00
1 parent a403f752f3
commit dd4c8f6876
385 files changed
+31016 -8268

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
FROM oven/bun:1-alpine
WORKDIR /app
COPY backend/package*.json ./
RUN bun install --production
COPY backend/ .
EXPOSE 3001
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \
CMD bun -e "require('http').get('http://localhost:3001/api/health', r => r.statusCode === 200 ? process.exit(0) : process.exit(1)).on('error', () => process.exit(1))"
CMD ["bun", "run", "server.js"]
+3
View File
@@ -0,0 +1,3 @@
const db = require('better-sqlite3')('backend/netify_data.db');
console.log('Devices:', db.prepare("SELECT * FROM devices WHERE ip_address = '192.168.9.2'").all());
console.log('Discovery:', db.prepare("SELECT * FROM intel_device_discovery WHERE ip_address = '192.168.9.2'").all());
+22
View File
@@ -0,0 +1,22 @@
const mongoose = require('mongoose');
require('dotenv').config({path: '../.env.local'});
mongoose.connect(process.env.MONGODB_URI).then(async () => {
const db = mongoose.connection;
const highEvents = await db.collection('events').find({
$or: [
{severity: {$in: ['Critical', 'High']}},
{category_label: 'Cybersecurity'}
]
}).toArray();
if (highEvents.length > 0) {
console.log("High Events timestamps:");
highEvents.forEach(e => {
console.log("- event_at:", e.event_at, " | timestamp:", e.timestamp);
});
} else {
console.log("No high events found in array");
}
process.exit(0);
}).catch(e => console.error(e));
+44
View File
@@ -0,0 +1,44 @@
const mongoose = require('mongoose');
mongoose.connect('mongodb://backone_user:SusuKudaLiar@103.80.237.29:27017/backone_dpi?authSource=backone_dpi')
.then(async () => {
const db = mongoose.connection.useDb('backone_dpi');
const yesterday = new Date(Date.now() - 24 * 3600 * 1000);
const SIAB = '6681452d_9cae_4ff4_8ae8_0d504774265e';
const catCount = await db.db.collection('appcategorystats').countDocuments({ site_uuid: SIAB, timestamp: { $gte: yesterday } });
const catSum = await db.db.collection('appcategorystats').aggregate([
{ $match: { site_uuid: SIAB, timestamp: { $gte: yesterday } } },
{ $group: { _id: null, dl: { $sum: '$download' }, ul: { $sum: '$upload' } } }
]).toArray();
const sumCount = await db.db.collection('summaries').countDocuments({ site_uuid: SIAB, timestamp: { $gte: yesterday } });
const sumSum = await db.db.collection('summaries').aggregate([
{ $match: { site_uuid: SIAB, timestamp: { $gte: yesterday } } },
{ $group: { _id: null, dl: { $sum: '$bandwidth_down' }, ul: { $sum: '$bandwidth_up' } } }
]).toArray();
const flowCount = await db.db.collection('flows').countDocuments({ site_uuid: SIAB, timestamp: { $gte: yesterday } });
const flowSum = await db.db.collection('flows').aggregate([
{ $match: { site_uuid: SIAB, timestamp: { $gte: yesterday } } },
{ $group: { _id: null, dl: { $sum: '$download' }, ul: { $sum: '$upload' } } }
]).toArray();
// Check latest timestamp in each collection for SIAB
const latestCat = await db.db.collection('appcategorystats').findOne({ site_uuid: SIAB }, { sort: { timestamp: -1 } });
const latestFlow = await db.db.collection('flows').findOne({ site_uuid: SIAB }, { sort: { timestamp: -1 } });
const latestSum = await db.db.collection('summaries').findOne({ site_uuid: SIAB }, { sort: { timestamp: -1 } });
console.log('=== SIAB Site Data Check (Last 24h) ===');
console.log('AppCatStats (24h):', catCount, 'docs | Sum:', JSON.stringify(catSum[0]));
console.log('Summaries (24h) :', sumCount, 'docs | Sum:', JSON.stringify(sumSum[0]));
console.log('Flows (24h) :', flowCount, 'docs | Sum:', JSON.stringify(flowSum[0]));
console.log('');
console.log('=== Latest Timestamps ===');
console.log('Latest AppCat :', latestCat?.timestamp);
console.log('Latest Flow :', latestFlow?.timestamp);
console.log('Latest Summary :', latestSum?.timestamp);
mongoose.disconnect();
})
.catch(e => { console.error('Error:', e.message); process.exit(1); });
+31
View File
@@ -0,0 +1,31 @@
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
const cmd = [
'export PM2=/home/adminbackend/.npm-global/bin/pm2',
'$PM2 list',
'echo "=== MEMORY ==="',
'free -m',
'echo "=== DISK ==="',
'df -h /',
'echo "=== FRONTEND LOGS ==="',
'$PM2 logs backone-frontend --lines 20 --nostream 2>&1',
'echo "=== BACKEND LOGS ==="',
'$PM2 logs backone-backend --lines 10 --nostream 2>&1',
].join(' && ');
conn.exec(cmd, (err, stream) => {
if (err) { console.error(err); conn.end(); return; }
stream.on('data', d => process.stdout.write(d.toString()));
stream.stderr.on('data', d => process.stderr.write(d.toString()));
stream.on('close', () => conn.end());
});
}).connect({
host: '103.185.47.52',
port: 2222,
username: 'adminbackend',
password: 'htEo7x6LsBQiEHHH',
});
conn.on('error', e => console.error('SSH Error:', e.message));
+15
View File
@@ -0,0 +1,15 @@
const mongoose = require('mongoose');
require('dotenv').config({path: '../.env.local'});
mongoose.connect(process.env.MONGODB_URI).then(async () => {
const db = mongoose.connection;
const threats = await db.collection('threats').countDocuments();
const events = await db.collection('events').countDocuments();
const highEvents = await db.collection('events').countDocuments({
$or: [
{severity: {$in: ['Critical', 'High']}},
{category_label: 'Cybersecurity'}
]
});
console.log({threats, events, highEvents});
process.exit(0);
}).catch(e => console.error(e));
+10
View File
@@ -0,0 +1,10 @@
const mongoose = require('mongoose');
require('dotenv').config({path: '../.env.local'});
mongoose.connect(process.env.MONGODB_URI).then(async () => {
const db = mongoose.connection;
const threats = await db.collection('threats').aggregate([{ $group: { _id: '$threat_type', count: { $sum: 1 } } }]).toArray();
console.log('Threat types:', threats);
const events = await db.collection('events').aggregate([{ $group: { _id: '$event_type', count: { $sum: 1 } } }]).toArray();
console.log('Event types:', events);
process.exit(0);
});
+2146
View File
File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -5,7 +5,8 @@
const mongoose = require('mongoose');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '../../.env.local') });
const envFile = process.env.NODE_ENV === 'production' ? '.env.production' : '.env.local';
require('dotenv').config({ path: path.join(__dirname, '../../', envFile) });
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/backone_dpi';
@@ -22,7 +23,7 @@ async function connectDB() {
serverSelectionTimeoutMS: 10000,
connectTimeoutMS: 10000,
});
console.log('[MongoDB] ✓ Connected successfully');
console.log('[MongoDB] ✓ Connected successfully to', MONGODB_URI);
const { logCapacityStats } = require('./capacityTracker');
logCapacityStats('[MongoDB]').catch(err => console.warn('[MongoDB] Capacity log failed:', err.message));
return;
+400 -400
View File
@@ -1,400 +1,400 @@
/**
* generate_export.js
*
* Mengekspor SELURUH data dari semua tabel SQLite (database)
* ke dalam file backone_data_export.txt
*
* Format output:
* - Header metadata (tanggal, versi, jumlah tabel)
* - Untuk setiap tabel: header section, row count, schema, dan semua data (JSON per baris)
* - Footer summary
*/
const fs = require('fs');
const path = require('path');
const db = require('./database');
const OUTPUT_FILE = path.join(__dirname, '../backone_data_export.txt');
const d = db.getDB();
// ─── Helpers ────────────────────────────────────────────────────────────────
function fmtBytes(bytes) {
if (!bytes || bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let b = Math.abs(bytes);
let i = 0;
while (b >= 1024 && i < units.length - 1) { b /= 1024; i++; }
return b.toFixed(2) + ' ' + units[i];
}
function fmtNum(n) {
if (n == null) return 'N/A';
return Number(n).toLocaleString('id-ID');
}
function separator(char = '═', len = 80) {
return char.repeat(len);
}
function sectionHeader(tableName, rowCount, description) {
return [
'',
separator('═'),
`[TABLE: ${tableName}]`,
`Row Count: ${fmtNum(rowCount)}`,
description ? `Description: ${description}` : '',
separator('─'),
].filter(l => l !== '').join('\n');
}
// ─── Table descriptions ──────────────────────────────────────────────────────
const TABLE_DESCRIPTIONS = {
bandwidth_apps : 'Bandwidth per aplikasi (YouTube, Facebook, dll) dari BackOne DPI',
bandwidth_timeline : 'Timeline bandwidth per menit (download/upload historis)',
bittorrent_info_hashes: 'Deteksi aktivitas BitTorrent berdasarkan info hash',
countries : 'Distribusi traffic berdasarkan negara tujuan',
devices : 'Daftar perangkat (IP/MAC) beserta bandwidth & OS',
dhcp_fingerprints : 'Fingerprint DHCP untuk identifikasi tipe device',
discovered_os : 'OS yang terdeteksi dari traffic scanning',
dns_stats : 'Query DNS teratas dan statistik resolusi domain',
events : 'Event log dari BackOne agent (koneksi, peringatan, dll)',
flows : 'Data aliran jaringan per-sesi (src IP, dst IP, aplikasi, domain, bytes)',
flow_origins : 'Asal flow: lokal (LAN) atau eksternal (WAN)',
flow_types : 'Tipe flow: TCP, UDP, ICMP, dll',
http_user_agents : 'HTTP User-Agent yang terdeteksi (browser, OS, framework)',
intel_crypto_mining : 'Deteksi aktivitas crypto mining (pool host, protokol)',
intel_device_discovery: 'Penemuan perangkat baru di jaringan (tipe, OS, manufaktur)',
intel_encryption_audit: 'Audit enkripsi traffic per perangkat (encrypted%, risk level)',
intel_insecure_protocols: 'Protokol tidak aman yang terdeteksi (HTTP, Telnet, FTP, dll)',
intel_ip_reputation : 'Reputasi IP eksternal (blacklist, threat score)',
intel_server_discovery: 'Server yang terdeteksi (HTTPS, SSH, HTTP, dll)',
intel_tor_detection : 'Deteksi penggunaan jaringan Tor',
intel_unencrypted_passwords: 'Deteksi pengiriman password dalam bentuk plaintext',
intel_vpn_detection : 'Deteksi penggunaan VPN (OpenVPN, WireGuard, dll)',
interfaces : 'Interface jaringan per agent (WAN/LAN, bandwidth)',
ip_versions : 'Distribusi traffic IPv4 vs IPv6',
mac_bandwidth : 'Bandwidth per MAC address perangkat',
mdns_hostnames : 'mDNS hostname yang terdeteksi di jaringan lokal',
netbios_hostnames : 'NetBIOS hostname (nama komputer Windows)',
protocols : 'Distribusi protokol jaringan (port usage)',
quic_hostnames : 'Hostname via QUIC/HTTP3 (Google, Cloudflare, dll)',
regions : 'Distribusi traffic berdasarkan region/kota tujuan',
remote_ips : 'IP remote teratas yang diakses perangkat',
sni_hostnames : 'Server Name Indication dari koneksi TLS',
ssh_versions : 'Versi SSH yang terdeteksi di jaringan',
ssl_server_cn : 'Common Name sertifikat SSL server',
threats : 'Ancaman keamanan terdeteksi (threat alerts)',
tls_ciphers : 'Cipher suite TLS yang digunakan',
tls_security : 'Tingkat keamanan TLS (Modern, Compatible, Old)',
tls_versions : 'Versi TLS yang digunakan (1.0, 1.2, 1.3)',
vlans : 'VLAN yang terdeteksi di jaringan',
};
// ─── Main Export Logic ───────────────────────────────────────────────────────
async function main() {
console.log('🚀 Memulai export data...');
const exportDate = new Date().toISOString();
const lines = [];
// ── File Header ──────────────────────────────────────────────────────────
lines.push(separator('═'));
lines.push(' BACKONE DATA EXPORT');
lines.push(' Seluruh data hasil parsing dari BackOne API');
lines.push(separator('─'));
lines.push(` Export Date: ${exportDate}`);
lines.push(` Generated by: generate_export.js`);
lines.push(` Source: database (SQLite lokal)`);
lines.push(` API Base: BackOne API Service`);
lines.push(` Format: Per-tabel, data JSON satu record per baris (JSONL)`);
lines.push(separator('─'));
// ── Get all tables ────────────────────────────────────────────────────────
const tables = d.prepare(
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name"
).all().map(r => r.name);
lines.push(` Total Tables: ${tables.length}`);
lines.push(separator('═'));
lines.push('');
// ── Table of Contents ─────────────────────────────────────────────────────
lines.push('TABLE OF CONTENTS');
lines.push(separator('─', 40));
let totalRows = 0;
const tableSummaries = [];
for (const tableName of tables) {
const cnt = d.prepare(`SELECT COUNT(*) as c FROM ${tableName}`).get().c;
totalRows += cnt;
const desc = TABLE_DESCRIPTIONS[tableName] || '-';
lines.push(` ${tableName.padEnd(35)} ${String(cnt).padStart(8)} rows`);
tableSummaries.push({ name: tableName, count: cnt, description: desc });
}
lines.push(separator('─', 40));
lines.push(` ${'TOTAL'.padEnd(35)} ${String(totalRows).padStart(8)} rows`);
lines.push('');
// ── Per-Table Export ──────────────────────────────────────────────────────
for (const { name: tableName, count, description } of tableSummaries) {
console.log(` 📋 Exporting: ${tableName} (${fmtNum(count)} rows)...`);
// Section header
lines.push(sectionHeader(tableName, count, description));
// Schema
const cols = d.prepare(`PRAGMA table_info(${tableName})`).all();
lines.push('Schema:');
cols.forEach(c => {
lines.push(` - ${c.name} [${c.type || 'TEXT'}]${c.notnull ? ' NOT NULL' : ''}${c.pk ? ' PRIMARY KEY' : ''}`);
});
lines.push('');
// Statistics for numeric columns
const numericCols = cols.filter(c =>
['INTEGER', 'REAL', 'NUMERIC'].includes((c.type || '').toUpperCase()) &&
!['id'].includes(c.name.toLowerCase())
);
if (count > 0 && numericCols.length > 0) {
lines.push('Statistics:');
for (const col of numericCols.slice(0, 5)) { // max 5 numeric cols
try {
const stat = d.prepare(`
SELECT MIN(${col.name}) as min, MAX(${col.name}) as max,
AVG(${col.name}) as avg, SUM(${col.name}) as total
FROM ${tableName}
`).get();
if (stat && stat.max !== null) {
lines.push(` ${col.name}: min=${fmtNum(stat.min)} max=${fmtNum(stat.max)} avg=${Number(stat.avg || 0).toFixed(2)} total=${fmtNum(stat.total)}`);
}
} catch(e) { /* skip */ }
}
lines.push('');
}
// Data rows (ALL rows)
if (count === 0) {
lines.push('(No data)');
} else {
lines.push(`Data (${fmtNum(count)} records):`);
const rows = d.prepare(`SELECT * FROM ${tableName}`).all();
for (const row of rows) {
lines.push(JSON.stringify(row));
}
}
lines.push('');
}
// ── Agent-specific sections (derived from flows) ───────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: AGENT ANALYSIS]');
lines.push('Description: Analisis traffic per agent berdasarkan MAC address dari flows table');
lines.push(separator('─'));
const AGENT_MAC_MAP = {
'2F-TF-1D-GK': { label: 'JRP Cibubur', macs: ['60:be:b4:1f:05:96'] },
'8A-V3-PB-85': { label: 'IFG LT.18', macs: ['04:f4:1c:ce:c2:e6'] },
'F6-2V-DT-8A': { label: 'CPI Balaraja', macs: ['2c:7b:a0:d8:86:91', '16:11:ac:73:34:1d', 'bc:45:5b:ca:d5:be', 'de:ed:cc:57:58:34', 'f4:6d:3f:ef:01:a0', '60:be:b4:29:d3:36'] },
};
for (const [uuid, agent] of Object.entries(AGENT_MAC_MAP)) {
lines.push('');
lines.push(`Agent: ${agent.label} (${uuid})`);
lines.push(`MACs: ${agent.macs.join(', ')}`);
lines.push(separator('─', 40));
const ph = agent.macs.map(() => '?').join(',');
// Summary
const sumRow = d.prepare(`
SELECT COUNT(DISTINCT src_ip) AS device_count, COUNT(*) AS flow_count,
SUM(bytes_download) AS total_dl, SUM(bytes_upload) AS total_ul
FROM flows WHERE src_mac IN (${ph})
`).get(...agent.macs);
lines.push(` Devices: ${fmtNum(sumRow.device_count)}`);
lines.push(` Total Flows: ${fmtNum(sumRow.flow_count)}`);
lines.push(` Total Download: ${fmtBytes(sumRow.total_dl)}`);
lines.push(` Total Upload: ${fmtBytes(sumRow.total_ul)}`);
// Top apps
const apps = d.prepare(`
SELECT app_label, SUM(bytes_download) AS dl, SUM(bytes_upload) AS ul, COUNT(*) AS cnt
FROM flows WHERE src_mac IN (${ph}) AND app_label IS NOT NULL
GROUP BY app_label ORDER BY dl DESC LIMIT 10
`).all(...agent.macs);
lines.push(` Top Applications:`);
apps.forEach((a, i) => {
lines.push(` ${String(i+1).padStart(2)}. ${(a.app_label||'?').padEnd(30)} DL:${fmtBytes(a.dl).padStart(12)} UL:${fmtBytes(a.ul).padStart(12)} Flows:${a.cnt}`);
});
// Top devices
const devs = d.prepare(`
SELECT src_ip, SUM(bytes_download) AS dl, MAX(last_seen) AS last
FROM flows WHERE src_mac IN (${ph})
GROUP BY src_ip ORDER BY dl DESC LIMIT 10
`).all(...agent.macs);
lines.push(` Top Devices:`);
devs.forEach((d2, i) => {
lines.push(` ${String(i+1).padStart(2)}. ${(d2.src_ip||'?').padEnd(20)} DL:${fmtBytes(d2.dl).padStart(12)} Last:${d2.last||'-'}`);
});
}
// ── Bandwidth Apps Summary ─────────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: BANDWIDTH APPS LATEST SNAPSHOT]');
lines.push('Description: Snapshot terakhir bandwidth per aplikasi (nilai aktual, bukan akumulasi)');
lines.push(separator('─'));
const latestBwSnap = d.prepare('SELECT MAX(fetched_at) as t FROM bandwidth_apps').get()?.t;
if (latestBwSnap) {
lines.push(`Latest Snapshot: ${latestBwSnap}`);
const bwApps = d.prepare('SELECT app_label, category, download, upload, total, flow_count FROM bandwidth_apps WHERE fetched_at = ? ORDER BY download DESC').all(latestBwSnap);
lines.push(`Total Apps: ${bwApps.length}`);
lines.push('');
bwApps.forEach((a, i) => {
lines.push(` ${String(i+1).padStart(3)}. ${(a.app_label||'?').padEnd(30)} [${(a.category||'?').padEnd(20)}] DL:${fmtBytes(a.download).padStart(12)} UL:${fmtBytes(a.upload).padStart(12)} Flows:${fmtNum(a.flow_count)}`);
});
}
// ── Encryption Audit Summary ───────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: ENCRYPTION RISK SUMMARY]');
lines.push('Description: Distribusi risk level enkripsi per perangkat (snapshot terbaru)');
lines.push(separator('─'));
const latestEncSnap = d.prepare('SELECT MAX(fetched_at) as t FROM intel_encryption_audit').get()?.t;
if (latestEncSnap) {
const riskDist = d.prepare(`
SELECT risk_level, COUNT(*) as cnt, AVG(encrypted_pct) as avg_enc
FROM intel_encryption_audit WHERE fetched_at = ?
GROUP BY risk_level ORDER BY cnt DESC
`).all(latestEncSnap);
lines.push(`Latest Snapshot: ${latestEncSnap}`);
lines.push('Risk Distribution:');
riskDist.forEach(r => {
lines.push(` ${(r.risk_level||'Unknown').padEnd(15)} ${String(r.cnt).padStart(5)} devices avg encrypted: ${Number(r.avg_enc||0).toFixed(1)}%`);
});
// Highest risk devices
lines.push('');
lines.push('Critical Risk Devices (0% encrypted):');
const critDevs = d.prepare(`
SELECT ip_address, mac_address, device_label, encrypted_pct, total
FROM intel_encryption_audit WHERE fetched_at = ? AND risk_level = 'Critical'
ORDER BY total DESC LIMIT 20
`).all(latestEncSnap);
critDevs.forEach(r => {
lines.push(JSON.stringify(r));
});
}
// ── DNS Top Domains ────────────────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: TOP DNS DOMAINS]');
lines.push('Description: Domain paling sering diquery dari DNS stats');
lines.push(separator('─'));
const latestDnsSnap = d.prepare('SELECT MAX(fetched_at) as t FROM dns_queries').get()?.t;
if (latestDnsSnap) {
const dnsRows = d.prepare('SELECT * FROM dns_queries WHERE fetched_at = ? ORDER BY query_count DESC LIMIT 30').all(latestDnsSnap);
lines.push(`Latest Snapshot: ${latestDnsSnap}`);
dnsRows.forEach(r => lines.push(JSON.stringify(r)));
}
// ── IP Reputation Blacklisted ─────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: BLACKLISTED IP ADDRESSES]');
lines.push('Description: IP address yang terdeteksi blacklisted (dari intel_ip_reputation)');
lines.push(separator('─'));
const latestRepSnap = d.prepare('SELECT MAX(fetched_at) as t FROM intel_ip_reputation').get()?.t;
if (latestRepSnap) {
const blacklisted = d.prepare('SELECT * FROM intel_ip_reputation WHERE fetched_at = ? AND blacklisted = 1').all(latestRepSnap);
lines.push(`Latest Snapshot: ${latestRepSnap}`);
lines.push(`Blacklisted count: ${blacklisted.length}`);
blacklisted.forEach(r => lines.push(JSON.stringify(r)));
}
// ── Flows: Active Sessions Summary ────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: ACTIVE FLOWS SUMMARY]');
lines.push('Description: Ringkasan aliran jaringan aktif (50 terbaru per download)');
lines.push(separator('─'));
const flowSummary = d.prepare(`
SELECT COUNT(*) as total_flows,
COUNT(DISTINCT src_ip) as unique_src_ips,
COUNT(DISTINCT dst_ip) as unique_dst_ips,
COUNT(DISTINCT src_mac) as unique_macs,
SUM(bytes_download) as total_dl,
SUM(bytes_upload) as total_ul,
MIN(first_seen) as earliest,
MAX(last_seen) as latest
FROM flows
`).get();
lines.push(`Total Flows in DB: ${fmtNum(flowSummary.total_flows)}`);
lines.push(`Unique Source IPs: ${fmtNum(flowSummary.unique_src_ips)}`);
lines.push(`Unique Dest IPs: ${fmtNum(flowSummary.unique_dst_ips)}`);
lines.push(`Unique MAC Addresses: ${fmtNum(flowSummary.unique_macs)}`);
lines.push(`Total Download: ${fmtBytes(flowSummary.total_dl)}`);
lines.push(`Total Upload: ${fmtBytes(flowSummary.total_ul)}`);
lines.push(`Data from: ${flowSummary.earliest}`);
lines.push(`Data to: ${flowSummary.latest}`);
lines.push('');
// Top 50 flows by download
lines.push('Top 50 Flows by Download:');
const topFlows = d.prepare('SELECT * FROM flows ORDER BY bytes_download DESC LIMIT 50').all();
topFlows.forEach(r => lines.push(JSON.stringify(r)));
// ── Unencrypted Password Events ───────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: UNENCRYPTED PASSWORD DETECTIONS]');
lines.push('Description: Kejadian pengiriman credential dalam plaintext (HIGH severity)');
lines.push(separator('─'));
const unencPwdHigh = d.prepare(`
SELECT ip_address, mac_address, dst_ip, dst_port, protocol, username, download, upload, severity, detected_at
FROM intel_unencrypted_passwords ORDER BY detected_at DESC
`).all();
lines.push(`Total detections: ${unencPwdHigh.length}`);
unencPwdHigh.forEach(r => lines.push(JSON.stringify(r)));
// ── Footer ────────────────────────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push(' END OF EXPORT');
lines.push(` Generated at: ${new Date().toISOString()}`);
lines.push(` Total lines: ${lines.length + 3}`);
lines.push(separator('═'));
// Write to file
const output = lines.join('\n');
fs.writeFileSync(OUTPUT_FILE, output, 'utf-8');
const stats = fs.statSync(OUTPUT_FILE);
console.log(`\n✅ Export selesai!`);
console.log(` File: ${OUTPUT_FILE}`);
console.log(` Size: ${fmtBytes(stats.size)}`);
console.log(` Lines: ${fmtNum(lines.length)}`);
console.log(` Tables: ${tables.length}`);
console.log(` Total Rows: ${fmtNum(totalRows)}`);
}
main().catch(e => {
console.error('❌ Export FAILED:', e);
process.exit(1);
});
/**
* generate_export.js
*
* Mengekspor SELURUH data dari semua tabel SQLite (database)
* ke dalam file backone_data_export.txt
*
* Format output:
* - Header metadata (tanggal, versi, jumlah tabel)
* - Untuk setiap tabel: header section, row count, schema, dan semua data (JSON per baris)
* - Footer summary
*/
const fs = require('fs');
const path = require('path');
const db = require('./database');
const OUTPUT_FILE = path.join(__dirname, '../backone_data_export.txt');
const d = db.getDB();
// ─── Helpers ────────────────────────────────────────────────────────────────
function fmtBytes(bytes) {
if (!bytes || bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let b = Math.abs(bytes);
let i = 0;
while (b >= 1024 && i < units.length - 1) { b /= 1024; i++; }
return b.toFixed(2) + ' ' + units[i];
}
function fmtNum(n) {
if (n == null) return 'N/A';
return Number(n).toLocaleString('id-ID');
}
function separator(char = '═', len = 80) {
return char.repeat(len);
}
function sectionHeader(tableName, rowCount, description) {
return [
'',
separator('═'),
`[TABLE: ${tableName}]`,
`Row Count: ${fmtNum(rowCount)}`,
description ? `Description: ${description}` : '',
separator('─'),
].filter(l => l !== '').join('\n');
}
// ─── Table descriptions ──────────────────────────────────────────────────────
const TABLE_DESCRIPTIONS = {
bandwidth_apps : 'Bandwidth per aplikasi (YouTube, Facebook, dll) dari BackOne DPI',
bandwidth_timeline : 'Timeline bandwidth per menit (download/upload historis)',
bittorrent_info_hashes: 'Deteksi aktivitas BitTorrent berdasarkan info hash',
countries : 'Distribusi traffic berdasarkan negara tujuan',
devices : 'Daftar perangkat (IP/MAC) beserta bandwidth & OS',
dhcp_fingerprints : 'Fingerprint DHCP untuk identifikasi tipe device',
discovered_os : 'OS yang terdeteksi dari traffic scanning',
dns_stats : 'Query DNS teratas dan statistik resolusi domain',
events : 'Event log dari BackOne agent (koneksi, peringatan, dll)',
flows : 'Data aliran jaringan per-sesi (src IP, dst IP, aplikasi, domain, bytes)',
flow_origins : 'Asal flow: lokal (LAN) atau eksternal (WAN)',
flow_types : 'Tipe flow: TCP, UDP, ICMP, dll',
http_user_agents : 'HTTP User-Agent yang terdeteksi (browser, OS, framework)',
intel_crypto_mining : 'Deteksi aktivitas crypto mining (pool host, protokol)',
intel_device_discovery: 'Penemuan perangkat baru di jaringan (tipe, OS, manufaktur)',
intel_encryption_audit: 'Audit enkripsi traffic per perangkat (encrypted%, risk level)',
intel_insecure_protocols: 'Protokol tidak aman yang terdeteksi (HTTP, Telnet, FTP, dll)',
intel_ip_reputation : 'Reputasi IP eksternal (blacklist, threat score)',
intel_server_discovery: 'Server yang terdeteksi (HTTPS, SSH, HTTP, dll)',
intel_tor_detection : 'Deteksi penggunaan jaringan Tor',
intel_unencrypted_passwords: 'Deteksi pengiriman password dalam bentuk plaintext',
intel_vpn_detection : 'Deteksi penggunaan VPN (OpenVPN, WireGuard, dll)',
interfaces : 'Interface jaringan per agent (WAN/LAN, bandwidth)',
ip_versions : 'Distribusi traffic IPv4 vs IPv6',
mac_bandwidth : 'Bandwidth per MAC address perangkat',
mdns_hostnames : 'mDNS hostname yang terdeteksi di jaringan lokal',
netbios_hostnames : 'NetBIOS hostname (nama komputer Windows)',
protocols : 'Distribusi protokol jaringan (port usage)',
quic_hostnames : 'Hostname via QUIC/HTTP3 (Google, Cloudflare, dll)',
regions : 'Distribusi traffic berdasarkan region/kota tujuan',
remote_ips : 'IP remote teratas yang diakses perangkat',
sni_hostnames : 'Server Name Indication dari koneksi TLS',
ssh_versions : 'Versi SSH yang terdeteksi di jaringan',
ssl_server_cn : 'Common Name sertifikat SSL server',
threats : 'Ancaman keamanan terdeteksi (threat alerts)',
tls_ciphers : 'Cipher suite TLS yang digunakan',
tls_security : 'Tingkat keamanan TLS (Modern, Compatible, Old)',
tls_versions : 'Versi TLS yang digunakan (1.0, 1.2, 1.3)',
vlans : 'VLAN yang terdeteksi di jaringan',
};
// ─── Main Export Logic ───────────────────────────────────────────────────────
async function main() {
console.log('🚀 Memulai export data...');
const exportDate = new Date().toISOString();
const lines = [];
// ── File Header ──────────────────────────────────────────────────────────
lines.push(separator('═'));
lines.push(' BACKONE DATA EXPORT');
lines.push(' Seluruh data hasil parsing dari BackOne API');
lines.push(separator('─'));
lines.push(` Export Date: ${exportDate}`);
lines.push(` Generated by: generate_export.js`);
lines.push(` Source: database (SQLite lokal)`);
lines.push(` API Base: BackOne API Service`);
lines.push(` Format: Per-tabel, data JSON satu record per baris (JSONL)`);
lines.push(separator('─'));
// ── Get all tables ────────────────────────────────────────────────────────
const tables = d.prepare(
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name"
).all().map(r => r.name);
lines.push(` Total Tables: ${tables.length}`);
lines.push(separator('═'));
lines.push('');
// ── Table of Contents ─────────────────────────────────────────────────────
lines.push('TABLE OF CONTENTS');
lines.push(separator('─', 40));
let totalRows = 0;
const tableSummaries = [];
for (const tableName of tables) {
const cnt = d.prepare(`SELECT COUNT(*) as c FROM ${tableName}`).get().c;
totalRows += cnt;
const desc = TABLE_DESCRIPTIONS[tableName] || '-';
lines.push(` ${tableName.padEnd(35)} ${String(cnt).padStart(8)} rows`);
tableSummaries.push({ name: tableName, count: cnt, description: desc });
}
lines.push(separator('─', 40));
lines.push(` ${'TOTAL'.padEnd(35)} ${String(totalRows).padStart(8)} rows`);
lines.push('');
// ── Per-Table Export ──────────────────────────────────────────────────────
for (const { name: tableName, count, description } of tableSummaries) {
console.log(` 📋 Exporting: ${tableName} (${fmtNum(count)} rows)...`);
// Section header
lines.push(sectionHeader(tableName, count, description));
// Schema
const cols = d.prepare(`PRAGMA table_info(${tableName})`).all();
lines.push('Schema:');
cols.forEach(c => {
lines.push(` - ${c.name} [${c.type || 'TEXT'}]${c.notnull ? ' NOT NULL' : ''}${c.pk ? ' PRIMARY KEY' : ''}`);
});
lines.push('');
// Statistics for numeric columns
const numericCols = cols.filter(c =>
['INTEGER', 'REAL', 'NUMERIC'].includes((c.type || '').toUpperCase()) &&
!['id'].includes(c.name.toLowerCase())
);
if (count > 0 && numericCols.length > 0) {
lines.push('Statistics:');
for (const col of numericCols.slice(0, 5)) { // max 5 numeric cols
try {
const stat = d.prepare(`
SELECT MIN(${col.name}) as min, MAX(${col.name}) as max,
AVG(${col.name}) as avg, SUM(${col.name}) as total
FROM ${tableName}
`).get();
if (stat && stat.max !== null) {
lines.push(` ${col.name}: min=${fmtNum(stat.min)} max=${fmtNum(stat.max)} avg=${Number(stat.avg || 0).toFixed(2)} total=${fmtNum(stat.total)}`);
}
} catch(e) { /* skip */ }
}
lines.push('');
}
// Data rows (ALL rows)
if (count === 0) {
lines.push('(No data)');
} else {
lines.push(`Data (${fmtNum(count)} records):`);
const rows = d.prepare(`SELECT * FROM ${tableName}`).all();
for (const row of rows) {
lines.push(JSON.stringify(row));
}
}
lines.push('');
}
// ── Agent-specific sections (derived from flows) ───────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: AGENT ANALYSIS]');
lines.push('Description: Analisis traffic per agent berdasarkan MAC address dari flows table');
lines.push(separator('─'));
const AGENT_MAC_MAP = {
'2F-TF-1D-GK': { label: 'JRP Cibubur', macs: ['60:be:b4:1f:05:96'] },
'8A-V3-PB-85': { label: 'IFG LT.18', macs: ['04:f4:1c:ce:c2:e6'] },
'F6-2V-DT-8A': { label: 'CPI Balaraja', macs: ['2c:7b:a0:d8:86:91', '16:11:ac:73:34:1d', 'bc:45:5b:ca:d5:be', 'de:ed:cc:57:58:34', 'f4:6d:3f:ef:01:a0', '60:be:b4:29:d3:36'] },
};
for (const [uuid, agent] of Object.entries(AGENT_MAC_MAP)) {
lines.push('');
lines.push(`Agent: ${agent.label} (${uuid})`);
lines.push(`MACs: ${agent.macs.join(', ')}`);
lines.push(separator('─', 40));
const ph = agent.macs.map(() => '?').join(',');
// Summary
const sumRow = d.prepare(`
SELECT COUNT(DISTINCT src_ip) AS device_count, COUNT(*) AS flow_count,
SUM(bytes_download) AS total_dl, SUM(bytes_upload) AS total_ul
FROM flows WHERE src_mac IN (${ph})
`).get(...agent.macs);
lines.push(` Devices: ${fmtNum(sumRow.device_count)}`);
lines.push(` Total Flows: ${fmtNum(sumRow.flow_count)}`);
lines.push(` Total Download: ${fmtBytes(sumRow.total_dl)}`);
lines.push(` Total Upload: ${fmtBytes(sumRow.total_ul)}`);
// Top apps
const apps = d.prepare(`
SELECT app_label, SUM(bytes_download) AS dl, SUM(bytes_upload) AS ul, COUNT(*) AS cnt
FROM flows WHERE src_mac IN (${ph}) AND app_label IS NOT NULL
GROUP BY app_label ORDER BY dl DESC LIMIT 10
`).all(...agent.macs);
lines.push(` Top Applications:`);
apps.forEach((a, i) => {
lines.push(` ${String(i+1).padStart(2)}. ${(a.app_label||'?').padEnd(30)} DL:${fmtBytes(a.dl).padStart(12)} UL:${fmtBytes(a.ul).padStart(12)} Flows:${a.cnt}`);
});
// Top devices
const devs = d.prepare(`
SELECT src_ip, SUM(bytes_download) AS dl, MAX(last_seen) AS last
FROM flows WHERE src_mac IN (${ph})
GROUP BY src_ip ORDER BY dl DESC LIMIT 10
`).all(...agent.macs);
lines.push(` Top Devices:`);
devs.forEach((d2, i) => {
lines.push(` ${String(i+1).padStart(2)}. ${(d2.src_ip||'?').padEnd(20)} DL:${fmtBytes(d2.dl).padStart(12)} Last:${d2.last||'-'}`);
});
}
// ── Bandwidth Apps Summary ─────────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: BANDWIDTH APPS LATEST SNAPSHOT]');
lines.push('Description: Snapshot terakhir bandwidth per aplikasi (nilai aktual, bukan akumulasi)');
lines.push(separator('─'));
const latestBwSnap = d.prepare('SELECT MAX(fetched_at) as t FROM bandwidth_apps').get()?.t;
if (latestBwSnap) {
lines.push(`Latest Snapshot: ${latestBwSnap}`);
const bwApps = d.prepare('SELECT app_label, category, download, upload, total, flow_count FROM bandwidth_apps WHERE fetched_at = ? ORDER BY download DESC').all(latestBwSnap);
lines.push(`Total Apps: ${bwApps.length}`);
lines.push('');
bwApps.forEach((a, i) => {
lines.push(` ${String(i+1).padStart(3)}. ${(a.app_label||'?').padEnd(30)} [${(a.category||'?').padEnd(20)}] DL:${fmtBytes(a.download).padStart(12)} UL:${fmtBytes(a.upload).padStart(12)} Flows:${fmtNum(a.flow_count)}`);
});
}
// ── Encryption Audit Summary ───────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: ENCRYPTION RISK SUMMARY]');
lines.push('Description: Distribusi risk level enkripsi per perangkat (snapshot terbaru)');
lines.push(separator('─'));
const latestEncSnap = d.prepare('SELECT MAX(fetched_at) as t FROM intel_encryption_audit').get()?.t;
if (latestEncSnap) {
const riskDist = d.prepare(`
SELECT risk_level, COUNT(*) as cnt, AVG(encrypted_pct) as avg_enc
FROM intel_encryption_audit WHERE fetched_at = ?
GROUP BY risk_level ORDER BY cnt DESC
`).all(latestEncSnap);
lines.push(`Latest Snapshot: ${latestEncSnap}`);
lines.push('Risk Distribution:');
riskDist.forEach(r => {
lines.push(` ${(r.risk_level||'Unknown').padEnd(15)} ${String(r.cnt).padStart(5)} devices avg encrypted: ${Number(r.avg_enc||0).toFixed(1)}%`);
});
// Highest risk devices
lines.push('');
lines.push('Critical Risk Devices (0% encrypted):');
const critDevs = d.prepare(`
SELECT ip_address, mac_address, device_label, encrypted_pct, total
FROM intel_encryption_audit WHERE fetched_at = ? AND risk_level = 'Critical'
ORDER BY total DESC LIMIT 20
`).all(latestEncSnap);
critDevs.forEach(r => {
lines.push(JSON.stringify(r));
});
}
// ── DNS Top Domains ────────────────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: TOP DNS DOMAINS]');
lines.push('Description: Domain paling sering diquery dari DNS stats');
lines.push(separator('─'));
const latestDnsSnap = d.prepare('SELECT MAX(fetched_at) as t FROM dns_queries').get()?.t;
if (latestDnsSnap) {
const dnsRows = d.prepare('SELECT * FROM dns_queries WHERE fetched_at = ? ORDER BY query_count DESC LIMIT 30').all(latestDnsSnap);
lines.push(`Latest Snapshot: ${latestDnsSnap}`);
dnsRows.forEach(r => lines.push(JSON.stringify(r)));
}
// ── IP Reputation Blacklisted ─────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: BLACKLISTED IP ADDRESSES]');
lines.push('Description: IP address yang terdeteksi blacklisted (dari intel_ip_reputation)');
lines.push(separator('─'));
const latestRepSnap = d.prepare('SELECT MAX(fetched_at) as t FROM intel_ip_reputation').get()?.t;
if (latestRepSnap) {
const blacklisted = d.prepare('SELECT * FROM intel_ip_reputation WHERE fetched_at = ? AND blacklisted = 1').all(latestRepSnap);
lines.push(`Latest Snapshot: ${latestRepSnap}`);
lines.push(`Blacklisted count: ${blacklisted.length}`);
blacklisted.forEach(r => lines.push(JSON.stringify(r)));
}
// ── Flows: Active Sessions Summary ────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: ACTIVE FLOWS SUMMARY]');
lines.push('Description: Ringkasan aliran jaringan aktif (50 terbaru per download)');
lines.push(separator('─'));
const flowSummary = d.prepare(`
SELECT COUNT(*) as total_flows,
COUNT(DISTINCT src_ip) as unique_src_ips,
COUNT(DISTINCT dst_ip) as unique_dst_ips,
COUNT(DISTINCT src_mac) as unique_macs,
SUM(bytes_download) as total_dl,
SUM(bytes_upload) as total_ul,
MIN(first_seen) as earliest,
MAX(last_seen) as latest
FROM flows
`).get();
lines.push(`Total Flows in DB: ${fmtNum(flowSummary.total_flows)}`);
lines.push(`Unique Source IPs: ${fmtNum(flowSummary.unique_src_ips)}`);
lines.push(`Unique Dest IPs: ${fmtNum(flowSummary.unique_dst_ips)}`);
lines.push(`Unique MAC Addresses: ${fmtNum(flowSummary.unique_macs)}`);
lines.push(`Total Download: ${fmtBytes(flowSummary.total_dl)}`);
lines.push(`Total Upload: ${fmtBytes(flowSummary.total_ul)}`);
lines.push(`Data from: ${flowSummary.earliest}`);
lines.push(`Data to: ${flowSummary.latest}`);
lines.push('');
// Top 50 flows by download
lines.push('Top 50 Flows by Download:');
const topFlows = d.prepare('SELECT * FROM flows ORDER BY bytes_download DESC LIMIT 50').all();
topFlows.forEach(r => lines.push(JSON.stringify(r)));
// ── Unencrypted Password Events ───────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push('[DERIVED: UNENCRYPTED PASSWORD DETECTIONS]');
lines.push('Description: Kejadian pengiriman credential dalam plaintext (HIGH severity)');
lines.push(separator('─'));
const unencPwdHigh = d.prepare(`
SELECT ip_address, mac_address, dst_ip, dst_port, protocol, username, download, upload, severity, detected_at
FROM intel_unencrypted_passwords ORDER BY detected_at DESC
`).all();
lines.push(`Total detections: ${unencPwdHigh.length}`);
unencPwdHigh.forEach(r => lines.push(JSON.stringify(r)));
// ── Footer ────────────────────────────────────────────────────────────────
lines.push('');
lines.push(separator('═'));
lines.push(' END OF EXPORT');
lines.push(` Generated at: ${new Date().toISOString()}`);
lines.push(` Total lines: ${lines.length + 3}`);
lines.push(separator('═'));
// Write to file
const output = lines.join('\n');
fs.writeFileSync(OUTPUT_FILE, output, 'utf-8');
const stats = fs.statSync(OUTPUT_FILE);
console.log(`\n✅ Export selesai!`);
console.log(` File: ${OUTPUT_FILE}`);
console.log(` Size: ${fmtBytes(stats.size)}`);
console.log(` Lines: ${fmtNum(lines.length)}`);
console.log(` Tables: ${tables.length}`);
console.log(` Total Rows: ${fmtNum(totalRows)}`);
}
main().catch(e => {
console.error('❌ Export FAILED:', e);
process.exit(1);
});
+47 -6
View File
@@ -1,5 +1,6 @@
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const Session = require('../models/Session');
const { Summary } = require('../models/Schemas');
const JWT_SECRET = process.env.JWT_SECRET || 'super-secret-backone-key';
@@ -11,19 +12,46 @@ async function requireAuth(req, res, next) {
try {
req.user = jwt.verify(token, JWT_SECRET);
// Verify session status in MongoDB
if (req.user.session_id) {
const activeSession = await Session.findById(req.user.session_id);
if (!activeSession) {
res.clearCookie('token');
return res.status(401).json({ error: 'Sesi login telah dinonaktifkan atau kedaluwarsa.' });
}
// Update last active
activeSession.last_active = new Date();
await activeSession.save();
}
// ── VIEW-AS MODE ──────────────────────────────────────────────────────────
const viewAsHeader = req.headers['x-view-as-agent'];
if (viewAsHeader && (req.user.role === 'SUPER_ADMIN' || req.user.role === 'TENANT_ADMIN')) {
const isAllowedViewAs = req.user.role === 'SUPER_ADMIN' ||
req.user.role === 'TENANT_ADMIN' ||
req.user.role === 'COMPANY_ADMIN' ||
req.user.role === 'COMPANY_OPERATOR';
if (viewAsHeader && isAllowedViewAs) {
try {
const viewDecoded = jwt.verify(viewAsHeader, JWT_SECRET);
if (viewDecoded.type === 'view-as' && viewDecoded.adminId === req.user.id && viewDecoded.viewAs) {
const targetAgentUser = await User.findOne({ agent_uuid: viewDecoded.viewAs, role: 'AGENT_VIEWER' }).lean();
const targetAgent = viewDecoded.viewAs;
// Validation: COMPANY_ADMIN and COMPANY_OPERATOR can only view-as their assigned agents
if (['COMPANY_ADMIN', 'COMPANY_OPERATOR'].includes(req.user.role)) {
const hasAccess = req.user.agent_uuids && req.user.agent_uuids.includes(targetAgent);
if (!hasAccess) {
throw new Error('Unauthorized view-as agent access');
}
}
const targetAgentUser = await User.findOne({ agent_uuid: targetAgent, role: 'AGENT_VIEWER' }).lean();
let targetSiteUuid = req.user.site_uuid;
if (targetAgentUser && targetAgentUser.site_uuid) {
targetSiteUuid = targetAgentUser.site_uuid;
} else {
const summaryDoc = await Summary.findOne({ agent_uuid: viewDecoded.viewAs }).lean();
const summaryDoc = await Summary.findOne({ agent_uuid: targetAgent }).lean();
if (summaryDoc && summaryDoc.site_uuid) {
targetSiteUuid = summaryDoc.site_uuid;
}
@@ -32,7 +60,7 @@ async function requireAuth(req, res, next) {
req.user = {
...req.user,
role: 'AGENT_VIEWER',
agent_uuid: viewDecoded.viewAs,
agent_uuid: targetAgent,
agent_label: viewDecoded.viewAsLabel,
site_uuid: targetSiteUuid,
_viewAsMode: true,
@@ -50,12 +78,25 @@ async function requireAuth(req, res, next) {
}
}
function requireAdmin(req, res, next) {
async function requireAdmin(req, res, next) {
const token = req.cookies?.token;
if (!token) return res.status(401).json({ error: 'Not authenticated' });
try {
const decoded = jwt.verify(token, JWT_SECRET);
if (decoded.role !== 'SUPER_ADMIN' && decoded.role !== 'TENANT_ADMIN' && decoded.role !== 'SOC_ANALYST') {
// Verify session status in MongoDB
if (decoded.session_id) {
const activeSession = await Session.findById(decoded.session_id);
if (!activeSession) {
res.clearCookie('token');
return res.status(401).json({ error: 'Sesi login telah dinonaktifkan atau kedaluwarsa.' });
}
activeSession.last_active = new Date();
await activeSession.save();
}
const validAdminRoles = ['SUPER_ADMIN', 'COMPANY_ADMIN', 'COMPANY_OPERATOR', 'TENANT_ADMIN', 'SOC_ANALYST'];
if (!validAdminRoles.includes(decoded.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
req.adminUser = decoded;
+187 -184
View File
@@ -1,184 +1,187 @@
// backend/models/Schemas.js
// ─────────────────────────────────────────────────────────────────────────────
// MongoDB Schemas untuk BackOne Backend (READ-ONLY)
//
// PENTING: Schema ini harus sinkron dengan proxy/models/Schemas.js
// Proxy yang MENULIS data, backend yang MEMBACA data.
//
// Setiap dokumen di-tag dengan:
// agent_uuid → identifikasi Network Agent spesifik (isolasi per tenant)
// site_uuid → identifikasi site DPI (BackOne)
// timestamp → waktu data dikumpulkan
// ─────────────────────────────────────────────────────────────────────────────
const mongoose = require('mongoose');
const baseOptions = {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
};
// ─── Bandwidth Summary (per agent, per collection cycle) ───────────────────────
const SummarySchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true }, // null = global/all agents
site_uuid: { type: String, index: true },
bandwidth_down: Number,
bandwidth_up: Number,
active_flows: Number,
download_speed: Number,
upload_speed: Number,
total_devices: Number,
total_threats: Number,
packet_drops: Number,
peak_flow_rate: Number,
cpu_usage: Number,
memory_usage: Number,
queue_depth: Number,
}, baseOptions);
// ─── Top Applications (per agent) ─────────────────────────────────────────────
const AppStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
app_label: { type: String, required: true },
download: Number,
upload: Number,
flows: Number,
}, baseOptions);
// ─── Protocol Statistics (per agent) ──────────────────────────────────────────
const ProtocolStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
protocol_label: { type: String, required: true },
download: Number,
upload: Number,
flows: Number,
}, baseOptions);
// ─── Discovered Devices (per agent, includes IP + MAC + device info) ───────────
const DeviceStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
ip_address: { type: String, required: true, index: true },
mac_address: { type: String, index: true },
device_label: String,
device_type: String,
os_label: String,
manufacturer: String,
download: Number,
upload: Number,
flows: Number,
last_seen: String,
}, baseOptions);
// ─── Network Flows (per agent) ─────────────────────────────────────────────────
const FlowSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
flow_id: String,
src_ip: { type: String, index: true },
src_mac: String,
dst_ip: { type: String, index: true },
dst_port: Number,
protocol: String,
app_label: String,
domain: { type: String, index: true },
download: Number,
upload: Number,
first_seen: String,
last_seen: String,
}, baseOptions);
// ─── Cyber Threats (per agent) ─────────────────────────────────────────────────
const ThreatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
threat_type: String,
severity: String,
src_ip: String,
dst_ip: String,
dst_port: Number,
protocol: String,
description: String,
event_at: String,
}, baseOptions);
// ─── App Categories (per agent) ───────────────────────────────────────────────
const AppCategoryStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
category_label: { type: String, required: true },
download: Number,
upload: Number,
flows: Number,
}, baseOptions);
// ─── System Events (per agent) ─────────────────────────────────────────────────
const EventSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
event_id: Number,
event_type: String,
severity: String,
description: String,
category_label: String,
ip_address: String,
mac_address: String,
event_at: Date,
}, baseOptions);
// ─── Compound Indexes for common dashboard queries ─────────────────────────────
SummarySchema.index({ agent_uuid: 1, timestamp: -1 });
AppStatSchema.index({ agent_uuid: 1, timestamp: -1, download: -1 });
DeviceStatSchema.index({ agent_uuid: 1, ip_address: 1 }, { unique: true });
FlowSchema.index({ agent_uuid: 1, timestamp: -1 });
FlowSchema.index({ agent_uuid: 1, flow_id: 1 });
FlowSchema.index({ agent_uuid: 1, protocol: 1, timestamp: -1 });
FlowSchema.index({ agent_uuid: 1, domain: 1, timestamp: -1 });
FlowSchema.index({ site_uuid: 1, app_label: 1, timestamp: -1 });
ThreatSchema.index({ agent_uuid: 1, timestamp: -1 });
AppCategoryStatSchema.index({ agent_uuid: 1, timestamp: -1 });
EventSchema.index({ agent_uuid: 1, timestamp: -1 });
// ── Per-Device Per-Application Stats (synced from proxy) ─────────────────
const DeviceAppStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
ip_address: { type: String, required: true, index: true },
app_label: { type: String, required: true },
app_id: Number,
download: { type: Number, default: 0 },
upload: { type: Number, default: 0 },
flows: { type: Number, default: 0 },
last_seen: String,
}, baseOptions);
DeviceAppStatSchema.index({ agent_uuid: 1, ip_address: 1, timestamp: -1 });
DeviceAppStatSchema.index({ ip_address: 1, app_label: 1, timestamp: -1 });
DeviceAppStatSchema.index({ site_uuid: 1, app_label: 1, timestamp: -1 });
const telemetrySchemas = require('./SchemasTelemetry');
const auxSchemas = require('./SchemasAux');
module.exports = {
Summary: mongoose.model('Summary', SummarySchema),
AppStat: mongoose.model('AppStat', AppStatSchema),
ProtocolStat: mongoose.model('ProtocolStat', ProtocolStatSchema),
DeviceStat: mongoose.model('DeviceStat', DeviceStatSchema),
DeviceAppStat: mongoose.model('DeviceAppStat', DeviceAppStatSchema),
Flow: mongoose.model('Flow', FlowSchema),
Threat: mongoose.model('Threat', ThreatSchema),
AppCategoryStat: mongoose.model('AppCategoryStat', AppCategoryStatSchema),
Event: mongoose.model('Event', EventSchema),
...auxSchemas,
...telemetrySchemas
};
// backend/models/Schemas.js
// ─────────────────────────────────────────────────────────────────────────────
// MongoDB Schemas untuk BackOne Backend (READ-ONLY)
//
// PENTING: Schema ini harus sinkron dengan proxy/models/Schemas.js
// Proxy yang MENULIS data, backend yang MEMBACA data.
//
// Setiap dokumen di-tag dengan:
// agent_uuid → identifikasi Network Agent spesifik (isolasi per tenant)
// site_uuid → identifikasi site DPI (BackOne)
// timestamp → waktu data dikumpulkan
// ─────────────────────────────────────────────────────────────────────────────
const mongoose = require('mongoose');
const baseOptions = {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
};
// ─── Bandwidth Summary (per agent, per collection cycle) ───────────────────────
const SummarySchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true }, // null = global/all agents
site_uuid: { type: String, index: true },
bandwidth_down: Number,
bandwidth_up: Number,
active_flows: Number,
download_speed: Number,
upload_speed: Number,
total_devices: Number,
total_threats: Number,
packet_drops: Number,
peak_flow_rate: Number,
cpu_usage: Number,
memory_usage: Number,
queue_depth: Number,
}, baseOptions);
// ─── Top Applications (per agent) ─────────────────────────────────────────────
const AppStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
app_label: { type: String, required: true },
download: Number,
upload: Number,
flows: Number,
}, baseOptions);
// ─── Protocol Statistics (per agent) ──────────────────────────────────────────
const ProtocolStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
protocol_label: { type: String, required: true },
download: Number,
upload: Number,
flows: Number,
}, baseOptions);
// ─── Discovered Devices (per agent, includes IP + MAC + device info) ───────────
const DeviceStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
ip_address: { type: String, required: true, index: true },
mac_address: { type: String, index: true },
device_label: String,
device_type: String,
os_label: String,
manufacturer: String,
download: Number,
upload: Number,
flows: Number,
last_seen: String,
}, baseOptions);
// ─── Network Flows (per agent) ─────────────────────────────────────────────────
const FlowSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
flow_id: String,
src_ip: { type: String, index: true },
src_mac: { type: String, index: true },
dst_ip: { type: String, index: true },
dst_port: Number,
protocol: String,
app_label: String,
domain: { type: String, index: true },
download: Number,
upload: Number,
first_seen: String,
last_seen: String,
}, baseOptions);
// ─── Cyber Threats (per agent) ─────────────────────────────────────────────────
const ThreatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
threat_type: String,
severity: String,
src_ip: String,
dst_ip: String,
dst_port: Number,
protocol: String,
description: String,
event_at: String,
flow_id: { type: String, index: true },
}, baseOptions);
// ─── App Categories (per agent) ───────────────────────────────────────────────
const AppCategoryStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
category_label: { type: String, required: true },
download: Number,
upload: Number,
flows: Number,
}, baseOptions);
// ─── System Events (per agent) ─────────────────────────────────────────────────
const EventSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
event_id: Number,
event_type: String,
severity: String,
description: String,
category_label: String,
ip_address: String,
mac_address: String,
event_at: Date,
flow_id: { type: String, index: true },
}, baseOptions);
// ─── Compound Indexes for common dashboard queries ─────────────────────────────
SummarySchema.index({ agent_uuid: 1, timestamp: -1 });
AppStatSchema.index({ agent_uuid: 1, timestamp: -1, download: -1 });
DeviceStatSchema.index({ agent_uuid: 1, ip_address: 1 }, { unique: true });
FlowSchema.index({ agent_uuid: 1, timestamp: -1 });
FlowSchema.index({ agent_uuid: 1, flow_id: 1 });
FlowSchema.index({ agent_uuid: 1, protocol: 1, timestamp: -1 });
FlowSchema.index({ agent_uuid: 1, domain: 1, timestamp: -1 });
FlowSchema.index({ site_uuid: 1, app_label: 1, timestamp: -1 });
FlowSchema.index({ site_uuid: 1, src_mac: 1, timestamp: -1 });
ThreatSchema.index({ agent_uuid: 1, timestamp: -1 });
AppCategoryStatSchema.index({ agent_uuid: 1, timestamp: -1 });
EventSchema.index({ agent_uuid: 1, timestamp: -1 });
// ── Per-Device Per-Application Stats (synced from proxy) ─────────────────
const DeviceAppStatSchema = new mongoose.Schema({
timestamp: { type: Date, required: true, index: true, expires: '7d' },
agent_uuid: { type: String, index: true },
site_uuid: { type: String, index: true },
ip_address: { type: String, required: true, index: true },
app_label: { type: String, required: true },
app_id: Number,
download: { type: Number, default: 0 },
upload: { type: Number, default: 0 },
flows: { type: Number, default: 0 },
last_seen: String,
}, baseOptions);
DeviceAppStatSchema.index({ agent_uuid: 1, ip_address: 1, timestamp: -1 });
DeviceAppStatSchema.index({ ip_address: 1, app_label: 1, timestamp: -1 });
DeviceAppStatSchema.index({ site_uuid: 1, app_label: 1, timestamp: -1 });
const telemetrySchemas = require('./SchemasTelemetry');
const auxSchemas = require('./SchemasAux');
module.exports = {
Summary: mongoose.model('Summary', SummarySchema),
AppStat: mongoose.model('AppStat', AppStatSchema),
ProtocolStat: mongoose.model('ProtocolStat', ProtocolStatSchema),
DeviceStat: mongoose.model('DeviceStat', DeviceStatSchema),
DeviceAppStat: mongoose.model('DeviceAppStat', DeviceAppStatSchema),
Flow: mongoose.model('Flow', FlowSchema),
Threat: mongoose.model('Threat', ThreatSchema),
AppCategoryStat: mongoose.model('AppCategoryStat', AppCategoryStatSchema),
Event: mongoose.model('Event', EventSchema),
...auxSchemas,
...telemetrySchemas
};
+22
View File
@@ -0,0 +1,22 @@
// backend/models/Session.js
// ─────────────────────────────────────────────────────────────────────────────
// MongoDB User Session Schema for remote revocation capability
// ─────────────────────────────────────────────────────────────────────────────
const mongoose = require('mongoose');
const SessionSchema = new mongoose.Schema({
user_id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, index: true },
ip_address: { type: String, default: 'Unknown' },
user_agent: { type: String, default: 'Unknown' },
session_token: { type: String, required: true, unique: true }, // JWT JTI or unique token hash
last_active: { type: Date, default: Date.now },
expires_at: { type: Date, required: true }, // MongoDB TTL Index specified below via SessionSchema.index
}, {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
});
// TTL index to automatically remove expired sessions from MongoDB
SessionSchema.index({ expires_at: 1 }, { expireAfterSeconds: 0 });
module.exports = mongoose.model('Session', SessionSchema);
+5 -1
View File
@@ -14,11 +14,15 @@ const UserSchema = new mongoose.Schema({
password_hash: { type: String, required: true },
account_name: { type: String, default: null },
profile_picture: { type: String, default: null },
role: { type: String, enum: ['SUPER_ADMIN', 'TENANT_ADMIN', 'SOC_ANALYST', 'ENGINEER', 'AGENT_VIEWER'], default: 'AGENT_VIEWER' },
role: { type: String, enum: ['SUPER_ADMIN', 'EXECUTIVE', 'TENANT_ADMIN', 'SOC_ANALYST', 'ENGINEER', 'AGENT_VIEWER', 'COMPANY_ADMIN', 'COMPANY_OPERATOR', 'COMPANY_VIEWER'], default: 'AGENT_VIEWER' },
site_uuid: { type: String, default: null, index: true },
agent_uuid: { type: String, default: null },
company_name: { type: String, default: null, index: true },
agent_uuids: { type: [String], default: [] },
created_by: { type: String, default: null, index: true },
is_active: { type: Boolean, default: true },
login_attempts: { type: Number, default: 0 },
lockout_until: { type: Date, default: null },
}, {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
});
+2718
View File
File diff suppressed because it is too large. Load diff
+312
View File
@@ -0,0 +1,312 @@
// backend/scheduler.js
const cron = require('node-cron');
const netify = require('./netify');
const db = require('./database');
const SITE_UUID = process.env.NETIFY_SITE_UUID || 'dummy_site_uuid';
let isRunning = false;
async function runPoll() {
if (isRunning) {
console.log('[Scheduler] Poll sedang berjalan, skip.');
return;
}
isRunning = true;
const fetchedAt = new Date().toISOString();
console.log(`[Scheduler] Mulai polling... (${fetchedAt})`);
try {
// 0. Sync agents and seed default user accounts dynamically
try {
apiAgents = await netify.fetchAgents();
if (apiAgents && apiAgents.length > 0) {
db.syncAgentUsers(apiAgents);
console.log(`[Scheduler] OK Sync Agents : ${apiAgents.length} agen terdeteksi`);
}
} catch (err) {
console.error('[Scheduler] Gagal sync agent users:', err.message);
}
async function fetchAndStore(fetchedAt, agentUuid) {
const agentLabel = agentUuid ? agentUuid : 'Global';
console.log(`[Scheduler] Fetching data for ${agentLabel}`);
// 1. Top Aplikasi
const apps = await netify.fetchTopApps(1440, 20, agentUuid);
if (apps && Array.isArray(apps)) {
db.insertBandwidthApps(apps, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Apps : ${apps.length} baris`);
} else {
console.log(`[Scheduler] -- Apps : tidak ada data`);
}
// 2. Top Devices — pakai fetchDiscoveredDevices yg sudah dinormalisasi
const devices = await netify.fetchDiscoveredDevices(1440, 200, agentUuid);
if (devices && Array.isArray(devices)) {
db.insertDevices(devices, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Devices : ${devices.length} baris`);
} else {
console.log(`[Scheduler] -- Devices : tidak ada data`);
}
// 3. Top Protokol
const protocols = await netify.fetchTopProtocols(1440, 20, agentUuid);
if (protocols && Array.isArray(protocols)) {
db.insertProtocols(protocols, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Protocols : ${protocols.length} baris`);
} else {
console.log(`[Scheduler] -- Protocols : tidak ada data`);
}
// 4. Top Negara
const countries = await netify.fetchTopCountries(1440, 15, agentUuid);
if (countries && Array.isArray(countries)) {
db.insertCountries(countries, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Countries : ${countries.length} baris`);
} else {
console.log(`[Scheduler] -- Countries : tidak ada data`);
}
// 5. Top Domain/DNS
const domains = await netify.fetchTopDomains(1440, 20, agentUuid);
if (domains && Array.isArray(domains)) {
db.insertDNS(domains, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK DNS : ${domains.length} baris`);
} else {
console.log(`[Scheduler] -- DNS : tidak ada data`);
}
// 6. Flows — pakai local_ip sebagai proxy
const flows = await netify.fetchFlows(200, agentUuid);
if (flows && Array.isArray(flows)) {
db.insertFlows(flows, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Flows : ${flows.length} baris`);
} else {
console.log(`[Scheduler] -- Flows : tidak ada data`);
}
// 7. Threats — dari Events Status
const threats = await netify.fetchCyberThreats(1440, 50, agentUuid);
if (threats && Array.isArray(threats)) {
db.insertThreats(threats, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Threats : ${threats.length} baris`);
} else {
console.log(`[Scheduler] -- Threats : tidak ada data`);
}
// 8. Events Log
const events = await netify.fetchEvents(50, agentUuid);
if (events && Array.isArray(events)) {
db.insertEvents(events, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Events : ${events.length} baris`);
} else {
console.log(`[Scheduler] -- Events : tidak ada data`);
}
// 10. App Categories
const appCats = await netify.fetchTopAppCategories(1440, 15, agentUuid);
if (appCats?.length) { db.insertAppCategories(appCats, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK AppCats : ${appCats.length} baris`); }
else console.log(`[Scheduler] -- AppCats : tidak ada data`);
// 11. Continents
const continents = await netify.fetchTopContinents(1440, 10, agentUuid);
if (continents?.length) { db.insertContinents(continents, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Continents : ${continents.length} baris`); }
else console.log(`[Scheduler] -- Continents : tidak ada data`);
// 12. Regions
const regions = await netify.fetchTopRegions(1440, 20, agentUuid);
if (regions?.length) { db.insertRegions(regions, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Regions : ${regions.length} baris`); }
else console.log(`[Scheduler] -- Regions : tidak ada data`);
// 13. Cities
const cities = await netify.fetchTopCities(1440, 20, agentUuid);
if (cities?.length) { db.insertCities(cities, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Cities : ${cities.length} baris`); }
else console.log(`[Scheduler] -- Cities : tidak ada data`);
// 14. VLANs
const vlans = await netify.fetchTopVLANs(1440, 20, agentUuid);
if (vlans?.length) { db.insertVLANs(vlans, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK VLANs : ${vlans.length} baris`); }
else console.log(`[Scheduler] -- VLANs : tidak ada data`);
// 15. Interfaces
const ifaces = await netify.fetchTopInterfaces(1440, 20, agentUuid);
if (ifaces?.length) { db.insertInterfaces(ifaces, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK Interfaces : ${ifaces.length} baris`); }
else console.log(`[Scheduler] -- Interfaces : tidak ada data`);
// 16. Flow Types
const flowTypes = await netify.fetchTopFlowTypes(1440, 10, agentUuid);
if (flowTypes?.length) { db.insertFlowTypes(flowTypes, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK FlowTypes : ${flowTypes.length} baris`); }
else console.log(`[Scheduler] -- FlowTypes : tidak ada data`);
// 17. Flow Origins
const flowOrigins = await netify.fetchTopFlowOrigins(1440, 10, agentUuid);
if (flowOrigins?.length) { db.insertFlowOrigins(flowOrigins, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK FlowOrigin : ${flowOrigins.length} baris`); }
else console.log(`[Scheduler] -- FlowOrigin : tidak ada data`);
// 18. IP Versions
const ipVersions = await netify.fetchTopIPVersions(1440, 5, agentUuid);
if (ipVersions?.length) { db.insertIPVersions(ipVersions, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK IPVersions : ${ipVersions.length} baris`); }
else console.log(`[Scheduler] -- IPVersions : tidak ada data`);
// 19. Remote IPs
const remoteIPs = await netify.fetchTopRemoteIPs(1440, 20, agentUuid);
if (remoteIPs?.length) { db.insertRemoteIPs(remoteIPs, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK RemoteIPs : ${remoteIPs.length} baris`); }
else console.log(`[Scheduler] -- RemoteIPs : tidak ada data`);
// 20. MAC Bandwidth
const macBW = await netify.fetchTopLocalMACs(1440, 50, agentUuid);
if (macBW?.length) { db.insertMACBandwidth(macBW, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK MACBandwdh : ${macBW.length} baris`); }
else console.log(`[Scheduler] -- MACBandwdh : tidak ada data`);
// 9. Bandwidth Timeline
const summary = await netify.fetchBandwidthSummary(1440, agentUuid);
const devCount = devices?.length ?? 0;
if (summary) {
db.insertBandwidthTimeline({ ...summary, devices: devCount }, fetchedAt, SITE_UUID, agentUuid);
console.log(`[Scheduler] OK Timeline : saved`);
} else {
console.log(`[Scheduler] -- Timeline : gagal ambil data`);
}
// 21. TLS Versions
const tlsVer = await netify.fetchTLSVersions(1440, 10, agentUuid);
if (tlsVer?.length) { db.insertTLSVersions(tlsVer, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TLS Ver : ${tlsVer.length} baris`); }
else console.log(`[Scheduler] -- TLS Ver : tidak ada data`);
// 22. TLS Ciphers
const tlsCipher = await netify.fetchTLSCiphers(1440, 15, agentUuid);
if (tlsCipher?.length) { db.insertTLSCiphers(tlsCipher, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TLS Cipher : ${tlsCipher.length} baris`); }
else console.log(`[Scheduler] -- TLS Cipher : tidak ada data`);
// 23. TLS Security
const tlsSec = await netify.fetchTLSSecurity(1440, 10, agentUuid);
if (tlsSec?.length) { db.insertTLSSecurity(tlsSec, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TLS Sec : ${tlsSec.length} baris`); }
else console.log(`[Scheduler] -- TLS Sec : tidak ada data`);
// 24. NetBIOS Hostnames
const netbios = await netify.fetchNetBIOSHostnames(1440, 30, agentUuid);
if (netbios?.length) { db.insertNetBIOSHostnames(netbios, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK NetBIOS : ${netbios.length} baris`); }
else console.log(`[Scheduler] -- NetBIOS : tidak ada data`);
// 25. Discovery OS (standalone — OS yang terdeteksi di jaringan)
const discOs = await netify.fetchTopDiscoveryOS(1440, 20, agentUuid);
if (discOs?.length) { db.insertDiscoveryOS(discOs, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK DiscOS : ${discOs.length} baris`); }
else console.log(`[Scheduler] -- DiscOS : tidak ada data`);
// 26. DHCP Class Fingerprint
const dhcpFp = await netify.fetchDHCPClassFingerprints(1440, 30, agentUuid);
if (dhcpFp?.length) { db.insertDHCPFingerprints(dhcpFp, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK DHCP FP : ${dhcpFp.length} baris`); }
else console.log(`[Scheduler] -- DHCP FP : tidak ada data`);
// 27. HTTP User-Agent
const userAgents = await netify.fetchHTTPUserAgents(1440, 30, agentUuid);
if (userAgents?.length) { db.insertHTTPUserAgents(userAgents, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK UserAgent : ${userAgents.length} baris`); }
else console.log(`[Scheduler] -- UserAgent : tidak ada data`);
// 28. HTTPS SNI Hostname
const sniHosts = await netify.fetchSNIHostnames(1440, 30, agentUuid);
if (sniHosts?.length) { db.insertSNIHostnames(sniHosts, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SNI Host : ${sniHosts.length} baris`); }
else console.log(`[Scheduler] -- SNI Host : tidak ada data`);
// 29. SSL Server Common Name
const sslCN = await netify.fetchSSLServerCN(1440, 30, agentUuid);
if (sslCN?.length) { db.insertSSLServerCN(sslCN, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SSL CN : ${sslCN.length} baris`); }
else console.log(`[Scheduler] -- SSL CN : tidak ada data`);
// 30. QUIC Hostname
const quicHosts = await netify.fetchQUICHostnames(1440, 30, agentUuid);
if (quicHosts?.length) { db.insertQUICHostnames(quicHosts, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK QUIC Host : ${quicHosts.length} baris`); }
else console.log(`[Scheduler] -- QUIC Host : tidak ada data`);
// 31. BitTorrent Info Hash
const btHashes = await netify.fetchBitTorrentInfoHashes(1440, 30, agentUuid);
if (btHashes?.length) { db.insertBitTorrentHashes(btHashes, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK BT Hash : ${btHashes.length} baris`); }
else console.log(`[Scheduler] -- BT Hash : tidak ada data`);
// 32. SSH Client (field: ssh_client)
const sshClient = await netify.fetchSSHClients(1440, 20, agentUuid);
if (sshClient?.length) { db.insertSSHVersions(sshClient, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SSH Client : ${sshClient.length} baris`); }
else console.log(`[Scheduler] -- SSH Client : tidak ada data`);
// 32b. SSH Server (field: ssh_server)
const sshServer = await netify.fetchSSHServers(1440, 20, agentUuid);
if (sshServer?.length) { db.insertSSHVersions(sshServer, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SSH Server : ${sshServer.length} baris`); }
else console.log(`[Scheduler] -- SSH Server : tidak ada data`);
// 33. mDNS Hostname (Chromecast, Apple TV, etc.)
const mdnsHosts = await netify.fetchMDNSHostnames(1440, 30, agentUuid);
if (mdnsHosts?.length) { db.insertMDNSHostnames(mdnsHosts, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK mDNS Host : ${mdnsHosts.length} baris`); }
else console.log(`[Scheduler] -- mDNS Host : tidak ada data`);
// ─── INTELLIGENCE 22-30 (derive dari data yang tersedia) ─────────────────
// 34. Cryptocurrency Mining (derive dari apps + flows ke port mining)
const cryptoMining = await netify.fetchCryptoMining(50, agentUuid);
if (cryptoMining?.length) { db.insertCryptoMining(cryptoMining, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK CryptoMine : ${cryptoMining.length} baris`); }
else console.log(`[Scheduler] -- CryptoMine : tidak ada data`);
// 35. Device Discovery (derive dari flows + bandwidth per-IP)
const devDisc = await netify.fetchDeviceDiscovery(100, agentUuid);
if (devDisc?.length) { db.insertDeviceDiscovery(devDisc, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK DevDisc : ${devDisc.length} baris`); }
else console.log(`[Scheduler] -- DevDisc : tidak ada data`);
// 36. Encryption Audit (derive dari flows per-IP: port encrypted vs plain)
const encAudit = await netify.fetchEncryptionAudit(50, agentUuid);
if (encAudit?.length) { db.insertEncryptionAudit(encAudit, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK EncAudit : ${encAudit.length} baris`); }
else console.log(`[Scheduler] -- EncAudit : tidak ada data`);
// 37. Insecure Protocols (derive dari top protocols)
const insecProto = await netify.fetchInsecureProtocols(1440, 50, agentUuid);
if (insecProto?.length) { db.insertInsecureProtocols(insecProto, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK InsecProto : ${insecProto.length} baris`); }
else console.log(`[Scheduler] -- InsecProto : tidak ada data`);
// 38. IP Reputation (derive dari top remote_ip + high-risk countries)
const ipRep = await netify.fetchIPReputation(50, agentUuid);
if (ipRep?.length) { db.insertIPReputation(ipRep, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK IPRepute : ${ipRep.length} baris`); }
else console.log(`[Scheduler] -- IPRepute : tidak ada data`);
// 39. Server Discovery (derive dari flows ke port server well-known)
const srvDisc = await netify.fetchServerDiscovery(100, agentUuid);
if (srvDisc?.length) { db.insertServerDiscovery(srvDisc, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK SrvDisc : ${srvDisc.length} baris`); }
else console.log(`[Scheduler] -- SrvDisc : tidak ada data`);
// 40. Tor Detection (derive dari apps/hostnames mengandung "tor")
const torDet = await netify.fetchTorDetection(50, agentUuid);
if (torDet?.length) { db.insertTorDetection(torDet, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK TorDet : ${torDet.length} baris`); }
else console.log(`[Scheduler] -- TorDet : tidak ada data`);
// 41. Unencrypted Password (derive dari flows ke port cleartext auth)
const unencPwd = await netify.fetchUnencryptedPasswords(50, agentUuid);
if (unencPwd?.length) { db.insertUnencryptedPasswords(unencPwd, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK UnencPwd : ${unencPwd.length} baris`); }
else console.log(`[Scheduler] -- UnencPwd : tidak ada data`);
// 42. VPN Detection (derive dari apps/protocols/ports VPN)
const vpnDet = await netify.fetchVPNDetection(50, agentUuid);
if (vpnDet?.length) { db.insertVPNDetection(vpnDet, fetchedAt, SITE_UUID, agentUuid); console.log(`[Scheduler] OK VPNDet : ${vpnDet.length} baris`); }
else console.log(`[Scheduler] -- VPNDet : tidak ada data`);
}
// --- Main loop
await fetchAndStore(fetchedAt, null);
if (apiAgents && apiAgents.length > 0) {
for (const agent of apiAgents) {
if (agent && agent.uuid) {
await fetchAndStore(fetchedAt, agent.uuid);
}
}
}
} catch (err) {
console.error('[Scheduler] ERROR:', err);
} finally {
isRunning = false;
console.log(`[Scheduler] Poll selesai.\n`);
}
}
function startScheduler() {
runPoll();
cron.schedule('* * * * *', () => runPoll());
console.log('[Scheduler] Aktif. Polling setiap 1 menit.\n');
}
module.exports = { startScheduler, runPoll };
+163 -123
View File
@@ -1,123 +1,163 @@
// backend/server.js
// ─────────────────────────────────────────────────────────────────────────────
// BackOne Backend API Server
//
// Tanggung jawab backend ini adalah READ-ONLY dari MongoDB.
// Semua data collection (ingestion) dilakukan oleh Proxy Server (port 4000).
// Backend TIDAK memanggil DPI API secara langsung.
//
// Environment Variables:
// MONGODB_URI - MongoDB connection string
// BACKEND_PORT - Port server ini (default: 3001)
// JWT_SECRET - Secret untuk JWT auth
// ALLOWED_ORIGINS- Comma-separated allowed CORS origins
// PROXY_URL - URL proxy server (untuk trigger manual refresh)
// ─────────────────────────────────────────────────────────────────────────────
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '..', '.env.local') });
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken');
const connectDB = require('./db/mongoose');
// ─── Connect to MongoDB (read-only mode) ──────────────────────────────────────
connectDB();
const app = express();
const PORT = process.env.BACKEND_PORT || 3001;
// ─── Middleware ────────────────────────────────────────────────────────────────
const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS
? process.env.ALLOWED_ORIGINS.split(',')
: ['http://localhost:3000', 'http://127.0.0.1:3000'];
app.use(cors({
origin: (origin, callback) => {
if (!origin) return callback(null, true);
if (ALLOWED_ORIGINS.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Blocked by CORS policy (Unauthorized Origin)'));
}
},
credentials: true
}));
app.use(express.json());
app.use(cookieParser());
// ─── Public Routes ────────────────────────────────────────────────────────────
const authRoutes = require('./routes/auth');
const { getUploadsDir } = require('./routes/auth/helpers');
app.use('/api/auth', authRoutes);
app.use('/api/uploads', express.static(getUploadsDir()));
// ─── Auth Middleware ──────────────────────────────────────────────────────────
const { requireAuth } = require('./middleware/auth');
const { getTimeFilter, getBaseFilter } = require('./routes/dashboard/helpers');
const {
generateMacFromIp,
resolveVendorFromIp,
resolveDeviceTypeFromIp,
resolveOSFromIp,
generateAutoLabel
} = require('./deviceResolver');
// ─── Protected Dashboard Routes ───────────────────────────────────────────────
const dashboardRoutes = require('./routes/dashboard');
// Override /api/dashboard/app-details to show real-time device mapping per application
app.get('/api/dashboard/app-details', requireAuth, (req, res) => {
require('./routes/appDetailsHandler')(req, res, {
getTimeFilter,
getBaseFilter
});
});
// Override /api/dashboard/device-details to map real-time classifications (Facebook, YouTube, etc.)
app.get('/api/dashboard/device-details', requireAuth, (req, res) => {
require('./routes/deviceDetailsHandler')(req, res, {
getTimeFilter,
getBaseFilter,
generateMacFromIp,
resolveDeviceTypeFromIp,
resolveOSFromIp,
resolveVendorFromIp,
generateAutoLabel
});
});
app.get('/api/dashboard/remote-ip-details', requireAuth, async (req, res) => {
require('./routes/remoteIpDetailsHandler')(req, res, {
getTimeFilter
});
});
const metadataDetailRoutes = require('./routes/metadataDetail');
app.use('/api/dashboard/metadata-detail', requireAuth, metadataDetailRoutes);
const categoryDetailRoutes = require('./routes/categoryDetail');
app.use('/api/dashboard/category-detail', requireAuth, categoryDetailRoutes);
app.use('/api/dashboard', requireAuth, dashboardRoutes);
// ─── Health Check ─────────────────────────────────────────────────────────────
app.get('/api/health', (req, res) => {
res.json({
ok: true,
message: 'BackOne Backend berjalan (MongoDB read-only mode)',
time: new Date().toISOString()
});
});
// ─── Start Server ─────────────────────────────────────────────────────────────
app.listen(PORT, () => {
console.log(`\n🚀 BackOne API Server berjalan di http://localhost:${PORT}`);
console.log(`🔌 API Health : http://localhost:${PORT}/api/health`);
console.log(`📡 Mode : READ-ONLY dari MongoDB (data dikirim oleh Proxy Server)\n`);
});
// backend/server.js
// ─────────────────────────────────────────────────────────────────────────────
// Polyfill global crypto for Node 18 compatibility (required by mongodb driver)
if (typeof globalThis.crypto === 'undefined') {
globalThis.crypto = require('crypto');
}
// BackOne Backend API Server
//
// Tanggung jawab backend ini adalah READ-ONLY dari MongoDB.
// Semua data collection (ingestion) dilakukan oleh Proxy Server (port 4000).
// Backend TIDAK memanggil DPI API secara langsung.
//
// Environment Variables:
// MONGODB_URI - MongoDB connection string
// BACKEND_PORT - Port server ini (default: 3001)
// JWT_SECRET - Secret untuk JWT auth
// ALLOWED_ORIGINS- Comma-separated allowed CORS origins
// PROXY_URL - URL proxy server (untuk trigger manual refresh)
// ─────────────────────────────────────────────────────────────────────────────
const path = require('path');
const envFile = process.env.NODE_ENV === 'production' ? '.env.production' : '.env.local';
require('dotenv').config({ path: path.join(__dirname, '..', envFile) });
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken');
const connectDB = require('./db/mongoose');
// ─── Connect to MongoDB (read-only mode) ──────────────────────────────────────
connectDB();
const app = express();
const PORT = process.env.BACKEND_PORT || 3001;
// ─── Middleware ────────────────────────────────────────────────────────────────
const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS
? process.env.ALLOWED_ORIGINS.split(',')
: ['http://localhost:3000', 'http://127.0.0.1:3000'];
app.use(cors({
origin: (origin, callback) => {
if (!origin) return callback(null, true);
if (ALLOWED_ORIGINS.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Blocked by CORS policy (Unauthorized Origin)'));
}
},
credentials: true
}));
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
app.use(cookieParser());
app.use((req, res, next) => {
if (req.originalUrl && req.originalUrl.includes('/api/dashboard')) {
try {
const fs = require('fs');
const path = require('path');
const logPath = path.join(__dirname, '../scratch/http_requests.log');
const logLine = `[${new Date().toISOString()}] ${req.method} ${req.originalUrl} - Query: ${JSON.stringify(req.query)}\n`;
fs.appendFileSync(logPath, logLine);
} catch (e) {
console.error('Logger error:', e.message);
}
}
next();
});
// ─── Public Routes ────────────────────────────────────────────────────────────
const authRoutes = require('./routes/auth');
const { getUploadsDir } = require('./routes/auth/helpers');
app.use('/api/auth', authRoutes);
app.use('/api/uploads', express.static(getUploadsDir()));
// ─── Auth Middleware ──────────────────────────────────────────────────────────
const { requireAuth } = require('./middleware/auth');
const { getTimeFilter, getBaseFilter } = require('./routes/dashboard/helpers');
const {
generateMacFromIp,
resolveVendorFromIp,
resolveDeviceTypeFromIp,
resolveOSFromIp,
generateAutoLabel
} = require('./deviceResolver');
// ─── Protected Dashboard Routes ───────────────────────────────────────────────
const dashboardRoutes = require('./routes/dashboard');
// Override /api/dashboard/app-details to show real-time device mapping per application
app.get('/api/dashboard/app-details', requireAuth, (req, res) => {
require('./routes/appDetailsHandler')(req, res, {
getTimeFilter,
getBaseFilter
});
});
// Override /api/dashboard/device-details to map real-time classifications (Facebook, YouTube, etc.)
app.get('/api/dashboard/device-details', requireAuth, (req, res) => {
require('./routes/deviceDetailsHandler')(req, res, {
getTimeFilter,
getBaseFilter,
generateMacFromIp,
resolveDeviceTypeFromIp,
resolveOSFromIp,
resolveVendorFromIp,
generateAutoLabel
});
});
app.get('/api/dashboard/remote-ip-details', requireAuth, async (req, res) => {
require('./routes/remoteIpDetailsHandler')(req, res, {
getTimeFilter
});
});
const metadataDetailRoutes = require('./routes/metadataDetail');
app.use('/api/dashboard/metadata-detail', requireAuth, metadataDetailRoutes);
const categoryDetailRoutes = require('./routes/categoryDetail');
app.use('/api/dashboard/category-detail', requireAuth, categoryDetailRoutes);
app.use('/api/dashboard', requireAuth, dashboardRoutes);
// ─── Health Check ─────────────────────────────────────────────────────────────
app.get('/api/health', (req, res) => {
res.json({
ok: true,
message: 'BackOne Backend berjalan (MongoDB read-only mode)',
time: new Date().toISOString()
});
});
// ─── Global JSON Error Handler ────────────────────────────────────────────────
// Menangkap semua error yang tidak di-handle (termasuk multer, mongoose, dll.)
// dan memastikan response selalu JSON, BUKAN HTML default Express.
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
console.error('[Global Error Handler]', err.message || err);
const status = err.status || err.statusCode || 500;
res.status(status).json({
error: err.message || 'Internal server error',
code: err.code || undefined,
});
});
// ─── Start Server ─────────────────────────────────────────────────────────────
// Bind to 127.0.0.1 in production to prevent direct external access to port 3001.
// All external traffic must go through the reverse proxy (Apache/Nginx) at port 80/443.
const BIND_HOST = process.env.NODE_ENV === 'production' ? '127.0.0.1' : '0.0.0.0';
app.listen(PORT, BIND_HOST, () => {
console.log(`\n🚀 BackOne API Server berjalan di http://${BIND_HOST}:${PORT}`);
console.log(`🔌 API Health : http://${BIND_HOST}:${PORT}/api/health`);
console.log(`📡 Mode : READ-ONLY dari MongoDB (data dikirim oleh Proxy Server)`);
console.log(`🔒 Security : Bound to ${BIND_HOST} (internal only in production)\n`);
});
+135
View File
@@ -0,0 +1,135 @@
const cron = require('node-cron');
const netify = require('../netify');
const { Summary, AppStat, ProtocolStat, DeviceStat, Flow, Threat } = require('../models/Schemas');
const SITE_UUID = process.env.NETIFY_SITE_UUID || process.env.BACKONE_SITE_UUID;
let isRunning = false;
async function runPoll() {
if (isRunning) return;
isRunning = true;
const timestamp = new Date();
console.log(`[Mongo-Ingestion] Started polling at ${timestamp.toISOString()}`);
try {
const agents = await netify.fetchAgents();
const agentList = agents && agents.length > 0 ? agents.map(a => a.uuid) : [null]; // null for global
for (const agentUuid of agentList) {
console.log(`[Mongo-Ingestion] Fetching data for Agent: ${agentUuid || 'Global'}`);
// 1. Summary
const summary = await netify.fetchBandwidthSummary(1440, agentUuid);
if (summary) {
await new Summary({
timestamp,
agent_uuid: agentUuid,
site_uuid: SITE_UUID,
...summary
}).save();
}
// 2. Apps
const apps = await netify.fetchTopApps(1440, 200, agentUuid); // high limit for data lake
if (apps && apps.length > 0) {
const appDocs = apps.map(app => ({
timestamp,
agent_uuid: agentUuid,
site_uuid: SITE_UUID,
app_label: app.application?.label || 'Unknown',
download: app.download || 0,
upload: app.upload || 0,
flows: app.flows || 0
}));
await AppStat.insertMany(appDocs);
}
const devices = await netify.fetchDiscoveredDevices(1440, 500, agentUuid);
if (devices && devices.length > 0) {
const devDocs = devices.map(d => ({
timestamp,
agent_uuid: agentUuid,
site_uuid: SITE_UUID,
ip_address: d.ip_address,
mac_address: d.mac_address,
device_label: d.device_label,
device_type: d.device_type,
os_label: d.os_label,
manufacturer: d.manufacturer,
download: d.download || 0,
upload: d.upload || 0,
flows: d.flows || 0,
last_seen: d.last_seen
})).filter(d => d.ip_address); // Ensure ip_address exists to avoid validation error
if (devDocs.length > 0) {
await DeviceStat.insertMany(devDocs);
}
}
// 4. Flows
const flows = await netify.fetchFlows(500, agentUuid);
if (flows && flows.length > 0) {
const flowDocs = flows.map(f => ({
timestamp,
agent_uuid: agentUuid,
site_uuid: SITE_UUID,
flow_id: f.flow_id,
src_ip: f.src_ip,
src_mac: f.src_mac,
dst_ip: f.dst_ip,
dst_port: f.dst_port,
protocol: f.protocol,
app_label: f.app_label,
domain: f.domain,
download: f.download || 0,
upload: f.upload || 0,
first_seen: f.first_seen,
last_seen: f.last_seen
})).filter(f => f.src_ip);
if (flowDocs.length > 0) {
await Flow.insertMany(flowDocs);
}
}
// 5. Threats
const threats = await netify.fetchCyberThreats(agentUuid);
if (threats && threats.length > 0) {
const threatDocs = threats.map(t => ({
timestamp,
agent_uuid: agentUuid,
site_uuid: SITE_UUID,
threat_type: t.threat_type || 'Unknown Threat',
severity: t.severity || 'Medium',
src_ip: t.src_ip,
dst_ip: t.dst_ip,
dst_port: t.dst_port,
protocol: t.protocol,
description: t.description,
event_at: t.event_at || new Date().toISOString()
}));
if (threatDocs.length > 0) {
await Threat.insertMany(threatDocs);
}
}
}
} catch (error) {
console.error('[Mongo-Ingestion] Error during polling:', error);
} finally {
isRunning = false;
}
}
function startScheduler() {
// Run every 5 minutes
cron.schedule('*/5 * * * *', () => {
runPoll();
});
console.log('[Mongo-Ingestion] Scheduler started (every 5 minutes)');
// Initial run
runPoll();
}
module.exports = { startScheduler };
+14
View File
@@ -0,0 +1,14 @@
const http = require('http');
http.get('http://localhost:3001/api/dashboard/tls-versions', {
headers: {
'Cookie': 'token=test', // Just checking schema, if it requires auth we might need to mock or use the proxy
}
}, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
console.log("Response TLS Versions:");
console.log(data.slice(0, 500));
});
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB