feat(alerts): remove legacy Slack webhook destination - #277
Conversation
Removes the `"slack"` alert destination type — the legacy incoming-webhook variant superseded by the `"slack-bot"` OAuth integration. No customer uses it, and the DB stores destination config generically (`type` text + `config_json` jsonb), so no migration is needed. Removed: the domain create/update schemas and the type literal, the v2 wire arms + OpenAPI examples, the API validation/build/dispatch branches, the dashboard provider tile and webhook form fields, the alchemy IaC union member, and the docs section. Preserved: the `"slack-bot"` destination end to end, every shared Slack formatting helper (buildSlackBlocks, markdownToSlackMrkdwn, slackAttachmentColor, buildSlackFallbackText), and the `"slack"` *template dialect* key — now typed as an explicit `TemplateDialect` so existing rules' notification-template overrides keep working. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Your Pullfrog Router balance is exhausted. You have a payment method on file but auto-reload is disabled, so runs paused once your balance went past the overdraft buffer. Top up balance → · Enable auto-reload →
|
🍁 Maple PR previewThe preview is ready:
Deployed from |
Removing `slack` from `AlertDestinationType` left existing `alert_destinations` rows with `type='slack'` in Postgres, and every read path decoded that column with a throwing sync decoder. One legacy row raised a defect — a bare 500 on `GET /alerts/destinations`, the v2 list and retrieve routes, and delivery history, plus a white-screened Alerts page (the web collection decodes during render). Decode the stored type tolerantly instead, following the existing `safeParsePublicConfig` precedent: lists skip rows that no longer decode, delivery history reuses the "webhook" fallback it already has for deleted destinations, and single-row reads fail with a typed validation error rather than a defect. On the web side the Electric row mapper returns null for such rows (skipped by the list hook) and PROVIDERS lookups go through a defensive `getProvider` so an unknown type can never deref undefined mid-render. `slack` stays removed from the union — this is back-compat for stray rows, not a reinstatement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Your Pullfrog Router balance is exhausted. You have a payment method on file but auto-reload is disabled, so runs paused once your balance went past the overdraft buffer. Top up balance → · Enable auto-reload →
|

What
Fully removes the legacy Slack webhook alert destination (
type: "slack"). It was superseded by the"slack-bot"OAuth integration, and we validated that no customer uses the webhook variant.The
"slack-bot"destination is untouched.Removed
packages/domain/src/http/alerts.ts): the"slack"literal inAlertDestinationType,SlackAlertDestinationConfig,UpdateSlackAlertDestinationConfig, and both union arms.packages/domain/src/http/v2/alert-destinations.ts): the create/update arms,webhook_url/channel_labelslack fields, and the OpenAPI examples + doc strings that enumeratedslack(examples now useslack-bot).apps/api):buildPublicConfig/buildSecretConfigslack arms, the create/update URL-validation branches, theslack:arm of the updateMatch.discriminatorsExhaustive, the{ type: "slack", webhookUrl }member ofDestinationSecretConfig, the v2 routetoCreateRequest/toUpdateRequestcases, and thedispatchDeliveryarm that POSTed toconfig.webhookUrl.apps/web): thePROVIDERS.slack"Slack (webhook)" tile, itsDESTINATION_TYPESentry, the webhook URL + channel-label form fields, the "Use a Slack webhook instead" fallback button inSlackBotFields, and thecase "slack"create/update param builders (plus the now-deadchannelLabelform field).lib/alchemy-maple): the{ type: "slack"; webhook_url; channel_label? }props union member, itsdesiredBodycase and drift check; examples/README now demo a Discord destination.alerting/notification-destinations.md, rewritten to describe the Slack (bot) install flow.Back-compat for existing
type='slack'rowsNo DB migration — destinations store config generically (
typetext +config_jsonjsonb) — but back-compat handling is required, and is included here. Rows withtype='slack'still exist in Postgres, and every read path decoded that column with a throwing sync decoder, so a single legacy row raised a defect: bare 500s onGET /alerts/destinations, the v2 list/retrieve routes and delivery history, plus a white-screened Alerts page (the web Electric collection decodes during render).The stored type is now decoded tolerantly, following the existing
safeParsePublicConfigprecedent:AlertsService.listDestinationsskips rows whosetypeno longer decodes, so an undecodable row simply doesn't appear instead of failing the whole list.listDeliveryEventsreuses the"webhook"fallback it already had for deleted destinations.AlertValidationErrorrather than a defect.rowToAlertDestinationDocumentreturnsnullfor such a row (skipped by the list hook), andPROVIDERS[...]lookups go through a defensivegetProviderso an unknown type can never derefundefinedmid-render.type='slack'row on both the API list path and the web collection path."slack"stays removed fromAlertDestinationType— this is tolerance for stray rows, not a reinstatement.Notes for review
slackarm is removed from/v2alert-destination create/update, so an existing v2 client sending"type": "slack"now gets a validation error. Accepted by the maintainer (no customer uses the webhook variant).lib/alchemy-maplenow has no Slack destination support at all. It carried a"slack"arm and never had a"slack-bot"one, so IaC users lose Slack entirely rather than migrating to the bot destination. Flagged for the maintainer's judgement — intentionally not addressed in this PR.Preserved
"slack-bot"path.buildSlackBlocks,buildSlackBlocksFromTemplate,buildSlackFallbackText,markdownToSlackMrkdwn,slackAttachmentColor(the last is also used byalert-email.ts).lib/url-validator.ts(shared with webhook/hazel/discord)."slack"used byrenderTitleBody(context, "slack", …). It was previously typed asAlertDestinationType, which no longer contains"slack", so it is now an explicitTemplateDialect = AlertDestinationType | "slack"with a comment explaining why the key must not be renamed — customers' stored notification-templateoverridesare keyed by it.Verification
bun typecheck— turbo 33/33 green. The roottsconfig.alchemy.jsonstep reports one pre-existingalchemy.run.ts(148)Cloudflare Worker type error, confirmed identical on a cleanmain.bun run test --concurrency=1—apps/api1191 passed,apps/web965 passed with only the 4 pre-existing failures inuse-timezone-preference/use-recently-used-times(the known jsdomlocalStoragegotcha), confirmed identical on a cleanmain. All alert/destination suites pass. The new API regression test was verified to fail (defect) without the tolerant decode.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.