mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-06 02:51:34 +00:00
There might be a delay before it gets sent, so it's better to record the time the event occurred at. It would have been simpler to just add it to the data hash, but I felt it was an important detail for an event and should be at the top level along with event name. In the case of order cycle opening, this is the same as opened_at. I've included this in the payload for clarity too.
53 lines
1.6 KiB
Ruby
53 lines
1.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "faraday"
|
|
require "private_address_check"
|
|
require "private_address_check/tcpsocket_ext"
|
|
|
|
# Deliver a webhook payload
|
|
# As a delayed job, it can run asynchronously and handle retries.
|
|
class WebhookDeliveryJob < ApplicationJob
|
|
# General failed request error that we're going to use to signal
|
|
# the job runner to retry our webhook worker.
|
|
class FailedWebhookRequestError < StandardError; end
|
|
|
|
queue_as :default
|
|
|
|
def perform(url, event, payload, at: Time.zone.now)
|
|
body = {
|
|
id: job_id,
|
|
event:,
|
|
at: at.to_s,
|
|
data: payload,
|
|
}
|
|
|
|
# Request user-submitted url, preventing any private connections being made
|
|
# (SSRF).
|
|
# This method may allow the socket to open, but is necessary in order to
|
|
# protect from TOC/TOU.
|
|
# Note that private_address_check provides some methods for pre-validating,
|
|
# but they're not as comprehensive and so unnecessary here. Simply
|
|
# momentarily opening sockets probably can't cause DoS or other damage.
|
|
PrivateAddressCheck.only_public_connections do
|
|
notify_endpoint(url, body)
|
|
end
|
|
end
|
|
|
|
def notify_endpoint(url, body)
|
|
connection = Faraday.new(
|
|
request: { timeout: 30 },
|
|
headers: {
|
|
'User-Agent' => 'openfoodnetwork_webhook/1.0',
|
|
'Content-Type' => 'application/json',
|
|
}
|
|
)
|
|
response = connection.post(url, body.to_json)
|
|
|
|
# Raise a failed request error and let job runner handle retrying.
|
|
# In theory, only 5xx errors should be retried, but who knows.
|
|
raise FailedWebhookRequestError, response.status.to_s unless response.success?
|
|
|
|
response.body
|
|
end
|
|
end
|