- scheduler.js: check agents every 2 min — alert on offline/online
transitions and CPU/memory/disk threshold breaches (90%)
- notificationService.js: sendAgentAlert() sends via email, webhook,
ntfy, and saves to in-app notifications table
- package.json: pin better-sqlite3 to 9.6.0 (compatible with Node 20)
- update-server.sh: add React client build step; use npm install
instead of rm -rf + npm ci for faster incremental updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug fixes:
- tags field was missing from INSERT/UPDATE SQL — tags never persisted
- is_public: `0 !== false` is true in JS, so private resources (is_public=0)
were silently flipped to public on every edit; fixed with explicit 0/false check
Global setting inheritance:
- One-time DB migration nulls out check_interval and timeout on all existing
resources so they inherit from global monitoring settings going forward
- Scheduler reads global check_interval and timeout from DB each cycle and
uses them as the fallback when per-resource values are null
- Create and edit modals: empty field = use global; placeholder shows the
current global value so it's clear what will be used
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes the consecutive-slow-checks requirement as a configurable setting
instead of hardcoding 3. Saved under key 'slow_alert_consecutive' in the
DB, readable via SLOW_ALERT_CONSECUTIVE env var. UI appears in Settings →
Monitoring alongside the existing Consecutive Failures Threshold field.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously a single check above 2× average response time fired an alert.
This caused false positives from normal network variance.
- scheduler.js: track consecutive slow checks per resource; only alert after
3 in a row (resets to 0 when response time normalises)
- monitorService.js: calculateResponseTimeBaseline now returns P95 instead
of mean, making 2× baseline a much more realistic slow threshold
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HTTP method + request body: GET/POST/PUT/PATCH/DELETE/HEAD selector and body textarea for http/health checks; stored in http_method/http_body columns; monitorService sends correct axios method+data
- Alert escalation: incidents open longer than escalation_hours get forwarded to fallback_webhook and marked escalated=1; hourly cron in scheduler
- Baseline-relative slow alerts: effectiveThreshold = response_time_threshold || (baseline * 2) — adaptive threshold without manual config
- Notification grouping: pendingAlerts collected per check cycle; ≥2 alerts flush as one grouped email+webhook; individual in-app entries still saved per resource
- Rate limiting: in-memory per-IP limiter (20/min heartbeat, 60/min status-page); no external package
- App.js split: Dashboard.js (monitors list + modals) and ResourceDetail.js extracted; App.js reduced from ~1987 to ~80 lines
- formatDuration moved to utils/timeUtils.js; imported by both new components
- DB migrations: http_method, http_body, incidents.escalated columns
- 18 passing API/unit tests (node:test, zero dependencies)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- Add in-app notification system with database storage
- Implement bell icon component with unread count badge in dashboard header
- Add full Notifications page tab with pagination and filtering (All/Unread/Read)
- Implement real-time WebSocket push notifications to all connected clients
- Add notification API endpoints: GET, PUT /read, DELETE, POST /clear
- Fix critical bug in notificationService where 'status' variable was undefined
- Add Notifications route to App.js routing
- Enhanced UI with notification popup in header and dedicated full-page view
- Auto-reconnect WebSocket with 5s timeout on disconnect
- Support rich notification content with resource details and timestamps
- notificationService: Parse comma-separated emails from resource.email_to or global config; validate and send to each recipient
- API: Add PATCH /api/resources/:id/group endpoint to move resource between groups with validation and cache invalidation
- UI: Add group selector to edit modal; wire group change to PATCH endpoint; add help text for multi-email format