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 {
|
||||
$scope.upgradelogBox = false;
|
||||
$scope.upgradeLog = response.data.upgradeLog;
|
||||
$timeout(getUpgradeStatus, 2000);
|
||||
timeout(getUpgradeStatus, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,3 +867,169 @@ 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 content %}
|
||||
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
@@ -13,6 +12,57 @@
|
||||
<p>{% trans "Use the tabs to navigate through the control panel." %}</p>
|
||||
</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--->
|
||||
<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'^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'),
|
||||
]
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user