From e92a7892d833370d8c173c10cefcb5b0440d9fb7 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Sat, 24 May 2025 16:15:24 +0500 Subject: [PATCH] dashboard stats --- .../baseTemplate/custom-js/system-status.js | 168 +++++++++++++++++- .../templates/baseTemplate/homePage.html | 52 +++++- baseTemplate/urls.py | 4 + baseTemplate/views.py | 82 +++++++++ 4 files changed, 304 insertions(+), 2 deletions(-) diff --git a/baseTemplate/static/baseTemplate/custom-js/system-status.js b/baseTemplate/static/baseTemplate/custom-js/system-status.js index 5fdf3370d..474691a32 100644 --- a/baseTemplate/static/baseTemplate/custom-js/system-status.js +++ b/baseTemplate/static/baseTemplate/custom-js/system-status.js @@ -647,7 +647,7 @@ app.controller('versionManagment', function ($scope, $http, $timeout) { } else { $scope.upgradelogBox = false; $scope.upgradeLog = response.data.upgradeLog; - $timeout(getUpgradeStatus, 2000); + timeout(getUpgradeStatus, 2000); } } @@ -866,4 +866,170 @@ app.controller('OnboardingCP', function ($scope, $http, $timeout, $window) { } }; +}); + +app.controller('dashboardStatsController', function ($scope, $http, $timeout) { + // Card values + $scope.totalSites = 0; + $scope.totalWPSites = 0; + + // Chart.js chart objects + var trafficChart, diskIOChart, cpuChart; + // Data arrays for live graphs + var trafficLabels = [], rxData = [], txData = []; + var diskLabels = [], readData = [], writeData = []; + var cpuLabels = [], cpuUsageData = []; + // For rate calculation + var lastRx = null, lastTx = null, lastDiskRead = null, lastDiskWrite = null, lastCPU = null; + var lastCPUTimes = null; + var pollInterval = 2000; // ms + var maxPoints = 30; + + function pollDashboardStats() { + $http.get('/base/getDashboardStats').then(function(response) { + if (response.data.status === 1) { + $scope.totalSites = response.data.total_sites; + $scope.totalWPSites = response.data.total_wp_sites; + } + }); + } + + function pollTraffic() { + $http.get('/base/getTrafficStats').then(function(response) { + if (response.data.status === 1) { + var now = new Date(); + var rx = response.data.rx_bytes; + var tx = response.data.tx_bytes; + if (lastRx !== null && lastTx !== null) { + var rxRate = (rx - lastRx) / (pollInterval / 1000); // bytes/sec + var txRate = (tx - lastTx) / (pollInterval / 1000); + trafficLabels.push(now.toLocaleTimeString()); + rxData.push(rxRate); + txData.push(txRate); + if (trafficLabels.length > maxPoints) { + trafficLabels.shift(); rxData.shift(); txData.shift(); + } + if (trafficChart) { + trafficChart.data.labels = trafficLabels.slice(); + trafficChart.data.datasets[0].data = rxData.slice(); + trafficChart.data.datasets[1].data = txData.slice(); + trafficChart.update(); + } + } + lastRx = rx; lastTx = tx; + } + }); + } + + function pollDiskIO() { + $http.get('/base/getDiskIOStats').then(function(response) { + if (response.data.status === 1) { + var now = new Date(); + var read = response.data.read_bytes; + var write = response.data.write_bytes; + if (lastDiskRead !== null && lastDiskWrite !== null) { + var readRate = (read - lastDiskRead) / (pollInterval / 1000); // bytes/sec + var writeRate = (write - lastDiskWrite) / (pollInterval / 1000); + diskLabels.push(now.toLocaleTimeString()); + readData.push(readRate); + writeData.push(writeRate); + if (diskLabels.length > maxPoints) { + diskLabels.shift(); readData.shift(); writeData.shift(); + } + if (diskIOChart) { + diskIOChart.data.labels = diskLabels.slice(); + diskIOChart.data.datasets[0].data = readData.slice(); + diskIOChart.data.datasets[1].data = writeData.slice(); + diskIOChart.update(); + } + } + lastDiskRead = read; lastDiskWrite = write; + } + }); + } + + function pollCPU() { + $http.get('/base/getCPULoadGraph').then(function(response) { + if (response.data.status === 1 && response.data.cpu_times && response.data.cpu_times.length >= 4) { + var now = new Date(); + var cpuTimes = response.data.cpu_times; + if (lastCPUTimes) { + var idle = cpuTimes[3]; + var total = cpuTimes.reduce(function(a, b) { return a + b; }, 0); + var lastIdle = lastCPUTimes[3]; + var lastTotal = lastCPUTimes.reduce(function(a, b) { return a + b; }, 0); + var idleDiff = idle - lastIdle; + var totalDiff = total - lastTotal; + var usage = totalDiff > 0 ? (100 * (1 - idleDiff / totalDiff)) : 0; + cpuLabels.push(now.toLocaleTimeString()); + cpuUsageData.push(usage); + if (cpuLabels.length > maxPoints) { + cpuLabels.shift(); cpuUsageData.shift(); + } + if (cpuChart) { + cpuChart.data.labels = cpuLabels.slice(); + cpuChart.data.datasets[0].data = cpuUsageData.slice(); + cpuChart.update(); + } + } + lastCPUTimes = cpuTimes; + } + }); + } + + function setupCharts() { + var trafficCtx = document.getElementById('trafficChart').getContext('2d'); + trafficChart = new Chart(trafficCtx, { + type: 'line', + data: { + labels: [], + datasets: [ + { label: 'RX (Bytes/sec)', data: [], borderColor: '#007bff', fill: false }, + { label: 'TX (Bytes/sec)', data: [], borderColor: '#28a745', fill: false } + ] + }, + options: { responsive: true, animation: false, scales: { y: { beginAtZero: true } } } + }); + var diskCtx = document.getElementById('diskIOChart').getContext('2d'); + diskIOChart = new Chart(diskCtx, { + type: 'line', + data: { + labels: [], + datasets: [ + { label: 'Read (Bytes/sec)', data: [], borderColor: '#17a2b8', fill: false }, + { label: 'Write (Bytes/sec)', data: [], borderColor: '#ffc107', fill: false } + ] + }, + options: { responsive: true, animation: false, scales: { y: { beginAtZero: true } } } + }); + var cpuCtx = document.getElementById('cpuChart').getContext('2d'); + cpuChart = new Chart(cpuCtx, { + type: 'line', + data: { + labels: [], + datasets: [ + { label: 'CPU Usage (%)', data: [], borderColor: '#dc3545', fill: false } + ] + }, + options: { responsive: true, animation: false, scales: { y: { beginAtZero: true, max: 100 } } } + }); + } + + // Initial setup + $timeout(function() { + setupCharts(); + pollDashboardStats(); + pollTraffic(); + pollDiskIO(); + pollCPU(); + // Start polling + function pollAll() { + pollDashboardStats(); + pollTraffic(); + pollDiskIO(); + pollCPU(); + $timeout(pollAll, pollInterval); + } + pollAll(); + }, 500); }); \ No newline at end of file diff --git a/baseTemplate/templates/baseTemplate/homePage.html b/baseTemplate/templates/baseTemplate/homePage.html index 856922552..4357a8c9d 100755 --- a/baseTemplate/templates/baseTemplate/homePage.html +++ b/baseTemplate/templates/baseTemplate/homePage.html @@ -3,7 +3,6 @@ {% block title %}{% trans "Home - CyberPanel" %}{% endblock %} {% block content %} - {% get_current_language as LANGUAGE_CODE %} @@ -13,6 +12,57 @@

{% trans "Use the tabs to navigate through the control panel." %}

+ +
+ +
+
+
Total Sites
+
+
{$ totalSites $}
+
+
+
+
+
+
WordPress Sites
+
+
{$ totalWPSites $}
+
+
+
+ +
+
+
Traffic (Bytes)
+
+ +
+
+
+ +
+
+
Disk IO (Bytes)
+
+ +
+
+
+ +
+
+
CPU Usage (%)
+
+ +
+
+
+
+ + + +
diff --git a/baseTemplate/urls.py b/baseTemplate/urls.py index 2d7230f66..dbc2cc9dc 100755 --- a/baseTemplate/urls.py +++ b/baseTemplate/urls.py @@ -15,4 +15,8 @@ urlpatterns = [ re_path(r'^runonboarding$', views.runonboarding, name='runonboarding'), re_path(r'^UpgradeStatus$', views.upgradeStatus, name='UpgradeStatus'), re_path(r'^upgradeVersion$', views.upgradeVersion, name='upgradeVersion'), + re_path(r'^getDashboardStats$', views.getDashboardStats, name='getDashboardStats'), + re_path(r'^getTrafficStats$', views.getTrafficStats, name='getTrafficStats'), + re_path(r'^getDiskIOStats$', views.getDiskIOStats, name='getDiskIOStats'), + re_path(r'^getCPULoadGraph$', views.getCPULoadGraph, name='getCPULoadGraph'), ] diff --git a/baseTemplate/views.py b/baseTemplate/views.py index 587612117..b516ed194 100755 --- a/baseTemplate/views.py +++ b/baseTemplate/views.py @@ -17,6 +17,7 @@ from manageServices.models import PDNSStatus from django.views.decorators.csrf import ensure_csrf_cookie from plogical.processUtilities import ProcessUtilities from plogical.httpProc import httpProc +from websiteFunctions.models import Websites, WPSites # Create your views here. @@ -420,3 +421,84 @@ def RestartCyberPanel(request): dic = {'status': 0, 'error_message': str(msg)} json_data = json.dumps(dic) return HttpResponse(json_data) + +def getDashboardStats(request): + try: + total_sites = Websites.objects.count() + total_wp_sites = WPSites.objects.count() + data = { + 'total_sites': total_sites, + 'total_wp_sites': total_wp_sites, + 'status': 1 + } + return HttpResponse(json.dumps(data), content_type='application/json') + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + +def getTrafficStats(request): + try: + # Get network stats from /proc/net/dev (Linux) + rx = tx = 0 + with open('/proc/net/dev', 'r') as f: + for line in f.readlines(): + if 'lo:' in line: + continue + if ':' in line: + parts = line.split() + rx += int(parts[1]) + tx += int(parts[9]) + data = { + 'rx_bytes': rx, + 'tx_bytes': tx, + 'status': 1 + } + return HttpResponse(json.dumps(data), content_type='application/json') + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + +def getDiskIOStats(request): + try: + # Parse /proc/diskstats for all disks + read_sectors = 0 + write_sectors = 0 + sector_size = 512 # Most Linux systems use 512 bytes per sector + with open('/proc/diskstats', 'r') as f: + for line in f: + parts = line.split() + if len(parts) < 14: + continue + # parts[2] is device name, skip loopback/ram devices + dev = parts[2] + if dev.startswith('loop') or dev.startswith('ram'): + continue + # 6th and 10th columns: sectors read/written + read_sectors += int(parts[5]) + write_sectors += int(parts[9]) + data = { + 'read_bytes': read_sectors * sector_size, + 'write_bytes': write_sectors * sector_size, + 'status': 1 + } + return HttpResponse(json.dumps(data), content_type='application/json') + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + +def getCPULoadGraph(request): + try: + # Parse /proc/stat for the 'cpu' line + with open('/proc/stat', 'r') as f: + for line in f: + if line.startswith('cpu '): + parts = line.strip().split() + # parts[1:] are user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice + cpu_times = [float(x) for x in parts[1:]] + break + else: + cpu_times = [] + data = { + 'cpu_times': cpu_times, + 'status': 1 + } + return HttpResponse(json.dumps(data), content_type='application/json') + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json')