feat: per-server notification thresholds with alert on agent metrics push

This commit is contained in:
2026-06-13 16:12:36 -04:00
parent b6c7275437
commit c63c5ffcfd
8 changed files with 181 additions and 1 deletions

View File

@@ -270,6 +270,12 @@ $canManage = $server_role === 'owner' || $server_role === 'manager';
<i class="fas fa-sync-alt"></i>
<span>Restart Service</span>
</button>
<?php if ($server_role === 'owner'): ?>
<button class="action-card" onclick="showThresholdModal()">
<i class="fas fa-bell"></i>
<span>Notification Thresholds</span>
</button>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
@@ -297,6 +303,55 @@ $canManage = $server_role === 'owner' || $server_role === 'manager';
</div>
</div>
<div id="thresholdModal" class="modal">
<div class="modal-dialog">
<div class="modal-header">
<h3><i class="fas fa-bell"></i> Notification Thresholds</h3>
<button type="button" class="modal-close" onclick="closeThresholdModal()">&times;</button>
</div>
<div class="modal-body">
<p class="text-muted" style="margin-bottom:20px">
Notifications are sent to all users with access to this server when the agent reports values
exceeding these thresholds. Only the server owner can modify them.
</p>
<form id="thresholdForm" onsubmit="return false">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Resource</th>
<th>Warning (%)</th>
<th>Critical (%)</th>
</tr>
</thead>
<tbody>
<?php foreach (['cpu' => 'CPU', 'ram' => 'RAM', 'disk' => 'Disk'] as $key => $label): ?>
<tr>
<td><strong><?= $label ?></strong></td>
<td>
<input type="number" name="<?= $key ?>_warning"
value="<?= htmlspecialchars((string)($server["threshold_{$key}_warning"] ?? 80)) ?>"
min="0" max="100" step="0.01" class="form-control" style="width:120px">
</td>
<td>
<input type="number" name="<?= $key ?>_critical"
value="<?= htmlspecialchars((string)($server["threshold_{$key}_critical"] ?? 95)) ?>"
min="0" max="100" step="0.01" class="form-control" style="width:120px">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" onclick="saveThresholds()"><i class="fas fa-save"></i> Save</button>
<button class="btn btn-secondary" onclick="closeThresholdModal()">Cancel</button>
</div>
</div>
</div>
<form id="csrfForm" style="display:none">
<input type="hidden" name="_csrf_token" value="<?= $this->csrfToken() ?>">
</form>
@@ -591,4 +646,41 @@ function refreshMetrics() {
}
setInterval(refreshMetrics, 30000);
function showThresholdModal() {
document.getElementById('thresholdModal').style.display = 'flex';
document.getElementById('thresholdModal').onclick = function(e) {
if (e.target === this) closeThresholdModal();
};
}
function closeThresholdModal() {
document.getElementById('thresholdModal').style.display = 'none';
}
function saveThresholds() {
const form = document.getElementById('thresholdForm');
const data = new URLSearchParams(new FormData(form));
data.append('_csrf_token', getCsrfToken());
document.querySelector('#thresholdModal .btn-primary').disabled = true;
fetch('/servers/<?= $server['id'] ?>/thresholds', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRF-Token': getCsrfToken(),
},
body: data.toString(),
})
.then(r => r.json())
.then(d => {
showToast(d.success ? 'success' : 'error', d.message ?? d.error);
if (d.success) closeThresholdModal();
})
.catch(e => showToast('error', 'Request failed: ' + e.message))
.finally(() => {
document.querySelector('#thresholdModal .btn-primary').disabled = false;
});
}
</script>