Construyendo un sistema de archivo de correos electrónicos: Almacenando el cuerpo del correo electrónico

Pájaro

4 mar 2019

Correo electrónico

1 min read

Construyendo un sistema de archivo de correos electrónicos: Almacenando el cuerpo del correo electrónico

Puntos clave

    • Propósito: Esta publicación describe la primera fase de construir un sistema de archivo de correos electrónicos utilizando SparkPost, Amazon S3 y MySQL. Explica cómo duplicar, capturar y almacenar correos electrónicos para acceso a largo plazo y cumplimiento.

    • Idea principal: El sistema almacena automáticamente el cuerpo del correo electrónico en bruto (formato rfc822) en S3 y registra los metadatos (asunto, remitente, sello de tiempo, etc.) en MySQL para una búsqueda y recuperación rápidas.

    • Aspectos esenciales cubiertos:

      1. Creación de duplicados para archivo: Use la función Archive de SparkPost para enviar copias idénticas de correos electrónicos salientes a una dirección de archivo designada, asegurando que el cuerpo y los enlaces de seguimiento permanezcan idénticos.

      2. Vinculación de datos mediante UID: Incruste un identificador único (UID) tanto en el cuerpo del correo electrónico como en los metadatos X-MSYS-API para enlazar los mensajes originales y archivados.

      3. Procesamiento de entrada: Configure un dominio de entrada y un webhook en SparkPost para recibir las cargas útiles JSON de los correos electrónicos archivados a través de un colector de aplicaciones.

      4. Almacenamiento de correos electrónicos en S3: Cargue el cuerpo rfc822 analizado en un bucket S3, usando reglas de ciclo de vida (por ejemplo, transición a Glacier después de un año) para reducir los costos de almacenamiento.

      5. Registro de metadatos en MySQL: Guarde campos clave como RCPT_TO, FROM, SUBJECT y el nombre de archivo de S3 para indexación de búsqueda y futura recuperación.

      6. Consideraciones de rendimiento: La eficiencia del código y el registro mínimo aseguran que el colector pueda manejar cientos de solicitudes por minuto con una latencia mínima.

    • Panorama general: Esta base apoya futuros mejoramientos, como almacenamiento de eventos de registro, alertas de fallos y visualización de la interfaz, sentando las bases para una solución de archivo de correos electrónicos escalable y auditada.

Destacados de Q&A

  • ¿Cuál es el objetivo de este proyecto?

    Crear un sistema automatizado de archivo de correos electrónicos que almacene los cuerpos de mensajes en Amazon S3 mientras mantiene metadatos buscables en una base de datos MySQL.

  • ¿Por qué usar la función Archive de SparkPost?

    Te permite generar copias exactas de los correos electrónicos salientes, preservando su estructura y datos de seguimiento para el cumplimiento y revisión.

  • ¿Cómo está vinculado cada correo electrónico archivado a su mensaje original?

    Un UID único está incrustado tanto en el cuerpo del correo electrónico como en los metadatos, lo que permite una referencia cruzada precisa entre las copias originales y archivadas.

  • ¿Por qué usar S3 para almacenamiento?

    S3 ofrece opciones de almacenamiento escalables y gestión del ciclo de vida (como Glacier), lo que lo hace rentable para la retención de correos electrónicos a largo plazo.

  • ¿Qué almacena la base de datos MySQL?

    Almacena campos de metadatos buscables, como la línea de asunto, el remitente, las marcas de tiempo y el nombre de archivo S3, lo que permite una consulta y recuperación eficientes.

  • ¿Cuáles son los próximos pasos de desarrollo?

    Agregando seguimiento de eventos de registro, informes automatizados de errores, un colector simplificado y una interfaz de usuario para ver o reenviar correos electrónicos archivados.

En este blog, describiré el proceso que seguí para almacenar el cuerpo del correo electrónico en S3 (Servicio de Almacenamiento Simple de Amazon) y los datos auxiliares en una tabla MySQL para facilitar la referencia cruzada. En última instancia, este es el punto de partida para la base de código que incluirá una aplicación que permitirá la búsqueda fácil de correos electrónicos archivados, y luego mostrar esos correos electrónicos junto con los datos de eventos (registro). El código para este proyecto se puede encontrar en el siguiente repositorio de GitHub: https://github.com/jeff-goldstein/PHPArchivePlatform.

Aunque aprovecharé S3 y MySQL en este proyecto, de ninguna manera son estas las únicas tecnologías que se pueden usar para construir una plataforma de archivado, pero dada su ubicuidad, pensé que eran una buena opción para este proyecto. En un sistema de alto volumen a gran escala, usaría una base de datos de mayor rendimiento que MySQL, pero para este proyecto de muestra, MySQL es perfecto. Para las organizaciones que consideran PostgreSQL como su elección de base de datos para archivo, es esencial implementar procedimientos adecuados de respaldo y restauración para mantener la integridad de los datos en sistemas de producción.

He detallado a continuación, los pasos que tomé en esta primera fase del proyecto:

  1. Creación del correo electrónico duplicado para archivado

  2. Usar las funciones de archivado y retransmisión entrante de SparkPost para enviar una copia del correo electrónico original nuevamente a SparkPost para su procesamiento en una estructura JSON, luego enviada a un recolector de webhook (aplicación)

  3. Desmantelar la estructura JSON para obtener los componentes necesarios

  4. Enviar el cuerpo del correo electrónico a S3 para su almacenamiento

  5. Registrar una entrada en MySQL para cada correo electrónico para la referencia cruzada

En este blog, describiré el proceso que seguí para almacenar el cuerpo del correo electrónico en S3 (Servicio de Almacenamiento Simple de Amazon) y los datos auxiliares en una tabla MySQL para facilitar la referencia cruzada. En última instancia, este es el punto de partida para la base de código que incluirá una aplicación que permitirá la búsqueda fácil de correos electrónicos archivados, y luego mostrar esos correos electrónicos junto con los datos de eventos (registro). El código para este proyecto se puede encontrar en el siguiente repositorio de GitHub: https://github.com/jeff-goldstein/PHPArchivePlatform.

Aunque aprovecharé S3 y MySQL en este proyecto, de ninguna manera son estas las únicas tecnologías que se pueden usar para construir una plataforma de archivado, pero dada su ubicuidad, pensé que eran una buena opción para este proyecto. En un sistema de alto volumen a gran escala, usaría una base de datos de mayor rendimiento que MySQL, pero para este proyecto de muestra, MySQL es perfecto. Para las organizaciones que consideran PostgreSQL como su elección de base de datos para archivo, es esencial implementar procedimientos adecuados de respaldo y restauración para mantener la integridad de los datos en sistemas de producción.

He detallado a continuación, los pasos que tomé en esta primera fase del proyecto:

  1. Creación del correo electrónico duplicado para archivado

  2. Usar las funciones de archivado y retransmisión entrante de SparkPost para enviar una copia del correo electrónico original nuevamente a SparkPost para su procesamiento en una estructura JSON, luego enviada a un recolector de webhook (aplicación)

  3. Desmantelar la estructura JSON para obtener los componentes necesarios

  4. Enviar el cuerpo del correo electrónico a S3 para su almacenamiento

  5. Registrar una entrada en MySQL para cada correo electrónico para la referencia cruzada

En este blog, describiré el proceso que seguí para almacenar el cuerpo del correo electrónico en S3 (Servicio de Almacenamiento Simple de Amazon) y los datos auxiliares en una tabla MySQL para facilitar la referencia cruzada. En última instancia, este es el punto de partida para la base de código que incluirá una aplicación que permitirá la búsqueda fácil de correos electrónicos archivados, y luego mostrar esos correos electrónicos junto con los datos de eventos (registro). El código para este proyecto se puede encontrar en el siguiente repositorio de GitHub: https://github.com/jeff-goldstein/PHPArchivePlatform.

Aunque aprovecharé S3 y MySQL en este proyecto, de ninguna manera son estas las únicas tecnologías que se pueden usar para construir una plataforma de archivado, pero dada su ubicuidad, pensé que eran una buena opción para este proyecto. En un sistema de alto volumen a gran escala, usaría una base de datos de mayor rendimiento que MySQL, pero para este proyecto de muestra, MySQL es perfecto. Para las organizaciones que consideran PostgreSQL como su elección de base de datos para archivo, es esencial implementar procedimientos adecuados de respaldo y restauración para mantener la integridad de los datos en sistemas de producción.

He detallado a continuación, los pasos que tomé en esta primera fase del proyecto:

  1. Creación del correo electrónico duplicado para archivado

  2. Usar las funciones de archivado y retransmisión entrante de SparkPost para enviar una copia del correo electrónico original nuevamente a SparkPost para su procesamiento en una estructura JSON, luego enviada a un recolector de webhook (aplicación)

  3. Desmantelar la estructura JSON para obtener los componentes necesarios

  4. Enviar el cuerpo del correo electrónico a S3 para su almacenamiento

  5. Registrar una entrada en MySQL para cada correo electrónico para la referencia cruzada

Creando un Duplicado del Email

En SparkPost, la mejor manera de archivar un correo electrónico es crear una copia idéntica del correo específicamente diseñada para fines de archivo. Esto se hace utilizando la función de Archivo de SparkPost. La función de Archivo de SparkPost le da al remitente la capacidad de enviar un duplicado del correo a una o más direcciones de correo electrónico.  Este duplicado utiliza los mismos enlaces de seguimiento y apertura que el original. La documentación de SparkPost define la función de Archivo de la siguiente manera:

Los destinatarios en la lista de archivo recibirán una réplica exacta del mensaje que fue enviado a la dirección RCPT TO. En particular, cualquier enlace codificado destinado al destinatario RCPT TO será idéntico en los mensajes archivados.

La única diferencia entre esta copia de archivo y el correo original de RCPT TO es que algunos de los encabezados serán diferentes ya que la dirección de destino para el correo de archivo es diferente, ¡pero el cuerpo del correo será una réplica exacta!

Si desea una explicación más profunda, aquí hay un enlace a la documentación de SparkPost sobre la creación de copias duplicadas (o de archivo) de un correo electrónico. Muestras de encabezados X-MSYS-API para este proyecto se muestran más adelante en este blog.

Hay una advertencia para este enfoque; si bien toda la información de eventos en el correo original está unida tanto por un transmission_id como por un message_id, no hay información en el evento de retransmisión entrante (el mecanismo para obtener y distribuir el correo de archivo) para el correo duplicado que remita a uno de esos dos id’s y, por lo tanto, la información del correo original. Esto significa que necesitamos colocar datos en el cuerpo del correo electrónico y en el encabezado del correo original como una forma de unir todos los datos de SparkPost del correo original y del archivo.

Para crear el código que se coloca en el cuerpo del correo, utilicé el siguiente proceso en la aplicación de creación de correos electrónicos.

  1. En algún lugar del cuerpo del correo, coloqué la siguiente entrada de entrada:<input name="ArchiveCode" type="hidden" value="<<UID>>">

  2. Luego creé un código único y reemplacé el campo <<UID>>:$uid = md5(uniqid(rand(), true)); $emailBody = str_replace(“<<UID>>,$uid,$emailBody);

    Aquí hay un ejemplo de salida:

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

  3. Después, me aseguré de agregar el $UID al bloque meta_data del encabezado X-MSYS-API. Este paso asegura que el UID esté incrustado en cada salida de evento para el correo original:

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>"
  }
}

Ahora tenemos una manera de unir todos los datos del correo original al cuerpo del correo del archivo.

En SparkPost, la mejor manera de archivar un correo electrónico es crear una copia idéntica del correo específicamente diseñada para fines de archivo. Esto se hace utilizando la función de Archivo de SparkPost. La función de Archivo de SparkPost le da al remitente la capacidad de enviar un duplicado del correo a una o más direcciones de correo electrónico.  Este duplicado utiliza los mismos enlaces de seguimiento y apertura que el original. La documentación de SparkPost define la función de Archivo de la siguiente manera:

Los destinatarios en la lista de archivo recibirán una réplica exacta del mensaje que fue enviado a la dirección RCPT TO. En particular, cualquier enlace codificado destinado al destinatario RCPT TO será idéntico en los mensajes archivados.

La única diferencia entre esta copia de archivo y el correo original de RCPT TO es que algunos de los encabezados serán diferentes ya que la dirección de destino para el correo de archivo es diferente, ¡pero el cuerpo del correo será una réplica exacta!

Si desea una explicación más profunda, aquí hay un enlace a la documentación de SparkPost sobre la creación de copias duplicadas (o de archivo) de un correo electrónico. Muestras de encabezados X-MSYS-API para este proyecto se muestran más adelante en este blog.

Hay una advertencia para este enfoque; si bien toda la información de eventos en el correo original está unida tanto por un transmission_id como por un message_id, no hay información en el evento de retransmisión entrante (el mecanismo para obtener y distribuir el correo de archivo) para el correo duplicado que remita a uno de esos dos id’s y, por lo tanto, la información del correo original. Esto significa que necesitamos colocar datos en el cuerpo del correo electrónico y en el encabezado del correo original como una forma de unir todos los datos de SparkPost del correo original y del archivo.

Para crear el código que se coloca en el cuerpo del correo, utilicé el siguiente proceso en la aplicación de creación de correos electrónicos.

  1. En algún lugar del cuerpo del correo, coloqué la siguiente entrada de entrada:<input name="ArchiveCode" type="hidden" value="<<UID>>">

  2. Luego creé un código único y reemplacé el campo <<UID>>:$uid = md5(uniqid(rand(), true)); $emailBody = str_replace(“<<UID>>,$uid,$emailBody);

    Aquí hay un ejemplo de salida:

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

  3. Después, me aseguré de agregar el $UID al bloque meta_data del encabezado X-MSYS-API. Este paso asegura que el UID esté incrustado en cada salida de evento para el correo original:

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>"
  }
}

Ahora tenemos una manera de unir todos los datos del correo original al cuerpo del correo del archivo.

En SparkPost, la mejor manera de archivar un correo electrónico es crear una copia idéntica del correo específicamente diseñada para fines de archivo. Esto se hace utilizando la función de Archivo de SparkPost. La función de Archivo de SparkPost le da al remitente la capacidad de enviar un duplicado del correo a una o más direcciones de correo electrónico.  Este duplicado utiliza los mismos enlaces de seguimiento y apertura que el original. La documentación de SparkPost define la función de Archivo de la siguiente manera:

Los destinatarios en la lista de archivo recibirán una réplica exacta del mensaje que fue enviado a la dirección RCPT TO. En particular, cualquier enlace codificado destinado al destinatario RCPT TO será idéntico en los mensajes archivados.

La única diferencia entre esta copia de archivo y el correo original de RCPT TO es que algunos de los encabezados serán diferentes ya que la dirección de destino para el correo de archivo es diferente, ¡pero el cuerpo del correo será una réplica exacta!

Si desea una explicación más profunda, aquí hay un enlace a la documentación de SparkPost sobre la creación de copias duplicadas (o de archivo) de un correo electrónico. Muestras de encabezados X-MSYS-API para este proyecto se muestran más adelante en este blog.

Hay una advertencia para este enfoque; si bien toda la información de eventos en el correo original está unida tanto por un transmission_id como por un message_id, no hay información en el evento de retransmisión entrante (el mecanismo para obtener y distribuir el correo de archivo) para el correo duplicado que remita a uno de esos dos id’s y, por lo tanto, la información del correo original. Esto significa que necesitamos colocar datos en el cuerpo del correo electrónico y en el encabezado del correo original como una forma de unir todos los datos de SparkPost del correo original y del archivo.

Para crear el código que se coloca en el cuerpo del correo, utilicé el siguiente proceso en la aplicación de creación de correos electrónicos.

  1. En algún lugar del cuerpo del correo, coloqué la siguiente entrada de entrada:<input name="ArchiveCode" type="hidden" value="<<UID>>">

  2. Luego creé un código único y reemplacé el campo <<UID>>:$uid = md5(uniqid(rand(), true)); $emailBody = str_replace(“<<UID>>,$uid,$emailBody);

    Aquí hay un ejemplo de salida:

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

  3. Después, me aseguré de agregar el $UID al bloque meta_data del encabezado X-MSYS-API. Este paso asegura que el UID esté incrustado en cada salida de evento para el correo original:

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>"
  }
}

Ahora tenemos una manera de unir todos los datos del correo original al cuerpo del correo del archivo.

Obteniendo la versión Archive

Para obtener una copia de un correo electrónico para archivo, debe seguir los siguientes pasos:

  1. Crear un subdominio al que enviará todos los correos electrónicos de archivo (duplicados)

  2. Configurar los registros DNS apropiados para que todos los correos enviados a ese subdominio se dirijan a SparkPost

  3. Crear un dominio de entrada en SparkPost

  4. Crear un webhook de entrada en SparkPost

  5. Crear una aplicación (colector) para recibir el flujo de datos del webhook de SparkPost

Los siguientes dos enlaces pueden ayudarle a guiarse a través de este proceso:

  1. Documento técnico de SparkPost: Enabling Inbound Email Relaying & Relay Webhooks

  2. Además, el blog que escribí el año pasado, Archiving Emails: A How-To Guide for Tracking Sent Mail le guiará a través de la creación del relay de entrada dentro de SparkPost

* Nota: a partir de octubre de 2018, la función de Archivo solo funciona al enviar correos electrónicos usando una conexión SMTP a SparkPost, la API RESTful no admite esta función.  Probablemente eso no sea un problema porque la mayoría de los correos electrónicos que necesitan este nivel de control de auditoría tienden a ser correos personalizados que se construyen completamente por una aplicación de backend antes de que se necesite la entrega del correo.

Para obtener una copia de un correo electrónico para archivo, debe seguir los siguientes pasos:

  1. Crear un subdominio al que enviará todos los correos electrónicos de archivo (duplicados)

  2. Configurar los registros DNS apropiados para que todos los correos enviados a ese subdominio se dirijan a SparkPost

  3. Crear un dominio de entrada en SparkPost

  4. Crear un webhook de entrada en SparkPost

  5. Crear una aplicación (colector) para recibir el flujo de datos del webhook de SparkPost

Los siguientes dos enlaces pueden ayudarle a guiarse a través de este proceso:

  1. Documento técnico de SparkPost: Enabling Inbound Email Relaying & Relay Webhooks

  2. Además, el blog que escribí el año pasado, Archiving Emails: A How-To Guide for Tracking Sent Mail le guiará a través de la creación del relay de entrada dentro de SparkPost

* Nota: a partir de octubre de 2018, la función de Archivo solo funciona al enviar correos electrónicos usando una conexión SMTP a SparkPost, la API RESTful no admite esta función.  Probablemente eso no sea un problema porque la mayoría de los correos electrónicos que necesitan este nivel de control de auditoría tienden a ser correos personalizados que se construyen completamente por una aplicación de backend antes de que se necesite la entrega del correo.

Para obtener una copia de un correo electrónico para archivo, debe seguir los siguientes pasos:

  1. Crear un subdominio al que enviará todos los correos electrónicos de archivo (duplicados)

  2. Configurar los registros DNS apropiados para que todos los correos enviados a ese subdominio se dirijan a SparkPost

  3. Crear un dominio de entrada en SparkPost

  4. Crear un webhook de entrada en SparkPost

  5. Crear una aplicación (colector) para recibir el flujo de datos del webhook de SparkPost

Los siguientes dos enlaces pueden ayudarle a guiarse a través de este proceso:

  1. Documento técnico de SparkPost: Enabling Inbound Email Relaying & Relay Webhooks

  2. Además, el blog que escribí el año pasado, Archiving Emails: A How-To Guide for Tracking Sent Mail le guiará a través de la creación del relay de entrada dentro de SparkPost

* Nota: a partir de octubre de 2018, la función de Archivo solo funciona al enviar correos electrónicos usando una conexión SMTP a SparkPost, la API RESTful no admite esta función.  Probablemente eso no sea un problema porque la mayoría de los correos electrónicos que necesitan este nivel de control de auditoría tienden a ser correos personalizados que se construyen completamente por una aplicación de backend antes de que se necesite la entrega del correo.

Obtener el correo electrónico duplicado en una estructura JSON

En la primera fase de este proyecto, todo lo que estoy almacenando es el formato de correo electrónico rfc822 en S3 y algunos campos de descripción de alto nivel en una tabla SQL para búsqueda.  Dado que SparkPost enviará los datos del correo electrónico en una estructura JSON a mi plataforma de archivo a través de flujos de datos de webhook, construí una aplicación (a menudo referida como un collector) que acepta el flujo de datos Relay_Webhook.

Cada paquete del Relay_Webhook de SparkPost contendrá la información de un correo electrónico duplicado a la vez, por lo que desglosar la estructura JSON en los componentes específicos para este proyecto es bastante sencillo.  En mi código PHP, obtener el correo electrónico con formato rfc822 fue tan fácil como las siguientes pocas líneas de código:

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'];
}

Parte de la información que quiero almacenar en mi tabla SQL reside en un arreglo de campos de cabecera.  Así que escribí una pequeña función que aceptaba el arreglo de cabeceras y recorría el arreglo para obtener los datos que me interesaba almacenar:

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;
            }
        }
    }
}

Ahora que tengo los datos, estoy listo para almacenar el cuerpo en S3.

En la primera fase de este proyecto, todo lo que estoy almacenando es el formato de correo electrónico rfc822 en S3 y algunos campos de descripción de alto nivel en una tabla SQL para búsqueda.  Dado que SparkPost enviará los datos del correo electrónico en una estructura JSON a mi plataforma de archivo a través de flujos de datos de webhook, construí una aplicación (a menudo referida como un collector) que acepta el flujo de datos Relay_Webhook.

Cada paquete del Relay_Webhook de SparkPost contendrá la información de un correo electrónico duplicado a la vez, por lo que desglosar la estructura JSON en los componentes específicos para este proyecto es bastante sencillo.  En mi código PHP, obtener el correo electrónico con formato rfc822 fue tan fácil como las siguientes pocas líneas de código:

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'];
}

Parte de la información que quiero almacenar en mi tabla SQL reside en un arreglo de campos de cabecera.  Así que escribí una pequeña función que aceptaba el arreglo de cabeceras y recorría el arreglo para obtener los datos que me interesaba almacenar:

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;
            }
        }
    }
}

Ahora que tengo los datos, estoy listo para almacenar el cuerpo en S3.

En la primera fase de este proyecto, todo lo que estoy almacenando es el formato de correo electrónico rfc822 en S3 y algunos campos de descripción de alto nivel en una tabla SQL para búsqueda.  Dado que SparkPost enviará los datos del correo electrónico en una estructura JSON a mi plataforma de archivo a través de flujos de datos de webhook, construí una aplicación (a menudo referida como un collector) que acepta el flujo de datos Relay_Webhook.

Cada paquete del Relay_Webhook de SparkPost contendrá la información de un correo electrónico duplicado a la vez, por lo que desglosar la estructura JSON en los componentes específicos para este proyecto es bastante sencillo.  En mi código PHP, obtener el correo electrónico con formato rfc822 fue tan fácil como las siguientes pocas líneas de código:

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'];
}

Parte de la información que quiero almacenar en mi tabla SQL reside en un arreglo de campos de cabecera.  Así que escribí una pequeña función que aceptaba el arreglo de cabeceras y recorría el arreglo para obtener los datos que me interesaba almacenar:

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;
            }
        }
    }
}

Ahora que tengo los datos, estoy listo para almacenar el cuerpo en S3.

Almacenando el correo electrónico duplicado en S3

Lamento decepcionarte, pero no voy a dar un tutorial paso a paso sobre cómo crear un bucket S3 para almacenar el correo electrónico ni voy a describir cómo crear la clave de acceso necesaria que necesitarás en tu aplicación para subir contenido a tu bucket; hay mejores tutoriales sobre este tema de los que yo podría escribir. Aquí hay un par de artículos que pueden ayudar:

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

Lo que haré es señalar algunas de las configuraciones que elegí que se relacionan con un proyecto como este.

  1. Control de Acceso. No solo necesitas establecer la seguridad para el bucket, sino que necesitas establecer los permisos para los propios elementos. En mi proyecto, utilizo una política muy abierta de lectura pública porque los datos de muestra no son personales y quería fácil acceso a los datos. Probablemente querrás un conjunto de políticas ACL mucho más estrictas. Aquí hay un buen artículo sobre configuraciones de ACL: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html

  2. Archivando el Archivo. En S3 hay algo llamado Gestión del Ciclo de Vida. Esto te permite mover datos de un tipo de clase de almacenamiento S3 a otro. Las diferentes clases de almacenamiento representan la cantidad de acceso que necesitas a los datos almacenados con costos más bajos asociados al almacenamiento al que accedes menos. Una buena descripción de las diferentes clases y la transición entre ellas se puede encontrar en una guía de AWS llamada, Transitioning Objects. En mi caso, elegí crear un ciclo de vida que moviera cada objeto de Standard a Glacier después de un año. El acceso a Glacier es mucho más barato que el archivo estándar S3 y me ahorrará dinero en costos de almacenamiento.

Una vez que tengo creado el bucket S3 y mis configuraciones en su lugar, S3 está listo para que suba el correo electrónico conforme a rfc822 que obtuve del flujo de datos de SparkPost Relay Webhook. Pero antes de subir el payload del correo electrónico rfc822 a S3, necesito crear un nombre de archivo único que usaré para almacenar ese correo.

Para el nombre de archivo único, voy a buscar en el cuerpo del correo electrónico el id oculto que la aplicación de envío colocó en el correo electrónico y usar ese id como el nombre del archivo. Hay formas más elegantes de extraer el connectorId del cuerpo html, pero por simplicidad y claridad voy a usar el siguiente código:

$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);
        }
    }
}

* estamos asumiendo que $inputField contiene el valor "ArchiveCode" y fue encontrado en mi archivo config.php.

Con el UID, entonces podemos crear el nombre del archivo que se usará en S3:

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

Ahora puedo abrir mi conexión a S3 y subir el archivo. Si miras el archivo s3.php en el repositorio de GitHub verás que se necesita muy poco código para subir el archivo.

Mi último paso es registrar esta entrada en la tabla MYSQL.

Lamento decepcionarte, pero no voy a dar un tutorial paso a paso sobre cómo crear un bucket S3 para almacenar el correo electrónico ni voy a describir cómo crear la clave de acceso necesaria que necesitarás en tu aplicación para subir contenido a tu bucket; hay mejores tutoriales sobre este tema de los que yo podría escribir. Aquí hay un par de artículos que pueden ayudar:

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

Lo que haré es señalar algunas de las configuraciones que elegí que se relacionan con un proyecto como este.

  1. Control de Acceso. No solo necesitas establecer la seguridad para el bucket, sino que necesitas establecer los permisos para los propios elementos. En mi proyecto, utilizo una política muy abierta de lectura pública porque los datos de muestra no son personales y quería fácil acceso a los datos. Probablemente querrás un conjunto de políticas ACL mucho más estrictas. Aquí hay un buen artículo sobre configuraciones de ACL: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html

  2. Archivando el Archivo. En S3 hay algo llamado Gestión del Ciclo de Vida. Esto te permite mover datos de un tipo de clase de almacenamiento S3 a otro. Las diferentes clases de almacenamiento representan la cantidad de acceso que necesitas a los datos almacenados con costos más bajos asociados al almacenamiento al que accedes menos. Una buena descripción de las diferentes clases y la transición entre ellas se puede encontrar en una guía de AWS llamada, Transitioning Objects. En mi caso, elegí crear un ciclo de vida que moviera cada objeto de Standard a Glacier después de un año. El acceso a Glacier es mucho más barato que el archivo estándar S3 y me ahorrará dinero en costos de almacenamiento.

Una vez que tengo creado el bucket S3 y mis configuraciones en su lugar, S3 está listo para que suba el correo electrónico conforme a rfc822 que obtuve del flujo de datos de SparkPost Relay Webhook. Pero antes de subir el payload del correo electrónico rfc822 a S3, necesito crear un nombre de archivo único que usaré para almacenar ese correo.

Para el nombre de archivo único, voy a buscar en el cuerpo del correo electrónico el id oculto que la aplicación de envío colocó en el correo electrónico y usar ese id como el nombre del archivo. Hay formas más elegantes de extraer el connectorId del cuerpo html, pero por simplicidad y claridad voy a usar el siguiente código:

$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);
        }
    }
}

* estamos asumiendo que $inputField contiene el valor "ArchiveCode" y fue encontrado en mi archivo config.php.

Con el UID, entonces podemos crear el nombre del archivo que se usará en S3:

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

Ahora puedo abrir mi conexión a S3 y subir el archivo. Si miras el archivo s3.php en el repositorio de GitHub verás que se necesita muy poco código para subir el archivo.

Mi último paso es registrar esta entrada en la tabla MYSQL.

Lamento decepcionarte, pero no voy a dar un tutorial paso a paso sobre cómo crear un bucket S3 para almacenar el correo electrónico ni voy a describir cómo crear la clave de acceso necesaria que necesitarás en tu aplicación para subir contenido a tu bucket; hay mejores tutoriales sobre este tema de los que yo podría escribir. Aquí hay un par de artículos que pueden ayudar:

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

Lo que haré es señalar algunas de las configuraciones que elegí que se relacionan con un proyecto como este.

  1. Control de Acceso. No solo necesitas establecer la seguridad para el bucket, sino que necesitas establecer los permisos para los propios elementos. En mi proyecto, utilizo una política muy abierta de lectura pública porque los datos de muestra no son personales y quería fácil acceso a los datos. Probablemente querrás un conjunto de políticas ACL mucho más estrictas. Aquí hay un buen artículo sobre configuraciones de ACL: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html

  2. Archivando el Archivo. En S3 hay algo llamado Gestión del Ciclo de Vida. Esto te permite mover datos de un tipo de clase de almacenamiento S3 a otro. Las diferentes clases de almacenamiento representan la cantidad de acceso que necesitas a los datos almacenados con costos más bajos asociados al almacenamiento al que accedes menos. Una buena descripción de las diferentes clases y la transición entre ellas se puede encontrar en una guía de AWS llamada, Transitioning Objects. En mi caso, elegí crear un ciclo de vida que moviera cada objeto de Standard a Glacier después de un año. El acceso a Glacier es mucho más barato que el archivo estándar S3 y me ahorrará dinero en costos de almacenamiento.

Una vez que tengo creado el bucket S3 y mis configuraciones en su lugar, S3 está listo para que suba el correo electrónico conforme a rfc822 que obtuve del flujo de datos de SparkPost Relay Webhook. Pero antes de subir el payload del correo electrónico rfc822 a S3, necesito crear un nombre de archivo único que usaré para almacenar ese correo.

Para el nombre de archivo único, voy a buscar en el cuerpo del correo electrónico el id oculto que la aplicación de envío colocó en el correo electrónico y usar ese id como el nombre del archivo. Hay formas más elegantes de extraer el connectorId del cuerpo html, pero por simplicidad y claridad voy a usar el siguiente código:

$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);
        }
    }
}

* estamos asumiendo que $inputField contiene el valor "ArchiveCode" y fue encontrado en mi archivo config.php.

Con el UID, entonces podemos crear el nombre del archivo que se usará en S3:

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

Ahora puedo abrir mi conexión a S3 y subir el archivo. Si miras el archivo s3.php en el repositorio de GitHub verás que se necesita muy poco código para subir el archivo.

Mi último paso es registrar esta entrada en la tabla MYSQL.

Almacenando los Meta Data en MySQL

Recopilamos todos los datos necesarios en un paso anterior, por lo que el paso de almacenamiento es fácil.  En esta primera fase elegí construir una tabla con los siguientes campos:

Campos de Metadatos de MySQL

Campo

Propósito

Fecha/hora (automático)

Marca de tiempo cuando se registró la entrada

Dirección RCPT_TO

Dirección de correo electrónico objetivo para el mensaje archivado

Marca de tiempo del encabezado DATE

Hora de envío del correo original

Encabezado SUBJECT

Línea de asunto para indexar y buscar

Encabezado FROM

Identificador del remitente para la búsqueda

Directorio S3

Ruta del directorio dentro del bucket de S3

Nombre de archivo S3

Archivo .eml único almacenado en S3

La función llamada MySQLLog dentro del archivo de aplicación upload.php pasa por los pasos necesarios para abrir el enlace a MySQL, insertar la nueva fila, probar los resultados y cerrar el enlace. Agrego un paso adicional por precaución, que es registrar estos datos en un archivo de texto. ¿Debería hacer mucho más registro de errores? Sí. Pero quiero mantener este código ligero para permitir que se ejecute extremadamente rápido. A veces este código se llamará cientos de veces por minuto y necesita ser lo más eficiente posible. En futuras actualizaciones, agregaré código auxiliar que procesará fallos y enviará esos fallos por correo electrónico a un administrador para su monitoreo.

Recopilamos todos los datos necesarios en un paso anterior, por lo que el paso de almacenamiento es fácil.  En esta primera fase elegí construir una tabla con los siguientes campos:

Campos de Metadatos de MySQL

Campo

Propósito

Fecha/hora (automático)

Marca de tiempo cuando se registró la entrada

Dirección RCPT_TO

Dirección de correo electrónico objetivo para el mensaje archivado

Marca de tiempo del encabezado DATE

Hora de envío del correo original

Encabezado SUBJECT

Línea de asunto para indexar y buscar

Encabezado FROM

Identificador del remitente para la búsqueda

Directorio S3

Ruta del directorio dentro del bucket de S3

Nombre de archivo S3

Archivo .eml único almacenado en S3

La función llamada MySQLLog dentro del archivo de aplicación upload.php pasa por los pasos necesarios para abrir el enlace a MySQL, insertar la nueva fila, probar los resultados y cerrar el enlace. Agrego un paso adicional por precaución, que es registrar estos datos en un archivo de texto. ¿Debería hacer mucho más registro de errores? Sí. Pero quiero mantener este código ligero para permitir que se ejecute extremadamente rápido. A veces este código se llamará cientos de veces por minuto y necesita ser lo más eficiente posible. En futuras actualizaciones, agregaré código auxiliar que procesará fallos y enviará esos fallos por correo electrónico a un administrador para su monitoreo.

Recopilamos todos los datos necesarios en un paso anterior, por lo que el paso de almacenamiento es fácil.  En esta primera fase elegí construir una tabla con los siguientes campos:

Campos de Metadatos de MySQL

Campo

Propósito

Fecha/hora (automático)

Marca de tiempo cuando se registró la entrada

Dirección RCPT_TO

Dirección de correo electrónico objetivo para el mensaje archivado

Marca de tiempo del encabezado DATE

Hora de envío del correo original

Encabezado SUBJECT

Línea de asunto para indexar y buscar

Encabezado FROM

Identificador del remitente para la búsqueda

Directorio S3

Ruta del directorio dentro del bucket de S3

Nombre de archivo S3

Archivo .eml único almacenado en S3

La función llamada MySQLLog dentro del archivo de aplicación upload.php pasa por los pasos necesarios para abrir el enlace a MySQL, insertar la nueva fila, probar los resultados y cerrar el enlace. Agrego un paso adicional por precaución, que es registrar estos datos en un archivo de texto. ¿Debería hacer mucho más registro de errores? Sí. Pero quiero mantener este código ligero para permitir que se ejecute extremadamente rápido. A veces este código se llamará cientos de veces por minuto y necesita ser lo más eficiente posible. En futuras actualizaciones, agregaré código auxiliar que procesará fallos y enviará esos fallos por correo electrónico a un administrador para su monitoreo.

Concluyendo

Así que en unos pocos pasos bastante sencillos, pudimos recorrer la primera fase de construir un sistema robusto de archivo de correos electrónicos que mantiene el duplicado del correo en S3 y referencias cruzadas de datos en una tabla MySQL.  Esto nos dará una base para el resto del proyecto que se abordará en varias publicaciones futuras.

En revisiones futuras de este proyecto, esperaría:

  1. Almacenar todos los eventos de registro del correo electrónico original

  2. Enviar errores de almacenamiento a un administrador cuando ocurra una falla al subir o registrar

  3. Minimizar la complejidad del colector.

  4. Agregar una interfaz de usuario para ver todos los datos

  5. Soportar la capacidad de reenviar el correo electrónico

Mientras tanto, espero que este proyecto te haya parecido interesante y útil; feliz envío.

Así que en unos pocos pasos bastante sencillos, pudimos recorrer la primera fase de construir un sistema robusto de archivo de correos electrónicos que mantiene el duplicado del correo en S3 y referencias cruzadas de datos en una tabla MySQL.  Esto nos dará una base para el resto del proyecto que se abordará en varias publicaciones futuras.

En revisiones futuras de este proyecto, esperaría:

  1. Almacenar todos los eventos de registro del correo electrónico original

  2. Enviar errores de almacenamiento a un administrador cuando ocurra una falla al subir o registrar

  3. Minimizar la complejidad del colector.

  4. Agregar una interfaz de usuario para ver todos los datos

  5. Soportar la capacidad de reenviar el correo electrónico

Mientras tanto, espero que este proyecto te haya parecido interesante y útil; feliz envío.

Así que en unos pocos pasos bastante sencillos, pudimos recorrer la primera fase de construir un sistema robusto de archivo de correos electrónicos que mantiene el duplicado del correo en S3 y referencias cruzadas de datos en una tabla MySQL.  Esto nos dará una base para el resto del proyecto que se abordará en varias publicaciones futuras.

En revisiones futuras de este proyecto, esperaría:

  1. Almacenar todos los eventos de registro del correo electrónico original

  2. Enviar errores de almacenamiento a un administrador cuando ocurra una falla al subir o registrar

  3. Minimizar la complejidad del colector.

  4. Agregar una interfaz de usuario para ver todos los datos

  5. Soportar la capacidad de reenviar el correo electrónico

Mientras tanto, espero que este proyecto te haya parecido interesante y útil; feliz envío.

Otras noticias

Leer más de esta categoría

A person is standing at a desk while typing on a laptop.

La plataforma completa AI-native que escala con tu negocio.

© 2025 Bird

A person is standing at a desk while typing on a laptop.

La plataforma completa AI-native que escala con tu negocio.

© 2025 Bird