diff --git a/baseTemplate/static/baseTemplate/custom-js/system-status.js b/baseTemplate/static/baseTemplate/custom-js/system-status.js
index 390cc9018..88a4e5bb5 100644
--- a/baseTemplate/static/baseTemplate/custom-js/system-status.js
+++ b/baseTemplate/static/baseTemplate/custom-js/system-status.js
@@ -875,6 +875,27 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) {
$scope.totalDBs = 0;
$scope.totalEmails = 0;
+ // SSH Logs
+ $scope.sshLogs = [];
+ $scope.loadingSSHLogs = true;
+ $scope.errorSSHLogs = '';
+ function fetchSSHLogs() {
+ $scope.loadingSSHLogs = true;
+ $http.get('/base/getRecentSSHLogs').then(function (response) {
+ $scope.loadingSSHLogs = false;
+ if (response.data && response.data.logs) {
+ $scope.sshLogs = response.data.logs;
+ } else {
+ $scope.sshLogs = [];
+ }
+ }, function (err) {
+ $scope.loadingSSHLogs = false;
+ $scope.errorSSHLogs = 'Failed to load SSH logs.';
+ });
+ }
+ fetchSSHLogs();
+ setInterval(fetchSSHLogs, 10000);
+
// SSH Logins
$scope.sshLogins = [];
$scope.loadingSSHLogins = true;
diff --git a/baseTemplate/templates/baseTemplate/homePage.html b/baseTemplate/templates/baseTemplate/homePage.html
index 81c8d2d96..1a906562b 100755
--- a/baseTemplate/templates/baseTemplate/homePage.html
+++ b/baseTemplate/templates/baseTemplate/homePage.html
@@ -6,10 +6,6 @@
{% get_current_language as LANGUAGE_CODE %}
-
-
+
-
-
- {% trans "CPU Usage" %}
-
-
-
- {$ cpuUsage $}%
-
-
-
-
+
+
{% trans "CPU Usage" %}
+
-
-
-
- {% trans "Ram Usage" %}
-
-
-
- {$ ramUsage $}%
-
-
-
-
+
+
{% trans "Ram Usage" %}
+
-
-
-
- {% trans "Disk Usage '/'" %}
-
-
-
- {$ diskUsage $}%
-
-
-
-
+
+
{% trans "Disk Usage '/'" %}
+
@@ -604,180 +486,66 @@
-
-
-
-
-
- Recent SSH Logins
-
-
-
-
-
- Loading recent SSH logins...
-
-
-
-
- No recent SSH logins found.
-
-
-
-
-
+
+ {% if admin %}
+
+
+
+
Recent SSH Logins
+
Loading recent SSH logins...
+
No recent SSH logins found.
+
+
-
- |
- User
- |
-
- IP
- |
-
- Country
- |
-
- Date/Time
- |
-
- Session
- |
+
+ | User |
+ IP |
+ Country |
+ Date/Time |
+ Session |
-
- |
- {$ login.user $}
- |
-
- {$ login.ip $}
- |
+
+ | {$ login.user $} |
+ {$ login.ip $} |
-
-
-
-
- {$ login.country $}
-
-
- -
-
- |
-
- {$ login.date $}
- |
-
- {$ login.session $}
+
+ {$ login.country $}
+ -
|
+ {$ login.date $} |
+ {$ login.session $} |
+
+
+
+
+
+
+
+
Recent SSH Logs
+
Loading recent SSH logs...
+
{$ errorSSHLogs $}
+
No recent SSH logs found.
+
+
+
+
+ | Timestamp |
+ Message |
+
+
+
+
+ | {$ log.timestamp $} |
+ {$ log.message $} |
+ {% endif %}
@@ -831,54 +599,294 @@
color: #4c5fad !important;
}
- /* CSS for circular progress bars - ensuring they display correctly */
+ /* CSS for circular progress bars */
.c100 {
position: relative;
- width: 100px;
- height: 100px;
+ font-size: 100px;
+ width: 1em;
+ height: 1em;
border-radius: 50%;
- background: #f0f0f0;
margin: 0 auto;
+ background-color: #f3f3f3;
+ display: inline-block;
+ }
+
+ .c100 *,
+ .c100 *:before,
+ .c100 *:after {
+ box-sizing: content-box;
+ }
+
+ .c100 > span {
+ position: absolute;
+ width: 100%;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ line-height: 1em;
+ font-size: 0.2em;
+ color: #333;
+ display: block;
+ text-align: center;
+ white-space: nowrap;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .c100:after {
+ position: absolute;
+ top: 0.08em;
+ left: 0.08em;
+ display: block;
+ content: " ";
+ border-radius: 50%;
+ background-color: #fff;
+ width: 0.84em;
+ height: 0.84em;
}
.c100 .slice {
position: absolute;
- width: 100px;
- height: 100px;
- clip: rect(0px, 100px, 100px, 50px);
+ width: 1em;
+ height: 1em;
+ clip: rect(0em, 1em, 1em, 0.5em);
+ }
+
+ .c100.p50 .slice {
+ clip: rect(auto, auto, auto, auto);
}
.c100 .bar {
position: absolute;
- border: 8px solid transparent;
- width: 84px;
- height: 84px;
+ border: 0.08em solid #307bbb;
+ width: 0.84em;
+ height: 0.84em;
+ clip: rect(0em, 0.5em, 1em, 0em);
border-radius: 50%;
- clip: rect(0px, 50px, 100px, 0px);
+ transform: rotate(0deg);
}
.c100 .fill {
position: absolute;
- border: 8px solid transparent;
- width: 84px;
- height: 84px;
+ border: 0.08em solid #307bbb;
+ width: 0.84em;
+ height: 0.84em;
+ clip: rect(0em, 0.5em, 1em, 0em);
border-radius: 50%;
- clip: rect(0px, 50px, 100px, 0px);
transform: rotate(180deg);
}
- .c100 span {
- position: absolute;
- width: 100px;
- z-index: 1;
- left: 0;
- top: 0;
- line-height: 100px;
- text-align: center;
- font-size: 20px;
- color: #333;
+ /* Generate rotation classes for percentages 0-49 */
+ .c100.p0 .bar { transform: rotate(0deg); }
+ .c100.p1 .bar { transform: rotate(3.6deg); }
+ .c100.p2 .bar { transform: rotate(7.2deg); }
+ .c100.p3 .bar { transform: rotate(10.8deg); }
+ .c100.p4 .bar { transform: rotate(14.4deg); }
+ .c100.p5 .bar { transform: rotate(18deg); }
+ .c100.p6 .bar { transform: rotate(21.6deg); }
+ .c100.p7 .bar { transform: rotate(25.2deg); }
+ .c100.p8 .bar { transform: rotate(28.8deg); }
+ .c100.p9 .bar { transform: rotate(32.4deg); }
+ .c100.p10 .bar { transform: rotate(36deg); }
+ .c100.p11 .bar { transform: rotate(39.6deg); }
+ .c100.p12 .bar { transform: rotate(43.2deg); }
+ .c100.p13 .bar { transform: rotate(46.8deg); }
+ .c100.p14 .bar { transform: rotate(50.4deg); }
+ .c100.p15 .bar { transform: rotate(54deg); }
+ .c100.p16 .bar { transform: rotate(57.6deg); }
+ .c100.p17 .bar { transform: rotate(61.2deg); }
+ .c100.p18 .bar { transform: rotate(64.8deg); }
+ .c100.p19 .bar { transform: rotate(68.4deg); }
+ .c100.p20 .bar { transform: rotate(72deg); }
+ .c100.p21 .bar { transform: rotate(75.6deg); }
+ .c100.p22 .bar { transform: rotate(79.2deg); }
+ .c100.p23 .bar { transform: rotate(82.8deg); }
+ .c100.p24 .bar { transform: rotate(86.4deg); }
+ .c100.p25 .bar { transform: rotate(90deg); }
+ .c100.p26 .bar { transform: rotate(93.6deg); }
+ .c100.p27 .bar { transform: rotate(97.2deg); }
+ .c100.p28 .bar { transform: rotate(100.8deg); }
+ .c100.p29 .bar { transform: rotate(104.4deg); }
+ .c100.p30 .bar { transform: rotate(108deg); }
+ .c100.p31 .bar { transform: rotate(111.6deg); }
+ .c100.p32 .bar { transform: rotate(115.2deg); }
+ .c100.p33 .bar { transform: rotate(118.8deg); }
+ .c100.p34 .bar { transform: rotate(122.4deg); }
+ .c100.p35 .bar { transform: rotate(126deg); }
+ .c100.p36 .bar { transform: rotate(129.6deg); }
+ .c100.p37 .bar { transform: rotate(133.2deg); }
+ .c100.p38 .bar { transform: rotate(136.8deg); }
+ .c100.p39 .bar { transform: rotate(140.4deg); }
+ .c100.p40 .bar { transform: rotate(144deg); }
+ .c100.p41 .bar { transform: rotate(147.6deg); }
+ .c100.p42 .bar { transform: rotate(151.2deg); }
+ .c100.p43 .bar { transform: rotate(154.8deg); }
+ .c100.p44 .bar { transform: rotate(158.4deg); }
+ .c100.p45 .bar { transform: rotate(162deg); }
+ .c100.p46 .bar { transform: rotate(165.6deg); }
+ .c100.p47 .bar { transform: rotate(169.2deg); }
+ .c100.p48 .bar { transform: rotate(172.8deg); }
+ .c100.p49 .bar { transform: rotate(176.4deg); }
+ .c100.p50 .bar { transform: rotate(180deg); }
+
+ /* For values > 50%, show the fill */
+ .c100.p51 .slice,
+ .c100.p52 .slice,
+ .c100.p53 .slice,
+ .c100.p54 .slice,
+ .c100.p55 .slice,
+ .c100.p56 .slice,
+ .c100.p57 .slice,
+ .c100.p58 .slice,
+ .c100.p59 .slice,
+ .c100.p60 .slice,
+ .c100.p61 .slice,
+ .c100.p62 .slice,
+ .c100.p63 .slice,
+ .c100.p64 .slice,
+ .c100.p65 .slice,
+ .c100.p66 .slice,
+ .c100.p67 .slice,
+ .c100.p68 .slice,
+ .c100.p69 .slice,
+ .c100.p70 .slice,
+ .c100.p71 .slice,
+ .c100.p72 .slice,
+ .c100.p73 .slice,
+ .c100.p74 .slice,
+ .c100.p75 .slice,
+ .c100.p76 .slice,
+ .c100.p77 .slice,
+ .c100.p78 .slice,
+ .c100.p79 .slice,
+ .c100.p80 .slice,
+ .c100.p81 .slice,
+ .c100.p82 .slice,
+ .c100.p83 .slice,
+ .c100.p84 .slice,
+ .c100.p85 .slice,
+ .c100.p86 .slice,
+ .c100.p87 .slice,
+ .c100.p88 .slice,
+ .c100.p89 .slice,
+ .c100.p90 .slice,
+ .c100.p91 .slice,
+ .c100.p92 .slice,
+ .c100.p93 .slice,
+ .c100.p94 .slice,
+ .c100.p95 .slice,
+ .c100.p96 .slice,
+ .c100.p97 .slice,
+ .c100.p98 .slice,
+ .c100.p99 .slice,
+ .c100.p100 .slice {
+ clip: rect(auto, auto, auto, auto);
}
+ .c100.p51 .bar { transform: rotate(180deg); }
+ .c100.p52 .bar { transform: rotate(180deg); }
+ .c100.p53 .bar { transform: rotate(180deg); }
+ .c100.p54 .bar { transform: rotate(180deg); }
+ .c100.p55 .bar { transform: rotate(180deg); }
+ .c100.p56 .bar { transform: rotate(180deg); }
+ .c100.p57 .bar { transform: rotate(180deg); }
+ .c100.p58 .bar { transform: rotate(180deg); }
+ .c100.p59 .bar { transform: rotate(180deg); }
+ .c100.p60 .bar { transform: rotate(180deg); }
+ .c100.p61 .bar { transform: rotate(180deg); }
+ .c100.p62 .bar { transform: rotate(180deg); }
+ .c100.p63 .bar { transform: rotate(180deg); }
+ .c100.p64 .bar { transform: rotate(180deg); }
+ .c100.p65 .bar { transform: rotate(180deg); }
+ .c100.p66 .bar { transform: rotate(180deg); }
+ .c100.p67 .bar { transform: rotate(180deg); }
+ .c100.p68 .bar { transform: rotate(180deg); }
+ .c100.p69 .bar { transform: rotate(180deg); }
+ .c100.p70 .bar { transform: rotate(180deg); }
+ .c100.p71 .bar { transform: rotate(180deg); }
+ .c100.p72 .bar { transform: rotate(180deg); }
+ .c100.p73 .bar { transform: rotate(180deg); }
+ .c100.p74 .bar { transform: rotate(180deg); }
+ .c100.p75 .bar { transform: rotate(180deg); }
+ .c100.p76 .bar { transform: rotate(180deg); }
+ .c100.p77 .bar { transform: rotate(180deg); }
+ .c100.p78 .bar { transform: rotate(180deg); }
+ .c100.p79 .bar { transform: rotate(180deg); }
+ .c100.p80 .bar { transform: rotate(180deg); }
+ .c100.p81 .bar { transform: rotate(180deg); }
+ .c100.p82 .bar { transform: rotate(180deg); }
+ .c100.p83 .bar { transform: rotate(180deg); }
+ .c100.p84 .bar { transform: rotate(180deg); }
+ .c100.p85 .bar { transform: rotate(180deg); }
+ .c100.p86 .bar { transform: rotate(180deg); }
+ .c100.p87 .bar { transform: rotate(180deg); }
+ .c100.p88 .bar { transform: rotate(180deg); }
+ .c100.p89 .bar { transform: rotate(180deg); }
+ .c100.p90 .bar { transform: rotate(180deg); }
+ .c100.p91 .bar { transform: rotate(180deg); }
+ .c100.p92 .bar { transform: rotate(180deg); }
+ .c100.p93 .bar { transform: rotate(180deg); }
+ .c100.p94 .bar { transform: rotate(180deg); }
+ .c100.p95 .bar { transform: rotate(180deg); }
+ .c100.p96 .bar { transform: rotate(180deg); }
+ .c100.p97 .bar { transform: rotate(180deg); }
+ .c100.p98 .bar { transform: rotate(180deg); }
+ .c100.p99 .bar { transform: rotate(180deg); }
+ .c100.p100 .bar { transform: rotate(180deg); }
+
+ .c100.p51 .fill { transform: rotate(183.6deg); }
+ .c100.p52 .fill { transform: rotate(187.2deg); }
+ .c100.p53 .fill { transform: rotate(190.8deg); }
+ .c100.p54 .fill { transform: rotate(194.4deg); }
+ .c100.p55 .fill { transform: rotate(198deg); }
+ .c100.p56 .fill { transform: rotate(201.6deg); }
+ .c100.p57 .fill { transform: rotate(205.2deg); }
+ .c100.p58 .fill { transform: rotate(208.8deg); }
+ .c100.p59 .fill { transform: rotate(212.4deg); }
+ .c100.p60 .fill { transform: rotate(216deg); }
+ .c100.p61 .fill { transform: rotate(219.6deg); }
+ .c100.p62 .fill { transform: rotate(223.2deg); }
+ .c100.p63 .fill { transform: rotate(226.8deg); }
+ .c100.p64 .fill { transform: rotate(230.4deg); }
+ .c100.p65 .fill { transform: rotate(234deg); }
+ .c100.p66 .fill { transform: rotate(237.6deg); }
+ .c100.p67 .fill { transform: rotate(241.2deg); }
+ .c100.p68 .fill { transform: rotate(244.8deg); }
+ .c100.p69 .fill { transform: rotate(248.4deg); }
+ .c100.p70 .fill { transform: rotate(252deg); }
+ .c100.p71 .fill { transform: rotate(255.6deg); }
+ .c100.p72 .fill { transform: rotate(259.2deg); }
+ .c100.p73 .fill { transform: rotate(262.8deg); }
+ .c100.p74 .fill { transform: rotate(266.4deg); }
+ .c100.p75 .fill { transform: rotate(270deg); }
+ .c100.p76 .fill { transform: rotate(273.6deg); }
+ .c100.p77 .fill { transform: rotate(277.2deg); }
+ .c100.p78 .fill { transform: rotate(280.8deg); }
+ .c100.p79 .fill { transform: rotate(284.4deg); }
+ .c100.p80 .fill { transform: rotate(288deg); }
+ .c100.p81 .fill { transform: rotate(291.6deg); }
+ .c100.p82 .fill { transform: rotate(295.2deg); }
+ .c100.p83 .fill { transform: rotate(298.8deg); }
+ .c100.p84 .fill { transform: rotate(302.4deg); }
+ .c100.p85 .fill { transform: rotate(306deg); }
+ .c100.p86 .fill { transform: rotate(309.6deg); }
+ .c100.p87 .fill { transform: rotate(313.2deg); }
+ .c100.p88 .fill { transform: rotate(316.8deg); }
+ .c100.p89 .fill { transform: rotate(320.4deg); }
+ .c100.p90 .fill { transform: rotate(324deg); }
+ .c100.p91 .fill { transform: rotate(327.6deg); }
+ .c100.p92 .fill { transform: rotate(331.2deg); }
+ .c100.p93 .fill { transform: rotate(334.8deg); }
+ .c100.p94 .fill { transform: rotate(338.4deg); }
+ .c100.p95 .fill { transform: rotate(342deg); }
+ .c100.p96 .fill { transform: rotate(345.6deg); }
+ .c100.p97 .fill { transform: rotate(349.2deg); }
+ .c100.p98 .fill { transform: rotate(352.8deg); }
+ .c100.p99 .fill { transform: rotate(356.4deg); }
+ .c100.p100 .fill { transform: rotate(360deg); }
+
/* Color variants for progress circles */
.c100.red .bar, .c100.red .fill {
border-color: #e53935;
@@ -896,6 +904,12 @@
canvas {
transition: none !important;
}
+
+ @media (min-width: 900px) {
+ .system-stats-row {
+ flex-wrap: nowrap !important;
+ }
+ }
diff --git a/baseTemplate/urls.py b/baseTemplate/urls.py
index b20d275b2..713d2909c 100755
--- a/baseTemplate/urls.py
+++ b/baseTemplate/urls.py
@@ -20,4 +20,5 @@ urlpatterns = [
re_path(r'^getDiskIOStats$', views.getDiskIOStats, name='getDiskIOStats'),
re_path(r'^getCPULoadGraph$', views.getCPULoadGraph, name='getCPULoadGraph'),
re_path(r'^getRecentSSHLogins$', views.getRecentSSHLogins, name='getRecentSSHLogins'),
+ re_path(r'^getRecentSSHLogs$', views.getRecentSSHLogs, name='getRecentSSHLogs'),
]
diff --git a/baseTemplate/views.py b/baseTemplate/views.py
index 2397ef63e..39709ee57 100755
--- a/baseTemplate/views.py
+++ b/baseTemplate/views.py
@@ -588,3 +588,40 @@ def getRecentSSHLogins(request):
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)
+
+@csrf_exempt
+@require_GET
+def getRecentSSHLogs(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)
+ from plogical.processUtilities import ProcessUtilities
+ distro = ProcessUtilities.decideDistro()
+ if distro in [ProcessUtilities.ubuntu, ProcessUtilities.ubuntu20]:
+ log_path = '/var/log/auth.log'
+ else:
+ log_path = '/var/log/secure'
+ try:
+ output = ProcessUtilities.outputExecutioner(f'tail -n 100 {log_path}')
+ except Exception as e:
+ return HttpResponse(json.dumps({'error': f'Failed to read log: {str(e)}'}), content_type='application/json', status=500)
+ lines = output.split('\n')
+ logs = []
+ for line in lines:
+ if 'sshd' in line:
+ # Try to split into timestamp and message
+ parts = line.split()
+ if len(parts) > 4:
+ timestamp = ' '.join(parts[:3])
+ message = ' '.join(parts[4:])
+ else:
+ timestamp = ''
+ message = line
+ logs.append({'timestamp': timestamp, 'message': message, 'raw': line})
+ return HttpResponse(json.dumps({'logs': logs}), content_type='application/json')
+ except Exception as e:
+ return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)