[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. 馃珫 Status page with uptime monitoring & API monitoring as code 馃珫 openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor

fix: add private locations incident trigger (#2503) master

* feat: add automatic incident creation and resolution for private location monitors * feat: add threshold check for incident creation/resolution to match cloud monitor behavior * ci: apply automated fixes * fix: private location incident violations - Add resolveIncident helper matching cloud checker pattern - Fix degraded case: resolve incidents when transitioning from error - Fix error case: capture and pass incident ID to notifications - Fix active case: capture resolved incident and pass to notifications Resolves violations P1 and P2 (incident data in notifications) * fix: handle race condition in incident creation gracefully - Detect constraint violations from concurrent incident creation - Fetch existing incident when conflict occurs instead of failing - Prevents duplicate incident errors in notifications * refactor: extract shared incident resolution utilities V1 (P3): Extract duplicate incident resolution logic - Created shared incident-utils.ts with findOpenIncident and resolveIncident - Removed duplicate code from index.ts (cloud checker) - Removed duplicate code from private-location.ts (private checker) - Single source of truth for incident resolution V2 (P2): Fix concurrent recovery race condition - Changed resolveIncident to use conditional update with resolvedAt IS NULL - Only publish audit log if update succeeded (.returning() check) - Prevents duplicate audits and notifications from concurrent recoveries - Atomic check-and-update pattern * fix: correct template string syntax in incident-utils Fixed syntax error where template string was corrupted during file creation. Changed 'id: monitor:,' to 'id: \monitor:\\,' Fixes CI build failure. * fix: resolve incident redeclaration in switch statement Moved 'let incident = null' outside switch to fix redeclaration error. Switch cases share scope, so declaring in each case caused conflicts. Fixes oxlint error: Identifier 'incident' has already been declared * ci: apply automated fixes * fix: add missing inArray import and remove invalid autoResolved - Added inArray to imports from @openstatus/db - Removed autoResolved from incident.resolved audit log metadata (not allowed in audit log metadata type) Fixes: - TS2552: Cannot find name 'inArray' - TS2345: autoResolved not in metadata type * ci: apply automated fixes * fix: filter null values from locationIds for inArray Added filter to remove null values from locationIds array. inArray doesn't accept nullable arrays, causing TS2769 error. Fixes: TS2769 type error on line 198 * fix: scope incident resolution to specific incident by ID Prevents race condition where multiple open incidents for the same monitor could be resolved but only the first was logged/returned. Changed resolveIncident to first find the open incident via findOpenIncident (.get()), then update by incident.id instead of monitorId. The resolvedAt IS NULL check still prevents concurrent recovery race conditions. This makes the function consistent and safe: only one incident per call, properly tracked. * fix: resolve all open incidents on monitor recovery Fixes P1 data integrity issue where multiple open incidents could exist per monitor, but only the first was resolved, leaving others orphaned. Changes: - Added findAllOpenIncidents() to get all open incidents for a monitor - Updated resolveIncident() to resolve ALL incidents with proper logging - Each incident gets its own audit log entry - Returns array instead of single incident (or null) - Updated all callers (cloud + private checkers) to handle array return - Uses first incident ID for notifications (maintains current UX) Benefits: - No orphaned incidents - Complete audit trail for each incident - Still atomic per-incident (prevents concurrent resolution races) - Backward compatible notification behavior * fix: use atomic bulk update for incident resolution Fixes P2 violation where loop-based resolution could fail midway, leaving some incidents resolved and others open (partial state). Changes: Replaced per-incident update loop with single inArray bulk update. All incidents now resolved atomically in one query. Audit logs moved after bulk update with try-catch (best-effort, non-critical). Benefits: No partial state on failures, same race condition protection via isNull check, audit failures don't corrupt incident data. * fix: remove unused isNull import ESLint warning: isNull was imported from @openstatus/db but never used in index.ts after refactoring to use inArray for bulk incident updates. * fix: resolve P1 issues in private location incident handling P1 #1: Removed dead code condition in degraded case that checked monitor.status === error. Private location handler never writes monitor.status, so this condition was always false, preventing incident resolution on degraded transitions. P1 #2: Replaced inline incident resolution in active case with shared resolveIncident() helper (matching degraded case). The inline code only resolved one incident and lacked atomic IS NULL check, reintroducing race conditions and multiple-incident bugs. Benefits: Incidents now resolve correctly on degraded transitions, all open incidents resolved (not just first), atomic resolution prevents duplicate notifications, consistent code between active/degraded cases, eliminated 41 lines of duplicate code. * fix: add error handling around incident resolution (P2) Added try-catch around resolveIncident() calls in degraded and active cases to prevent transient failures from aborting recovery handling. Without error handling, if resolveIncident() throws after status is upserted, the handler aborts and retries exit early at status === priorStatus check, leaving incident open and recovery notifications unsent. Now logs warnings but continues with notifications, ensuring recovery completes even if incident resolution fails temporarily. * ci: apply automated fixes * fix: align error logging pattern with existing convention (P3) Changed catch blocks in degraded and active cases to extract error message explicitly (error_message: error instanceof Error ? error.message : String(error)) instead of passing raw error object. Raw Error objects have non-enumerable properties (message, stack) that don't serialize properly in structured logging. The extracted pattern ensures error messages are always captured, matching the existing convention at line 422. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>


+275 -58
3 changed files