Aggiunge IMAP bounce e flag email non valide nel CRM.
CI / scan_ruby (push) Failing after 13m27s
CI / scan_js (push) Successful in 11m45s
CI / lint (push) Failing after 12m4s

Permette di leggere i bounce dalla stessa MailIdentity SMTP, aggiornare le email in anagrafica e saltare gli indirizzi invalidi nei mailing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-08 19:46:04 +02:00
co-authored by Cursor
parent 353ace4d99
commit 36b3ffe507
26 changed files with 922 additions and 12 deletions
+63
View File
@@ -9,15 +9,30 @@ class MailIdentity < ApplicationRecord
ENCRYPTIONS = Catalog::MAIL_ENCRYPTIONS.keys.freeze
# Mapping host SMTP → IMAP tipici dei provider usati in produzione.
IMAP_HOST_HINTS = {
"aruba.it" => "imaps.aruba.it",
"smtps.aruba.it" => "imaps.aruba.it",
"smtp.aruba.it" => "imaps.aruba.it",
"gmail.com" => "imap.gmail.com",
"smtp.gmail.com" => "imap.gmail.com",
"outlook.com" => "outlook.office365.com",
"smtp.office365.com" => "outlook.office365.com"
}.freeze
validates :name, :from_name, :from_email, :smtp_host, :smtp_port, presence: true
validates :from_email, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :reply_to, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true
validates :smtp_port, numericality: { in: 1..65535 }
validates :imap_port, numericality: { in: 1..65535 }, allow_nil: true
validates :encryption, inclusion: { in: ENCRYPTIONS }
validates :smtp_authentication, inclusion: { in: Catalog::MAIL_AUTH_METHODS.keys }
validate :encryption_matches_port
before_validation :apply_imap_defaults
scope :active, -> { where(active: true) }
scope :imap_enabled, -> { where(imap_enabled: true) }
scope :ordered, -> { order(:name) }
def from_header
@@ -42,8 +57,56 @@ class MailIdentity < ApplicationRecord
settings
end
def imap_username
smtp_username.presence || from_email
end
def effective_imap_host
imap_host.presence || inferred_imap_host
end
def effective_imap_port
(imap_port.presence || 993).to_i
end
def imap_ready?
imap_enabled? && effective_imap_host.present? && effective_imap_port.positive?
end
def imap_settings
{
address: effective_imap_host,
port: effective_imap_port,
ssl: true,
user_name: imap_username,
password: smtp_password
}
end
def inferred_imap_host
host = smtp_host.to_s.downcase.strip
return if host.blank?
IMAP_HOST_HINTS.each do |needle, imap|
return imap if host == needle || host.end_with?(".#{needle}") || host.include?(needle)
end
# Fallback generico: smtp.X → imap.X, smtps.X → imaps.X
if host.start_with?("smtps.")
host.sub(/\Asmtps\./, "imaps.")
elsif host.start_with?("smtp.")
host.sub(/\Asmtp\./, "imap.")
end
end
private
def apply_imap_defaults
self.imap_port = 993 if imap_port.blank?
self.imap_enabled = true if imap_enabled.nil?
self.imap_host = inferred_imap_host if imap_host.blank? && inferred_imap_host.present?
end
def encryption_matches_port
return if smtp_port.blank? || encryption.blank?