display and acknowledge alerts

This commit is contained in:
Andreas Wrede
2026-04-03 06:35:45 -04:00
parent c5770006f7
commit 941f3ea4b0
6 changed files with 414 additions and 62 deletions
+189 -44
View File
@@ -3,10 +3,16 @@
{% include 'head.html' %}
<style>
body {
margin: 10px;
background: #f5f5f5;
overflow: hidden;
}
.nav {
background: #fff;
padding: 15px;
margin-bottom: 20px;
padding: 10px 15px;
margin-bottom: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-radius: 4px;
}
@@ -16,6 +22,7 @@
text-decoration: none;
color: #0066cc;
font-weight: 500;
font-size: 0.9em;
}
.nav a:hover {
@@ -27,53 +34,140 @@
font-weight: bold;
}
.container {
max-width: 1600px;
margin: 0 auto;
max-height: calc(100vh - 120px);
overflow-y: auto;
padding-right: 10px;
}
h1 {
color: #333;
margin-bottom: 5px;
font-size: 1.5em;
}
h2 {
color: #333;
margin-bottom: 10px;
font-size: 1.2em;
padding: 10px 15px;
background: white;
border-radius: 6px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
.subtitle {
color: #666;
margin-bottom: 15px;
font-size: 0.9em;
}
.content {
display: flex;
display: flex;
flex-direction: column;
gap: 15px;
}
.table {
/* flex: 1; */
flex-grow: none;
.table-section {
background: white;
border-radius: 6px;
padding: 15px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
.log {
flex: 2;
flex-grow: 1;
.log-section {
background: white;
border-radius: 6px;
padding: 15px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
max-height: 400px;
overflow-y: auto;
}
#ntable {
border-collapse: collapse;
font-size: 95%;
/* width: 100%; */
width: 100%;
font-size: 0.9em;
}
#ntable td,
#ntable th {
border: 1px solid #ddd;
border: 1px solid #e0e0e0;
text-align: left;
padding: 0px;
padding: 8px 10px;
}
#ntable tr:nth-child(even) {
background-color: #f2f2f2;
background-color: #fafafa;
}
#ntable tr:hover {
background-color: #ddd;
background-color: #e3f2fd;
}
#ntable th {
padding-top: 12px;
padding-bottom: 12px;
background-color: #9d9d9d;
padding: 12px 10px;
background-color: #2196f3;
color: white;
font-weight: 600;
position: sticky;
top: 0;
z-index: 10;
}
#ntable
th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after {
content: " \2195";
content: " ";
opacity: 0.5;
}
/* Alert count column styling */
#ntable td.alert-warning {
color: #ff9800;
font-weight: bold;
text-align: center;
}
#ntable td.alert-critical {
color: #f44336;
font-weight: bold;
text-align: center;
}
/* Scrollbar styling */
.container::-webkit-scrollbar,
.log-section::-webkit-scrollbar {
width: 8px;
}
.container::-webkit-scrollbar-track,
.log-section::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.container::-webkit-scrollbar-thumb,
.log-section::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}
.container::-webkit-scrollbar-thumb:hover,
.log-section::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Message styling */
#messages {
font-size: 0.85em;
line-height: 1.6;
}
#messages div {
padding: 5px 0;
border-bottom: 1px solid #f0f0f0;
}
/* Modal for connection status messages */
@@ -85,7 +179,7 @@
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
background-color: rgba(0, 0, 0, 0.5);
}
.connection-modal.show {
@@ -95,20 +189,35 @@
}
.connection-modal-content {
background-color: #f9f9f9;
padding: 20px;
border: 1px solid #888;
border-radius: 5px;
background-color: white;
padding: 30px 40px;
border-radius: 8px;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
min-width: 300px;
}
.connection-modal-content p {
margin: 10px 0;
margin: 0;
font-size: 16px;
color: #333;
}
/* State indicators */
.state-up {
color: #4caf50;
font-weight: 600;
}
.state-down {
color: #f44336;
font-weight: 700;
}
.state-overdue {
color: #ff9800;
font-weight: 700;
}
</style>
<script type="text/javascript">
var cnt = 0;
@@ -130,6 +239,14 @@
function createRow(data) {
var row = document.createElement("tr");
var c_name = document.createElement("td");
var c_warning = document.createElement("td");
c_warning.style.textAlign = "center";
c_warning.style.color = "#ff9800";
c_warning.style.fontWeight = "bold";
var c_critical = document.createElement("td");
c_critical.style.textAlign = "center";
c_critical.style.color = "#f44336";
c_critical.style.fontWeight = "bold";
var c_ipv4addr = document.createElement("td");
var c_ipv4state = document.createElement("td");
var c_ipv4latency = document.createElement("td");
@@ -143,6 +260,8 @@
var c_ipv6statets = document.createElement("td");
c_ipv6statets.style.textAlign = "right";
row.appendChild(c_name);
row.appendChild(c_warning);
row.appendChild(c_critical);
row.appendChild(c_ipv4addr);
row.appendChild(c_ipv4state);
row.appendChild(c_ipv4latency);
@@ -156,6 +275,13 @@
} else {
c_name.innerHTML = data.name;
}
// Set alert counts
var warningCount = data.alert_warning_count || 0;
var criticalCount = data.alert_critical_count || 0;
c_warning.innerHTML = warningCount > 0 ? warningCount : "";
c_critical.innerHTML = criticalCount > 0 ? criticalCount : "";
c_ipv4addr.innerHTML = data.connections[0].addr;
c_ipv4state.innerHTML = data.connections[0].state;
if (data.connections.length > 1) {
@@ -178,28 +304,41 @@
createRow(data);
setup();
}
// Update warning and critical counts
var warningCount = data.alert_warning_count || 0;
var criticalCount = data.alert_critical_count || 0;
name_idx[data.name].cells[1].innerHTML = warningCount > 0 ? warningCount : "";
name_idx[data.name].cells[2].innerHTML = criticalCount > 0 ? criticalCount : "";
for (var i = 0; i < data.connections.length; i++) {
name_idx[data.name].cells[2 + i * 4].innerHTML = data.connections[i].addr;
name_idx[data.name].cells[5 + i * 4].innerHTML = formatTS(
// Offset by 2 for the warning/critical count columns
name_idx[data.name].cells[3 + i * 4].innerHTML = data.connections[i].addr;
name_idx[data.name].cells[6 + i * 4].innerHTML = formatTS(
data.connections[i].statetime
);
if (data.connections[i].state == "up") {
state = "up";
state = '<span class="state-up">up</span>';
latency = Number.parseFloat(data.connections[i].rtts[0]).toFixed(2);
} else {
if (data.connections[i].state == "unknown") {
state = "";
latency = "";
name_idx[data.name].cells[2 + i * 4].innerHTML = "";
name_idx[data.name].cells[5 + i * 4].innerHTML = "";
name_idx[data.name].cells[3 + i * 4].innerHTML = "";
name_idx[data.name].cells[6 + i * 4].innerHTML = "";
} else if (data.connections[i].state == "down") {
state = '<span class="state-down">down</span>';
latency = "-";
} else if (data.connections[i].state == "overdue") {
state = '<span class="state-overdue">overdue</span>';
latency = "-";
} else {
state = "<b>" + data.connections[i].state + "</b>";
latency = "-";
}
}
name_idx[data.name].cells[3 + i * 4].innerHTML = state;
name_idx[data.name].cells[4 + i * 4].innerHTML = latency;
name_idx[data.name].cells[4 + i * 4].innerHTML = state;
name_idx[data.name].cells[5 + i * 4].innerHTML = latency;
}
}
@@ -231,7 +370,7 @@
update_table(state.data);
} else if (state.type == "message") {
var msgs = document.getElementById("messages");
msgs.insertAdjacentHTML("afterbegin", state.data + "<br>");
msgs.insertAdjacentHTML("afterbegin", "<div>" + state.data + "</div>");
}
cnt++;
};
@@ -264,20 +403,24 @@
{% include 'menu.html' %}
<div id="content" class="content" style="overflow: hidden">
<div id="table" class="table" style="overflow: hidden">
<!-- <h2>{{title}}</h2> -->
<div class="container">
<h1>{{ header }}</h1>
<p class="subtitle">Real-time host monitoring and event log</p>
<div class="table-section">
<table id="ntable" class="sortable">
<thead>
<tr>
<th>Name</th>
<th style="text-align: center" title="Warning Alerts">⚠️</th>
<th style="text-align: center" title="Critical Alerts">🔴</th>
<th>IPv4 Addr</th>
<th>State</th>
<th style="text-align: right">Latencey</th>
<th style="text-align: right">Latency</th>
<th style="text-align: right">Last State</th>
<th>IPv6 Addr</th>
<th>State</th>
<th style="text-align: right">Latencey</th>
<th style="text-align: right">Latency</th>
<th style="text-align: right">Last State</th>
</tr>
</thead>
@@ -285,6 +428,8 @@
{% for host in hosts %}
<tr>
<td>{{ host.name }}</td>
<td style="text-align: center; color: #ff9800; font-weight: bold;">{{ host.alert_warning_count if host.alert_warning_count > 0 else '' }}</td>
<td style="text-align: center; color: #f44336; font-weight: bold;">{{ host.alert_critical_count if host.alert_critical_count > 0 else '' }}</td>
{% for conn in host.connections %}
<td>{{ conn.addr if conn.addr else '' }}</td>
<td>{{ conn.state if conn.state else '' }}</td>
@@ -302,13 +447,13 @@
</tbody>
</table>
</div>
<div id="log" class="log" style="overflow: auto;">
<div class="log-section">
<h2>Log of Events</h2>
<div id="messages">
</div>
<div id="messages"></div>
</div>
</div>
{% include 'foot.html' %}
<!-- Connection status modal -->