From 2463cea5e2519f41afda31a6cbe6de55ff9bfb58 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Mon, 2 Jun 2025 00:58:11 +0500 Subject: [PATCH] improve home page design --- .../baseTemplate/custom-js/system-status.js | 26 +++ .../templates/baseTemplate/homePage.html | 190 ++++++++++++------ baseTemplate/urls.py | 1 + baseTemplate/views.py | 74 ++++++- 4 files changed, 228 insertions(+), 63 deletions(-) diff --git a/baseTemplate/static/baseTemplate/custom-js/system-status.js b/baseTemplate/static/baseTemplate/custom-js/system-status.js index a49776c3d..63bd0016f 100644 --- a/baseTemplate/static/baseTemplate/custom-js/system-status.js +++ b/baseTemplate/static/baseTemplate/custom-js/system-status.js @@ -875,6 +875,32 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) { $scope.totalDBs = 0; $scope.totalEmails = 0; + // SSH Logins + $scope.sshLogins = []; + $scope.loadingSSHLogins = true; + $scope.errorSSHLogins = ''; + function fetchSSHLogins() { + $scope.loadingSSHLogins = true; + $http.get('/baseTemplate/getRecentSSHLogins').then(function (response) { + $scope.loadingSSHLogins = false; + if (response.data && response.data.logins) { + $scope.sshLogins = response.data.logins; + } else { + $scope.sshLogins = []; + } + }, function (error) { + $scope.loadingSSHLogins = false; + $scope.sshLogins = []; + $scope.errorSSHLogins = 'Failed to load SSH logins.'; + }); + } + fetchSSHLogins(); + var sshPoller = function() { + fetchSSHLogins(); + $timeout(sshPoller, 10000); + }; + $timeout(sshPoller, 10000); + // Chart.js chart objects var trafficChart, diskIOChart, cpuChart; // Data arrays for live graphs diff --git a/baseTemplate/templates/baseTemplate/homePage.html b/baseTemplate/templates/baseTemplate/homePage.html index a796570d0..b3e6dbfdb 100755 --- a/baseTemplate/templates/baseTemplate/homePage.html +++ b/baseTemplate/templates/baseTemplate/homePage.html @@ -6,60 +6,64 @@ {% get_current_language as LANGUAGE_CODE %} -
-
-

{% trans "Home" %}

-

{% trans "Use the tabs to navigate through the control panel." %}

+
+
+

{% trans "Home" %}

+

{% trans "Use the tabs to navigate through the control panel." %}

-
+
-
+
- -
-
-
+
+
+
+
+
-
{$ totalSites $}
-
Total Sites
+
{$ totalSites $}
+
Total Sites
- -
-
-
+
+
+
+
+
-
{$ totalWPSites $}
-
WordPress Sites
+
{$ totalWPSites $}
+
WordPress Sites
- -
-
-
+
+
+
+
+
-
{$ totalDBs $}
-
Total Databases
+
{$ totalDBs $}
+
Total Databases
- -
- {% if admin %} -
-
+
+
-
-
{% trans "CPU Usage" %}
-
- {$ cpuUsage $}% +
+
{% trans "CPU Usage" %}
+
+ {$ cpuUsage $}%
@@ -81,10 +85,10 @@
-
-
{% trans "Ram Usage" %}
-
- {$ ramUsage $}% +
+
{% trans "Ram Usage" %}
+
+ {$ ramUsage $}%
@@ -92,10 +96,10 @@
-
-
{% trans "Disk Usage '/'" %}
-
- {$ diskUsage $}% +
+
{% trans "Disk Usage '/'" %}
+
+ {$ diskUsage $}%
@@ -104,33 +108,74 @@
+ +
+
+
+ + Recent SSH Logins +
+
+ Loading recent SSH logins... +
+
+ No recent SSH logins found. +
+
+ + + + + + + + + + + + + + + + + + + +
UserIPCountryDate/TimeSession
{{login.user}}{{login.ip}} + {{login.country}} + {{login.country}} + - + {{login.date}}{{login.session}}
+
+
+
{% endif %} -
+
-
-
    + -
    +
    @@ -151,12 +196,29 @@ cursor: pointer; } #dashboardGraphTabs a[data-toggle="tab"]:hover { - background: #e9ecef !important; - color: #495057 !important; + background: rgba(255,255,255,0.9) !important; + color: #667eea !important; + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(102,126,234,0.3); } #dashboardGraphTabs a[data-toggle="tab"].active { - background: #007bff !important; - color: white !important; + background: rgba(255,255,255,0.9) !important; + color: #667eea !important; + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(102,126,234,0.3); + } + + /* Enhanced hover effects for tabs */ + a[data-toggle="tab"]:hover { + background: rgba(255,255,255,0.9) !important; + color: #667eea !important; + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(102,126,234,0.3); + } + + /* Smooth animations for all interactive elements */ + * { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } @@ -177,7 +239,9 @@ tabs.forEach(function(t) { t.classList.remove('active'); t.style.background = 'transparent'; - t.style.color = '#6c757d'; + t.style.color = 'rgba(255,255,255,0.8)'; + t.style.transform = 'translateY(0)'; + t.style.boxShadow = 'none'; }); document.querySelectorAll('.tab-pane').forEach(function(pane) { pane.classList.remove('show', 'active'); @@ -185,8 +249,10 @@ // Add active to clicked tab this.classList.add('active'); - this.style.background = '#007bff'; - this.style.color = 'white'; + this.style.background = 'rgba(255,255,255,0.9)'; + this.style.color = '#667eea'; + this.style.transform = 'translateY(-2px)'; + this.style.boxShadow = '0 4px 15px rgba(102,126,234,0.3)'; // Show corresponding pane var target = this.getAttribute('href'); @@ -211,4 +277,4 @@
    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/baseTemplate/urls.py b/baseTemplate/urls.py index dbc2cc9dc..b20d275b2 100755 --- a/baseTemplate/urls.py +++ b/baseTemplate/urls.py @@ -19,4 +19,5 @@ urlpatterns = [ re_path(r'^getTrafficStats$', views.getTrafficStats, name='getTrafficStats'), re_path(r'^getDiskIOStats$', views.getDiskIOStats, name='getDiskIOStats'), re_path(r'^getCPULoadGraph$', views.getCPULoadGraph, name='getCPULoadGraph'), + re_path(r'^getRecentSSHLogins$', views.getRecentSSHLogins, name='getRecentSSHLogins'), ] diff --git a/baseTemplate/views.py b/baseTemplate/views.py index f4e8c1457..2397ef63e 100755 --- a/baseTemplate/views.py +++ b/baseTemplate/views.py @@ -14,12 +14,13 @@ import os import plogical.CyberCPLogFileWriter as logging from plogical.acl import ACLManager from manageServices.models import PDNSStatus -from django.views.decorators.csrf import ensure_csrf_cookie +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt from plogical.processUtilities import ProcessUtilities from plogical.httpProc import httpProc from websiteFunctions.models import Websites, WPSites from databases.models import Databases from mailServer.models import EUsers +from django.views.decorators.http import require_GET # Create your views here. @@ -516,3 +517,74 @@ def getCPULoadGraph(request): return HttpResponse(json.dumps(data), content_type='application/json') except Exception as e: return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + +@csrf_exempt +@require_GET +def getRecentSSHLogins(request): + try: + user_id = request.session.get('userID') + if not user_id: + return HttpResponse(json.dumps({'error': 'Not logged in'}), content_type='application/json', status=403) + currentACL = ACLManager.loadedACL(user_id) + if not currentACL.get('admin', 0): + return HttpResponse(json.dumps({'error': 'Admin only'}), content_type='application/json', status=403) + + import re, time + from collections import OrderedDict + + # Run 'last -n 20' to get recent SSH logins + try: + output = ProcessUtilities.outputExecutioner('last -n 20') + except Exception as e: + return HttpResponse(json.dumps({'error': 'Failed to run last: %s' % str(e)}), content_type='application/json', status=500) + + lines = output.strip().split('\n') + logins = [] + ip_cache = {} + for line in lines: + if not line.strip() or any(x in line for x in ['reboot', 'system boot', 'wtmp begins']): + continue + # Example: ubuntu pts/0 206.84.168.7 Sun Jun 1 19:41 still logged in + # or: ubuntu pts/0 206.84.169.36 Tue May 27 11:34 - 13:47 (02:13) + parts = re.split(r'\s+', line, maxsplit=5) + if len(parts) < 5: + continue + user, tty, ip, *rest = parts + # Find date/time and session info + date_session = rest[-1] if rest else '' + # Try to extract date/session + date_match = re.search(r'([A-Za-z]{3} [A-Za-z]{3} +\d+ [\d:]+)', line) + date_str = date_match.group(1) if date_match else '' + session_info = '' + if '-' in line: + # Session ended + session_info = line.split('-')[-1].strip() + elif 'still logged in' in line: + session_info = 'still logged in' + # GeoIP lookup (cache per request) + country = flag = '' + if re.match(r'\d+\.\d+\.\d+\.\d+', ip) and ip != '127.0.0.1': + if ip in ip_cache: + country, flag = ip_cache[ip] + else: + try: + geo = requests.get(f'http://ip-api.com/json/{ip}', timeout=2).json() + country = geo.get('countryCode', '') + flag = f"https://flagcdn.com/24x18/{country.lower()}.png" if country else '' + ip_cache[ip] = (country, flag) + except Exception: + country, flag = '', '' + elif ip == '127.0.0.1': + country, flag = 'Local', '' + logins.append({ + 'user': user, + 'ip': ip, + 'country': country, + 'flag': flag, + 'date': date_str, + 'session': session_info, + 'raw': line + }) + return HttpResponse(json.dumps({'logins': logins}), content_type='application/json') + except Exception as e: + return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)