Files
CyberPanel/static/baseTemplate/custom-js/system-status.js
master3395 9390551ebd Firewall: fix search bar, Search button, Modify centering, system-status $ error
- Search input: add firewall-search-input class, blue focus instead of red (avoids read-only/error look)
- Search button: use btn-search with round futuristic style (match Ban IP/Overview)
- Actions column: center Modify/Unban/Delete in Firewall Rules and Banned IPs tables
- system-status.js: increment() uses document.querySelectorAll (no jQuery), fixes $ is not defined
- upgrade_modules/09_sync.sh: sync firewall static to public/static during upgrade
- to-do/FIREWALL-LOAD-CHANGES.md: doc on file locations and deploy steps
2026-02-16 03:16:18 +01:00

2547 lines
95 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Created by usman on 7/24/17.
*/
/* Utilities */
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = (cookies[i] || '').replace(/^\s+|\s+$/g, '');
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function randomPassword(length) {
var chars = "abcdefghijklmnopqrstuvwxyz!@#%^*-+ABCDEFGHIJKLMNOP1234567890";
var pass = "";
for (var x = 0; x < length; x++) {
var i = Math.floor(Math.random() * chars.length);
pass += chars.charAt(i);
}
return pass;
}
/* Utilities ends here */
/* Java script code to monitor system status */
// Create global app reference for CyberCP module so other scripts can access it
window.app = angular.module('CyberCP', []);
var app = window.app; // Local reference for this file
// MUST be first: register dashboard controller before any other setup (avoids ctrlreg when CDN/Tracking Prevention blocks scripts)
app.controller('dashboardStatsController', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
$scope.cpuUsage = 0; $scope.ramUsage = 0; $scope.diskUsage = 0; $scope.cpuCores = 0;
$scope.ramTotalMB = 0; $scope.diskTotalGB = 0; $scope.diskFreeGB = 0;
$scope.totalUsers = 0; $scope.totalSites = 0; $scope.totalWPSites = 0;
$scope.totalDBs = 0; $scope.totalEmails = 0; $scope.totalFTPUsers = 0;
$scope.topProcesses = []; $scope.sshLogins = []; $scope.sshLogs = [];
$scope.loadingTopProcesses = true; $scope.loadingSSHLogins = true; $scope.loadingSSHLogs = true;
$scope.blockedIPs = {}; $scope.blockingIP = null; $scope.securityAlerts = [];
var opts = { headers: { 'X-CSRFToken': (typeof getCookie === 'function') ? getCookie('csrftoken') : '' } };
try {
$http.get('/base/getSystemStatus', opts).then(function (r) {
if (r && r.data && r.data.status === 1) {
$scope.cpuUsage = r.data.cpuUsage || 0; $scope.ramUsage = r.data.ramUsage || 0;
$scope.diskUsage = r.data.diskUsage || 0; $scope.cpuCores = r.data.cpuCores || 0;
$scope.ramTotalMB = r.data.ramTotalMB || 0; $scope.diskTotalGB = r.data.diskTotalGB || 0;
$scope.diskFreeGB = r.data.diskFreeGB || 0;
}
});
$http.get('/base/getDashboardStats', opts).then(function (r) {
if (r && r.data && r.data.status === 1) {
$scope.totalUsers = r.data.total_users || 0; $scope.totalSites = r.data.total_sites || 0;
$scope.totalWPSites = r.data.total_wp_sites || 0; $scope.totalDBs = r.data.total_dbs || 0;
$scope.totalEmails = r.data.total_emails || 0; $scope.totalFTPUsers = r.data.total_ftp_users || 0;
}
});
$http.get('/base/getRecentSSHLogins', opts).then(function (r) {
$scope.loadingSSHLogins = false;
$scope.sshLogins = (r && r.data && r.data.logins) ? r.data.logins : [];
}, function () { $scope.loadingSSHLogins = false; $scope.sshLogins = []; });
$http.get('/base/getRecentSSHLogs', opts).then(function (r) {
$scope.loadingSSHLogs = false;
$scope.sshLogs = (r && r.data && r.data.logs) ? r.data.logs : [];
}, function () { $scope.loadingSSHLogs = false; $scope.sshLogs = []; });
$http.get('/base/getTopProcesses', opts).then(function (r) {
$scope.loadingTopProcesses = false;
$scope.topProcesses = (r && r.data && r.data.status === 1 && r.data.processes) ? r.data.processes : [];
}, function () { $scope.loadingTopProcesses = false; $scope.topProcesses = []; });
if (typeof $timeout === 'function') { $timeout(function() { /* refresh */ }, 10000); }
} catch (e) { /* ignore */ }
}]);
// Overview CPU/RAM/Disk cards use systemStatusInfo register early so data loads even if later script fails
app.controller('systemStatusInfo', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
$scope.uptimeLoaded = false;
$scope.uptime = 'Loading...';
$scope.cpuUsage = 0; $scope.ramUsage = 0; $scope.diskUsage = 0;
$scope.cpuCores = 0; $scope.ramTotalMB = 0; $scope.diskTotalGB = 0; $scope.diskFreeGB = 0;
$scope.getSystemStatus = function() { fetchStatus(); };
function fetchStatus() {
try {
var csrf = (typeof getCookie === 'function') ? getCookie('csrftoken') : '';
$http.get('/base/getSystemStatus', { headers: { 'X-CSRFToken': csrf } }).then(function (r) {
if (r && r.data && r.data.status === 1) {
$scope.cpuUsage = r.data.cpuUsage != null ? r.data.cpuUsage : 0;
$scope.ramUsage = r.data.ramUsage != null ? r.data.ramUsage : 0;
$scope.diskUsage = r.data.diskUsage != null ? r.data.diskUsage : 0;
$scope.cpuCores = r.data.cpuCores != null ? r.data.cpuCores : 0;
$scope.ramTotalMB = r.data.ramTotalMB != null ? r.data.ramTotalMB : 0;
$scope.diskTotalGB = r.data.diskTotalGB != null ? r.data.diskTotalGB : 0;
$scope.diskFreeGB = r.data.diskFreeGB != null ? r.data.diskFreeGB : 0;
$scope.uptime = r.data.uptime || 'N/A';
}
$scope.uptimeLoaded = true;
}, function() { $scope.uptime = 'Unavailable'; $scope.uptimeLoaded = true; });
if (typeof $timeout === 'function') { $timeout(fetchStatus, 60000); }
} catch (e) { $scope.uptimeLoaded = true; }
}
fetchStatus();
}]);
var globalScope;
function GlobalRespSuccess(response) {
globalScope.cyberPanelLoading = true;
if (response.data.status === 1) {
new PNotify({
title: 'Success',
text: 'Successfully executed.',
type: 'success'
});
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function GlobalRespFailed(response) {
globalScope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
function GLobalAjaxCall(http, url, data, successCallBack, failureCallBack) {
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
http.post(url, data, config).then(successCallBack, failureCallBack);
}
app.config(['$interpolateProvider', function ($interpolateProvider) {
$interpolateProvider.startSymbol('{$');
$interpolateProvider.endSymbol('$}');
}]);
app.filter('getwebsitename', function () {
return function (domain, uppercase) {
if (domain !== undefined) {
domain = domain.replace(/-/g, '');
var domainName = domain.split(".");
var finalDomainName = domainName[0];
if (finalDomainName.length > 5) {
finalDomainName = finalDomainName.substring(0, 4);
}
return finalDomainName;
}
};
});
function getWebsiteName(domain) {
if (domain !== undefined) {
domain = domain.replace(/-/g, '');
var domainName = domain.split(".");
var finalDomainName = domainName[0];
if (finalDomainName.length > 5) {
finalDomainName = finalDomainName.substring(0, 4);
}
return finalDomainName;
}
}
app.controller('systemStatusInfo', function ($scope, $http, $timeout) {
$scope.uptimeLoaded = false;
$scope.uptime = 'Loading...';
// Defaults so template never shows undefined (avoids raw {$ cpuUsage $} when API is slow or fails)
$scope.cpuUsage = 0;
$scope.ramUsage = 0;
$scope.diskUsage = 0;
$scope.cpuCores = 0;
$scope.ramTotalMB = 0;
$scope.diskTotalGB = 0;
$scope.diskFreeGB = 0;
getStuff();
$scope.getSystemStatus = function() {
getStuff();
};
function getStuff() {
url = "/base/getSystemStatus";
$http.get(url).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
$scope.cpuUsage = response.data.cpuUsage != null ? response.data.cpuUsage : 0;
$scope.ramUsage = response.data.ramUsage != null ? response.data.ramUsage : 0;
$scope.diskUsage = response.data.diskUsage != null ? response.data.diskUsage : 0;
$scope.cpuCores = response.data.cpuCores != null ? response.data.cpuCores : 0;
$scope.ramTotalMB = response.data.ramTotalMB != null ? response.data.ramTotalMB : 0;
$scope.diskTotalGB = response.data.diskTotalGB != null ? response.data.diskTotalGB : 0;
$scope.diskFreeGB = response.data.diskFreeGB != null ? response.data.diskFreeGB : 0;
if (response.data.uptime) {
$scope.uptime = response.data.uptime;
$scope.uptimeLoaded = true;
} else {
$scope.uptime = 'N/A';
$scope.uptimeLoaded = true;
}
}
function cantLoadInitialData(response) {
$scope.uptime = 'Unavailable';
$scope.uptimeLoaded = true;
$scope.cpuUsage = 0;
$scope.ramUsage = 0;
$scope.diskUsage = 0;
}
$timeout(getStuff, 60000); // Update every minute
}
});
/* Admin status */
app.controller('adminController', function ($scope, $http, $timeout) {
url = "/base/getAdminStatus";
$http.get(url).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
$scope.currentAdmin = response.data.adminName;
$scope.admin_type = response.data.admin_type;
$("#serverIPAddress").text(response.data.serverIPAddress);
if (response.data.admin === 0) {
$('.serverACL').hide();
if (!Boolean(response.data.versionManagement)) {
$('.versionManagement').hide();
}
// User Management
if (!Boolean(response.data.createNewUser)) {
$('.createNewUser').hide();
}
if (!Boolean(response.data.listUsers)) {
$('.listUsers').hide();
}
if (!Boolean(response.data.resellerCenter)) {
$('.resellerCenter').hide();
}
if (!Boolean(response.data.deleteUser)) {
$('.deleteUser').hide();
}
if (!Boolean(response.data.changeUserACL)) {
$('.changeUserACL').hide();
}
// Website Management
if (!Boolean(response.data.createWebsite)) {
$('.createWebsite').hide();
}
if (!Boolean(response.data.modifyWebsite)) {
$('.modifyWebsite').hide();
}
if (!Boolean(response.data.suspendWebsite)) {
$('.suspendWebsite').hide();
}
if (!Boolean(response.data.deleteWebsite)) {
$('.deleteWebsite').hide();
}
// Package Management
if (!Boolean(response.data.createPackage)) {
$('.createPackage').hide();
}
if (!Boolean(response.data.listPackages)) {
$('.listPackages').hide();
}
if (!Boolean(response.data.deletePackage)) {
$('.deletePackage').hide();
}
if (!Boolean(response.data.modifyPackage)) {
$('.modifyPackage').hide();
}
// Database Management
if (!Boolean(response.data.createDatabase)) {
$('.createDatabase').hide();
}
if (!Boolean(response.data.deleteDatabase)) {
$('.deleteDatabase').hide();
}
if (!Boolean(response.data.listDatabases)) {
$('.listDatabases').hide();
}
// DNS Management
if (!Boolean(response.data.dnsAsWhole)) {
$('.dnsAsWhole').hide();
}
if (!Boolean(response.data.createNameServer)) {
$('.createNameServer').hide();
}
if (!Boolean(response.data.createDNSZone)) {
$('.createDNSZone').hide();
}
if (!Boolean(response.data.deleteZone)) {
$('.deleteZone').hide();
}
if (!Boolean(response.data.addDeleteRecords)) {
$('.addDeleteRecords').hide();
}
// Email Management
if (!Boolean(response.data.emailAsWhole)) {
$('.emailAsWhole').hide();
}
if (!Boolean(response.data.listEmails)) {
$('.listEmails').hide();
}
if (!Boolean(response.data.createEmail)) {
$('.createEmail').hide();
}
if (!Boolean(response.data.deleteEmail)) {
$('.deleteEmail').hide();
}
if (!Boolean(response.data.emailForwarding)) {
$('.emailForwarding').hide();
}
if (!Boolean(response.data.changeEmailPassword)) {
$('.changeEmailPassword').hide();
}
if (!Boolean(response.data.dkimManager)) {
$('.dkimManager').hide();
}
// FTP Management
if (!Boolean(response.data.ftpAsWhole)) {
$('.ftpAsWhole').hide();
}
if (!Boolean(response.data.createFTPAccount)) {
$('.createFTPAccount').hide();
}
if (!Boolean(response.data.deleteFTPAccount)) {
$('.deleteFTPAccount').hide();
}
if (!Boolean(response.data.listFTPAccounts)) {
$('.listFTPAccounts').hide();
}
// Backup Management
if (!Boolean(response.data.createBackup)) {
$('.createBackup').hide();
}
if (!Boolean(response.data.restoreBackup)) {
$('.restoreBackup').hide();
}
if (!Boolean(response.data.addDeleteDestinations)) {
$('.addDeleteDestinations').hide();
}
if (!Boolean(response.data.scheduleBackups)) {
$('.scheduleBackups').hide();
}
if (!Boolean(response.data.remoteBackups)) {
$('.remoteBackups').hide();
}
// SSL Management
if (!Boolean(response.data.manageSSL)) {
$('.manageSSL').hide();
}
if (!Boolean(response.data.hostnameSSL)) {
$('.hostnameSSL').hide();
}
if (!Boolean(response.data.mailServerSSL)) {
$('.mailServerSSL').hide();
}
} else {
if (!Boolean(response.data.emailAsWhole)) {
$('.emailAsWhole').hide();
}
if (!Boolean(response.data.ftpAsWhole)) {
$('.ftpAsWhole').hide();
}
if (!Boolean(response.data.dnsAsWhole)) {
$('.dnsAsWhole').hide();
}
}
}
function cantLoadInitialData(response) {
}
});
/* Load average */
app.controller('loadAvg', function ($scope, $http, $timeout) {
getLoadAvg();
function getLoadAvg() {
url = "/base/getLoadAverage";
$http.get(url).then(ListLoadAvgData, cantGetLoadAvgData);
function ListLoadAvgData(response) {
$scope.one = response.data.one;
$scope.two = response.data.two;
$scope.three = response.data.three;
}
function cantGetLoadAvgData(response) {
console.log("Can't get load average data");
}
//$timeout(getStuff, 2000);
}
});
/// home page system status
app.controller('homePageStatus', function ($scope, $http, $timeout) {
getStuff();
getLoadAvg();
function getStuff() {
url = "/base/getSystemStatus";
$http.get(url).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
console.log(response.data);
$("#redcircle").removeClass();
$("#greencircle").removeClass();
$("#pinkcircle").removeClass();
$scope.cpuUsage = response.data.cpuUsage;
$scope.ramUsage = response.data.ramUsage;
$scope.diskUsage = response.data.diskUsage;
$scope.RequestProcessing = response.data.RequestProcessing;
$scope.TotalRequests = response.data.TotalRequests;
$scope.MAXCONN = response.data.MAXCONN;
$scope.MAXSSL = response.data.MAXSSL;
$scope.Avail = response.data.Avail;
$scope.AvailSSL = response.data.AvailSSL;
$("#redcircle").addClass("c100");
$("#redcircle").addClass("p" + $scope.cpuUsage);
$("#redcircle").addClass("red");
$("#greencircle").addClass("c100");
$("#greencircle").addClass("p" + $scope.ramUsage);
$("#greencircle").addClass("green");
$("#pinkcircle").addClass("c100");
$("#pinkcircle").addClass("p" + $scope.diskUsage);
$("#pinkcircle").addClass("red");
// home page cpu,ram and disk update.
var rotationMultiplier = 3.6;
// For each div that its id ends with "circle", do the following.
$("div[id$='circle']").each(function () {
// Save all of its classes in an array.
var classList = $(this).attr('class').split(/\s+/);
// Iterate over the array
for (var i = 0; i < classList.length; i++) {
/* If there's about a percentage class, take the actual percentage and apply the
css transformations in all occurences of the specified percentage class,
even for the divs without an id ending with "circle" */
if (classList[i].match("^p" + $scope.cpuUsage)) {
var rotationPercentage = $scope.cpuUsage;
var rotationDegrees = rotationMultiplier * rotationPercentage;
$('.c100.p' + rotationPercentage + ' .bar').css({
'-webkit-transform': 'rotate(' + rotationDegrees + 'deg)',
'-moz-transform': 'rotate(' + rotationDegrees + 'deg)',
'-ms-transform': 'rotate(' + rotationDegrees + 'deg)',
'-o-transform': 'rotate(' + rotationDegrees + 'deg)',
'transform': 'rotate(' + rotationDegrees + 'deg)'
});
} else if (classList[i].match("^p" + $scope.ramUsage)) {
var rotationPercentage = response.data.ramUsage;
;
var rotationDegrees = rotationMultiplier * rotationPercentage;
$('.c100.p' + rotationPercentage + ' .bar').css({
'-webkit-transform': 'rotate(' + rotationDegrees + 'deg)',
'-moz-transform': 'rotate(' + rotationDegrees + 'deg)',
'-ms-transform': 'rotate(' + rotationDegrees + 'deg)',
'-o-transform': 'rotate(' + rotationDegrees + 'deg)',
'transform': 'rotate(' + rotationDegrees + 'deg)'
});
} else if (classList[i].match("^p" + $scope.diskUsage)) {
var rotationPercentage = response.data.diskUsage;
;
var rotationDegrees = rotationMultiplier * rotationPercentage;
$('.c100.p' + rotationPercentage + ' .bar').css({
'-webkit-transform': 'rotate(' + rotationDegrees + 'deg)',
'-moz-transform': 'rotate(' + rotationDegrees + 'deg)',
'-ms-transform': 'rotate(' + rotationDegrees + 'deg)',
'-o-transform': 'rotate(' + rotationDegrees + 'deg)',
'transform': 'rotate(' + rotationDegrees + 'deg)'
});
}
}
});
}
function cantLoadInitialData(response) {
console.log("not good");
}
$timeout(getStuff, 2000);
}
function getLoadAvg() {
url = "/base/getLoadAverage";
$http.get(url).then(ListLoadAvgData, cantGetLoadAvgData);
function ListLoadAvgData(response) {
$scope.one = response.data.one;
$scope.two = response.data.two;
$scope.three = response.data.three;
//document.getElementById("load1").innerHTML = $scope.one;
//document.getElementById("load2").innerHTML = $scope.two;
//document.getElementById("load3").innerHTML = $scope.three;
}
function cantGetLoadAvgData(response) {
console.log("Can't get load average data");
}
$timeout(getLoadAvg, 2000);
}
});
////////////
function increment() {
var boxes = document.querySelectorAll ? document.querySelectorAll('.box') : [];
for (var i = 0; i < boxes.length; i++) boxes[i].style.display = 'none';
setTimeout(function () {
for (var j = 0; j < boxes.length; j++) boxes[j].style.display = '';
}, 100);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', increment);
} else {
increment();
}
////////////
app.controller('versionManagment', function ($scope, $http, $timeout) {
$scope.upgradeLoading = true;
$scope.upgradelogBox = true;
$scope.updateError = true;
$scope.updateStarted = true;
$scope.updateFinish = true;
$scope.couldNotConnect = true;
var upgradeStatusTimer = null;
$scope.upgrade = function () {
$scope.upgradeLoading = false;
$scope.updateError = true;
$scope.updateStarted = true;
$scope.updateFinish = true;
$scope.couldNotConnect = true;
var data = {
branchSelect: document.getElementById("branchSelect").value,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
url = "/base/upgrade";
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
if (response.data.upgrade == 1) {
$scope.upgradeLoading = true;
$scope.updateError = true;
$scope.updateStarted = false;
$scope.updateFinish = true;
$scope.couldNotConnect = true;
getUpgradeStatus();
} else {
$scope.updateError = false;
$scope.updateStarted = true;
$scope.updateFinish = true;
$scope.couldNotConnect = true;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialData(response) {
$scope.updateError = true;
$scope.updateStarted = true;
$scope.updateFinish = true;
$scope.couldNotConnect = false;
}
}
function getUpgradeStatus() {
$scope.upgradeLoading = false;
url = "/base/UpgradeStatus";
var data = {};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
console.log(response.data.upgradeLog);
if (response.data.upgradeStatus === 1) {
if (response.data.finished === 1) {
if (upgradeStatusTimer) $timeout.cancel(upgradeStatusTimer);
upgradeStatusTimer = null;
$scope.upgradelogBox = false;
$scope.upgradeLog = response.data.upgradeLog;
$scope.upgradeLoading = true;
$scope.updateError = true;
$scope.updateStarted = true;
$scope.updateFinish = false;
$scope.couldNotConnect = true;
} else {
$scope.upgradelogBox = false;
$scope.upgradeLog = response.data.upgradeLog;
upgradeStatusTimer = $timeout(getUpgradeStatus, 2000);
}
}
}
function cantLoadInitialDatas(response) {
$scope.updateError = true;
$scope.updateStarted = true;
$scope.updateFinish = true;
$scope.couldNotConnect = false;
}
};
});
app.controller('designtheme', function ($scope, $http, $timeout) {
$scope.themeloading = true;
$scope.getthemedata = function () {
$scope.themeloading = false;
url = "/base/getthemedata";
var data = {
package: "helo world",
Themename: $('#stheme').val(),
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(Listgetthemedata, cantgetthemedata);
function Listgetthemedata(response) {
$scope.themeloading = true;
if (response.data.status === 1) {
document.getElementById('appendthemedata').innerHTML = "";
$("#appendthemedata").val(response.data.csscontent)
} else {
alert(response.data.error_message)
}
}
function cantgetthemedata(response) {
$scope.themeloading = true;
console.log(response);
}
//$timeout(getStuff, 2000);
};
});
app.controller('OnboardingCP', function ($scope, $http, $timeout, $window) {
$scope.cyberpanelLoading = true;
$scope.ExecutionStatus = true;
$scope.ReportStatus = true;
$scope.OnboardineDone = true;
var statusTimer = null;
function statusFunc() {
$scope.cyberpanelLoading = false;
$scope.ExecutionStatus = false;
var url = "/emailPremium/statusFunc";
var data = {
statusFile: statusFile
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
if (response.data.status === 1) {
if (response.data.abort === 1) {
$scope.functionProgress = {"width": "100%"};
$scope.functionStatus = response.data.currentStatus;
$scope.cyberpanelLoading = true;
$scope.OnboardineDone = false;
if (statusTimer) {
$timeout.cancel(statusTimer);
statusTimer = null;
}
} else {
$scope.functionProgress = {"width": response.data.installationProgress + "%"};
$scope.functionStatus = response.data.currentStatus;
statusTimer = $timeout(statusFunc, 3000);
}
} else {
$scope.cyberpanelLoading = true;
$scope.functionStatus = response.data.error_message;
$scope.functionProgress = {"width": response.data.installationProgress + "%"};
if (statusTimer) {
$timeout.cancel(statusTimer);
statusTimer = null;
}
}
}
function cantLoadInitialData(response) {
$scope.functionProgress = {"width": response.data.installationProgress + "%"};
$scope.functionStatus = 'Could not connect to server, please refresh this page.';
$timeout.cancel();
}
}
$scope.RunOnboarding = function () {
$scope.cyberpanelLoading = false;
$scope.OnboardineDone = true;
var url = "/base/runonboarding";
var data = {
hostname: $scope.hostname,
rDNSCheck: $scope.rDNSCheck
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
$scope.cyberpanelLoading = true;
if (response.data.status === 1) {
statusFile = response.data.tempStatusPath;
statusFunc();
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialData(response) {
$scope.cyberpanelLoading = true;
new PNotify({
title: 'Error',
text: 'Could not connect to server, please refresh this page.',
type: 'error'
});
}
};
$scope.RestartCyberPanel = function () {
$scope.cyberpanelLoading = false;
var url = "/base/RestartCyberPanel";
var data = {
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
$scope.cyberpanelLoading = true;
new PNotify({
title: 'Success',
text: 'Refresh your browser after 3 seconds to fetch new SSL.',
type: 'success'
});
function ListInitialData(response) {
$scope.cyberpanelLoading = true;
if (response.data.status === 1) {
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialData(response) {
$scope.cyberpanelLoading = true;
new PNotify({
title: 'Error',
text: 'Could not connect to server, please refresh this page.',
type: 'error'
});
}
};
});
// Single implementation registered under both names for compatibility (some templates/caches use newDashboardStat)
var dashboardStatsControllerFn = function ($scope, $http, $timeout) {
console.log('dashboardStatsController initialized');
// Card values
$scope.totalUsers = 0;
$scope.totalSites = 0;
$scope.totalWPSites = 0;
$scope.totalDBs = 0;
$scope.totalEmails = 0;
$scope.totalFTPUsers = 0;
// Hide system charts for non-admin users
$scope.hideSystemCharts = false;
// Top Processes
$scope.topProcesses = [];
$scope.loadingTopProcesses = true;
$scope.errorTopProcesses = '';
$scope.refreshTopProcesses = function() {
$scope.loadingTopProcesses = true;
var h = { headers: { 'X-CSRFToken': (typeof getCookie === 'function') ? getCookie('csrftoken') : '' } };
$http.get('/base/getTopProcesses', h).then(function (response) {
$scope.loadingTopProcesses = false;
if (response.data && response.data.status === 1 && response.data.processes) {
$scope.topProcesses = response.data.processes;
} else {
$scope.topProcesses = [];
}
}, function (err) {
$scope.loadingTopProcesses = false;
$scope.errorTopProcesses = 'Failed to load top processes.';
});
};
// SSH Logins
$scope.sshLogins = [];
$scope.loadingSSHLogins = true;
$scope.errorSSHLogins = '';
$scope.refreshSSHLogins = function() {
$scope.loadingSSHLogins = true;
var h = { headers: { 'X-CSRFToken': (typeof getCookie === 'function') ? getCookie('csrftoken') : '' } };
$http.get('/base/getRecentSSHLogins', h).then(function (response) {
$scope.loadingSSHLogins = false;
if (response.data && response.data.logins) {
$scope.sshLogins = response.data.logins;
// Debug: Log first login to see structure
if ($scope.sshLogins.length > 0) {
console.log('First SSH login object:', $scope.sshLogins[0]);
console.log('IP field:', $scope.sshLogins[0].ip);
console.log('All keys:', Object.keys($scope.sshLogins[0]));
}
} else {
$scope.sshLogins = [];
}
}, function (err) {
$scope.loadingSSHLogins = false;
$scope.errorSSHLogins = 'Failed to load SSH logins.';
console.error('Failed to load SSH logins:', err);
});
};
// SSH Logs
$scope.sshLogs = [];
$scope.loadingSSHLogs = true;
$scope.errorSSHLogs = '';
$scope.securityAlerts = [];
$scope.loadingSecurityAnalysis = false;
$scope.refreshSSHLogs = function() {
$scope.loadingSSHLogs = true;
var h = { headers: { 'X-CSRFToken': (typeof getCookie === 'function') ? getCookie('csrftoken') : '' } };
$http.get('/base/getRecentSSHLogs', h).then(function (response) {
$scope.loadingSSHLogs = false;
if (response.data && response.data.logs) {
$scope.sshLogs = response.data.logs;
// Analyze logs for security issues
$scope.analyzeSSHSecurity();
} else {
$scope.sshLogs = [];
}
}, function (err) {
$scope.loadingSSHLogs = false;
$scope.errorSSHLogs = 'Failed to load SSH logs.';
});
};
// Security Analysis
$scope.showAddonRequired = false;
$scope.addonInfo = {};
// IP Blocking functionality
$scope.blockingIP = null;
$scope.blockedIPs = {};
$scope.analyzeSSHSecurity = function() {
$scope.loadingSecurityAnalysis = true;
$scope.showAddonRequired = false;
$http.post('/base/analyzeSSHSecurity', {}).then(function (response) {
$scope.loadingSecurityAnalysis = false;
if (response.data) {
if (response.data.addon_required) {
$scope.showAddonRequired = true;
$scope.addonInfo = response.data;
$scope.securityAlerts = [];
} else if (response.data.status === 1) {
$scope.securityAlerts = response.data.alerts;
$scope.showAddonRequired = false;
}
}
}, function (err) {
$scope.loadingSecurityAnalysis = false;
});
};
$scope.blockIPAddress = function(ipAddress) {
try {
console.log('========================================');
console.log('=== blockIPAddress CALLED ===');
console.log('========================================');
console.log('blockIPAddress called with:', ipAddress);
console.log('ipAddress type:', typeof ipAddress);
console.log('ipAddress value:', ipAddress);
console.log('$scope:', $scope);
console.log('$scope.blockIPAddress:', typeof $scope.blockIPAddress);
// Validate IP address parameter
if (!ipAddress) {
console.error('No IP address provided:', ipAddress);
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Error',
text: 'No IP address provided',
type: 'error',
delay: 5000
});
}
return;
}
// Ensure it's a string and trim it
ipAddress = String(ipAddress).trim();
// Validate after trimming
if (!ipAddress || ipAddress === '' || ipAddress === 'undefined' || ipAddress === 'null') {
console.error('IP address is empty or invalid after trim:', ipAddress);
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Error',
text: 'Invalid IP address provided: ' + ipAddress,
type: 'error',
delay: 5000
});
}
return;
}
// Basic IP format validation
var ipPattern = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
if (!ipPattern.test(ipAddress)) {
console.error('IP address format is invalid:', ipAddress);
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Error',
text: 'Invalid IP address format: ' + ipAddress,
type: 'error',
delay: 5000
});
}
return;
}
// Prevent duplicate requests
if ($scope.blockingIP === ipAddress) {
console.log('Already processing IP:', ipAddress);
return; // Already processing this IP
}
// Check if already blocked
if ($scope.blockedIPs && $scope.blockedIPs[ipAddress]) {
console.log('IP already blocked:', ipAddress);
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Info',
text: `IP address ${ipAddress} is already banned`,
type: 'info',
delay: 3000
});
}
return;
}
// Set blocking flag to prevent duplicate requests
$scope.blockingIP = ipAddress;
// Use the new Banned IPs system instead of the old blockIPAddress
var data = {
ip: ipAddress,
reason: 'Brute force attack detected from SSH Security Analysis',
duration: 'permanent'
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
console.log('Sending ban IP request:', data);
console.log('CSRF Token:', getCookie('csrftoken'));
console.log('Config:', config);
$http.post('/firewall/addBannedIP', data, config).then(function (response) {
console.log('=== addBannedIP SUCCESS ===');
console.log('Full response:', response);
console.log('response.data:', response.data);
console.log('response.data type:', typeof response.data);
console.log('response.status:', response.status);
// Reset blocking flag
$scope.blockingIP = null;
// Apply scope changes
if (!$scope.$$phase && !$scope.$root.$$phase) {
$scope.$apply();
}
// Handle both JSON string and object responses
var responseData = response.data;
if (typeof responseData === 'string') {
try {
responseData = JSON.parse(responseData);
console.log('Parsed responseData from string:', responseData);
} catch (e) {
console.error('Failed to parse response as JSON:', e);
var errorMsg = responseData && responseData.length ? responseData : 'Failed to block IP address';
if (typeof PNotify !== 'undefined') {
new PNotify({ title: 'Error', text: errorMsg, type: 'error', delay: 5000 });
}
$scope.blockingIP = null;
return;
}
}
console.log('Final responseData:', responseData);
console.log('responseData.status:', responseData ? responseData.status : 'undefined');
console.log('responseData.message:', responseData ? responseData.message : 'undefined');
console.log('responseData.error_message:', responseData ? responseData.error_message : 'undefined');
// Check for success (status === 1 or status === '1')
if (responseData && (responseData.status === 1 || responseData.status === '1')) {
// Mark IP as blocked
if (!$scope.blockedIPs) {
$scope.blockedIPs = {};
}
$scope.blockedIPs[ipAddress] = true;
// Show success notification
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'IP Address Banned',
text: `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`,
type: 'success',
delay: 5000
});
}
// Refresh security analysis to update alerts
if ($scope.analyzeSSHSecurity) {
$scope.analyzeSSHSecurity();
}
// Apply scope changes
if (!$scope.$$phase && !$scope.$root.$$phase) {
$scope.$apply();
}
} else {
// Show error notification
var errorMsg = 'Failed to block IP address';
if (responseData && responseData.error_message) {
errorMsg = responseData.error_message;
} else if (responseData && responseData.error) {
errorMsg = responseData.error;
} else if (responseData && responseData.message) {
errorMsg = responseData.message;
} else if (responseData) {
errorMsg = JSON.stringify(responseData);
}
console.error('Ban IP failed:', errorMsg);
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Error',
text: errorMsg,
type: 'error',
delay: 5000
});
}
}
}, function (err) {
$scope.blockingIP = null;
console.error('addBannedIP error:', err);
console.error('Error status:', err.status);
console.error('Error statusText:', err.statusText);
console.error('Error data:', err.data);
// Prevent showing duplicate error notifications
if ($scope.lastErrorIP === ipAddress && $scope.lastErrorTime && (Date.now() - $scope.lastErrorTime) < 2000) {
console.log('Skipping duplicate error notification for IP:', ipAddress);
return;
}
$scope.lastErrorIP = ipAddress;
$scope.lastErrorTime = Date.now();
var errorMessage = 'Failed to block IP address';
var errData = err.data;
if (typeof errData === 'string') {
try {
errData = JSON.parse(errData);
} catch (e) {
if (errData && errData.length) {
errorMessage = errData.length > 200 ? errData.substring(0, 200) + '...' : errData;
}
}
}
if (errData && typeof errData === 'object') {
errorMessage = errData.error_message || errData.error || errData.message || errorMessage;
} else if (err.status) {
errorMessage = 'HTTP ' + err.status + ': ' + (errorMessage);
}
console.error('Final error message:', errorMessage);
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Error',
text: errorMessage,
type: 'error',
delay: 5000
});
}
});
} catch (e) {
console.error('========================================');
console.error('=== ERROR in blockIPAddress ===');
console.error('========================================');
console.error('Error:', e);
console.error('Error message:', e.message);
console.error('Error stack:', e.stack);
$scope.blockingIP = null;
if (typeof PNotify !== 'undefined') {
new PNotify({
title: 'Error',
text: 'An error occurred while trying to ban the IP address: ' + (e.message || String(e)),
type: 'error',
delay: 5000
});
}
}
};
// Ban IP from SSH Logs
$scope.banIPFromSSHLog = function(ipAddress) {
if (!ipAddress) {
new PNotify({
title: 'Error',
text: 'No IP address provided',
type: 'error',
delay: 5000
});
return;
}
if ($scope.blockingIP === ipAddress) {
return; // Already processing
}
if ($scope.blockedIPs[ipAddress]) {
new PNotify({
title: 'Info',
text: `IP address ${ipAddress} is already banned`,
type: 'info',
delay: 3000
});
return;
}
$scope.blockingIP = ipAddress;
// Use the Banned IPs system
var data = {
ip: ipAddress,
reason: 'Suspicious activity detected from SSH logs',
duration: 'permanent'
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post('/firewall/addBannedIP', data, config).then(function (response) {
$scope.blockingIP = null;
if (response.data && response.data.status === 1) {
// Mark IP as blocked
$scope.blockedIPs[ipAddress] = true;
// Show success notification
new PNotify({
title: 'IP Address Banned',
text: `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`,
type: 'success',
delay: 5000
});
// Refresh SSH logs to update the UI
$scope.refreshSSHLogs();
} else {
// Show error notification
var errorMsg = 'Failed to ban IP address';
if (response.data && response.data.error_message) {
errorMsg = response.data.error_message;
} else if (response.data && response.data.error) {
errorMsg = response.data.error;
}
new PNotify({
title: 'Error',
text: errorMsg,
type: 'error',
delay: 5000
});
}
}, function (err) {
$scope.blockingIP = null;
var errorMessage = 'Failed to ban IP address';
if (err.data && err.data.error_message) {
errorMessage = err.data.error_message;
} else if (err.data && err.data.error) {
errorMessage = err.data.error;
} else if (err.data && err.data.message) {
errorMessage = err.data.message;
}
new PNotify({
title: 'Error',
text: errorMessage,
type: 'error',
delay: 5000
});
});
};
// Ban IP from SSH Logs
$scope.banIPFromSSHLog = function(ipAddress) {
if (!ipAddress) {
new PNotify({
title: 'Error',
text: 'No IP address provided',
type: 'error',
delay: 5000
});
return;
}
if ($scope.blockingIP === ipAddress) {
return; // Already processing
}
if ($scope.blockedIPs[ipAddress]) {
new PNotify({
title: 'Info',
text: `IP address ${ipAddress} is already banned`,
type: 'info',
delay: 3000
});
return;
}
$scope.blockingIP = ipAddress;
// Use the Banned IPs system
var data = {
ip: ipAddress,
reason: 'Suspicious activity detected from SSH logs',
duration: 'permanent'
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post('/firewall/addBannedIP', data, config).then(function (response) {
$scope.blockingIP = null;
if (response.data && response.data.status === 1) {
// Mark IP as blocked
$scope.blockedIPs[ipAddress] = true;
// Show success notification
new PNotify({
title: 'IP Address Banned',
text: `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`,
type: 'success',
delay: 5000
});
// Refresh SSH logs to update the UI
$scope.refreshSSHLogs();
} else {
// Show error notification
var errorMsg = 'Failed to ban IP address';
if (response.data && response.data.error_message) {
errorMsg = response.data.error_message;
} else if (response.data && response.data.error) {
errorMsg = response.data.error;
}
new PNotify({
title: 'Error',
text: errorMsg,
type: 'error',
delay: 5000
});
}
}, function (err) {
$scope.blockingIP = null;
var errorMessage = 'Failed to ban IP address';
if (err.data && err.data.error_message) {
errorMessage = err.data.error_message;
} else if (err.data && err.data.error) {
errorMessage = err.data.error;
} else if (err.data && err.data.message) {
errorMessage = err.data.message;
}
new PNotify({
title: 'Error',
text: errorMessage,
type: 'error',
delay: 5000
});
});
};
// Initial fetch
$scope.refreshTopProcesses();
$scope.refreshSSHLogins();
$scope.refreshSSHLogs();
// 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;
// Expose so switchTab can create charts on first tab click if they weren't created at load
window.cyberPanelSetupChartsIfNeeded = function() {
if (window.trafficChart && window.diskIOChart && window.cpuChart) return;
try { setupCharts(); } catch (e) { console.error('cyberPanelSetupChartsIfNeeded:', e); }
};
function pollDashboardStats() {
console.log('[dashboardStatsController] pollDashboardStats() called');
console.log('[dashboardStatsController] Fetching dashboard stats from /base/getDashboardStats');
$http({
method: 'GET',
url: '/base/getDashboardStats',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': getCookie('csrftoken')
}
}).then(function(response) {
console.log('[dashboardStatsController] pollDashboardStats SUCCESS callback called');
console.log('[dashboardStatsController] Dashboard stats response received:', response);
console.log('[dashboardStatsController] Response status:', response.status);
console.log('[dashboardStatsController] Response data:', response.data);
if (response.data && response.data.status === 1) {
$scope.totalUsers = response.data.total_users || 0;
$scope.totalSites = response.data.total_sites || 0;
$scope.totalWPSites = response.data.total_wp_sites || 0;
$scope.totalDBs = response.data.total_dbs || 0;
$scope.totalEmails = response.data.total_emails || 0;
$scope.totalFTPUsers = response.data.total_ftp_users || 0;
console.log('[dashboardStatsController] Dashboard stats updated:', {
users: $scope.totalUsers,
sites: $scope.totalSites,
wp: $scope.totalWPSites,
dbs: $scope.totalDBs,
emails: $scope.totalEmails,
ftp: $scope.totalFTPUsers
});
// No $apply needed - $http already triggers digest cycle
} else {
// Set default values if request fails
console.error('[dashboardStatsController] Failed to load dashboard stats - invalid response:', response.data);
$scope.$apply(function() {
$scope.totalUsers = 0;
$scope.totalSites = 0;
$scope.totalWPSites = 0;
$scope.totalDBs = 0;
$scope.totalEmails = 0;
$scope.totalFTPUsers = 0;
});
}
}, function(error) {
console.error('[dashboardStatsController] Error loading dashboard stats:', error);
console.error('[dashboardStatsController] Error status:', error.status);
console.error('[dashboardStatsController] Error data:', error.data);
// Set default values on error (no $apply needed - error callback also triggers digest)
$scope.totalUsers = 0;
$scope.totalSites = 0;
$scope.totalWPSites = 0;
$scope.totalDBs = 0;
$scope.totalEmails = 0;
$scope.totalFTPUsers = 0;
});
}
function pollTraffic() {
$http.get('/base/getTrafficStats').then(function(response) {
if (!response || !response.data) return;
if (response.data.admin_only) {
// Hide chart for non-admin users
$scope.hideSystemCharts = true;
return;
}
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();
console.log('trafficChart updated:', trafficChart.data.labels, trafficChart.data.datasets[0].data, trafficChart.data.datasets[1].data);
}
} else {
// First poll, push zero data point
trafficLabels.push(now.toLocaleTimeString());
rxData.push(0);
txData.push(0);
if (trafficChart) {
trafficChart.data.labels = trafficLabels.slice();
trafficChart.data.datasets[0].data = rxData.slice();
trafficChart.data.datasets[1].data = txData.slice();
trafficChart.update();
console.log('trafficChart first update:', trafficChart.data.labels, trafficChart.data.datasets[0].data, trafficChart.data.datasets[1].data);
setTimeout(function() {
if (window.trafficChart) {
window.trafficChart.resize();
window.trafficChart.update();
console.log('trafficChart forced resize/update after first poll.');
}
}, 1000);
}
}
lastRx = rx; lastTx = tx;
} else {
console.warn('pollTraffic: no data or status', response.data);
}
}).catch(function(err) {
console.warn('pollTraffic failed:', err);
});
}
function pollDiskIO() {
$http.get('/base/getDiskIOStats').then(function(response) {
if (!response || !response.data) return;
if (response.data.admin_only) {
// Hide chart for non-admin users
$scope.hideSystemCharts = true;
return;
}
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();
}
} else {
// First poll, push zero data point
diskLabels.push(now.toLocaleTimeString());
readData.push(0);
writeData.push(0);
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;
}
}).catch(function(err) {
console.warn('pollDiskIO failed:', err);
});
}
function pollCPU() {
$http.get('/base/getCPULoadGraph').then(function(response) {
if (!response || !response.data) return;
if (response.data.admin_only) {
// Hide chart for non-admin users
$scope.hideSystemCharts = true;
return;
}
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();
}
} else {
// First poll, push zero data point
cpuLabels.push(now.toLocaleTimeString());
cpuUsageData.push(0);
if (cpuChart) {
cpuChart.data.labels = cpuLabels.slice();
cpuChart.data.datasets[0].data = cpuUsageData.slice();
cpuChart.update();
}
}
lastCPUTimes = cpuTimes;
}
}).catch(function(err) {
console.warn('pollCPU failed:', err);
});
}
function setupCharts(retryCount) {
retryCount = retryCount || 0;
if (typeof Chart === 'undefined') {
if (retryCount < 3) {
$timeout(function() { setupCharts(retryCount + 1); }, 400);
}
return;
}
var trafficEl = document.getElementById('trafficChart');
if (!trafficEl) {
if (retryCount < 5) {
$timeout(function() { setupCharts(retryCount + 1); }, 300);
}
return;
}
try {
var trafficCtx = trafficEl.getContext('2d');
} catch (e) {
console.error('trafficChart getContext failed:', e);
return;
}
try {
trafficChart = new Chart(trafficCtx, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'Download',
data: [],
borderColor: '#5b5fcf',
backgroundColor: 'rgba(91,95,207,0.1)',
pointBackgroundColor: '#5b5fcf',
pointBorderColor: '#5b5fcf',
pointRadius: 3,
pointHoverRadius: 5,
borderWidth: 2,
tension: 0.4,
fill: true
},
{
label: 'Upload',
data: [],
borderColor: '#4a90e2',
backgroundColor: 'rgba(74,144,226,0.1)',
pointBackgroundColor: '#4a90e2',
pointBorderColor: '#4a90e2',
pointRadius: 3,
pointHoverRadius: 5,
borderWidth: 2,
tension: 0.4,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 0 },
plugins: {
legend: {
display: true,
position: 'top',
labels: {
font: { size: 12, weight: '600' },
color: '#64748b',
usePointStyle: true,
padding: 20
}
},
title: { display: false },
tooltip: {
enabled: true,
mode: 'index',
intersect: false,
backgroundColor: 'rgba(255,255,255,0.95)',
titleColor: '#2f3640',
bodyColor: '#64748b',
borderColor: '#e8e9ff',
borderWidth: 1,
cornerRadius: 8,
padding: 12
}
},
interaction: { mode: 'nearest', axis: 'x', intersect: false },
scales: {
x: {
grid: { color: '#f0f0ff', drawBorder: false },
ticks: {
font: { size: 11 },
color: '#94a3b8',
maxTicksLimit: 8
}
},
y: {
beginAtZero: true,
grid: { color: '#f0f0ff', drawBorder: false },
ticks: {
font: { size: 11 },
color: '#94a3b8'
}
}
},
layout: { padding: { top: 10, bottom: 10, left: 10, right: 10 } }
}
});
window.trafficChart = trafficChart;
setTimeout(function() {
if (window.trafficChart) {
window.trafficChart.resize();
window.trafficChart.update();
console.log('trafficChart resized and updated after setup.');
}
}, 500);
var diskEl = document.getElementById('diskIOChart');
if (!diskEl) { console.warn('diskIOChart canvas not found'); return; }
var diskCtx = diskEl.getContext('2d');
diskIOChart = new Chart(diskCtx, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'Read',
data: [],
borderColor: '#5b5fcf',
backgroundColor: 'rgba(91,95,207,0.1)',
pointBackgroundColor: '#5b5fcf',
pointBorderColor: '#5b5fcf',
pointRadius: 3,
pointHoverRadius: 5,
borderWidth: 2,
tension: 0.4,
fill: true
},
{
label: 'Write',
data: [],
borderColor: '#e74c3c',
backgroundColor: 'rgba(231,76,60,0.1)',
pointBackgroundColor: '#e74c3c',
pointBorderColor: '#e74c3c',
pointRadius: 3,
pointHoverRadius: 5,
borderWidth: 2,
tension: 0.4,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 0 },
plugins: {
legend: {
display: true,
position: 'top',
labels: {
font: { size: 12, weight: '600' },
color: '#64748b',
usePointStyle: true,
padding: 20
}
},
title: { display: false },
tooltip: {
enabled: true,
mode: 'index',
intersect: false,
backgroundColor: 'rgba(255,255,255,0.95)',
titleColor: '#2f3640',
bodyColor: '#64748b',
borderColor: '#e8e9ff',
borderWidth: 1,
cornerRadius: 8,
padding: 12
}
},
interaction: { mode: 'nearest', axis: 'x', intersect: false },
scales: {
x: {
grid: { color: '#f0f0ff', drawBorder: false },
ticks: {
font: { size: 11 },
color: '#94a3b8',
maxTicksLimit: 8
}
},
y: {
beginAtZero: true,
grid: { color: '#f0f0ff', drawBorder: false },
ticks: {
font: { size: 11 },
color: '#94a3b8'
}
}
},
layout: { padding: { top: 10, bottom: 10, left: 10, right: 10 } }
}
});
window.diskIOChart = diskIOChart;
var cpuEl = document.getElementById('cpuChart');
if (!cpuEl) { console.warn('cpuChart canvas not found'); return; }
var cpuCtx = cpuEl.getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'CPU Usage (%)',
data: [],
borderColor: '#5b5fcf',
backgroundColor: 'rgba(91,95,207,0.1)',
pointBackgroundColor: '#5b5fcf',
pointBorderColor: '#5b5fcf',
pointRadius: 3,
pointHoverRadius: 5,
borderWidth: 2,
tension: 0.4,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 0 },
plugins: {
legend: {
display: true,
position: 'top',
labels: {
font: { size: 12, weight: '600' },
color: '#64748b',
usePointStyle: true,
padding: 20
}
},
title: { display: false },
tooltip: {
enabled: true,
mode: 'index',
intersect: false,
backgroundColor: 'rgba(255,255,255,0.95)',
titleColor: '#2f3640',
bodyColor: '#64748b',
borderColor: '#e8e9ff',
borderWidth: 1,
cornerRadius: 8,
padding: 12
}
},
interaction: { mode: 'nearest', axis: 'x', intersect: false },
scales: {
x: {
grid: { color: '#f0f0ff', drawBorder: false },
ticks: {
font: { size: 11 },
color: '#94a3b8',
maxTicksLimit: 8
}
},
y: {
beginAtZero: true,
max: 100,
grid: { color: '#f0f0ff', drawBorder: false },
ticks: {
font: { size: 11 },
color: '#94a3b8'
}
}
},
layout: { padding: { top: 10, bottom: 10, left: 10, right: 10 } }
}
});
window.cpuChart = cpuChart;
} catch (e) {
console.error('setupCharts error:', e);
}
// Redraw charts on tab shown
$("a[data-toggle='tab']").on('shown.bs.tab', function (e) {
setTimeout(function() {
if (trafficChart) trafficChart.resize();
if (diskIOChart) diskIOChart.resize();
if (cpuChart) cpuChart.resize();
}, 100);
});
// Also handle custom tab switching
document.addEventListener('DOMContentLoaded', function() {
var tabs = document.querySelectorAll('a[data-toggle="tab"]');
tabs.forEach(function(tab) {
tab.addEventListener('click', function(e) {
setTimeout(function() {
if (trafficChart) trafficChart.resize();
if (diskIOChart) diskIOChart.resize();
if (cpuChart) cpuChart.resize();
}, 200);
});
});
});
}
// Initial setup - fetch stats immediately
pollDashboardStats();
$scope.refreshTopProcesses();
$scope.refreshSSHLogins();
$scope.refreshSSHLogs();
$timeout(function() {
// Always create charts so Traffic/Disk IO/CPU tabs have something to show; admin check only affects hideSystemCharts
setupCharts();
$http.get('/base/getAdminStatus').then(function(response) {
if (response.data && (response.data.admin === 1 || response.data.admin === true)) {
$scope.hideSystemCharts = false;
} else {
$scope.hideSystemCharts = true;
}
}).catch(function(err) {
console.warn('getAdminStatus failed:', err);
$scope.hideSystemCharts = true;
});
// Start polling for all stats (data feeds charts)
function pollAll() {
pollDashboardStats();
pollTraffic();
pollDiskIO();
pollCPU();
$scope.refreshTopProcesses();
$timeout(pollAll, pollInterval);
}
pollAll();
}, 800);
// SSH User Activity Modal
$scope.showSSHActivityModal = false;
$scope.sshActivity = { processes: [], w: [] };
$scope.sshActivityUser = '';
$scope.loadingSSHActivity = false;
$scope.errorSSHActivity = '';
$scope.viewSSHActivity = function(login, event) {
$scope.showSSHActivityModal = true;
$scope.sshActivity = { processes: [], w: [] };
$scope.sshActivityUser = login.user;
// Extract IP from multiple sources - comprehensive extraction for IPv4 and IPv6
var extractedIP = '';
// Method 1: Direct property access (highest priority - from backend)
if (login && login.ip) {
extractedIP = login.ip.toString().trim();
} else if (login && login['ip']) {
extractedIP = login['ip'].toString().trim();
}
// Method 2: Alternative field names
if (!extractedIP && login) {
if (login.ipAddress) extractedIP = login.ipAddress.toString().trim();
else if (login['IP Address']) extractedIP = login['IP Address'].toString().trim();
else if (login['IP']) extractedIP = login['IP'].toString().trim();
}
// Method 3: Extract from raw line using regex (IPv4 and IPv6)
if (!extractedIP && login && login.raw) {
// Try IPv4 first (most common)
var ipv4Match = login.raw.match(/\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b/);
if (ipv4Match && ipv4Match[1]) {
var ipv4 = ipv4Match[1].trim();
if (ipv4 !== '127.0.0.1' && ipv4 !== '0.0.0.0') {
extractedIP = ipv4;
}
}
// If no valid IPv4, try IPv6
if (!extractedIP) {
// IPv6 pattern: matches full IPv6 addresses and compressed forms
var ipv6Pattern = /([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}/;
var ipv6Match = login.raw.match(ipv6Pattern);
if (ipv6Match && ipv6Match[0]) {
var ipv6 = ipv6Match[0].trim();
if (ipv6 !== '::1' && ipv6.length > 0) {
extractedIP = ipv6;
}
}
}
}
// Method 4: Try to get from event target data attribute as fallback
if (!extractedIP && event && event.currentTarget) {
var dataIP = event.currentTarget.getAttribute('data-ip');
if (dataIP) extractedIP = dataIP.toString().trim();
}
// Final fallback: search entire raw line for any IP
if (!extractedIP && login && login.raw) {
// Try all IPv4 addresses
var allIPv4s = login.raw.match(/\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b/g);
if (allIPv4s && allIPv4s.length > 0) {
for (var i = 0; i < allIPv4s.length; i++) {
var ip = allIPv4s[i].trim();
if (ip !== '127.0.0.1' && ip !== '0.0.0.0' && ip.length > 0) {
extractedIP = ip;
break;
}
}
}
// If no IPv4, try all IPv6 addresses
if (!extractedIP) {
var allIPv6s = login.raw.match(/([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}/g);
if (allIPv6s && allIPv6s.length > 0) {
for (var j = 0; j < allIPv6s.length; j++) {
var ip6 = allIPv6s[j].trim();
if (ip6 !== '::1' && ip6.length > 0) {
extractedIP = ip6;
break;
}
}
}
}
}
// Final cleanup
$scope.sshActivityIP = (extractedIP || '').toString().trim();
$scope.sshActivityTTY = ''; // Store TTY for kill session
// Check both 'session' and 'activity' fields for status
$scope.sshActivityStatus = login.session || login.activity || '';
// Use backend is_active field if available (most reliable)
// Fallback to checking session text if is_active is not set
// IMPORTANT: Check for both boolean true and string 'true' (JSON might serialize differently)
if (login.is_active !== undefined && login.is_active !== null) {
// Backend explicitly set is_active
$scope.isActiveSession = (login.is_active === true || login.is_active === 'true' || login.is_active === 1 || login.is_active === '1');
console.log('Using backend is_active field:', login.is_active, '-> isActiveSession:', $scope.isActiveSession);
} else {
// Fallback: check session text
var sessionStatus = ($scope.sshActivityStatus || '').toLowerCase();
$scope.isActiveSession = (sessionStatus.indexOf('still logged in') !== -1);
console.log('Using fallback session text check:', sessionStatus, '-> isActiveSession:', $scope.isActiveSession);
}
// If IP is still empty, try one more time with more aggressive extraction
if (!$scope.sshActivityIP && login) {
console.log('IP still empty, trying aggressive extraction...');
// Try every possible field name variation
var possibleIPFields = ['ip', 'IP', 'ipAddress', 'IP Address', 'ip_address', 'IP_ADDRESS', 'client_ip', 'clientIP', 'source_ip', 'sourceIP'];
for (var k = 0; k < possibleIPFields.length; k++) {
if (login[possibleIPFields[k]]) {
var testIP = login[possibleIPFields[k]].toString().trim();
// Validate it looks like an IP (IPv4 or IPv6)
if (testIP.match(/^(\d{1,3}\.){3}\d{1,3}$/) || testIP.match(/^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/)) {
if (testIP !== '127.0.0.1' && testIP !== '0.0.0.0' && testIP !== '::1') {
$scope.sshActivityIP = testIP;
console.log('Found IP in field', possibleIPFields[k], ':', $scope.sshActivityIP);
break;
}
}
}
}
// Last resort: check if IP is in the table cell itself (from DOM)
if (!$scope.sshActivityIP && event && event.currentTarget) {
try {
var row = event.currentTarget.closest('tr');
if (row) {
// Try different column positions (IP could be in different positions)
var cells = row.querySelectorAll('td');
for (var cellIdx = 0; cellIdx < cells.length; cellIdx++) {
var cellText = cells[cellIdx].textContent.trim();
// Check if this cell contains an IP address
var ipMatch = cellText.match(/^(\d{1,3}\.){3}\d{1,3}$/) || cellText.match(/^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/);
if (ipMatch && cellText !== '127.0.0.1' && cellText !== '0.0.0.0' && cellText !== '::1') {
$scope.sshActivityIP = cellText;
console.log('Found IP from table cell (column ' + cellIdx + '):', $scope.sshActivityIP);
break;
}
}
}
} catch (e) {
console.log('Error extracting IP from DOM:', e);
}
}
}
// Debug logging - detailed inspection
console.log('View SSH Activity - Login object:', login);
console.log('Login keys:', Object.keys(login));
console.log('login.ip:', login.ip);
console.log('login.is_active:', login.is_active);
console.log('Extracted IP:', $scope.sshActivityIP);
console.log('Session status:', $scope.sshActivityStatus);
console.log('Is active session:', $scope.isActiveSession);
$scope.showFullJSON = false; // Collapsible JSON view
$scope.loadingSSHActivity = true;
$scope.errorSSHActivity = '';
$scope.killingProcess = null;
$scope.killingSession = false;
var tty = '';
// Try to extract tty from login.raw or login.session if available
if (login.raw) {
var match = login.raw.match(/(pts\/[0-9]+)/);
if (match) {
tty = match[1];
$scope.sshActivityTTY = tty;
}
}
// Also try to extract from session field or raw line
if (!tty && login.session) {
var sessionMatch = login.session.match(/(pts\/[0-9]+)/);
if (sessionMatch) {
tty = sessionMatch[1];
$scope.sshActivityTTY = tty;
}
}
// Also check raw line for TTY
if (!tty && login.raw) {
var rawMatch = login.raw.match(/(pts\/[0-9]+)/);
if (rawMatch) {
tty = rawMatch[1];
$scope.sshActivityTTY = tty;
}
}
// Make API call with IP included - reduced timeout for faster response
var requestData = {
user: login.user,
tty: tty,
ip: $scope.sshActivityIP
};
// Set shorter timeout for faster feedback
var timeoutPromise = $timeout(function() {
$scope.loadingSSHActivity = false;
$scope.errorSSHActivity = 'Request timed out. The user may not have any active processes.';
$scope.sshActivity = { processes: [], w: [] };
}, 5000); // 5 second timeout (reduced from 10)
$http.post('/base/getSSHUserActivity', requestData, { timeout: 3000 }).then(function(response) {
$timeout.cancel(timeoutPromise); // Cancel timeout on success
$scope.loadingSSHActivity = false;
if (response.data) {
// Check if response has error field
if (response.data.error) {
$scope.errorSSHActivity = response.data.error;
$scope.sshActivity = { processes: [], w: [] };
} else {
$scope.sshActivity = response.data;
// Ensure all expected fields exist
if (!$scope.sshActivity.processes) $scope.sshActivity.processes = [];
if (!$scope.sshActivity.w) $scope.sshActivity.w = [];
if (!$scope.sshActivity.process_tree) $scope.sshActivity.process_tree = [];
if (!$scope.sshActivity.shell_history) $scope.sshActivity.shell_history = [];
// Try to extract TTY from processes if not already set
if (!$scope.sshActivityTTY && response.data.processes && response.data.processes.length > 0) {
var firstProcess = response.data.processes[0];
if (firstProcess.tty) {
$scope.sshActivityTTY = firstProcess.tty;
}
}
// Update active session status - prioritize backend is_active field
// The backend already determined if session is active, so trust that first
// Only update if we have additional evidence (processes/w output)
var hasProcesses = response.data.processes && response.data.processes.length > 0;
var hasActiveW = response.data.w && response.data.w.length > 0;
// If backend says it's active, keep it active (don't override)
// If backend says inactive but we find processes/w, mark as active
if ($scope.isActiveSession === true) {
// Backend already marked as active, keep it that way
// (processes might not have loaded yet, but session is still active)
} else if (hasProcesses || hasActiveW) {
// Backend said inactive, but we found evidence it's active
$scope.isActiveSession = true;
}
// If backend said inactive and no processes found, keep as inactive
// Debug logging
console.log('SSH Activity loaded:', {
processes: response.data.processes ? response.data.processes.length : 0,
w: response.data.w ? response.data.w.length : 0,
hasProcesses: hasProcesses,
hasActiveW: hasActiveW,
originalStatus: $scope.sshActivityStatus,
isActiveSession: $scope.isActiveSession,
ip: $scope.sshActivityIP
});
}
} else {
$scope.sshActivity = { processes: [], w: [] };
$scope.errorSSHActivity = 'No data returned from server.';
}
}, function(err) {
$timeout.cancel(timeoutPromise); // Cancel timeout on error
$scope.loadingSSHActivity = false;
var errorMsg = 'Failed to fetch activity.';
// Handle different error scenarios
if (err.data) {
// Server returned error data
if (err.data.error) {
errorMsg = err.data.error;
} else if (typeof err.data === 'string') {
errorMsg = err.data;
} else if (err.data.message) {
errorMsg = err.data.message;
}
} else if (err.status === 0 || err.status === -1) {
errorMsg = 'Network error. Please check your connection and try again.';
} else if (err.status >= 500) {
errorMsg = 'Server error (HTTP ' + err.status + '). Please try again later.';
} else if (err.status === 404) {
errorMsg = 'Endpoint not found. Please refresh the page.';
} else if (err.status === 403) {
errorMsg = 'Access denied. Admin privileges required.';
} else if (err.status === 400) {
errorMsg = 'Invalid request. Please check the user information.';
} else if (err.status) {
errorMsg = 'Request failed with status ' + err.status + '.';
} else if (err.message) {
errorMsg = err.message;
}
$scope.errorSSHActivity = errorMsg;
// Set empty activity data so modal can still display
$scope.sshActivity = {
processes: [],
w: [],
process_tree: [],
shell_history: [],
disk_usage: '',
geoip: {}
};
// Log error for debugging
console.error('SSH Activity fetch error:', err);
});
};
// Kill individual process
$scope.killProcess = function(pid, user) {
if (!confirm('Are you sure you want to force kill process ' + pid + '? This action cannot be undone.')) {
return;
}
$scope.killingProcess = pid;
var data = {
pid: pid,
user: user
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post('/base/killSSHProcess', data, config).then(function(response) {
$scope.killingProcess = null;
if (response.data && response.data.success) {
new PNotify({
title: 'Process Killed',
text: response.data.message || 'Process ' + pid + ' has been terminated.',
type: 'success',
delay: 3000
});
// Refresh activity to update process list
$scope.viewSSHActivity({ user: user, ip: $scope.sshActivityIP, raw: '', session: '' });
} else {
new PNotify({
title: 'Error',
text: (response.data && response.data.error) || 'Failed to kill process.',
type: 'error',
delay: 5000
});
}
}, function(err) {
$scope.killingProcess = null;
var errorMsg = 'Failed to kill process.';
if (err.data && err.data.error) {
errorMsg = err.data.error;
} else if (err.data && err.data.message) {
errorMsg = err.data.message;
}
new PNotify({
title: 'Error',
text: errorMsg,
type: 'error',
delay: 5000
});
});
};
// Kill entire SSH session
$scope.killSSHSession = function(user, tty) {
var confirmMsg = 'Are you sure you want to kill all processes for user ' + user;
if (tty) {
confirmMsg += ' on terminal ' + tty;
}
confirmMsg += '? This will terminate their SSH session.';
if (!confirm(confirmMsg)) {
return;
}
$scope.killingSession = true;
var data = {
user: user,
tty: tty || ''
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post('/base/killSSHSession', data, config).then(function(response) {
$scope.killingSession = false;
if (response.data && response.data.success) {
new PNotify({
title: 'Session Terminated',
text: response.data.message || 'SSH session has been terminated successfully.',
type: 'success',
delay: 3000
});
// Close modal and refresh page after a delay
setTimeout(function() {
$scope.closeSSHActivityModal();
location.reload();
}, 1500);
} else {
new PNotify({
title: 'Error',
text: (response.data && response.data.error) || 'Failed to kill session.',
type: 'error',
delay: 5000
});
}
}, function(err) {
$scope.killingSession = false;
var errorMsg = 'Failed to kill session.';
if (err.data && err.data.error) {
errorMsg = err.data.error;
} else if (err.data && err.data.message) {
errorMsg = err.data.message;
}
new PNotify({
title: 'Error',
text: errorMsg,
type: 'error',
delay: 5000
});
});
};
$scope.closeSSHActivityModal = function() {
$scope.showSSHActivityModal = false;
$scope.sshActivity = { processes: [], w: [] };
$scope.sshActivityUser = '';
$scope.sshActivityIP = ''; // Clear IP when closing modal
$scope.sshActivityTTY = ''; // Clear TTY when closing modal
$scope.sshActivityStatus = ''; // Clear activity status
$scope.isActiveSession = false; // Reset active session flag
$scope.showFullJSON = false; // Reset JSON view
$scope.loadingSSHActivity = false;
$scope.errorSSHActivity = '';
$scope.killingProcess = null;
$scope.killingSession = false;
};
// Close modal when clicking backdrop
$scope.closeModalOnBackdrop = function(event) {
if (event.target === event.currentTarget) {
$scope.closeSSHActivityModal();
}
};
};
app.controller('dashboardStatsController', dashboardStatsControllerFn);
app.controller('newDashboardStat', dashboardStatsControllerFn);