actionmailbox 6.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/MIT-LICENSE +21 -0
- data/README.md +13 -0
- data/app/controllers/action_mailbox/base_controller.rb +38 -0
- data/app/controllers/action_mailbox/ingresses/amazon/inbound_emails_controller.rb +54 -0
- data/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb +103 -0
- data/app/controllers/action_mailbox/ingresses/mandrill/inbound_emails_controller.rb +82 -0
- data/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb +62 -0
- data/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb +65 -0
- data/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb +54 -0
- data/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb +34 -0
- data/app/controllers/rails/conductor/action_mailbox/reroutes_controller.rb +19 -0
- data/app/controllers/rails/conductor/base_controller.rb +14 -0
- data/app/jobs/action_mailbox/incineration_job.rb +22 -0
- data/app/jobs/action_mailbox/routing_job.rb +13 -0
- data/app/models/action_mailbox/inbound_email.rb +49 -0
- data/app/models/action_mailbox/inbound_email/incineratable.rb +20 -0
- data/app/models/action_mailbox/inbound_email/incineratable/incineration.rb +26 -0
- data/app/models/action_mailbox/inbound_email/message_id.rb +38 -0
- data/app/models/action_mailbox/inbound_email/routable.rb +24 -0
- data/app/views/layouts/rails/conductor.html.erb +7 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb +15 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb +47 -0
- data/app/views/rails/conductor/action_mailbox/inbound_emails/show.html.erb +15 -0
- data/config/routes.rb +20 -0
- data/db/migrate/20180917164000_create_action_mailbox_tables.rb +17 -0
- data/lib/action_mailbox.rb +16 -0
- data/lib/action_mailbox/base.rb +118 -0
- data/lib/action_mailbox/callbacks.rb +34 -0
- data/lib/action_mailbox/engine.rb +42 -0
- data/lib/action_mailbox/gem_version.rb +17 -0
- data/lib/action_mailbox/mail_ext.rb +6 -0
- data/lib/action_mailbox/mail_ext/address_equality.rb +9 -0
- data/lib/action_mailbox/mail_ext/address_wrapping.rb +9 -0
- data/lib/action_mailbox/mail_ext/addresses.rb +29 -0
- data/lib/action_mailbox/mail_ext/from_source.rb +7 -0
- data/lib/action_mailbox/mail_ext/recipients.rb +9 -0
- data/lib/action_mailbox/relayer.rb +75 -0
- data/lib/action_mailbox/router.rb +42 -0
- data/lib/action_mailbox/router/route.rb +42 -0
- data/lib/action_mailbox/routing.rb +22 -0
- data/lib/action_mailbox/test_case.rb +12 -0
- data/lib/action_mailbox/test_helper.rb +44 -0
- data/lib/action_mailbox/version.rb +10 -0
- data/lib/rails/generators/installer.rb +10 -0
- data/lib/rails/generators/mailbox/USAGE +12 -0
- data/lib/rails/generators/mailbox/mailbox_generator.rb +32 -0
- data/lib/rails/generators/mailbox/templates/application_mailbox.rb.tt +3 -0
- data/lib/rails/generators/mailbox/templates/mailbox.rb.tt +4 -0
- data/lib/rails/generators/test_unit/mailbox_generator.rb +20 -0
- data/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt +13 -0
- data/lib/tasks/ingress.rake +72 -0
- data/lib/tasks/install.rake +20 -0
- metadata +184 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Ingests inbound emails from SendGrid. Requires an +email+ parameter containing a full RFC 822 message.
|
5
|
+
#
|
6
|
+
# Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
|
7
|
+
# password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
|
8
|
+
#
|
9
|
+
# Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
|
10
|
+
# the SendGrid ingress can learn its password. You should only use the SendGrid ingress over HTTPS.
|
11
|
+
#
|
12
|
+
# Returns:
|
13
|
+
#
|
14
|
+
# - <tt>204 No Content</tt> if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
|
15
|
+
# - <tt>401 Unauthorized</tt> if the request's signature could not be validated
|
16
|
+
# - <tt>404 Not Found</tt> if Action Mailbox is not configured to accept inbound emails from SendGrid
|
17
|
+
# - <tt>422 Unprocessable Entity</tt> if the request is missing the required +email+ parameter
|
18
|
+
# - <tt>500 Server Error</tt> if the ingress password is not configured, or if one of the Active Record database,
|
19
|
+
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
|
20
|
+
#
|
21
|
+
# == Usage
|
22
|
+
#
|
23
|
+
# 1. Tell Action Mailbox to accept emails from SendGrid:
|
24
|
+
#
|
25
|
+
# # config/environments/production.rb
|
26
|
+
# config.action_mailbox.ingress = :sendgrid
|
27
|
+
#
|
28
|
+
# 2. Generate a strong password that Action Mailbox can use to authenticate requests to the SendGrid ingress.
|
29
|
+
#
|
30
|
+
# Use <tt>rails credentials:edit</tt> to add the password to your application's encrypted credentials under
|
31
|
+
# +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
|
32
|
+
#
|
33
|
+
# action_mailbox:
|
34
|
+
# ingress_password: ...
|
35
|
+
#
|
36
|
+
# Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
|
37
|
+
#
|
38
|
+
# 3. {Configure SendGrid Inbound Parse}[https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/]
|
39
|
+
# to forward inbound emails to +/rails/action_mailbox/sendgrid/inbound_emails+ with the username +actionmailbox+ and
|
40
|
+
# the password you previously generated. If your application lived at <tt>https://example.com</tt>, you would
|
41
|
+
# configure SendGrid with the following fully-qualified URL:
|
42
|
+
#
|
43
|
+
# https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails
|
44
|
+
#
|
45
|
+
# *NOTE:* When configuring your SendGrid Inbound Parse webhook, be sure to check the box labeled *"Post the raw,
|
46
|
+
# full MIME message."* Action Mailbox needs the raw MIME message to work.
|
47
|
+
class Ingresses::Sendgrid::InboundEmailsController < ActionMailbox::BaseController
|
48
|
+
before_action :authenticate_by_password
|
49
|
+
|
50
|
+
def create
|
51
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id! params.require(:email)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
class Conductor::ActionMailbox::InboundEmailsController < Rails::Conductor::BaseController
|
5
|
+
def index
|
6
|
+
@inbound_emails = ActionMailbox::InboundEmail.order(created_at: :desc)
|
7
|
+
end
|
8
|
+
|
9
|
+
def new
|
10
|
+
end
|
11
|
+
|
12
|
+
def show
|
13
|
+
@inbound_email = ActionMailbox::InboundEmail.find(params[:id])
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
inbound_email = create_inbound_email(new_mail)
|
18
|
+
redirect_to main_app.rails_conductor_inbound_email_url(inbound_email)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def new_mail
|
23
|
+
Mail.new(params.require(:mail).permit(:from, :to, :cc, :bcc, :in_reply_to, :subject, :body).to_h).tap do |mail|
|
24
|
+
params[:mail][:attachments].to_a.each do |attachment|
|
25
|
+
mail.attachments[attachment.original_filename] = { filename: attachment.path, content_type: attachment.content_type }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_inbound_email(mail)
|
31
|
+
ActionMailbox::InboundEmail.create_and_extract_message_id!(mail.to_s)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
# Rerouting will run routing and processing on an email that has already been, or attempted to be, processed.
|
5
|
+
class Conductor::ActionMailbox::ReroutesController < Rails::Conductor::BaseController
|
6
|
+
def create
|
7
|
+
inbound_email = ActionMailbox::InboundEmail.find(params[:inbound_email_id])
|
8
|
+
reroute inbound_email
|
9
|
+
|
10
|
+
redirect_to main_app.rails_conductor_inbound_email_url(inbound_email)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def reroute(inbound_email)
|
15
|
+
inbound_email.pending!
|
16
|
+
inbound_email.route_later
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
# TODO: Move this to Rails::Conductor gem
|
5
|
+
class Conductor::BaseController < ActionController::Base
|
6
|
+
layout "rails/conductor"
|
7
|
+
before_action :ensure_development_env
|
8
|
+
|
9
|
+
private
|
10
|
+
def ensure_development_env
|
11
|
+
head :forbidden unless Rails.env.development?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# You can configure when this +IncinerationJob+ will be run as a time-after-processing using the
|
5
|
+
# +config.action_mailbox.incinerate_after+ or +ActionMailbox.incinerate_after+ setting.
|
6
|
+
#
|
7
|
+
# Since this incineration is set for the future, it'll automatically ignore any <tt>InboundEmail</tt>s
|
8
|
+
# that have already been deleted and discard itself if so.
|
9
|
+
class IncinerationJob < ActiveJob::Base
|
10
|
+
queue_as { ActionMailbox.queues[:incineration] }
|
11
|
+
|
12
|
+
discard_on ActiveRecord::RecordNotFound
|
13
|
+
|
14
|
+
def self.schedule(inbound_email)
|
15
|
+
set(wait: ActionMailbox.incinerate_after).perform_later(inbound_email)
|
16
|
+
end
|
17
|
+
|
18
|
+
def perform(inbound_email)
|
19
|
+
inbound_email.incinerate
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Routing a new InboundEmail is an asynchronous operation, which allows the ingress controllers to quickly
|
5
|
+
# accept new incoming emails without being burdened to hang while they're actually being processed.
|
6
|
+
class RoutingJob < ActiveJob::Base
|
7
|
+
queue_as { ActionMailbox.queues[:routing] }
|
8
|
+
|
9
|
+
def perform(inbound_email)
|
10
|
+
inbound_email.route
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mail"
|
4
|
+
|
5
|
+
module ActionMailbox
|
6
|
+
# The +InboundEmail+ is an Active Record that keeps a reference to the raw email stored in Active Storage
|
7
|
+
# and tracks the status of processing. By default, incoming emails will go through the following lifecycle:
|
8
|
+
#
|
9
|
+
# * Pending: Just received by one of the ingress controllers and scheduled for routing.
|
10
|
+
# * Processing: During active processing, while a specific mailbox is running its #process method.
|
11
|
+
# * Delivered: Successfully processed by the specific mailbox.
|
12
|
+
# * Failed: An exception was raised during the specific mailbox's execution of the +#process+ method.
|
13
|
+
# * Bounced: Rejected processing by the specific mailbox and bounced to sender.
|
14
|
+
#
|
15
|
+
# Once the +InboundEmail+ has reached the status of being either +delivered+, +failed+, or +bounced+,
|
16
|
+
# it'll count as having been +#processed?+. Once processed, the +InboundEmail+ will be scheduled for
|
17
|
+
# automatic incineration at a later point.
|
18
|
+
#
|
19
|
+
# When working with an +InboundEmail+, you'll usually interact with the parsed version of the source,
|
20
|
+
# which is available as a +Mail+ object from +#mail+. But you can also access the raw source directly
|
21
|
+
# using the +#source+ method.
|
22
|
+
#
|
23
|
+
# Examples:
|
24
|
+
#
|
25
|
+
# inbound_email.mail.from # => 'david@loudthinking.com'
|
26
|
+
# inbound_email.source # Returns the full rfc822 source of the email as text
|
27
|
+
class InboundEmail < ActiveRecord::Base
|
28
|
+
self.table_name = "action_mailbox_inbound_emails"
|
29
|
+
|
30
|
+
include Incineratable, MessageId, Routable
|
31
|
+
|
32
|
+
has_one_attached :raw_email
|
33
|
+
enum status: %i[ pending processing delivered failed bounced ]
|
34
|
+
|
35
|
+
def mail
|
36
|
+
@mail ||= Mail.from_source(source)
|
37
|
+
end
|
38
|
+
|
39
|
+
def source
|
40
|
+
@source ||= raw_email.download
|
41
|
+
end
|
42
|
+
|
43
|
+
def processed?
|
44
|
+
delivered? || failed? || bounced?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ActiveSupport.run_load_hooks :action_mailbox_inbound_email, ActionMailbox::InboundEmail
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Ensure that the +InboundEmail+ is automatically scheduled for later incineration if the status has been
|
4
|
+
# changed to +processed+. The later incineration will be invoked at the time specified by the
|
5
|
+
# +ActionMailbox.incinerate_after+ time using the +IncinerationJob+.
|
6
|
+
module ActionMailbox::InboundEmail::Incineratable
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
after_update_commit :incinerate_later, if: -> { status_previously_changed? && processed? }
|
11
|
+
end
|
12
|
+
|
13
|
+
def incinerate_later
|
14
|
+
ActionMailbox::IncinerationJob.schedule self
|
15
|
+
end
|
16
|
+
|
17
|
+
def incinerate
|
18
|
+
Incineration.new(self).run
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMailbox
|
4
|
+
# Command class for carrying out the actual incineration of the +InboundMail+ that's been scheduled
|
5
|
+
# for removal. Before the incineration – which really is just a call to +#destroy!+ – is run, we verify
|
6
|
+
# that it's both eligible (by virtue of having already been processed) and time to do so (that is,
|
7
|
+
# the +InboundEmail+ was processed after the +incinerate_after+ time).
|
8
|
+
class InboundEmail::Incineratable::Incineration
|
9
|
+
def initialize(inbound_email)
|
10
|
+
@inbound_email = inbound_email
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
@inbound_email.destroy! if due? && processed?
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def due?
|
19
|
+
@inbound_email.updated_at < ActionMailbox.incinerate_after.ago.end_of_day
|
20
|
+
end
|
21
|
+
|
22
|
+
def processed?
|
23
|
+
@inbound_email.processed?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The +Message-ID+ as specified by rfc822 is supposed to be a unique identifier for that individual email.
|
4
|
+
# That makes it an ideal tracking token for debugging and forensics, just like +X-Request-Id+ does for
|
5
|
+
# web request.
|
6
|
+
#
|
7
|
+
# If an inbound email does not, against the rfc822 mandate, specify a Message-ID, one will be generated
|
8
|
+
# using the approach from <tt>Mail::MessageIdField</tt>.
|
9
|
+
module ActionMailbox::InboundEmail::MessageId
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
class_methods do
|
13
|
+
# Create a new +InboundEmail+ from the raw +source+ of the email, which be uploaded as a Active Storage
|
14
|
+
# attachment called +raw_email+. Before the upload, extract the Message-ID from the +source+ and set
|
15
|
+
# it as an attribute on the new +InboundEmail+.
|
16
|
+
def create_and_extract_message_id!(source, **options)
|
17
|
+
message_checksum = Digest::SHA1.hexdigest(source)
|
18
|
+
message_id = extract_message_id(source) || generate_missing_message_id(message_checksum)
|
19
|
+
|
20
|
+
create! options.merge(message_id: message_id, message_checksum: message_checksum) do |inbound_email|
|
21
|
+
inbound_email.raw_email.attach io: StringIO.new(source), filename: "message.eml", content_type: "message/rfc822"
|
22
|
+
end
|
23
|
+
rescue ActiveRecord::RecordNotUnique
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def extract_message_id(source)
|
29
|
+
Mail.from_source(source).message_id rescue nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_missing_message_id(message_checksum)
|
33
|
+
Mail::MessageIdField.new("<#{message_checksum}@#{::Socket.gethostname}.mail>").message_id.tap do |message_id|
|
34
|
+
logger.warn "Message-ID couldn't be parsed or is missing. Generated a new Message-ID: #{message_id}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A newly received +InboundEmail+ will not be routed synchronously as part of ingress controller's receival.
|
4
|
+
# Instead, the routing will be done asynchronously, using a +RoutingJob+, to ensure maximum parallel capacity.
|
5
|
+
#
|
6
|
+
# By default, all newly created +InboundEmail+ records that have the status of +pending+, which is the default,
|
7
|
+
# will be scheduled for automatic, deferred routing.
|
8
|
+
module ActionMailbox::InboundEmail::Routable
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
after_create_commit :route_later, if: :pending?
|
13
|
+
end
|
14
|
+
|
15
|
+
# Enqueue a +RoutingJob+ for this +InboundEmail+.
|
16
|
+
def route_later
|
17
|
+
ActionMailbox::RoutingJob.perform_later self
|
18
|
+
end
|
19
|
+
|
20
|
+
# Route this +InboundEmail+ using the routing rules declared on the +ApplicationMailbox+.
|
21
|
+
def route
|
22
|
+
ApplicationMailbox.route self
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% provide :title, "Deliver new inbound email" %>
|
2
|
+
|
3
|
+
<h1>All inbound emails</h1>
|
4
|
+
|
5
|
+
<table>
|
6
|
+
<tr><th>Message ID</th><th>Status</th></tr>
|
7
|
+
<% @inbound_emails.each do |inbound_email| %>
|
8
|
+
<tr>
|
9
|
+
<td><%= link_to inbound_email.message_id, main_app.rails_conductor_inbound_email_path(inbound_email) %></td>
|
10
|
+
<td><%= inbound_email.status %></td>
|
11
|
+
</tr>
|
12
|
+
<% end %>
|
13
|
+
</table>
|
14
|
+
|
15
|
+
<%= link_to "Deliver new inbound email", main_app.new_rails_conductor_inbound_email_path %>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<% provide :title, "Deliver new inbound email" %>
|
2
|
+
|
3
|
+
<h1>Deliver new inbound email</h1>
|
4
|
+
|
5
|
+
<%= form_with(url: main_app.rails_conductor_inbound_emails_path, scope: :mail, local: true) do |form| %>
|
6
|
+
<div>
|
7
|
+
<%= form.label :from, "From" %><br>
|
8
|
+
<%= form.text_field :from %>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div>
|
12
|
+
<%= form.label :to, "To" %><br>
|
13
|
+
<%= form.text_field :to %>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div>
|
17
|
+
<%= form.label :cc, "CC" %><br>
|
18
|
+
<%= form.text_field :cc %>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div>
|
22
|
+
<%= form.label :bcc, "BCC" %><br>
|
23
|
+
<%= form.text_field :bcc %>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
<div>
|
27
|
+
<%= form.label :in_reply_to, "In-Reply-To" %><br>
|
28
|
+
<%= form.text_field :in_reply_to %>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div>
|
32
|
+
<%= form.label :subject, "Subject" %><br>
|
33
|
+
<%= form.text_field :subject %>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div>
|
37
|
+
<%= form.label :body, "Body" %><br>
|
38
|
+
<%= form.text_area :body, size: "40x20" %>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div>
|
42
|
+
<%= form.label :attachments, "Attachments" %><br>
|
43
|
+
<%= form.file_field :attachments, multiple: true %>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<%= form.submit "Deliver inbound email" %>
|
47
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% provide :title, @inbound_email.message_id %>
|
2
|
+
|
3
|
+
<h1><%= @inbound_email.message_id %>: <%= @inbound_email.status %></h1>
|
4
|
+
|
5
|
+
<ul>
|
6
|
+
<li><%= button_to "Route again", main_app.rails_conductor_inbound_email_reroute_path(@inbound_email), method: :post %></li>
|
7
|
+
<li>Incinerate</li>
|
8
|
+
</ul>
|
9
|
+
|
10
|
+
<details>
|
11
|
+
<summary>Full email source</summary>
|
12
|
+
<pre><%= @inbound_email.source %></pre>
|
13
|
+
</details>
|
14
|
+
|
15
|
+
<%= link_to "Back to all inbound emails", main_app.rails_conductor_inbound_emails_path %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Rails.application.routes.draw do
|
4
|
+
scope "/rails/action_mailbox", module: "action_mailbox/ingresses" do
|
5
|
+
post "/amazon/inbound_emails" => "amazon/inbound_emails#create", as: :rails_amazon_inbound_emails
|
6
|
+
post "/mandrill/inbound_emails" => "mandrill/inbound_emails#create", as: :rails_mandrill_inbound_emails
|
7
|
+
post "/postmark/inbound_emails" => "postmark/inbound_emails#create", as: :rails_postmark_inbound_emails
|
8
|
+
post "/relay/inbound_emails" => "relay/inbound_emails#create", as: :rails_relay_inbound_emails
|
9
|
+
post "/sendgrid/inbound_emails" => "sendgrid/inbound_emails#create", as: :rails_sendgrid_inbound_emails
|
10
|
+
|
11
|
+
# Mailgun requires that a webhook's URL end in 'mime' for it to receive the raw contents of emails.
|
12
|
+
post "/mailgun/inbound_emails/mime" => "mailgun/inbound_emails#create", as: :rails_mailgun_inbound_emails
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: Should these be mounted within the engine only?
|
16
|
+
scope "rails/conductor/action_mailbox/", module: "rails/conductor/action_mailbox" do
|
17
|
+
resources :inbound_emails, as: :rails_conductor_inbound_emails
|
18
|
+
post ":inbound_email_id/reroute" => "reroutes#create", as: :rails_conductor_inbound_email_reroute
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateActionMailboxTables < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :action_mailbox_inbound_emails do |t|
|
4
|
+
t.integer :status, default: 0, null: false
|
5
|
+
t.string :message_id, null: false
|
6
|
+
t.string :message_checksum, null: false
|
7
|
+
|
8
|
+
if supports_datetime_with_precision?
|
9
|
+
t.timestamps precision: 6
|
10
|
+
else
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
t.index [ :message_id, :message_checksum ], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|