Skip to content

refactor(alerts): drop AlertWebhook model#197

Merged
TerrifiedBug merged 1 commit intomainfrom
refactor/drop-alertwebhook
Apr 28, 2026
Merged

refactor(alerts): drop AlertWebhook model#197
TerrifiedBug merged 1 commit intomainfrom
refactor/drop-alertwebhook

Conversation

@TerrifiedBug
Copy link
Copy Markdown
Owner

Summary

Removes the legacy AlertWebhook model and its env-scoped fan-out, consolidating all alert delivery onto NotificationChannel(type=webhook) which already has the necessary infrastructure:

  • channel drivers in src/server/services/channels/ (slack / email / pagerduty / webhook)
  • DeliveryAttempt tracking via trackChannelDelivery
  • per-rule routing via AlertRuleChannel
  • backoff + retries via RetryService

AlertWebhook had none of these — just prisma.alertWebhook.findMany({ where: { environmentId } }) followed by raw fetch() calls. The webhook driver in channels/webhook.ts already does the same thing (URL validation, custom headers, optional HMAC signature) inside the channel framework.

Why

  • Plaintext hmacSecret on AlertWebhook rows — no encryption-at-rest.
  • No DeliveryAttempt tracking — failures had no audit trail.
  • No retries on transient failures.
  • No per-rule routing — every webhook fired for every alert in the env.

All of those are subsumed by NotificationChannel. The shape parity is exact (WebhookPayload and ChannelPayload were structurally identical), so the migration is purely a deletion.

Breaking change

Warning

Existing AlertWebhook rows are dropped by this PR's migration. Each one must be re-created as NotificationChannel(type=webhook) before merge — manually copy url / headers / hmacSecret into the channel config JSON, and link via AlertRuleChannel if rule-scoped routing is desired.

User has confirmed they will migrate prod rows by hand prior to deploy.

Files changed

Schema / migration

  • prisma/schema.prisma — drops model AlertWebhook, the Environment.alertWebhooks relation, and DeliveryAttempt.webhookId.
  • prisma/migrations/20260428000000_drop_alert_webhook/migration.sqlDROP TABLE IF EXISTS \"AlertWebhook\" CASCADE plus ALTER TABLE \"DeliveryAttempt\" DROP COLUMN IF EXISTS \"webhookId\".

Service callers

  • src/server/services/webhook-delivery.ts — DELETED. formatWebhookMessage moved to channels/webhook.ts (only place it's used now).
  • src/server/services/event-alerts.ts — drop deliverWebhooks(...); channel delivery now passes event.id for tracking.
  • src/server/services/fleet-alert-service.ts — drop the AlertWebhook fan-out block; channel delivery is the single path.
  • src/server/services/cost-alert.ts — drop the AlertWebhook query + loop.
  • src/server/services/retry-service.ts — drop retryWebhook and the if (attempt.webhookId) branch in processRetries. WebhookPayload replaced with ChannelPayload.
  • src/server/services/delivery-tracking.ts — drop trackWebhookDelivery and the webhookId parameter on trackDelivery.

tRPC

  • src/server/routers/alert-webhooks.ts — DELETED.
  • src/server/routers/alert.ts — drop alertWebhooksRouter registration.
  • src/server/routers/alert-deliveries.ts — drop the if (attempt.webhookId) retry path; channel-only retry now.

Middleware / audit / actions

  • src/trpc/init.ts — drop the AlertWebhook lookup in withTeamAccess.
  • src/server/middleware/audit.ts — drop AlertWebhook from ENTITY_LOADERS, resolveTeamId, and resolveEnvironmentId.
  • src/lib/audit-actions.ts — drop the alertWebhook.* action labels.

UI

  • src/app/(dashboard)/alerts/_components/webhooks-section.tsx — DELETED.
  • src/app/(dashboard)/alerts/page.tsx — remove the WebhooksSection import and render call. Two sections remain: NotificationChannelsSection and OutboundWebhooksSection.
  • src/app/(dashboard)/alerts/_components/{delivery-status-panel,failed-deliveries-section}.tsx — drop the legacy_webhook channel-icon entry.

Tests

  • DELETED: src/server/routers/__tests__/alert-webhooks.test.ts, src/server/services/__tests__/webhook-delivery.test.ts.
  • ADDED: src/server/services/channels/__tests__/webhook.test.ts — covers formatWebhookMessage and the webhook driver (success / error status / HMAC / custom headers / SSRF / fetch failure / missing-url cases).
  • UPDATED: fleet-alert-service.test.ts, cost-alert.test.ts, retry-service.test.ts, alert-deliveries.test.ts, alert-retry-delivery.test.ts, delivery-tracking.test.ts, heartbeat-async.test.ts — strip AlertWebhook mocks / assertions.

Follow-up (out of scope)

Encrypting NotificationChannel.config secrets at rest is tracked separately. Today the webhook driver reads hmacSecret directly from the JSON config, which is plaintext-at-rest just like AlertWebhook.hmacSecret was — so this PR doesn't regress security, but it doesn't fix it either. The fix is a follow-up Notion ticket already filed.

Test plan

  • pnpm install clean
  • npx prisma generate succeeds
  • pnpm lint — no new warnings/errors
  • npx tsc --noEmit — clean
  • pnpm test — 2509 tests pass (5 pre-existing skips)
  • pnpm build — production build succeeds
  • After merge: alerts page renders 2 sections (NotificationChannels + OutboundWebhooks)
  • After merge: creating a NotificationChannel(type=webhook) still works
  • After merge: alerts that fire deliver via NotificationChannel only
  • After merge: failed channel deliveries get retried via RetryService

…ype=webhook)

The legacy AlertWebhook model was strictly inferior to NotificationChannel:
plaintext hmacSecret, no DeliveryAttempt tracking, no retries, env-scoped
fan-out only. NotificationChannel (with the webhook driver in
src/server/services/channels/webhook.ts) already provides all of those
behaviors and supports per-rule routing via AlertRuleChannel.

Changes:
- Drop the AlertWebhook model from prisma/schema.prisma (and its
  Environment.alertWebhooks back-relation).
- Drop the dangling DeliveryAttempt.webhookId column — every retry now
  resolves via channelId.
- Move formatWebhookMessage into channels/webhook.ts and delete the
  webhook-delivery.ts service module entirely.
- Strip AlertWebhook fan-out from event-alerts, fleet-alert-service,
  cost-alert; they now go solely through deliverToChannels (which already
  wraps each delivery in trackChannelDelivery for retries + audit).
- Delete the alert-webhooks router and its registration on the alert
  router; remove the AlertWebhook lookup from withTeamAccess and the
  audit ENTITY_LOADERS / resolver maps.
- Delete the alertWebhook.* audit action labels.
- Delete the legacy webhooks UI section from the alerts page.
- Drop the legacy_webhook channel-icon entries.
- Migration: drop_alert_webhook drops the table CASCADE and removes
  DeliveryAttempt.webhookId.

Tests: rewrote alert-deliveries / alert-retry-delivery / retry-service /
delivery-tracking suites to remove webhook-target paths and exercise the
channel-only retry flow. Added channels/__tests__/webhook.test.ts to
cover formatWebhookMessage and the webhook driver.

BREAKING: any existing AlertWebhook rows must be re-created as
NotificationChannel(type=webhook) before this lands. Operator already
confirmed prod will be migrated manually.

Encrypting NotificationChannel.config secrets at rest is tracked
separately as a follow-up.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccae685aca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


-- Drop the dangling webhookId column on DeliveryAttempt (only legacy_webhook
-- rows used it; all DeliveryAttempts now route via channelId).
ALTER TABLE "DeliveryAttempt" DROP COLUMN IF EXISTS "webhookId";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve retryability of existing webhook delivery attempts

Dropping DeliveryAttempt.webhookId here makes pre-migration failed webhook attempts unrecoverable: historical rows created via trackWebhookDelivery only have webhookId (with channelId = null), but the new retry paths now require channelId (retryDelivery throws on missing channel and RetryService skips). After this migration, those existing failures can still appear in failed-delivery lists but cannot actually be retried. Please migrate or clean up legacy DeliveryAttempt rows (or backfill a retry target) before removing the column.

Useful? React with 👍 / 👎.

@TerrifiedBug TerrifiedBug merged commit f5afc23 into main Apr 28, 2026
12 checks passed
@TerrifiedBug TerrifiedBug deleted the refactor/drop-alertwebhook branch April 28, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant