mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-15 09:46:11 +01:00
dashboard stats
This commit is contained in:
@@ -647,7 +647,7 @@ app.controller('versionManagment', function ($scope, $http, $timeout) {
|
|||||||
} else {
|
} else {
|
||||||
$scope.upgradelogBox = false;
|
$scope.upgradelogBox = false;
|
||||||
$scope.upgradeLog = response.data.upgradeLog;
|
$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);
|
||||||
});
|
});
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
{% block title %}{% trans "Home - CyberPanel" %}{% endblock %}
|
{% block title %}{% trans "Home - CyberPanel" %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||||
|
|
||||||
@@ -13,6 +12,57 @@
|
|||||||
<p>{% trans "Use the tabs to navigate through the control panel." %}</p>
|
<p>{% trans "Use the tabs to navigate through the control panel." %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Dashboard Stats Section -->
|
||||||
|
<div ng-controller="dashboardStatsController" class="row" style="margin-bottom: 30px;">
|
||||||
|
<!-- Info Cards -->
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card text-white bg-primary mb-3" style="max-width: 18rem;">
|
||||||
|
<div class="card-header">Total Sites</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="font-size:2.5rem;">{$ totalSites $}</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card text-white bg-info mb-3" style="max-width: 18rem;">
|
||||||
|
<div class="card-header">WordPress Sites</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="font-size:2.5rem;">{$ totalWPSites $}</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Traffic Graph -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">Traffic (Bytes)</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="trafficChart" height="80"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Disk IO Graph -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">Disk IO (Bytes)</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="diskIOChart" height="80"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- CPU Usage Graph -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">CPU Usage (%)</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="cpuChart" height="80"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Dashboard Stats Section -->
|
||||||
|
|
||||||
|
<!-- Chart.js CDN -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
|
||||||
<!--- Hide statistics for non-admins--->
|
<!--- Hide statistics for non-admins--->
|
||||||
<div class="mx-10 col-lg-9 panel col-md-push-50">
|
<div class="mx-10 col-lg-9 panel col-md-push-50">
|
||||||
|
|||||||
@@ -15,4 +15,8 @@ urlpatterns = [
|
|||||||
re_path(r'^runonboarding$', views.runonboarding, name='runonboarding'),
|
re_path(r'^runonboarding$', views.runonboarding, name='runonboarding'),
|
||||||
re_path(r'^UpgradeStatus$', views.upgradeStatus, name='UpgradeStatus'),
|
re_path(r'^UpgradeStatus$', views.upgradeStatus, name='UpgradeStatus'),
|
||||||
re_path(r'^upgradeVersion$', views.upgradeVersion, name='upgradeVersion'),
|
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'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from manageServices.models import PDNSStatus
|
|||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from plogical.processUtilities import ProcessUtilities
|
from plogical.processUtilities import ProcessUtilities
|
||||||
from plogical.httpProc import httpProc
|
from plogical.httpProc import httpProc
|
||||||
|
from websiteFunctions.models import Websites, WPSites
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
@@ -420,3 +421,84 @@ def RestartCyberPanel(request):
|
|||||||
dic = {'status': 0, 'error_message': str(msg)}
|
dic = {'status': 0, 'error_message': str(msg)}
|
||||||
json_data = json.dumps(dic)
|
json_data = json.dumps(dic)
|
||||||
return HttpResponse(json_data)
|
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')
|
||||||
|
|||||||
Reference in New Issue
Block a user