✨ Added channel-aware dispatch to update-check notifications#28293
Draft
rob-ghost wants to merge 3 commits into
Draft
✨ Added channel-aware dispatch to update-check notifications#28293rob-ghost wants to merge 3 commits into
rob-ghost wants to merge 3 commits into
Conversation
The notification stack needs a shared, source-neutral way to send a notification email. That's three responsibilities introduced here: - A styled email shell template every sender wraps its semantic content in, so notification emails look like the rest of Ghost's transactional mail without each source duplicating presentation markup. - A sanitiser that strips scripts, inline handlers and unsafe links from notification content, so an untrusted upstream source cannot ship a phishing or injection payload through this path. - A small service that takes a recipient list, sanitises the content, renders the shell per recipient, and sends. It is intentionally recipient-agnostic: who receives a notification is a notification concern that callers decide for themselves. Nothing wires it in yet; a follow-up commit adopts the primitive for update-check's alert email.
update-check's alert branch had its own inline email assembly: it looked up administrator users, built the subject, called the mailer per recipient, and shipped the raw notification HTML unstyled. It owned no concept beyond "call out to a mailer." It now composes the notification email primitive with an admin lookup via the users API and sends the alert through the shared, sanitised, shell-rendered path. The admin lookup is pushed to a SQL filter (NQL: status:active and roles.name in Owner/Administrator), so the database returns only the relevant users instead of fetching all active users to filter in JS. The same Owner/Administrator users receive the same email, just through the shared primitive that future sources (security advisories, etc.) can compose the same way.
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
d8afba4 to
97d3fac
Compare
UpdateChecker now serves a channels-first response: each notification carries a banner channel and/or an email channel with content tailored to the surface. The legacy messages array continues to ride alongside for pre-channels Ghost installs, but for installs running this code, channels is the first-party shape and messages is the shim. The update-check dispatcher routes per channel. When the response carries channels, banner.content is added through the existing notifications API as a banner record, and email.content is sent through the notification email service to Owner/Administrator users. The two surfaces no longer share a single body, so a critical security advisory can be a short banner and a substantive email at the same time. When channels is absent the dispatcher falls back to the existing messages handling, so pre-channels UpdateChecker responses (and the legacy updates.ghost.org service) continue to work unchanged. When both channels and messages are present the dispatcher picks channels and ignores messages so installs never get double-delivered. A notification.dispatched log event names the notification id, the shape used (channels or messages), and the channels actually delivered, so production can see at a glance what was sent and how.
97d3fac to
0189ab6
Compare
This was referenced Jun 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When UpdateChecker sends a critical security advisory, the dispatcher today renders the same content as both a banner in admin and an email to Owners and Administrators. The shared content is wrong on at least one surface: either the banner overflows with email-shaped body, or the email is a one-liner that does not carry the substance an admin needs. There is no path for an advisory to be tersely banner-worthy and substantively email-worthy at the same time.
Solution
The update-check dispatcher now reads channels from the upstream response and routes per surface. When the response carries channels, the banner channel goes through the existing notifications API as a banner record, and the email channel goes through the notification email service to Owner and Administrator users. The two surfaces no longer share a single body, so a security advisory can be a short banner and a substantive email at the same time, with content tailored to each.
When channels is absent the dispatcher falls back to the existing messages handling, so pre-channels UpdateChecker responses and the legacy updates.ghost.org service continue to work unchanged. When both channels and messages are present the dispatcher prefers channels and ignores messages, so installs never receive duplicate banners or duplicate emails.
A
notification.dispatchedlog event names the notification id, the shape used (channels or messages), and the channels actually delivered, so production has a structured signal for what was sent and how.Stacked on