Maintenance and incident cards rendered affected monitors in a
non-wrapping flex row; with many monitors the badges overflowed the
card (maintenance) or hid behind a horizontal scrollbar (incident).
Badge text was also nowrap, so a single long monitor name could still
bleed past the card edge on narrow viewports.
Apply one rule to both components: badges wrap within the box, and
badge text itself wraps when a name exceeds the row width.
Fixes#794
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Agreed. ĐANG XÁC MINH is closer to “verifying/confirming” and can imply that the cause is already being checked. In this incident-management context, INVESTIGATING refers to the earlier phase where the team is still looking into the issue, so ĐANG ĐIỀU TRA is more accurate and clearer for Vietnamese users.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
- Import GetAllSiteData directly from siteDataController instead of the
controller barrel, which re-exports incidentController and creates a
circular import (risk of partially-initialized modules at runtime).
- Pass a stable per-comment deduplication id to subscriberQueue.push so a
retried/double push notifies once; without it the queue falls back to a
Date.now()-suffixed id that never deduplicates.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Manually created/updated incidents never reached the subscriber
notification workflow (#774): only alertingQueue pushed to
subscriberQueue, so dashboard and API incidents stayed silent.
Make AddIncidentComment the single notification choke point: posting a
comment on an INCIDENT-type incident notifies "incidents" subscribers,
regardless of source (alert, dashboard, or API). Remove the two bespoke
pushes in alertingQueue — alert-driven incidents now notify through the
same path via their auto-created comments, instead of twice.
The dedup id is now the comment id (previously the incident id with a
Date.now() suffix, which never deduplicated anything). Alert-created
incident emails now carry the incident state (INVESTIGATING) instead of
the alert status (TRIGGERED) in the subject; body content is unchanged.
Also drop the dead commented-out queueController blocks left from the
old notification system.
Fixes#774
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The field was already stored in the DB, mapped through the controller,
and present on MonitorRecordTyped — but missing from CreateMonitorRequest
and UpdateMonitorRequest, so the API silently dropped it.
- layoutController/site-configurations: use strict boolean check instead of
Boolean() coercion for showInlineEvents so persisted "false" strings don't
flip the toggle
- (kener)/+page.svelte: collapse confusing triple negation !!! to single !
- NotificationsList: add aria-label/title to the icon-only events button
(+ "Open events page" en locale key)
- move NotificationEvent into shared $lib/types/notifications so client code
no longer imports from the server dashboardController; controller re-exports
it for backwards compatibility
- [page_path] and monitor pages: pass hideNotificationsPopover={showInlineEvents}
to ThemePlus so inline and popover event surfaces stay mutually exclusive
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET / was throwing KnexTimeoutError ("Timeout acquiring a connection")
in production. Root cause was the connection pool, not the database:
the single process (SvelteKit + cron scheduler + BullMQ workers) shared
one pool capped at 10, while one GET / fans out ~6 queries. A couple of
concurrent page loads, or a per-minute monitor burst overlapping a load,
exceeded 10 and queued acquires blew past the 15s timeout. Postgres
itself had 97 free slots the whole time and no leak.
Split into two pools so background work can't starve page loads:
- web pool (DATABASE_POOL_MAX, default 10) serves HTTP requests
- worker pool (DATABASE_WORKER_POOL_MAX, default 5) serves background jobs
Routing is by execution context via AsyncLocalStorage: q.createWorker
(the single chokepoint all workers/schedulers flow through) runs each
processor inside a worker-pool context, and BaseRepository.knex resolves
the pool from that context, defaulting to the web pool. This keeps shared
controllers correct whether they run in a request or a job. SQLite has no
real pool and reuses a single connection, so the split is a no-op there.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ioredis v5 dropped the namespace merge on the default export, so
`Redis.RedisOptions` resolves to TS2702 (type used as a namespace).
Import the `RedisOptions` type by name instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-row status+note backfill is one logical confirmation flip; wrap the
read+updates in a knex transaction so a mid-loop failure can't leave the window
half-confirmed/half-held (coderabbit out-of-diff finding).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PATCH: confirmation_threshold:null resets to 1 (off); undefined keeps existing (Copilot)
- backfill note is per-row severity-aware: 'Down'/'Degraded confirmed after N…' (Copilot)
- enforce 1–60 at the data layer via clampConfirmationThreshold on insert/update,
covering all app write paths incl. the manage API (coderabbit)
- anchor via dedicated getLastObservedStatus query so a long incident/maintenance
window can no longer push the anchor out of the lookback and bypass damping (coderabbit)
- overlays fetched AFTER execute() and keyed by job ts, making the freeze gate
timestamp-safe and catching mid-check overlays (coderabbit + greptile)
- use Array.includes over indexOf!==-1 (greptile); refresh pendingHold doc (coderabbit)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New v4/monitors/grace-period.md covering behavior, config, API, interactions
(alerts/maintenance/NO_DATA/groups/heartbeat), and verification; linked from the
Monitors sidebar and the Monitors Overview related-docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>