Site icon Gaurav Goyal

S3 as outbound mail content storage for Ruby On Rails

“Information is the oil of the 21st century, and analytics is the combustion engine.”

Since Amazon SES and some other email service providers don’t store show you the email content and history, you are left hanging if you want to some historical email performance and analytics. Keep track of outbound emails for Logging, Analytics and Audit purposes by following this simple setup.

First and foremost, setup paperclip gem, it is an awesome gem for attachment management. https://github.com/thoughtbot/paperclip

To begin, first, we will create a model for our emails. We will record the email-id and raw email content with timestamps.

rails g model OutboundEmail email:string content:attachment

This should create a migration that looks something like this:

class CreateOutboundEmails < ActiveRecord::Migration[5.0]
  def change
    create_table :outbound_emails do |t|
      t.string :email, index: true
      t.attachment :content
      t.timestamps
    end
  end
end

Add an index on email column. Then we need to declare that our model has an attachment by adding

class OutboundEmail < ApplicationRecord  
    has_attached_file :content,
    storage: :s3,
    s3_permissions: :private,
    path: "outbound/:year/:month/:day/:email/:id.email"

...

end

This will upload the file in your S3 bucket with object path something like this, “outbound/2018/1/7/me@gauravgoyal.in/1.email

Note: As per AWS, ‘@’ in object key can create some issues while accessing S3 in certain browsers.

Read: https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html. I didn’t face any issues in any of the Standard browsers.

 

The symbols :year, :month, :date have to be interpolated by paperclip

# config/initializers/paperclip.rb
Paperclip.interpolates :year do |attachment, style|
  attachment.instance.created_at.year
end
Paperclip.interpolates :month do |attachment, style|
  attachment.instance.created_at.month
end
Paperclip.interpolates :day do |attachment, style|
  attachment.instance.created_at.day
end

Now add an Mail Observer and create records for all outbound emails.

class MailObserver
  def self.delivered_email(message)
    file = Tempfile.new
    file.write(message.content)
    OutboundEmail.create(email: message.to, content: file)
  end
end

Register this observer in after_initialize block

# config/environments/production.rb
Rails.application.configure do
...

  config.after_initialize do
    ActionMailer::Base.register_observer(MailObserver)
  end

...
end

All your emails will now be stored in encoded format on S3. ? ?

The email content can be easily decoded back to a Mail object using mail gem.


For further reading on Mail Observers and Interceptors read this great article by Leigh Halliday on https://www.leighhalliday.com/mail-interceptors-and-observers

Exit mobile version