discourse/app/services/hub_push_notification_pusher.rb
Rafael dos Santos Silva 75718576c6
DEV: Centralize push notification delivery into a single job (#39207)
## Summary

- Unifies the two parallel push notification delivery paths (web push
via VAPID and hub/native app relay) into a single
`DeliverPushNotification` job
- Extracts hub push delivery into `HubPushNotificationPusher` service,
symmetric with the existing `PushNotificationPusher` for web push
- Adds `UserApiKey.push_clients_for(user)` to encapsulate hub client
lookup previously inlined in PostAlerter
- Simplifies `PostAlerter.push_notification()` from two parallel enqueue
blocks to a single call
- Converts old jobs (`SendPushNotification`, `PushNotification`) to thin
delegators for Sidekiq backward compatibility with in-flight jobs

## Test plan

- [ ] `bin/rspec spec/jobs/deliver_push_notification_spec.rb` — new
unified job tests
- [ ] `bin/rspec spec/services/hub_push_notification_pusher_spec.rb` —
new hub pusher tests
- [ ] `bin/rspec spec/jobs/send_push_notification_spec.rb
spec/jobs/push_notification_spec.rb` — backward compat delegation
- [ ] `bin/rspec spec/services/post_alerter_spec.rb` — PostAlerter
integration
- [ ] `bin/rspec spec/services/push_notification_pusher_spec.rb` — web
push unchanged
- [ ] `bin/rspec
plugins/chat/spec/jobs/regular/chat/notify_watching_spec.rb
plugins/chat/spec/jobs/regular/chat/notify_mentioned_spec.rb` — chat
plugin callers
2026-04-14 11:55:45 -03:00

53 lines
1.5 KiB
Ruby

# frozen_string_literal: true
class HubPushNotificationPusher
def self.push(user, payload)
clients = UserApiKey.push_clients_for(user)
return if clients.empty?
notification = payload.dup
notification["url"] = UrlHelper.absolute_without_cdn(
Discourse.base_path + notification["post_url"],
)
notification.delete("post_url")
hub_payload = {
secret_key: SiteSetting.push_api_secret_key,
url: Discourse.base_url,
title: SiteSetting.title,
description: SiteSetting.site_description,
}
clients
.group_by { |r| r[1] }
.each do |push_url, group|
notifications = group.map { |client_id, _| notification.merge(client_id: client_id) }
next if push_url.blank?
uri = URI.parse(push_url)
http = FinalDestination::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == "https"
request =
FinalDestination::HTTP::Post.new(
uri.request_uri,
{ "Content-Type" => "application/json" },
)
request.body = hub_payload.merge(notifications: notifications).to_json
begin
response = http.request(request)
if response.code.to_i != 200
Rails.logger.warn(
"Failed to push a notification to #{push_url} Status: #{response.code}: #{response.body}",
)
end
rescue => e
Rails.logger.error("An error occurred while pushing a notification: #{e.message}")
end
end
end
end