1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- # frozen_string_literal: true
- class Web::PushNotificationWorker
- include Sidekiq::Worker
- include RoutingHelper
- sidekiq_options queue: 'push', retry: 5
- TTL = 48.hours
- URGENCY = 'normal'
- def perform(subscription_id, notification_id)
- @subscription = Web::PushSubscription.find(subscription_id)
- @notification = Notification.find(notification_id)
- return if @notification.updated_at < TTL.ago
- # Polymorphically associated activity could have been deleted
- # in the meantime, so we have to double-check before proceeding
- return unless @notification.activity.present? && @subscription.pushable?(@notification)
- payload = web_push_request.encrypt(push_notification_json)
- request_pool.with(web_push_request.audience) do |http_client|
- request = Request.new(:post, web_push_request.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)
- request.add_headers(
- 'Content-Type' => 'application/octet-stream',
- 'Ttl' => TTL.to_s,
- 'Urgency' => URGENCY,
- 'Content-Encoding' => 'aesgcm',
- 'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
- 'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{web_push_request.crypto_key_header}",
- 'Authorization' => web_push_request.authorization_header,
- 'Unsubscribe-URL' => subscription_url
- )
- request.perform do |response|
- # If the server responds with an error in the 4xx range
- # that isn't about rate-limiting or timeouts, we can
- # assume that the subscription is invalid or expired
- # and must be removed
- if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
- @subscription.destroy!
- elsif !(200...300).cover?(response.code)
- raise Mastodon::UnexpectedResponseError, response
- end
- end
- end
- rescue ActiveRecord::RecordNotFound
- true
- end
- private
- def web_push_request
- @web_push_request || WebPushRequest.new(@subscription)
- end
- def push_notification_json
- I18n.with_locale(@subscription.locale.presence || I18n.default_locale) do
- Oj.dump(serialized_notification.as_json)
- end
- end
- def serialized_notification
- ActiveModelSerializers::SerializableResource.new(
- @notification,
- serializer: Web::NotificationSerializer,
- scope: @subscription,
- scope_name: :current_push_subscription
- )
- end
- def request_pool
- RequestPool.current
- end
- def subscription_url
- api_web_push_subscription_url(id: @subscription.generate_token_for(:unsubscribe))
- end
- end
|