refactor(alerts): drop AlertWebhook model#197
Conversation
…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.
There was a problem hiding this comment.
💡 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"; |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
Removes the legacy
AlertWebhookmodel and its env-scoped fan-out, consolidating all alert delivery ontoNotificationChannel(type=webhook)which already has the necessary infrastructure:src/server/services/channels/(slack / email / pagerduty / webhook)DeliveryAttempttracking viatrackChannelDeliveryAlertRuleChannelRetryServiceAlertWebhookhad none of these — justprisma.alertWebhook.findMany({ where: { environmentId } })followed by rawfetch()calls. The webhook driver inchannels/webhook.tsalready does the same thing (URL validation, custom headers, optional HMAC signature) inside the channel framework.Why
hmacSecretonAlertWebhookrows — no encryption-at-rest.DeliveryAttempttracking — failures had no audit trail.All of those are subsumed by
NotificationChannel. The shape parity is exact (WebhookPayloadandChannelPayloadwere structurally identical), so the migration is purely a deletion.Breaking change
Warning
Existing
AlertWebhookrows are dropped by this PR's migration. Each one must be re-created asNotificationChannel(type=webhook)before merge — manually copyurl/headers/hmacSecretinto the channelconfigJSON, and link viaAlertRuleChannelif 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— dropsmodel AlertWebhook, theEnvironment.alertWebhooksrelation, andDeliveryAttempt.webhookId.prisma/migrations/20260428000000_drop_alert_webhook/migration.sql—DROP TABLE IF EXISTS \"AlertWebhook\" CASCADEplusALTER TABLE \"DeliveryAttempt\" DROP COLUMN IF EXISTS \"webhookId\".Service callers
src/server/services/webhook-delivery.ts— DELETED.formatWebhookMessagemoved tochannels/webhook.ts(only place it's used now).src/server/services/event-alerts.ts— dropdeliverWebhooks(...); channel delivery now passesevent.idfor 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— dropretryWebhookand theif (attempt.webhookId)branch inprocessRetries.WebhookPayloadreplaced withChannelPayload.src/server/services/delivery-tracking.ts— droptrackWebhookDeliveryand thewebhookIdparameter ontrackDelivery.tRPC
src/server/routers/alert-webhooks.ts— DELETED.src/server/routers/alert.ts— dropalertWebhooksRouterregistration.src/server/routers/alert-deliveries.ts— drop theif (attempt.webhookId)retry path; channel-only retry now.Middleware / audit / actions
src/trpc/init.ts— drop theAlertWebhooklookup inwithTeamAccess.src/server/middleware/audit.ts— dropAlertWebhookfromENTITY_LOADERS,resolveTeamId, andresolveEnvironmentId.src/lib/audit-actions.ts— drop thealertWebhook.*action labels.UI
src/app/(dashboard)/alerts/_components/webhooks-section.tsx— DELETED.src/app/(dashboard)/alerts/page.tsx— remove theWebhooksSectionimport and render call. Two sections remain:NotificationChannelsSectionandOutboundWebhooksSection.src/app/(dashboard)/alerts/_components/{delivery-status-panel,failed-deliveries-section}.tsx— drop thelegacy_webhookchannel-icon entry.Tests
src/server/routers/__tests__/alert-webhooks.test.ts,src/server/services/__tests__/webhook-delivery.test.ts.src/server/services/channels/__tests__/webhook.test.ts— coversformatWebhookMessageand the webhook driver (success / error status / HMAC / custom headers / SSRF / fetch failure / missing-url cases).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.configsecrets at rest is tracked separately. Today the webhook driver readshmacSecretdirectly from the JSON config, which is plaintext-at-rest just likeAlertWebhook.hmacSecretwas — 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 installcleannpx prisma generatesucceedspnpm lint— no new warnings/errorsnpx tsc --noEmit— cleanpnpm test— 2509 tests pass (5 pre-existing skips)pnpm build— production build succeeds