שליחת מייל מ Rails דרך AWS SES
בימים האחרונים אני עובד על שינוי מנגנון שליחת המיילים של האתר ולשלוח דרך AWS במקום דרך Mailgun. זה קורה קצת בגלל העלאות מחיר של מיילגאן והרבה בגלל שרציתי לשחק עם עוד סרביס של AWS. כמו תמיד אצלם דברים מסתבכים והפוסט הזה מסכם את ההסתבכויות המרכזיות וגם תוכניות לעתיד לגבי המיילים.
1. שליחת מייל יחיד באמצעות SMTP
נתחיל עם מה שפשוט: גם Mailgun וגם SES (שירות המיילים של AWS) תומכים בפרוטוקול SMTP ודרך כפתורים בממשק ווב אפשר לקבל שם משתמש וסיסמה לשרת SMTP שישלח את המיילים בשבילכם. זה אומר שבריילס כל מה שאני צריך בשביל לשלוח מייל יחיד הוא לשנות שתי פקודות בקובץ הקונפיגורציה של הסביבה ולכתוב משהו כמו:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "email-smtp.eu-north-1.amazonaws.com",
port: 587,
user_name: Rails.application.credentials.dig(:aws, :ses, :user_name),
password: Rails.application.credentials.dig(:aws, :ses, :password),
authentication: "plain",
enable_starttls: true,
open_timeout: 5,
read_timeout: 5
}
מכאן כל שליחת מייל דרך תשתית המיילים המובנית ב Rails תשתמש בפרטי המשתמש והסיסמה שהגדרתי בקובץ הסיסמאות ותעבור דרך השרת של אמזון.
2. שליחת מייל להרבה אנשים עם פרמטרים
הסיפור היותר מסובך היה לשלוח מה שהם קוראים Bulk Email. זה המנגנון איתו אני שולח לכם את הפוסט הזה לאימייל. למיילגאן יש מנגנון מובנה שמאפשר בקריאת API אחת להוציא אימייל למאות אנשים. ב SES זה קצת יותר מורכב כי קריאת ה API היחידה שלהם מוגבלת ל 50 נמענים (לפי התיעוד, לא ניסיתי יותר). ועדיין מכל מה שחיפשתי ברשת שליחת מייל לכמות גדולה של נמענים תהיה הרבה יותר מהירה ויעילה דרך ה API שלהם.
בשביל לשלב את ה SDK של SES בתוך ריילס היה צריך ליצור משהו שנקרא Delivery Method, שזה המחלקה שאומרת לריילס איך לשלוח מיילים. אם אתם זוכרים מהפיסקה הקודמת בשביל לשלוח מייל באמצעות SMTP כתבתי:
config.action_mailer.delivery_method = :smtp
זה אומר שבשביל לשלוח מייל בשיטה חדשה שלי אצטרך לבנות משהו שעושה את מה שמחלקת ה SMTP של ריילס עושה. בעזרת תיעוד ו AI כתבתי את הקוד הבא בתוך קובץ בשם ses_delivery_method
בתיקיית lib
:
class SesDeliveryMethod
attr_accessor :settings
def initialize(options = {})
@settings = options.merge(
access_key_id: Rails.application.credentials.dig(:aws, :ses, :access_key_id),
secret_access_key: Rails.application.credentials.dig(:aws, :ses, :access_key_secret),
region: 'eu-north-1'
)
@client = Aws::SESV2::Client.new(region: @settings[:region], credentials: aws_credentials)
end
def deliver!(mail)
template_data = JSON.parse(mail['template-data'].value)
options = {
from_email_address: mail.from.first,
# default_email_tags: tags,
default_content: {
template: {
template_data: {"token" => ""}.to_json,
template_content: {
subject: mail.subject,
text: mail.text_part.body.decoded,
html: mail.html_part.body.decoded
}
}
},
bulk_email_entries: template_data.map do |recipient|
{
destination: { to_addresses: Array.wrap(recipient["email"]) },
replacement_email_content: {
replacement_template: {
replacement_template_data: {"token" => recipient["token"]}.to_json
}
}
}
end
}
response = @client.send_bulk_email(options)
rescue => err
Rails.logger.info("SES Error")
Rails.logger.error(err)
raise err
end
private
def aws_credentials
Aws::Credentials.new(@settings[:access_key_id], @settings[:secret_access_key])
end
def template_data(mail)
{ "subject" => mail.subject, "body" => mail.body.raw_source }.to_json
end
end
# Register with ActionMailer
ActionMailer::Base.add_delivery_method :ses, SesDeliveryMethod
וכך בשביל לשלוח מייל דרך SES כל מה שצריך לעשות בקוד היישום הוא קריאת פונקציה שנראית בערך כך:
mail(
to:,
from: 'Ynon Perek <ynon@tocode.co.il>',
delivery_method: :ses,
subject: "[פוסט חדש] #{@post.title}",
template_data: recipients_with_tokens.map { |email, token| { "email" => email, "token" => token } }.to_json
)
ה token בכל קטעי הקוד האלה הוא פרמטר שיישתל לכל אימייל ובעזרתו נמענים יכולים להסיר את עצמם מהרשימה, כאשר לכל נמען יש token שונה. אפשר להוסיף איזה פרמטרים שרוצים שם ובשביל לשתול אותם בגוף המייל אנחנו צריכים רק לציין את שמם בתוך סוגריים מסולסלים כפולים לדוגמה:
<%= subscriptions_edit_url %>?token={{token}}&remove=all
3. תוכניות לעתיד
על הדרך ועם כל ההתעסקות עם קוד המיילים הוספתי גם מנגנון של אימות כפול למי שרוצה לקבל את הפוסטים למייל, כך שעכשיו אם תרשמו את המייל שלכם בתיבת הרישום תקבלו הודעה שאתם צריכים לאמת את כתובת המייל, המערכת תשלח לכם אימייל ורק אחרי לחיצה על הכפתור שבמייל באמת אוסיף אתכם לרשימת התפוצה. בצורה כזאת אני מקווה לשלוח את המיילים ליותר אנשים רלוונטים ולפחות בוטים.
בנוסף כנראה שהמעבר ל AWS שינה חלק מהגדרות השולח שלי ולכן חלק מכם אולי רואים את המיילים האלה מכתובת אחרת או אפילו בתיבת הספאם. אם זה המצב בבקשה תסמנו לגוגל או למי שצריך שהמייל אינו ספאם ותגררו אותו לתיבת הדואר הנכנס.
לסיום אם מאיזושהי סיבה כתוצאה מכל השינויים האחרונים המייל ממני מפסיק להגיע אל תהיו מודאגים, לכו לאתר לקרוא את הפוסט וכתבו לי (אפשר להגיב לכל אחד מהמיילים הישנים שכן יש לכם בתיבה) כדי שאבדוק מה קרה ואם צריך אוסיף אתכם מחדש לרשימה.