monitor.js
5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
const express = require('express');
const fs = require('fs');
const readline = require('readline');
const { exec } = require('child_process');
const { isAuthenticated } = require('../middleware/auth');
const router = express.Router();
const NGINX_LOG = process.env.NGINX_LOG || '/var/log/nginx/access.log';
// ─── Bandwidth dari /proc/net/dev ───────────────────────────────────────────
function getNetStats() {
try {
const raw = fs.readFileSync('/proc/net/dev', 'utf8');
const lines = raw.trim().split('\n').slice(2);
const result = {};
lines.forEach(line => {
const parts = line.trim().split(/\s+/);
const iface = parts[0].replace(':', '');
if (iface === 'lo') return;
result[iface] = {
rxBytes: parseInt(parts[1]),
txBytes: parseInt(parts[9]),
};
});
return result;
} catch(e) { return {}; }
}
// Cache untuk hitung delta bps
let prevStats = {};
let prevTime = Date.now();
function getBandwidth() {
const now = Date.now();
const current = getNetStats();
const elapsed = (now - prevTime) / 1000; // detik
const bandwidth = {};
Object.keys(current).forEach(iface => {
const prev = prevStats[iface];
if (prev && elapsed > 0) {
bandwidth[iface] = {
rxBps: Math.max(0, (current[iface].rxBytes - prev.rxBytes) / elapsed),
txBps: Math.max(0, (current[iface].txBytes - prev.txBytes) / elapsed),
rxBytes: current[iface].rxBytes,
txBytes: current[iface].txBytes,
};
} else {
bandwidth[iface] = { rxBps: 0, txBps: 0, ...current[iface] };
}
});
prevStats = current;
prevTime = now;
return bandwidth;
}
// ─── Parse nginx access log ──────────────────────────────────────────────────
// Format nginx default: IP - - [date] "METHOD /path HTTP/x.x" status bytes "ref" "ua"
function parseNginxLog(limit = 100) {
return new Promise((resolve) => {
const entries = [];
try {
if (!fs.existsSync(NGINX_LOG)) return resolve([]);
const content = fs.readFileSync(NGINX_LOG, 'utf8');
const lines = content.trim().split('\n').filter(Boolean);
// Ambil dari bawah (terbaru)
const recent = lines.slice(-limit).reverse();
recent.forEach(line => {
// Hanya tampilkan request ke /debian12/live/
if (!line.includes('/debian12/live/')) return;
// Parse format nginx combined log
const m = line.match(/^(\S+)\s+-\s+-\s+\[([^\]]+)\]\s+"(\S+)\s+(\S+)\s+\S+"\s+(\d+)\s+(\d+)/);
if (!m) return;
const [, ip, time, method, path, status, bytes] = m;
const filename = path.split('/').pop().split('?')[0] || path;
entries.push({
ip,
time,
method,
path,
filename,
status: parseInt(status),
bytes: parseInt(bytes),
});
});
} catch(e) { console.error('Gagal baca nginx log:', e.message); }
resolve(entries);
});
}
// ─── Routes ──────────────────────────────────────────────────────────────────
// Halaman monitoring
router.get('/', isAuthenticated, async (req, res) => {
const logs = await parseNginxLog(100);
const bandwidth = getBandwidth();
// Statistik ringkasan dari log
const stats = {
totalRequests: logs.length,
uniqueClients: new Set(logs.map(l => l.ip)).size,
totalBytes: logs.reduce((s, l) => s + (l.bytes || 0), 0),
success: logs.filter(l => l.status >= 200 && l.status < 300).length,
errors: logs.filter(l => l.status >= 400).length,
};
// Top file yang paling sering diakses
const fileCounts = {};
logs.forEach(l => {
fileCounts[l.filename] = (fileCounts[l.filename] || 0) + 1;
});
const topFiles = Object.entries(fileCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([name, count]) => ({ name, count }));
// Top client IP
const ipCounts = {};
logs.forEach(l => { ipCounts[l.ip] = (ipCounts[l.ip] || 0) + 1; });
const topClients = Object.entries(ipCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([ip, count]) => ({ ip, count }));
res.render('monitor', {
title: 'Traffic Monitor',
logs: logs.slice(0, 50),
bandwidth,
stats,
topFiles,
topClients,
nginxLog: NGINX_LOG,
});
});
// ─── API endpoint untuk polling realtime ─────────────────────────────────────
router.get('/api/bandwidth', isAuthenticated, (req, res) => {
res.json({ bandwidth: getBandwidth(), timestamp: Date.now() });
});
router.get('/api/logs', isAuthenticated, async (req, res) => {
const logs = await parseNginxLog(50);
const stats = {
totalRequests: logs.length,
uniqueClients: new Set(logs.map(l => l.ip)).size,
totalBytes: logs.reduce((s, l) => s + (l.bytes || 0), 0),
success: logs.filter(l => l.status >= 200 && l.status < 300).length,
errors: logs.filter(l => l.status >= 400).length,
};
res.json({ logs: logs.slice(0, 50), stats, timestamp: Date.now() });
});
module.exports = router;