diff --git a/websiteFunctions/resource_monitoring.py b/websiteFunctions/resource_monitoring.py new file mode 100644 index 000000000..243dbc805 --- /dev/null +++ b/websiteFunctions/resource_monitoring.py @@ -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)} \ No newline at end of file diff --git a/websiteFunctions/static/js/resource-monitoring.js b/websiteFunctions/static/js/resource-monitoring.js new file mode 100644 index 000000000..6b0f3ed87 --- /dev/null +++ b/websiteFunctions/static/js/resource-monitoring.js @@ -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(); + } +}); \ No newline at end of file diff --git a/websiteFunctions/static/js/websiteFunctions.js b/websiteFunctions/static/js/websiteFunctions.js new file mode 100644 index 000000000..c7c4eca13 --- /dev/null +++ b/websiteFunctions/static/js/websiteFunctions.js @@ -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(); +}); \ No newline at end of file diff --git a/websiteFunctions/templates/websiteFunctions/website.html b/websiteFunctions/templates/websiteFunctions/website.html index 1cff43688..ff1cc0f51 100755 --- a/websiteFunctions/templates/websiteFunctions/website.html +++ b/websiteFunctions/templates/websiteFunctions/website.html @@ -1,9 +1,9 @@ {% extends "baseTemplate/index.html" %} {% load i18n %} +{% load static %} {% block title %}{{ domain }} - CyberPanel{% endblock %} {% block content %} - {% load static %} {% get_current_language as LANGUAGE_CODE %} @@ -222,6 +222,29 @@ + +
+
+ + + + {% trans "Real-time Resource Usage" %} +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+

@@ -1196,3 +1219,10 @@ {% endblock %} + +{% block footer_scripts %} +{{ block.super }} + + + +{% endblock %} diff --git a/websiteFunctions/urls.py b/websiteFunctions/urls.py index 263492a83..9b68b8712 100755 --- a/websiteFunctions/urls.py +++ b/websiteFunctions/urls.py @@ -200,4 +200,6 @@ urlpatterns = [ # Catch all for domains path('/', views.launchChild, name='launchChild'), path('', views.domain, name='domain'), + + path('get_website_resources/', views.get_website_resources, name='get_website_resources'), ] diff --git a/websiteFunctions/views.py b/websiteFunctions/views.py index ddfe3276e..a1cad4436 100755 --- a/websiteFunctions/views.py +++ b/websiteFunctions/views.py @@ -2,11 +2,12 @@ from django.shortcuts import redirect -from django.http import HttpResponse +from django.http import HttpResponse, JsonResponse from loginSystem.models import Administrator from loginSystem.views import loadLoginPage import json import plogical.CyberCPLogFileWriter as logging +from plogical.acl import ACLManager 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 stopContainer as docker_stopContainer from .dockerviews import restartContainer as docker_restartContainer +from .resource_monitoring import get_website_resource_usage def loadWebsitesHome(request): val = request.session['userID'] @@ -1882,4 +1884,42 @@ def restartContainer(request): return docker_restartContainer(request) return HttpResponse('Not allowed') except KeyError: - return redirect(loadLoginPage) \ No newline at end of file + 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)}) \ No newline at end of file