Sitelet https://github.com/uExel/CryoHealth-api/pull/20
Skip to content

fix(alerts): 500s on manual broadcast + geo dedupe; add admin edit/clear/delete routes - #20

Merged
shaan360 merged 3 commits into
mainfrom
fix/alert-broadcast-and-admin-routes
Sep 27, 2026
Merged

shaan360 merged 3 commits into
mainfrom
fix/alert-broadcast-and-admin-routes

Conversation

@shaan360

Copy link
Copy Markdown
Member

Summary

Broadcasting an alert from the admin portal failed with a generic "Failed to broadcast alert", and deleting an alert returned 404. Three API bugs plus missing routes:

  • Invalid window timestamp → 500. The dashboard posted free text ("next 24h") as windowStart; new Date() produced an Invalid Date and Postgres rejected it. windowStart/windowEnd are now @IsDateString (400 instead of 500), and IssueAlertDto accepts free-text estimatedWindow, which the dashboard displays everywhere.
  • Duplicate manual alert → 500 instead of 409. insertAlert caught the unique violation and then queried the existing alert inside the now-aborted transaction (25P02). The manual path now uses ON CONFLICT DO NOTHING, the same as the geo path.
  • Geo dedupe path also 500'd. TypeORM 0.3.31 pushes one identifiers entry per value set even when RETURNING yields no row, so identifiers.length === 0 never fired and identifiers[0].id threw. We now check the id itself. This affected POST /alerts/hazard-scores whenever a lake returned to a tier that still had an active alert.
  • Missing PUT/PATCH/DELETE /admin/alerts/:id. The dashboard calls these routes. They were lost when the dashboard dropped its own DB access (uExel/cryohealth 20da49a). This ports that logic: edit (body/tier/window, audited diff), clear (reason required), and delete (reason required; 409 with dependents if CHWs acknowledged the alert).

Companion dashboard PR: uExel/cryohealth fix/broadcast-alert-form. Either repo can deploy first: whitelist: true without forbidNonWhitelisted means an older API silently drops estimatedWindow.

Not changed (policy, flagged for discussion)

  • Neither the geo path nor manual issuing clears the previous tier's active alert. Prod has several lakes with more than one active alert.
  • Deleting or clearing an alert doesn't restore the lake's currentTier. Shishper currently shows watch because of the manual "test" alert, while geo has an active CRITICAL.

Test plan

  • npm test: 45/45 pass. The two updated dedupe tests fail on the old code and pass on the new.
  • tsc --noEmit and eslint src/alerts clean
  • After deploy: broadcast an alert with an estimated window from the admin portal
  • After deploy: delete or clear the "test" WATCH alert on Shishper

🤖 Generated with Claude Code

…ear/delete routes

- insertAlert: manual issue now uses ON CONFLICT DO NOTHING like the geo path.
  Catching the unique violation and then querying ran inside an aborted Postgres
  transaction (25P02), turning the intended 409 into a 500.
- insertAlert: detect a skipped row by the id itself. TypeORM 0.3 pushes one
  identifier entry per value set even when RETURNING yields nothing, so
  `identifiers.length === 0` never fired and `identifiers[0].id` threw — this
  also 500'd geo hazard-score reports that hit an existing active alert.
- IssueAlertDto: windowStart/windowEnd are @IsDateString (400, not a 500 from an
  invalid timestamp); accept free-text estimatedWindow used by the dashboard.
- Add PUT/PATCH/DELETE /admin/alerts/:id (edit, clear, delete) that the web
  dashboard calls; they were lost when the dashboard dropped its own DB access
  (cryohealth 20da49a). Clear/delete require an audited reason; delete 409s with
  dependents when CHWs have acknowledged the alert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shaan360 and others added 2 commits September 27, 2026 22:59
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shaan360
shaan360 merged commit 14636dd into main Sep 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant