Add heartbeat monitoring, alert rules, cert badges, webhook templates, and status page visibility

- Heartbeat type: cron jobs POST to /api/heartbeat/:token to stay alive; scheduler marks
  them down if no ping is received within the configured timeout window
- Alert rules: per-resource consecutive_failures_threshold now respected in handleIncident,
  overriding the global setting; GET /api/resources/:id/alert-rules endpoint added
- SSL cert expiry badge: dashboard rows surface days_remaining for TLS monitors, turning
  orange at ≤30 days and red at ≤7 days
- Webhook payload templates: {{name}}/{{status}}/{{url}}/{{type}}/{{message}}/{{timestamp}}
  variables interpolated before POST; Slack-compatible text wrapping as fallback
- Status page visibility: is_public column per resource; GET /api/status-page returns only
  public resources; Status.js updated to use the new endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-07-31 09:08:15 -04:00
1 parent 34986b5760
commit a6cf6a9762
16 files changed
+554 -422

No files matched your search

+4 -8
View File
@@ -18,14 +18,10 @@ function Status() {
const loadStatusData = async () => {
try {
const [resourcesRes, incidentsRes, maintenanceRes] = await Promise.all([
axios.get('/api/resources'),
axios.get('/api/incidents'),
axios.get('/api/maintenance-windows'),
]);
setResources(resourcesRes.data.resources || []);
setIncidents(incidentsRes.data.incidents || []);
setMaintenanceWindows(maintenanceRes.data.maintenanceWindows || []);
const statusRes = await axios.get('/api/status-page');
setResources(statusRes.data.resources || []);
setIncidents(statusRes.data.incidents || []);
setMaintenanceWindows(statusRes.data.maintenanceWindows || []);
setLastUpdated(new Date());
} catch (error) {
// Status load error handled