1pub fn get_content() -> String {
11 r###"<!DOCTYPE html>
12<html lang="en">
13<head>
14 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
15 <title>SysMonk - System Monitor - v{{ version }}</title>
16 <meta property="og:type" content="SystemMonitor">
17 <meta name="keywords" content="Rust, Monitor, actix, JavaScript, HTML, CSS">
18 <meta name="author" content="Vignesh Rao">
19 <!-- Favicon.ico and Apple Touch Icon -->
20 <link rel="icon" href="https://thevickypedia.github.io/open-source/images/logo/actix.ico">
21 <link rel="apple-touch-icon" href="https://thevickypedia.github.io/open-source/images/logo/actix.png">
22 <meta content="width=device-width, initial-scale=1" name="viewport">
23 <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
24 <!-- CSS and JS for night mode -->
25 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
26 <script type="text/javascript" src="https://thevickypedia.github.io/open-source/nightmode/night.js" defer></script>
27 <link rel="stylesheet" type="text/css" href="https://thevickypedia.github.io/open-source/nightmode/night.css">
28 <!-- Font Awesome icons -->
29 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/font-awesome.min.css">
30 <!--suppress CssUnusedSymbol -->
31 <style id="main-css">
32 body {
33 font-family: Arial, sans-serif;
34 overflow-x: hidden;
35 }
36
37 .docker-stats {
38 height: 100%;
39 margin: 2%;
40 display: none; /* Hide the container initially */
41 align-items: center;
42 justify-content: center;
43 flex-direction: column; /* Ensure vertical alignment */
44 }
45
46 .docker-stats h3 {
47 text-align: center;
48 margin-bottom: 20px;
49 }
50
51 table {
52 width: 80%;
53 border-collapse: collapse;
54 display: none; /* Hide the table initially */
55 }
56
57 table, th, td {
58 border: 1px solid #ccc;
59 }
60
61 th, td {
62 padding: 10px;
63 text-align: left;
64 }
65
66 .container {
67 display: flex;
68 justify-content: space-between;
69 margin-top: 50px;
70 }
71
72 .box {
73 border: 1px solid #ccc;
74 padding: 20px;
75 width: 30%;
76 text-align: center;
77 margin: 1%;
78 }
79
80 .progress {
81 width: 100%;
82 background-color: transparent;
83 border-radius: 5px;
84 overflow: hidden;
85 transition: background-color 0.5s ease;
86 }
87
88 .progress-bar {
89 height: 25px;
90 transition: width 0.5s ease, background-color 0.5s ease;
91 width: 0;
92 }
93
94 .progress-bar-green {
95 background-color: #4caf50;
96 }
97
98 .progress-bar-yellow {
99 background-color: #ffeb3b;
100 }
101
102 .progress-bar-orange {
103 background-color: #ff9800;
104 }
105
106 .progress-bar-red {
107 background-color: #f44336;
108 }
109
110 .chart-container {
111 position: relative;
112 height: 200px;
113 width: 80%;
114 margin: 0 auto;
115 max-width: 100%;
116 }
117
118 canvas {
119 width: 100% !important;
120 height: inherit !important;
121 max-height: 100% !important;
122 }
123
124 .tooltip-button {
125 padding: 5px 5px;
126 font-size: 14px;
127 cursor: pointer;
128 border: 1px solid #ccc;
129 border-radius: 5px;
130 background-color: #f0f0f0;
131 }
132
133 .tooltip-button:hover {
134 background-color: #e0e0e0;
135 }
136
137 .center-container {
138 width: 100%;
139 margin-left: 40%;
140 }
141
142 .center-container details {
143 text-align: left;
144 }
145
146 h1 {
147 width: 100%;
148 text-align: center;
149 align-content: center;
150 }
151
152 .logout {
153 position: absolute;
154 top: 3.8%;
155 right: 30px;
156 border: none;
157 padding: 10px 14px;
158 font-size: 16px;
159 cursor: pointer;
160 }
161
162 footer {
163 width: 100%;
164 text-align: center;
165 align-content: center;
166 font-size: 14px;
167 font-style: italic;
168 }
169
170 .graph-canvas {
171 max-width: 600px;
172 }
173 </style>
174 <noscript>
175 <style>
176 body {
177 width: 100%;
178 height: 100%;
179 overflow: hidden;
180 }
181 </style>
182 <div style="position: fixed; text-align:center; height: 100%; width: 100%; background-color: #151515;">
183 <h2 style="margin-top:5%">This page requires JavaScript
184 to be enabled.
185 <br><br>
186 Please refer <a href="https://www.enable-javascript.com/">enable-javascript</a> for how to.
187 </h2>
188 <form>
189 <button type="submit" onClick="<meta httpEquiv='refresh' content='0'>">RETRY</button>
190 </form>
191 </div>
192 </noscript>
193</head>
194<body translate="no">
195<div class="toggler fa fa-moon-o"></div>
196<button class="logout" onclick="logOut()"><i class="fa fa-sign-out"></i> Logout</button>
197<h1>SysMonk - System Monitor</h1>
198<div class="center-container">
199 <details>
200 <summary><strong>System Information</strong></summary>
201 <br>
202 {% for key, value in sys_info_basic|items() %}
203 <strong>{{ key }}: </strong>{{ value }}<br>
204 {% endfor %}
205 </details>
206 <br>
207 <details>
208 <summary><strong>Memory and Storage</strong></summary>
209 <br>
210 {% for key, value in sys_info_mem_storage|items() %}
211 <strong>{{ key }}: </strong>{{ value }}<br>
212 {% endfor %}
213 </details>
214 <br>
215 <details>
216 <summary><strong>Network Information</strong></summary>
217 <br>
218 {% for key, value in sys_info_network|items() %}
219 <strong>{{ key }}: </strong>{{ value }}<br>
220 {% endfor %}
221 </details>
222 {% if sys_info_disks %}
223 <br>
224 <details>
225 <summary><strong>Disk Information</strong></summary>
226 {% for disk_info in sys_info_disks %}
227 <br>
228 {% for key, value in disk_info|items() %}
229 <strong>{{ key }}: </strong>{{ value }}<br>
230 {% endfor %}
231 {% endfor %}
232 </details>
233 {% endif %}
234</div>
235<div class="container">
236 <!-- Box to display utilization per CPU -->
237 <div class="box">
238 <h3>CPU Usage</h3>
239 <div class="cpu-box" id="cpuUsageContainer">
240 <!-- CPU Usage will be dynamically added here -->
241 </div>
242 </div>
243 <!-- Box to display Memory, Swap and Disk usage along with CPU load avg -->
244 <div class="box">
245 <h3>Memory Usage</h3>
246 <div class="progress">
247 <div id="memoryUsage" class="progress-bar"></div>
248 </div>
249 <p id="memoryUsageText">Memory: 0%</p>
250
251 {% if 'Swap' in sys_info_mem_storage %}
252 <h3>Swap Usage</h3>
253 <div class="progress">
254 <div id="swapUsage" class="progress-bar"></div>
255 </div>
256 <p id="swapUsageText">Swap: 0%</p>
257 {% endif %}
258
259 <h3>Disk Usage</h3>
260 <div class="progress">
261 <div id="diskUsage" class="progress-bar"></div>
262 </div>
263 <p id="diskUsageText">Disk: 0%</p>
264
265 <div class="graph">
266 <h3>CPU Load Averages</h3>
267 <canvas class="graph-canvas" id="loadChart" width="400" height="200"></canvas>
268 </div>
269 </div>
270 <!-- Box to display Memory, Swap and Disk usage as Pie charts -->
271 <div class="box">
272 <h3>Memory Usage</h3>
273 <h5 id="memoryTotal"></h5>
274 <div class="chart-container">
275 <canvas id="memoryChart"></canvas>
276 </div>
277 {% if 'Swap' in sys_info_mem_storage %}
278 <h3>Swap Usage</h3>
279 <h5 id="swapTotal"></h5>
280 <div class="chart-container">
281 <canvas id="swapChart"></canvas>
282 </div>
283 {% endif %}
284 <h3>Disk Usage</h3>
285 <h5 id="diskTotal"></h5>
286 <div class="chart-container">
287 <canvas id="diskChart"></canvas>
288 </div>
289 </div>
290</div>
291<div id="docker-stats" class="docker-stats">
292 <h3>Docker Stats</h3>
293 <table id="dockerStatsTable">
294 <thead>
295 <tr>
296 <th>Container ID</th>
297 <th>Container Name</th>
298 <th>CPU %</th>
299 <th>Memory Usage</th>
300 <th>Memory %</th>
301 <th>Net I/O</th>
302 <th>Block I/O</th>
303 <th>PIDs</th>
304 </tr>
305 </thead>
306 <tbody>
307 </tbody>
308 </table>
309</div>
310<script>
311 document.addEventListener('DOMContentLoaded', function () {
312 const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
313 const wsHost = window.location.host;
314 const ws = new WebSocket(`${wsProtocol}://${wsHost}/ws/system`);
315
316 ws.onopen = () => {
317 console.log('WebSocket connection established');
318 };
319 ws.onclose = () => {
320 console.log('WebSocket connection closed');
321 alert('WebSocket connection closed by the server!');
322 logOut();
323 return;
324 };
325
326 let memoryChartInstance = null;
327 let swapChartInstance = null;
328 let diskChartInstance = null;
329 let loadChartInstance = null;
330
331 ws.onmessage = function (event) {
332 let data;
333 try {
334 data = JSON.parse(event.data);
335 } catch (error) {
336 console.warn('Error parsing JSON data:', error);
337 alert(event.data);
338 logOut();
339 return;
340 }
341
342 const dockerStatsJSON = data.docker_stats;
343 // Check if dockerStatsJSON is valid
344 if (dockerStatsJSON && dockerStatsJSON.length > 0) {
345 // Show the container and the table
346 const statsContainer = document.getElementById("docker-stats");
347 statsContainer.style.display = "flex";
348 const table = document.getElementById("dockerStatsTable");
349 table.style.display = "table";
350 // Get reference to the table body
351 const tableBody = document.querySelector('#dockerStatsTable tbody');
352 // Clear the existing table rows
353 tableBody.innerHTML = '';
354 // Loop through the JSON data and populate the table
355 dockerStatsJSON.forEach(container => {
356 const row = document.createElement('tr');
357 row.innerHTML = `
358 <td>${container.ID}</td>
359 <td>${container.Name}</td>
360 <td>${container.CPUPerc}</td>
361 <td>${container.MemUsage}</td>
362 <td>${container.MemPerc}</td>
363 <td>${container.NetIO}</td>
364 <td>${container.BlockIO}</td>
365 <td>${container.PIDs}</td>
366 `;
367 tableBody.appendChild(row);
368 });
369 } else {
370 // Hide the container if no data is available
371 document.getElementById("docker-stats").style.display = "none";
372 }
373
374 // Update CPU usage
375 const cpuUsage = data.cpu_usage;
376 const cpuContainer = document.getElementById('cpuUsageContainer');
377 cpuContainer.innerHTML = ''; // Clear previous content
378 cpuUsage.forEach((usage, index) => {
379 const cpuDiv = document.createElement('div');
380 cpuDiv.innerHTML = `
381 <strong>CPU ${index + 1}:</strong> ${usage}%
382 <div class="progress">
383 <div id="cpu${index}" class="progress-bar"></div>
384 </div>
385 `;
386 cpuContainer.appendChild(cpuDiv);
387 updateProgressBar(`cpu${index}`, usage);
388 });
389
390 // Memory Usage Progress Bar
391 const memoryInfo = data.memory_info;
392 const memoryUsage = (memoryInfo.used / memoryInfo.total) * 100;
393 document.getElementById('memoryUsage').style.width = memoryUsage.toFixed(2) + '%';
394 document.getElementById('memoryUsageText').innerText = `Memory: ${memoryUsage.toFixed(2)}%`;
395 updateProgressBar('memoryUsage', memoryUsage);
396
397 // Swap Usage Progress Bar
398 const swapInfo = data.swap_info;
399 if (swapInfo) {
400 const swapUsage = (swapInfo.used / swapInfo.total) * 100;
401 document.getElementById('swapUsage').style.width = swapUsage.toFixed(2) + '%';
402 document.getElementById('swapUsageText').innerText = `Swap: ${swapUsage.toFixed(2)}%`;
403 updateProgressBar('swapUsage', swapUsage);
404 }
405
406 // Disk Usage Progress Bar
407 const diskInfo = data.disk_info;
408 const diskUsage = (diskInfo.used / diskInfo.total) * 100;
409 document.getElementById('diskUsage').style.width = diskUsage.toFixed(2) + '%';
410 document.getElementById('diskUsageText').innerText = `Disk: ${diskUsage.toFixed(2)}%`;
411 updateProgressBar('diskUsage', diskUsage);
412
413 // CPU Load Avg Graph
414 const loadAverages = data.load_averages;
415 if (loadChartInstance) {
416 loadChartInstance.data.datasets[0].data = [loadAverages["m1"], loadAverages["m5"], loadAverages["m15"]];
417 loadChartInstance.update();
418 } else {
419 const ctx = document.getElementById('loadChart').getContext('2d');
420 loadChartInstance = new Chart(ctx, {
421 type: 'bar',
422 data: {
423 labels: ['1 minute', '5 minutes', '15 minutes'],
424 datasets: [{
425 label: 'Load Average',
426 data: [loadAverages["m1"], loadAverages["m5"], loadAverages["m15"]],
427 backgroundColor: [
428 'rgba(75, 192, 192, 0.2)',
429 'rgba(153, 102, 255, 0.2)',
430 'rgba(255, 159, 64, 0.2)'
431 ],
432 borderColor: [
433 'rgba(75, 192, 192, 1)',
434 'rgba(153, 102, 255, 1)',
435 'rgba(255, 159, 64, 1)'
436 ],
437 borderWidth: 1
438 }]
439 },
440 options: {
441 plugins: {
442 // Hide the legend
443 legend: {
444 display: false
445 }
446 },
447 scales: {
448 y: {
449 beginAtZero: true,
450 title: {
451 display: true,
452 text: 'Number of Processes'
453 },
454 ticks: {
455 // Set integer step size
456 stepSize: 1,
457 callback: function (value) {
458 return Number.isInteger(value) ? value : '';
459 }
460 }
461 }
462 }
463 }
464 });
465 }
466
467 // Memory Chart
468 document.getElementById("memoryTotal").innerText = `Total: ${formatBytes(memoryInfo.total)}`;
469 if (memoryChartInstance) {
470 memoryChartInstance.data.datasets[0].data = [memoryInfo.used, memoryInfo.total - memoryInfo.used];
471 memoryChartInstance.update();
472 } else {
473 const memoryChart = document.getElementById('memoryChart').getContext('2d');
474 memoryChartInstance = new Chart(memoryChart, {
475 type: 'pie',
476 data: {
477 labels: ['Used', 'Free'],
478 datasets: [{
479 label: 'Memory Usage',
480 data: [memoryInfo.used, memoryInfo.total - memoryInfo.used],
481 backgroundColor: ['#FF6384', '#36A2EB']
482 }]
483 },
484 options: {
485 responsive: true,
486 plugins: {
487 tooltip: {
488 callbacks: {
489 label: function (tooltipItem) {
490 const value = tooltipItem.raw;
491 const formattedValue = formatBytes(value);
492 return `${tooltipItem.label}: ${formattedValue}`;
493 }
494 }
495 }
496 }
497 }
498 });
499 }
500
501 // Swap Chart
502 const swapChart = document.getElementById('swapChart');
503 if (swapChart) {
504 document.getElementById("swapTotal").innerText = `Total: ${formatBytes(swapInfo.total)}`;
505 }
506 if (swapChartInstance) {
507 swapChartInstance.data.datasets[0].data = [swapInfo.used, swapInfo.total - swapInfo.used];
508 swapChartInstance.update();
509 } else {
510 if (swapChart) {
511 const swapContext = swapChart.getContext('2d')
512 swapChartInstance = new Chart(swapContext, {
513 type: 'pie',
514 data: {
515 labels: ['Used', 'Free'],
516 datasets: [{
517 label: 'Swap Usage',
518 data: [swapInfo.used, swapInfo.total - swapInfo.used],
519 backgroundColor: ['#FFCE56', '#E7E9ED']
520 }]
521 },
522 options: {
523 responsive: true,
524 plugins: {
525 tooltip: {
526 callbacks: {
527 label: function (tooltipItem) {
528 const value = tooltipItem.raw;
529 const formattedValue = formatBytes(value);
530 return `${tooltipItem.label}: ${formattedValue}`;
531 }
532 }
533 }
534 }
535 }
536 });
537 }
538 }
539
540 // Disk Chart
541 document.getElementById("diskTotal").innerText = `Total: ${formatBytes(diskInfo.total)}`;
542 if (diskChartInstance) {
543 diskChartInstance.data.datasets[0].data = [diskInfo.used, diskInfo.total - diskInfo.used];
544 diskChartInstance.update();
545 } else {
546 const diskChart = document.getElementById('diskChart').getContext('2d');
547 diskChartInstance = new Chart(diskChart, {
548 type: 'pie',
549 data: {
550 labels: ['Used', 'Free'],
551 datasets: [{
552 label: 'Disk Usage',
553 data: [diskInfo.used, diskInfo.total - diskInfo.used],
554 backgroundColor: ['#63950d', '#ca7b00']
555 }]
556 },
557 options: {
558 responsive: true,
559 plugins: {
560 tooltip: {
561 callbacks: {
562 label: function (tooltipItem) {
563 const value = tooltipItem.raw;
564 const formattedValue = formatBytes(value);
565 return `${tooltipItem.label}: ${formattedValue}`;
566 }
567 }
568 }
569 }
570 }
571 });
572 }
573
574 };
575
576 function updateProgressBar(id, percentage) {
577 const bar = document.getElementById(id);
578 bar.style.width = percentage + '%';
579
580 // Remove old color classes
581 bar.classList.remove('progress-bar-green', 'progress-bar-yellow', 'progress-bar-orange', 'progress-bar-red');
582
583 // Add new color class based on percentage
584 if (percentage <= 50) {
585 bar.classList.add('progress-bar-green');
586 } else if (percentage <= 70) {
587 bar.classList.add('progress-bar-yellow');
588 } else if (percentage <= 90) {
589 bar.classList.add('progress-bar-orange');
590 } else {
591 bar.classList.add('progress-bar-red');
592 }
593 }
594
595 function formatBytes(bytes) {
596 const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
597 let unitIndex = 0;
598 while (bytes >= 1024 && unitIndex < units.length - 1) {
599 bytes /= 1024;
600 unitIndex++;
601 }
602 return bytes.toFixed(2) + ' ' + units[unitIndex];
603 }
604
605 });
606
607 function logOut() {
608 window.location.href = window.location.origin + "{{ logout }}";
609 }
610</script>
611<footer>
612 Generated by <a href="https://github.com/thevickypedia/SysMonk/releases/tag/v{{ version }}">SysMonk - v{{ version }}</a>
613</footer>
614</body>
615</html>
616"###.to_string()
617}