feat(admin): operator notification backend (outbox, channels, delivery log) - #781
Conversation
🦋 Changeset detectedLatest commit: 0e1007e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Webhook credentials can leak, claimed messages can be stranded, and configured retry behavior is not fully implemented.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (9)
Reclaim stale processing notifications · New Redact webhook URLs in notification targets · New Reject non-positive or fractional notification limits · New Add retention cleanup for terminal outbox rows · New Apply configured retry base delay · New Align delivery log route with API contract · New Retry failed targets without duplicating successful deliveries · New Validate notification target shapes and field types · New Add adapter coverage for HTTP, Slack, and Telegram · New
What changed in this PR
Adds a PostgreSQL-backed operator notification pipeline with configurable channels, event hooks, retries, delivery history, retention, and admin endpoints.
Changes:
- Adds transactional outbox and delivery-log persistence.
- Implements HTTP, Discord, Slack, and Telegram dispatch.
- Wires invoice, settings, and relay lifecycle notifications.
| File | Description |
|---|---|
.changeset/operator-notification-backend.md |
Records the notification feature release. |
migrations/20260919_120000_create_notification_outbox_table.js |
Creates the outbox table. |
migrations/20260919_130000_create_notification_delivery_log_table.js |
Creates delivery-history storage. |
resources/default-settings.yaml |
Adds notification defaults. |
src/@types/notification-outbox.ts |
Defines outbox models and statuses. |
src/@types/operator-notifications.ts |
Defines notification settings, targets, and events. |
src/@types/repositories.ts |
Adds notification repository contracts. |
src/@types/services.ts |
Adds dispatcher and outbox service contracts. |
src/@types/settings.ts |
Extends admin settings with notifications. |
src/app/app.ts |
Enqueues relay restart events. |
src/app/maintenance-worker.ts |
Dispatches outbox entries and purges logs. |
src/controllers/admin/get-notification-delivery-log-controller.ts |
Exposes delivery history. |
src/controllers/admin/patch-settings-controller.ts |
Enqueues settings-change notifications. |
src/controllers/admin/post-notification-test-controller.ts |
Sends test notifications. |
src/factories/controllers/get-admin-notification-delivery-log-controller-factory.ts |
Constructs the history controller. |
src/factories/controllers/patch-admin-settings-controller-factory.ts |
Injects the outbox repository. |
src/factories/controllers/post-admin-notification-test-controller-factory.ts |
Constructs the test controller. |
src/factories/maintenance-worker-factory.ts |
Injects notification dependencies. |
src/factories/notification-outbox-service-factory.ts |
Constructs dispatch services. |
src/factories/payments-service-factory.ts |
Injects invoice outbox storage. |
src/repositories/notification-delivery-log-repository.ts |
Persists and queries delivery attempts. |
src/repositories/notification-outbox-repository.ts |
Implements claiming and retry state. |
src/routes/admin/index.ts |
Registers notification endpoints. |
src/services/notification-channels.ts |
Implements channel adapters. |
src/services/notification-outbox-service.ts |
Processes queued events. |
src/services/operator-notification-service.ts |
Fans events out to targets. |
src/services/payments-service.ts |
Enqueues invoice events transactionally. |
src/utils/operator-notification-enqueue.ts |
Provides startup enqueue helper. |
src/utils/settings-config.ts |
Validates notification configuration. |
src/utils/settings-guided-schema.ts |
Adds the notification toggle. |
src/utils/settings-redaction.ts |
Redacts Telegram tokens. |
test/unit/app/maintenance-worker.spec.ts |
Adds notification worker dependencies. |
test/unit/routes/admin-settings.spec.ts |
Updates settings route setup. |
test/unit/services/notification-outbox-service.spec.ts |
Tests basic outbox processing. |
test/unit/services/operator-notification-service.spec.ts |
Tests dispatch behavior. |
test/unit/services/payments-service.spec.ts |
Tests paid-invoice enqueueing. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.



Description
This PR implements the server-side operator notification pipeline for #759 (parent #637) using a Postgres transactional outbox and async dispatch from the maintenance worker.
Settings
admin.notifications:enabled,targets[](http|discord|slack|telegram), per-event toggles, retry policy, delivery-log retentionvalidateSettings; safe defaults (disabled, no targets)botToken, etc.) in settings payloads and audit-related notification dataPipeline
notification_outbox— enqueue withFOR UPDATE SKIP LOCKEDclaim, retries, dead-letter after max attemptsnotification_delivery_log— per-target delivery attempts (status, error snippet, timestamps); retention purge on maintenance tickOperatorNotificationService— fan-out to channel adapters with JSON envelope{ event, relay, timestamp, data }Event hooks
admission.invoice.createdPaymentsService.createInvoice(same DB transaction as invoice upsert)admission.invoice.paidPaymentsService.confirmInvoice(same transaction as confirm/admit)admission.invoice.failedsettings.changedrelay.restartedRelated Issue
Closes #759 and partial #760
Motivation and Context
How Has This Been Tested?
Screenshots (if appropriate):
Types of changes
Checklist: