2022-01-26 11:33:12 +01:00
|
|
|
# A Mail delivery method that randomly balances the actual delivery between different
|
|
|
|
# methods.
|
|
|
|
#
|
|
|
|
# Usage:
|
|
|
|
#
|
|
|
|
# ```ruby
|
|
|
|
# ActionMailer::Base.add_delivery_method :balancer, BalancerDeliveryMethod
|
|
|
|
# config.action_mailer.balancer_settings = {
|
|
|
|
# smtp: 25,
|
|
|
|
# sendmail: 75
|
|
|
|
# }
|
|
|
|
# config.action_mailer.delivery_method = :balancer
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# Be sure to restart your server when you modify this file.
|
|
|
|
class BalancerDeliveryMethod
|
2024-05-28 11:08:34 +02:00
|
|
|
BYPASS_UNVERIFIED_MAIL_PROTECTION = 'BYPASS_UNVERIFIED_MAIL_PROTECTION'.freeze
|
2023-01-11 09:35:41 +01:00
|
|
|
FORCE_DELIVERY_METHOD_HEADER = 'X-deliver-with'
|
2022-01-26 11:33:12 +01:00
|
|
|
# Allows configuring the random number generator used for selecting a delivery method,
|
|
|
|
# mostly for testing purposes.
|
|
|
|
mattr_accessor :random, default: Random.new
|
|
|
|
|
|
|
|
def initialize(settings)
|
|
|
|
@delivery_methods = settings
|
|
|
|
end
|
|
|
|
|
|
|
|
def deliver!(mail)
|
2024-05-28 11:08:34 +02:00
|
|
|
return if prevent_delivery?(mail)
|
|
|
|
|
2022-01-26 11:33:12 +01:00
|
|
|
balanced_delivery_method = delivery_method(mail)
|
|
|
|
ApplicationMailer.wrap_delivery_behavior(mail, balanced_delivery_method)
|
2023-01-11 00:30:12 +01:00
|
|
|
|
|
|
|
# Because we don't want to invoke observers or interceptors twice,
|
|
|
|
# we can't call again `mail.deliver` here to send the email with balanced method
|
|
|
|
# (it was first called before by deliver_now in ActiveJob or application code, which leads us here).
|
|
|
|
#
|
|
|
|
# Instead, we directly deliver the email from the handler (set by the wrapper above)
|
|
|
|
# like Mail::Message.deliver does.
|
|
|
|
#
|
|
|
|
# See https://github.com/mikel/mail/blob/199a76bed3fc518508b46135691914a1cfd8bff8/lib/mail/message.rb#L250
|
|
|
|
mail.delivery_handler.deliver_mail(mail) { mail.send :do_delivery }
|
2022-01-26 11:33:12 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2024-05-28 11:08:34 +02:00
|
|
|
def prevent_delivery?(mail)
|
|
|
|
return false if mail[BYPASS_UNVERIFIED_MAIL_PROTECTION].present?
|
2024-06-03 22:38:23 +02:00
|
|
|
return false if mail.to.blank? # bcc list
|
2024-05-28 11:08:34 +02:00
|
|
|
|
|
|
|
user = User.find_by(email: mail.to.first)
|
|
|
|
return user.unverified_email? if user.present?
|
|
|
|
|
|
|
|
individual = Individual.find_by(email: mail.to.first)
|
|
|
|
return individual.unverified_email? if individual.present?
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2023-01-11 09:35:41 +01:00
|
|
|
def force_delivery_method?(mail)
|
|
|
|
@delivery_methods.keys.map(&:to_s).include?(mail[FORCE_DELIVERY_METHOD_HEADER]&.value)
|
|
|
|
end
|
|
|
|
|
2022-01-26 11:33:12 +01:00
|
|
|
def delivery_method(mail)
|
2023-01-11 09:35:41 +01:00
|
|
|
return mail[FORCE_DELIVERY_METHOD_HEADER].value.to_sym if force_delivery_method?(mail)
|
|
|
|
|
2023-02-03 16:30:40 +01:00
|
|
|
compatible_delivery_methods_for(mail)
|
2022-01-26 11:33:12 +01:00
|
|
|
.flat_map { |delivery_method, weight| [delivery_method] * weight }
|
|
|
|
.sample(random: self.class.random)
|
|
|
|
end
|
2023-02-03 16:30:40 +01:00
|
|
|
|
|
|
|
def compatible_delivery_methods_for(mail)
|
|
|
|
@delivery_methods.reject { |delivery_method, _weight| delivery_method.to_s == 'dolist_api' && !Dolist::API.sendable?(mail) }
|
|
|
|
end
|
2022-01-26 11:33:12 +01:00
|
|
|
end
|