mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-07 22:06:05 +01:00
resource usage graphs
This commit is contained in:
52
websiteFunctions/resource_monitoring.py
Normal file
52
websiteFunctions/resource_monitoring.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import psutil
|
||||||
|
import os
|
||||||
|
from plogical.processUtilities import ProcessUtilities
|
||||||
|
from plogical.acl import ACLManager
|
||||||
|
import plogical.CyberCPLogFileWriter as logging
|
||||||
|
|
||||||
|
def get_website_resource_usage(externalApp):
|
||||||
|
try:
|
||||||
|
user = externalApp
|
||||||
|
if not user:
|
||||||
|
return {'status': 0, 'error_message': 'User not found'}
|
||||||
|
|
||||||
|
# Get CPU and Memory usage using ps command
|
||||||
|
command = f"ps -u {user} -o pcpu,pmem | grep -v CPU | awk '{{cpu += $1; mem += $2}} END {{print cpu, mem}}'"
|
||||||
|
result = ProcessUtilities.outputExecutioner(command)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cpu_percent, memory_percent = map(float, result.split())
|
||||||
|
except:
|
||||||
|
cpu_percent = 0
|
||||||
|
memory_percent = 0
|
||||||
|
|
||||||
|
# Get disk usage using du command
|
||||||
|
website_path = f"/home/{user}/public_html"
|
||||||
|
if os.path.exists(website_path):
|
||||||
|
# Get disk usage in MB
|
||||||
|
command = f"du -sm {website_path} | cut -f1"
|
||||||
|
disk_used = float(ProcessUtilities.outputExecutioner(command))
|
||||||
|
|
||||||
|
# Get total disk space
|
||||||
|
command = f"df -m {website_path} | tail -1 | awk '{{print $2}}'"
|
||||||
|
disk_total = float(ProcessUtilities.outputExecutioner(command))
|
||||||
|
|
||||||
|
# Calculate percentage
|
||||||
|
disk_percent = (disk_used / disk_total) * 100 if disk_total > 0 else 0
|
||||||
|
else:
|
||||||
|
disk_used = 0
|
||||||
|
disk_total = 0
|
||||||
|
disk_percent = 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
'status': 1,
|
||||||
|
'cpu_usage': round(cpu_percent, 2),
|
||||||
|
'memory_usage': round(memory_percent, 2),
|
||||||
|
'disk_used': round(disk_used, 2),
|
||||||
|
'disk_total': round(disk_total, 2),
|
||||||
|
'disk_percent': round(disk_percent, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
except BaseException as msg:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Error in get_website_resource_usage: {str(msg)}')
|
||||||
|
return {'status': 0, 'error_message': str(msg)}
|
||||||
143
websiteFunctions/static/js/resource-monitoring.js
Normal file
143
websiteFunctions/static/js/resource-monitoring.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Resource Monitoring
|
||||||
|
let cpuChart, memoryChart, diskChart;
|
||||||
|
let cpuData = [], memoryData = [], diskData = [];
|
||||||
|
const maxDataPoints = 30;
|
||||||
|
|
||||||
|
function initializeCharts() {
|
||||||
|
const chartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return value + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
duration: 750
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// CPU Chart
|
||||||
|
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
|
||||||
|
cpuChart = new Chart(cpuCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'CPU Usage (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#2563eb',
|
||||||
|
backgroundColor: 'rgba(37, 99, 235, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: chartOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
// Memory Chart
|
||||||
|
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
|
||||||
|
memoryChart = new Chart(memoryCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'Memory Usage (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#00b894',
|
||||||
|
backgroundColor: 'rgba(0, 184, 148, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: chartOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disk Chart
|
||||||
|
const diskCtx = document.getElementById('diskChart').getContext('2d');
|
||||||
|
diskChart = new Chart(diskCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'Disk Usage (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#ff9800',
|
||||||
|
backgroundColor: 'rgba(255, 152, 0, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: chartOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCharts(data) {
|
||||||
|
const now = new Date();
|
||||||
|
const timeLabel = now.toLocaleTimeString();
|
||||||
|
|
||||||
|
// Update CPU Chart
|
||||||
|
cpuData.push(data.cpu_usage);
|
||||||
|
if (cpuData.length > maxDataPoints) cpuData.shift();
|
||||||
|
cpuChart.data.labels.push(timeLabel);
|
||||||
|
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
|
||||||
|
cpuChart.data.datasets[0].data = cpuData;
|
||||||
|
cpuChart.update('none'); // Use 'none' mode for better performance
|
||||||
|
|
||||||
|
// Update Memory Chart
|
||||||
|
memoryData.push(data.memory_usage);
|
||||||
|
if (memoryData.length > maxDataPoints) memoryData.shift();
|
||||||
|
memoryChart.data.labels.push(timeLabel);
|
||||||
|
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
|
||||||
|
memoryChart.data.datasets[0].data = memoryData;
|
||||||
|
memoryChart.update('none');
|
||||||
|
|
||||||
|
// Update Disk Chart
|
||||||
|
diskData.push(data.disk_percent);
|
||||||
|
if (diskData.length > maxDataPoints) diskData.shift();
|
||||||
|
diskChart.data.labels.push(timeLabel);
|
||||||
|
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
|
||||||
|
diskChart.data.datasets[0].data = diskData;
|
||||||
|
diskChart.update('none');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchResourceUsage() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/websites/get_website_resources/',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify({
|
||||||
|
'domain': $('#domainNamePage').text().trim()
|
||||||
|
}),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function(data) {
|
||||||
|
if (data.status === 1) {
|
||||||
|
updateCharts(data);
|
||||||
|
} else {
|
||||||
|
console.error('Error fetching resource data:', data.error_message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('Failed to fetch resource usage:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize charts when the page loads
|
||||||
|
$(document).ready(function() {
|
||||||
|
if (document.getElementById('cpuChart')) {
|
||||||
|
initializeCharts();
|
||||||
|
// Fetch resource usage every 5 seconds
|
||||||
|
setInterval(fetchResourceUsage, 5000);
|
||||||
|
// Initial fetch
|
||||||
|
fetchResourceUsage();
|
||||||
|
}
|
||||||
|
});
|
||||||
162
websiteFunctions/static/js/websiteFunctions.js
Normal file
162
websiteFunctions/static/js/websiteFunctions.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// Resource Monitoring
|
||||||
|
let cpuChart, memoryChart, diskChart;
|
||||||
|
let cpuData = [], memoryData = [], diskData = [];
|
||||||
|
const maxDataPoints = 30;
|
||||||
|
|
||||||
|
function initializeCharts() {
|
||||||
|
// CPU Chart
|
||||||
|
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
|
||||||
|
cpuChart = new Chart(cpuCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'CPU Usage (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#2563eb',
|
||||||
|
backgroundColor: 'rgba(37, 99, 235, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return value + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Memory Chart
|
||||||
|
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
|
||||||
|
memoryChart = new Chart(memoryCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'Memory Usage (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#00b894',
|
||||||
|
backgroundColor: 'rgba(0, 184, 148, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return value + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disk Chart
|
||||||
|
const diskCtx = document.getElementById('diskChart').getContext('2d');
|
||||||
|
diskChart = new Chart(diskCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'Disk Usage (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#ff9800',
|
||||||
|
backgroundColor: 'rgba(255, 152, 0, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return value + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCharts(data) {
|
||||||
|
const now = new Date();
|
||||||
|
const timeLabel = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
|
||||||
|
|
||||||
|
// Update CPU Chart
|
||||||
|
cpuData.push(data.cpu_usage);
|
||||||
|
if (cpuData.length > maxDataPoints) cpuData.shift();
|
||||||
|
cpuChart.data.labels.push(timeLabel);
|
||||||
|
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
|
||||||
|
cpuChart.data.datasets[0].data = cpuData;
|
||||||
|
cpuChart.update();
|
||||||
|
|
||||||
|
// Update Memory Chart
|
||||||
|
memoryData.push(data.memory_usage);
|
||||||
|
if (memoryData.length > maxDataPoints) memoryData.shift();
|
||||||
|
memoryChart.data.labels.push(timeLabel);
|
||||||
|
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
|
||||||
|
memoryChart.data.datasets[0].data = memoryData;
|
||||||
|
memoryChart.update();
|
||||||
|
|
||||||
|
// Update Disk Chart
|
||||||
|
diskData.push(data.disk_percent);
|
||||||
|
if (diskData.length > maxDataPoints) diskData.shift();
|
||||||
|
diskChart.data.labels.push(timeLabel);
|
||||||
|
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
|
||||||
|
diskChart.data.datasets[0].data = diskData;
|
||||||
|
diskChart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchResourceUsage() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/website/get_website_resources/',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify({
|
||||||
|
'domain': $('#domainNamePage').text()
|
||||||
|
}),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function(data) {
|
||||||
|
if (data.status === 1) {
|
||||||
|
updateCharts(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
console.error('Error fetching resource usage data');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize charts when the page loads
|
||||||
|
$(document).ready(function() {
|
||||||
|
initializeCharts();
|
||||||
|
// Fetch resource usage every 5 seconds
|
||||||
|
setInterval(fetchResourceUsage, 5000);
|
||||||
|
// Initial fetch
|
||||||
|
fetchResourceUsage();
|
||||||
|
});
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
{% extends "baseTemplate/index.html" %}
|
{% extends "baseTemplate/index.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
{% block title %}{{ domain }} - CyberPanel{% endblock %}
|
{% block title %}{{ domain }} - CyberPanel{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% load static %}
|
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||||
|
|
||||||
@@ -222,6 +222,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Resource Usage Graphs -->
|
||||||
|
<div class="cyber-card">
|
||||||
|
<div class="cyber-section-title" style="margin-bottom:28px;">
|
||||||
|
<span style="display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border:1.5px solid #dbeafe;border-radius:8px;margin-right:12px;background:#f6faff;">
|
||||||
|
<svg width="26" height="26" viewBox="0 0 26 26" fill="none"><path d="M3 3V21H23" stroke="#222b38" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 16L9 10L13 14L23 4" stroke="#222b38" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Real-time Resource Usage" %}</span>
|
||||||
|
</div>
|
||||||
|
<div class="cyber-resource-row">
|
||||||
|
<div class="cyber-resource-col">
|
||||||
|
<canvas id="cpuChart" style="width:100%;height:200px;"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="cyber-resource-col">
|
||||||
|
<canvas id="memoryChart" style="width:100%;height:200px;"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cyber-resource-row" style="margin-top:20px;">
|
||||||
|
<div class="cyber-resource-col">
|
||||||
|
<canvas id="diskChart" style="width:100%;height:200px;"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="example-box-wrapper my-10">
|
<div class="example-box-wrapper my-10">
|
||||||
<div class="panel panel-body">
|
<div class="panel panel-body">
|
||||||
<h3 class="content-box-header">
|
<h3 class="content-box-header">
|
||||||
@@ -1196,3 +1219,10 @@
|
|||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer_scripts %}
|
||||||
|
{{ block.super }}
|
||||||
|
<!-- Add Chart.js and resource monitoring script -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="{% static 'websiteFunctions/js/resource-monitoring.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -200,4 +200,6 @@ urlpatterns = [
|
|||||||
# Catch all for domains
|
# Catch all for domains
|
||||||
path('<domain>/<childDomain>', views.launchChild, name='launchChild'),
|
path('<domain>/<childDomain>', views.launchChild, name='launchChild'),
|
||||||
path('<domain>', views.domain, name='domain'),
|
path('<domain>', views.domain, name='domain'),
|
||||||
|
|
||||||
|
path('get_website_resources/', views.get_website_resources, name='get_website_resources'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from loginSystem.models import Administrator
|
from loginSystem.models import Administrator
|
||||||
from loginSystem.views import loadLoginPage
|
from loginSystem.views import loadLoginPage
|
||||||
import json
|
import json
|
||||||
import plogical.CyberCPLogFileWriter as logging
|
import plogical.CyberCPLogFileWriter as logging
|
||||||
|
from plogical.acl import ACLManager
|
||||||
|
|
||||||
|
|
||||||
from plogical.httpProc import httpProc
|
from plogical.httpProc import httpProc
|
||||||
@@ -17,6 +18,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|||||||
from .dockerviews import startContainer as docker_startContainer
|
from .dockerviews import startContainer as docker_startContainer
|
||||||
from .dockerviews import stopContainer as docker_stopContainer
|
from .dockerviews import stopContainer as docker_stopContainer
|
||||||
from .dockerviews import restartContainer as docker_restartContainer
|
from .dockerviews import restartContainer as docker_restartContainer
|
||||||
|
from .resource_monitoring import get_website_resource_usage
|
||||||
|
|
||||||
def loadWebsitesHome(request):
|
def loadWebsitesHome(request):
|
||||||
val = request.session['userID']
|
val = request.session['userID']
|
||||||
@@ -1882,4 +1884,42 @@ def restartContainer(request):
|
|||||||
return docker_restartContainer(request)
|
return docker_restartContainer(request)
|
||||||
return HttpResponse('Not allowed')
|
return HttpResponse('Not allowed')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return redirect(loadLoginPage)
|
return redirect(loadLoginPage)
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def get_website_resources(request):
|
||||||
|
try:
|
||||||
|
data = json.loads(request.body)
|
||||||
|
domain = data['domain']
|
||||||
|
|
||||||
|
# Get userID from session
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
except:
|
||||||
|
return JsonResponse({'status': 0, 'error_message': 'Unauthorized access'})
|
||||||
|
|
||||||
|
# Verify domain ownership
|
||||||
|
currentACL = ACLManager.loadedACL(userID)
|
||||||
|
|
||||||
|
from websiteFunctions.models import Websites
|
||||||
|
try:
|
||||||
|
website = Websites.objects.get(domain=domain)
|
||||||
|
except Websites.DoesNotExist:
|
||||||
|
return JsonResponse({'status': 0, 'error_message': 'Website not found'})
|
||||||
|
|
||||||
|
if ACLManager.checkOwnership(domain, admin, currentACL) == 1:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
# Get resource usage data using externalApp
|
||||||
|
resource_data = get_website_resource_usage(website.externalApp)
|
||||||
|
if resource_data['status'] == 0:
|
||||||
|
return JsonResponse(resource_data)
|
||||||
|
|
||||||
|
return JsonResponse(resource_data)
|
||||||
|
|
||||||
|
except BaseException as msg:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Error in get_website_resources: {str(msg)}')
|
||||||
|
return JsonResponse({'status': 0, 'error_message': str(msg)})
|
||||||
Reference in New Issue
Block a user