بناء نظام أرشفة البريد الإلكتروني: تخزين محتوى البريد الإلكتروني

Bird

04‏/03‏/2019

البريد الإلكتروني

1 min read

بناء نظام أرشفة البريد الإلكتروني: تخزين محتوى البريد الإلكتروني

Bird

04‏/03‏/2019

البريد الإلكتروني

1 min read

بناء نظام أرشفة البريد الإلكتروني: تخزين محتوى البريد الإلكتروني

في هذه المدونة، سأصف العملية التي مررت بها لتخزين محتوى البريد الإلكتروني على S3 (خدمة التخزين البسيطة من Amazon) والبيانات المساعدة في جدول MySQL لسهولة الرجوع المتبادل.

في هذه المدونة، سأصف العملية التي مررت بها لتخزين محتوى البريد الإلكتروني على S3 (خدمة التخزين البسيطة لشركة أمازون) والبيانات المساعدة في جدول MySQL لسهولة الرجوع المتبادل. في النهاية، هذه هي نقطة الانطلاق لقاعدة الكود التي ستشمل تطبيقًا سيسمح بالبحث السهل في رسائل البريد الإلكتروني المؤرشفة، ثم عرض تلك الرسائل إلى جانب بيانات الحدث (سجل). يمكن العثور على الكود الخاص بهذا المشروع في مستودع GitHub التالي: https://github.com/jeff-goldstein/PHPArchivePlatform.

مع أنني سأعتمد على S3 وMySQL في هذا المشروع، فلا يعني ذلك أنها التقنيات الوحيدة التي يمكن استخدامها لبناء منصة للأرشفة، ولكن بالنظر إلى انتشارها، اعتقدت أنها اختيار جيد لهذا المشروع. في نظام واسع النطاق وعالي الحجم سأستخدم قاعدة بيانات أداء أعلى من MySQL، ولكن لمشروعنا العرضي، MySQL هي الأنسب. بالنسبة للمنظمات التي تدرس PostgreSQL كخيار لقاعدة البيانات للأرشفة، فإن تنفيذ إجراءات النسخ الاحتياطي والاستعادة الصحيحة هو أمر ضروري للحفاظ على سلامة البيانات في نظم الإنتاج.

لقد قمت بتفصيل الخطوات التي اتبعتها في المرحلة الأولى من المشروع:

  1. إنشاء نسخة مكررة من البريد الإلكتروني للأرشفة

  2. استخدام ميزات أرشفة SparkPost وInbound Relay لإرسال نسخة من البريد الإلكتروني الأصلي إلى SparkPost للمعالجة في بنية JSON، ثم إرسالها إلى جامع webhook (تطبيق)

  3. تفكيك بنية JSON للحصول على العناصر الضرورية

  4. إرسال محتوى البريد الإلكتروني إلى S3 للتخزين

  5. تسجيل مدخل في MySQL لكل بريد إلكتروني لأغراض الرجوع المتبادل

إنشاء نسخة من البريد الإلكتروني

في SparkPost أفضل طريقة لأرشفة بريد إلكتروني هي إنشاء نسخة مطابقة من البريد الإلكتروني مصممة خصيصًا لأغراض الأرشفة. يتم ذلك باستخدام ميزة الأرشيف في SparkPost. تمنح ميزة الأرشيف في SparkPost المرسل القدرة على إرسال نسخة من البريد الإلكتروني إلى عنوان بريد إلكتروني واحد أو أكثر.  تستخدم هذه النسخة الروابط ذات التتبع والفتح نفسها كما في الأصل. تُعرّف وثائق SparkPost ميزة الأرشيف بالطريقة التالية:

سيستلم المستلمون في قائمة الأرشيف نسخة مطابقة للرسالة التي تم إرسالها إلى عنوان RCPT TO. على وجه الخصوص، أي روابط مشفرة مخصصة لمستلم RCPT TO ستكون متطابقة في رسائل الأرشيف.

الفرق الوحيد بين هذه النسخة المؤرشفة والبريد الإلكتروني الأصلي RCPT TO هو أن بعض الرؤوس ستكون مختلفة نظرًا لأن العنوان المستهدف للبريد الإلكتروني المؤرشف مختلف، لكن جسم البريد الإلكتروني سيكون نسخة مطابقة تمامًا!

إذا كنت ترغب في تفسير أعمق، هنا رابط لوثائق SparkPost حول إنشاء نسخ مكررة (أو أرشيفية) من بريد إلكتروني. سيتم عرض أمثلة على رؤوس X-MSYS-API لهذا المشروع لاحقًا في هذه المدونة.

هناك تحذير واحد لهذا النهج؛ بينما يتم ربط جميع معلومات الحدث في البريد الإلكتروني الأصلي معًا بواسطة كل من transmission_id وmessage_id، لا توجد معلومات في حدث الترحيل الوارد (الآلية للحصول على ونشر البريد الإلكتروني المؤرشف) للبريد الإلكتروني المكرر تربط مرة أخرى بأحد هذين المعرّفين ومن ثم معلومات البريد الإلكتروني الأصلي. هذا يعني أننا نحتاج إلى وضع البيانات في جسم البريد الإلكتروني ورأس البريد الإلكتروني الأصلي كطريقة لربط جميع بيانات SparkPost من البريد الإلكتروني الأصلي والأرشيف.

من أجل إنشاء الشفرة التي توضع في جسم البريد الإلكتروني، استخدمت العملية التالية في تطبيق إنشاء البريد الإلكتروني.

  1. في مكان ما في جسم البريد الإلكتروني، وضعت إدخال المدخل التالي:<input name="ArchiveCode" type="hidden" value="<<UID>>">

  2. ثم أنشأت رمزًا فريدًا واستبدلت حقل <<UID>>: $uid = md5(uniqid(rand(), true)); $emailBody = str_replace(“<<UID>>,$uid,$emailBody);

    هنا مثال على المخرجات:

    <input name="ArchiveCode" type="hidden" value="00006365263145">

  3. التالي، تأكدت من إضافة $UID إلى كتلة meta_data في رأس X-MSYS-API. تضمن هذه الخطوة تضمين UID في كل مخرجات حدث للبريد الإلكتروني الأصلي:

X-MSYS-API: {
  "campaign_id": "<my_campaign>",
  "metadata": {
    "UID": "<UID>"
  },
  "archive": [
    {
      "email": "archive@geekwithapersonality.com"
    }
  ],
  "options": {
    "open_tracking": false,
    "click_tracking": false,
    "transactional": false,
    "ip_pool": "<my_ip_pool>"
  }
}

الآن لدينا طريقة لربط جميع البيانات من البريد الإلكتروني الأصلي بجسم البريد الإلكتروني المؤرشف.

الحصول على النسخة الأرشيفية

من أجل الحصول على نسخة من البريد الإلكتروني للأرشيف، تحتاج إلى اتباع الخطوات التالية:

  1. إنشاء نطاق فرعي سترسل إليه جميع رسائل البريد الإلكتروني المؤرشفة (المكررة)

  2. ضبط سجلات DNS المناسبة بحيث يتم إرسال جميع رسائل البريد الإلكتروني إلى هذا النطاق الفرعي إلى SparkPost

  3. إنشاء نطاق وارد في SparkPost

  4. إنشاء webhook وارد في SparkPost

  5. إنشاء تطبيق (مجمّع) لاستقبال تيار بيانات webhook الخاص بـ SparkPost

يمكنك الاستفادة من الرابطين التاليين للمساعدة في الإرشاد خلال هذه العملية:

  1. مستند تقني SparkPost: Enabling Inbound Email Relaying & Relay Webhooks

  2. أيضًا، المدونة التي كتبتها العام الماضي، Archiving Emails: A How-To Guide for Tracking Sent Mail ستوجهك خلال عملية إنشاء يتم الارتباط الوارد داخل SparkPost

* ملاحظة: منذ أكتوبر 2018، ميزة الأرشفة تعمل فقط عند إرسال الرسائل عبر اتصال SMTP إلى SparkPost، لا يدعم RESTful API هذه الميزة.  وربما لن تكون هذه مشكلة لأن معظم رسائل البريد الإلكتروني التي تحتاج إلى هذا المستوى من التحكم في التدقيق تكون عادةً رسائل بريد إلكتروني مخصصة يتم إنشاؤها بالكامل بواسطة تطبيق خلفي قبل الحاجة لإرسال البريد الإلكتروني.

من أجل الحصول على نسخة من البريد الإلكتروني للأرشيف، تحتاج إلى اتباع الخطوات التالية:

  1. إنشاء نطاق فرعي سترسل إليه جميع رسائل البريد الإلكتروني المؤرشفة (المكررة)

  2. ضبط سجلات DNS المناسبة بحيث يتم إرسال جميع رسائل البريد الإلكتروني إلى هذا النطاق الفرعي إلى SparkPost

  3. إنشاء نطاق وارد في SparkPost

  4. إنشاء webhook وارد في SparkPost

  5. إنشاء تطبيق (مجمّع) لاستقبال تيار بيانات webhook الخاص بـ SparkPost

يمكنك الاستفادة من الرابطين التاليين للمساعدة في الإرشاد خلال هذه العملية:

  1. مستند تقني SparkPost: Enabling Inbound Email Relaying & Relay Webhooks

  2. أيضًا، المدونة التي كتبتها العام الماضي، Archiving Emails: A How-To Guide for Tracking Sent Mail ستوجهك خلال عملية إنشاء يتم الارتباط الوارد داخل SparkPost

* ملاحظة: منذ أكتوبر 2018، ميزة الأرشفة تعمل فقط عند إرسال الرسائل عبر اتصال SMTP إلى SparkPost، لا يدعم RESTful API هذه الميزة.  وربما لن تكون هذه مشكلة لأن معظم رسائل البريد الإلكتروني التي تحتاج إلى هذا المستوى من التحكم في التدقيق تكون عادةً رسائل بريد إلكتروني مخصصة يتم إنشاؤها بالكامل بواسطة تطبيق خلفي قبل الحاجة لإرسال البريد الإلكتروني.

من أجل الحصول على نسخة من البريد الإلكتروني للأرشيف، تحتاج إلى اتباع الخطوات التالية:

  1. إنشاء نطاق فرعي سترسل إليه جميع رسائل البريد الإلكتروني المؤرشفة (المكررة)

  2. ضبط سجلات DNS المناسبة بحيث يتم إرسال جميع رسائل البريد الإلكتروني إلى هذا النطاق الفرعي إلى SparkPost

  3. إنشاء نطاق وارد في SparkPost

  4. إنشاء webhook وارد في SparkPost

  5. إنشاء تطبيق (مجمّع) لاستقبال تيار بيانات webhook الخاص بـ SparkPost

يمكنك الاستفادة من الرابطين التاليين للمساعدة في الإرشاد خلال هذه العملية:

  1. مستند تقني SparkPost: Enabling Inbound Email Relaying & Relay Webhooks

  2. أيضًا، المدونة التي كتبتها العام الماضي، Archiving Emails: A How-To Guide for Tracking Sent Mail ستوجهك خلال عملية إنشاء يتم الارتباط الوارد داخل SparkPost

* ملاحظة: منذ أكتوبر 2018، ميزة الأرشفة تعمل فقط عند إرسال الرسائل عبر اتصال SMTP إلى SparkPost، لا يدعم RESTful API هذه الميزة.  وربما لن تكون هذه مشكلة لأن معظم رسائل البريد الإلكتروني التي تحتاج إلى هذا المستوى من التحكم في التدقيق تكون عادةً رسائل بريد إلكتروني مخصصة يتم إنشاؤها بالكامل بواسطة تطبيق خلفي قبل الحاجة لإرسال البريد الإلكتروني.

الحصول على البريد الإلكتروني المكرر في بنية JSON

في المرحلة الأولى من هذا المشروع، كل ما أقوم بتخزينه هو تنسيق البريد الإلكتروني rfc822 في S3 وبعض حقول الوصف العالي المستوى في جدول SQL للبحث.  بما أن SparkPost سيرسل بيانات البريد الإلكتروني في هيكل JSON إلى منصة الأرشفة الخاصة بي عبر تدفقات بيانات webhook، فقد قمت ببناء تطبيق (يشار إليه غالبًا باسم جامع) يقبل تدفق بيانات Relay_Webhook.

سيحتوي كل حزمة من SparkPost Relay_Webhook على معلومات بريد إلكتروني مكرر واحد في كل مرة، لذا فإن تحليل هيكل JSON إلى المكونات المستهدفة لهذا المشروع بسيط نوعاً ما.  في شيفرة PHP الخاصة بي، كان الحصول على البريد الإلكتروني بتنسيق rfc822 سهلاً كتلك الخطوات القليلة التالية من الشيفرة:

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $body = file_get_contents("php://input");
    $fields = json_decode($body, true);
    $rfc822body = $fields[0]['msys']['relay_message']['content']['email_rfc822'];
    $htmlbody = $fields[0]['msys']['relay_message']['content']['html'];
    $headers  = $fields[0]['msys']['relay_message']['content']['headers'];
}

توجد بعض المعلومات التي أريد تخزينها في جدول SQL الخاص بي في مصفوفة من حقول الرأس.  لذا كتبت دالة صغيرة تقبل مصفوفة الرأس وتجول عبر المصفوفة للحصول على البيانات التي أرغب في تخزينها:

function get_important_headers($headers, &$original_to, &$headerDate, &$subject, &$from) {
    foreach ($headers as $headerGroup) {
        foreach ($headerGroup as $key => $value) {
            if ($key === 'To') {
                $original_to = $value;
            } elseif ($key === 'Date') {
                $headerDate = $value;
            } elseif ($key === 'Subject') {
                $subject = $value;
            } elseif ($key === 'From') {
                $from = $value;
            }
        }
    }
}

الآن وبعد أن حصلت على البيانات، أنا جاهز لتخزين الجسم في S3.

تخزين البريد الإلكتروني المكرر في S3

أنا آسف لإحباطك لكنني لن أقدم درسًا تدريجيًا حول إنشاء S3 bucket لتخزين البريد الإلكتروني ولن أصف كيفية إنشاء مفتاح الوصول اللازم الذي ستحتاجه في تطبيقك لتحميل المحتوى إلى bucket الخاص بك؛ هناك دروس أفضل حول هذا الموضوع مما أستطيع كتابته.  هنا بعض المقالات التي قد تساعد:

https://docs.aws.amazon.com/quickstarts/latest/s3backup/step-1-create-bucket.html
https://aws.amazon.com/blogs/security/wheres-my-secret-access-key/

ما سأفعله هو تسليط الضوء على بعض الإعدادات التي اخترتها والتي تتعلق بمشروع مثل هذا.

  1. التحكم في الوصول.  ليس عليك فقط أن تقوم بتعيين الأمان للbucket، بل عليك أيضًا إعداد الأذونات للعناصر نفسها.  في مشروعي، استخدم سياسة مفتوحة جدًا من القراءة العامة لأن البيانات النموذجية ليست شخصية وأردت سهولة الوصول إلى البيانات.  ربما ترغب في مجموعة أكثر صرامة من سياسات ACL. هنا مقال جيد حول إعدادات ACL: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html

  2. أرشفة الأرشيف. في S3 هناك شيء يسمى إدارة دورة الحياة.  هذا يسمح لك بنقل البيانات من نوع من فئة تخزين S3 إلى أخرى.  تمثل فئات التخزين المختلفة مقدار الوصول الذي تحتاجه للبيانات المخزنة مع تكاليف أقل مرتبطة بالتخزين الذي تصل إليه الأقل. يمكن العثور على كتابة جيدة عن الفئات المختلفة والانتقال عبرها في دليل AWS يسمى، Transitioning Objects. في حالتي، قررت إنشاء دورة حياة تنقل كل كائن من Standard إلى Glacier بعد سنة واحدة. الوصول إلى Glacier أرخص بكثير من أرشيف S3 القياسي وسوف يوفر لي المال في تكاليف التخزين.

بمجرد أن أقوم بإنشاء S3 bucket ووضع إعداداتي في المكان، تكون S3 جاهزة لي لتحميل البريد الإلكتروني المتوافق مع RFC822 الذي حصلت عليه من مصدر البيانات SparkPost Relay Webhook. ولكن قبل تحميل حزمة البريد الإلكتروني RFC822 إلى S3 أحتاج إلى إنشاء اسم ملف فريد سأستخدمه لتخزين ذلك البريد الإلكتروني.

لأجل الاسم الفريد للملف، سأبحث في نص البريد الإلكتروني عن المعرف المخفي الذي وضعه التطبيق المرسل في البريد الإلكتروني واستخدام ذلك المعرف كاسم للملف. هناك طرق أكثر أناقة لاستخراج connectorId من نص HTML، لكن للبساطة والوضوح سأستخدم الكود التالي:

$start = strpos($htmlbody, $inputField);
if ($start !== false) {
    $start = strpos($htmlbody, 'value="', $start);
    if ($start !== false) {
        $start += 7; // Move past 'value="'
        $end = strpos($htmlbody, '"', $start);
        if ($end !== false) {
            $length = $end - $start;
            $UID = substr($htmlbody, $start, $length);
        }
    }
}

* نحن نفترض أن $inputField يحمل القيمة “ArchiveCode” وتم العثور عليه في ملف config.php الخاص بي.

مع UID، يمكننا بعد ذلك صنع الاسم الذي سيتم استخدامه في S3:

$fileName = $ArchiveDirectory . '/' . $UID . '.eml';

الآن أنا قادر على فتح اتصالي بـ S3 وتحميل الملف. إذا نظرت إلى ملف s3.php في مستودع GitHub سترى أنه يتطلب القليل من الكود لتحميل الملف.

خطوتي الأخيرة هي تسجيل هذا الإدخال في جدول MYSQL.

تخزين البيانات الوصفية في MySQL

لقد حصلنا على جميع البيانات اللازمة في خطوة سابقة، لذلك فإن خطوة التخزين سهلة. في هذه المرحلة الأولى اخترت بناء جدول مع الحقول التالية:

  • إدخال حقل تلقائي للتاريخ/الوقت

  • عنوان البريد الإلكتروني المستهدف (RCPT_TO)

  • التوقيت الزمني من رأس تاريخ البريد الإلكتروني

  • رأس الموضوع

  • رأس عنوان البريد الإلكتروني الوارد

  • الدليل المستخدم في حاوية S3

  • اسم الملف في S3 للبريد الإلكتروني المؤرشف

الوظيفة المسماة، MySQLLog داخل ملف التطبيق upload.php تمر بالخطوات اللازمة لفتح الرابط إلى MySQL، حقن الصف الجديد، اختبار النتائج وإغلاق الرابط. أضف خطوة أخرى للاحتياط وهي تسجيل هذه البيانات في ملف نصي. هل يجب علي القيام بالكثير من التسجيل للأخطاء؟ نعم. لكنني أريد أن أبقي هذه الشيفرة خفيفة بحيث تعمل بسرعة فائقة. في بعض الأحيان، سيتم استدعاء هذه الشيفرة مئات المرات في الدقيقة وتحتاج إلى أن تكون فعالة قدر الإمكان. في التحديثات المستقبلية، سأضيف شيفرة مساعدة لمعالجة الإخفاقات وإرسال تلك الإخفاقات إلى مدير للمراقبة.

ختامًا

لذلك في بعض الخطوات السهلة نسبيًا ، تمكنا من اجتياز المرحلة الأولى من بناء نظام قوي لأرشفة البريد الإلكتروني الذي يحتفظ بنسخة البريد الإلكتروني المكررة في S3 ومقارنة البيانات في جدول MySQL.  سيمنحنا هذا أساسًا لبقية المشروع الذي سيتم التعامل معه في العديد من المشاركات المستقبلية.

في النسخ المستقبلية من هذا المشروع أتوقع أن:

  1. تخزين جميع أحداث السجل للبريد الإلكتروني الأصلي

  2. إرسال أخطاء التخزين إلى المسؤول عند حدوث فشل في التحميل أو التسجيل

  3. تقليل تعقيد المجمع.

  4. إضافة واجهة مستخدم لعرض جميع البيانات

  5. دعم القدرة على إعادة إرسال البريد الإلكتروني

في الوقت الحالي، آمل أن يكون هذا المشروع قد كان مثيرًا للاهتمام ومفيدًا لك؛ إرسال سعيد.

دعنا نوصلك بخبير من Bird.
رؤية القوة الكاملة لـ Bird في 30 دقيقة.

بتقديمك طلبًا، فإنك توافق على أن تقوم Bird بالاتصال بك بشأن منتجاتنا وخدماتنا.

يمكنك إلغاء الاشتراك في أي وقت. انظر بيان الخصوصية الخاص بـ Bird للتفاصيل حول معالجة البيانات.

دعنا نوصلك بخبير من Bird.
رؤية القوة الكاملة لـ Bird في 30 دقيقة.

بتقديمك طلبًا، فإنك توافق على أن تقوم Bird بالاتصال بك بشأن منتجاتنا وخدماتنا.

يمكنك إلغاء الاشتراك في أي وقت. انظر بيان الخصوصية الخاص بـ Bird للتفاصيل حول معالجة البيانات.

دعنا نوصلك بخبير من Bird.
رؤية القوة الكاملة لـ Bird في 30 دقيقة.

بتقديمك طلبًا، فإنك توافق على أن تقوم Bird بالاتصال بك بشأن منتجاتنا وخدماتنا.

يمكنك إلغاء الاشتراك في أي وقت. انظر بيان الخصوصية الخاص بـ Bird للتفاصيل حول معالجة البيانات.

R

وصول

G

نمو

م

إدارة

A

أتمتة

النشرة الإخبارية

ابقَ على اطلاع مع Bird من خلال التحديثات الأسبوعية إلى بريدك الوارد.