
In questo blog, descriverò il processo che ho seguito per memorizzare il corpo dell'email su S3 (Amazon Simple Store Service) e dati accessori in una tabella MySQL per un facile riferimento incrociato.
In questo blog, descriverò il processo che ho seguito per memorizzare il corpo dell'email su S3 (Amazon’s Simple Store Service) e i dati accessori in una tabella MySQL per facile riferimento incrociato. In definitiva, questo è il punto di partenza per la base di codice che includerà un'applicazione che consentirà una facile ricerca delle email archiviate e quindi visualizzare quelle email insieme ai dati di evento (log). Il codice di questo progetto può essere trovato nel seguente repository GitHub: https://github.com/jeff-goldstein/PHPArchivePlatform.
Pur sfruttando S3 e MySQL in questo progetto, non sono assolutamente le uniche tecnologie che possono essere utilizzate per costruire una piattaforma di archiviazione, ma data la loro diffusione, ho pensato fossero una buona scelta per questo progetto. In un sistema ad alto volume su scala completa userei un database ad alte prestazioni diverso da MySQL, ma per questo progetto di esempio, MySQL è perfetto. Per le organizzazioni che stanno considerando PostgreSQL come loro scelta di database di archiviazione, implementare corrette procedure di backup e ripristino è essenziale per mantenere l'integrità dei dati nei sistemi di produzione.
Ho dettagliato di seguito, i passaggi che ho seguito in questa prima fase del progetto:
Creare l'email duplicata per l'archiviazione
Usare le funzionalità di Archiving e Inbound Relay di SparkPost per inviare una copia dell'email originale di ritorno a SparkPost per l'elaborazione in una struttura JSON, quindi inviata a un raccoglitore webhook (applicazione)
Smontare la struttura JSON per ottenere i componenti necessari
Inviare il corpo dell'email a S3 per la memorizzazione
Registrare una voce in MySQL per ogni email per riferimento incrociato
Creare un Duplicato dell'Email
In SparkPost il miglior modo per archiviare un'email è creare una copia identica dell'email appositamente progettata per scopi di archiviazione. Questo viene fatto utilizzando la funzione Archive di SparkPost. La funzione Archive di SparkPost offre al mittente la possibilità di inviare un duplicato dell'email a uno o più indirizzi email. Questo duplicato utilizza gli stessi link tracciabili e di apertura dell'originale. La documentazione di SparkPost definisce la funzione Archive nel modo seguente:
I destinatari nella lista dell'archivio riceveranno una replica esatta del messaggio inviato all'indirizzo RCPT TO. In particolare, qualsiasi link codificato destinato al destinatario RCPT TO sarà identico nei messaggi dell'archivio
L'unica differenza tra questa copia dell'archivio e l'email originale RCPT TO è che alcune delle intestazioni saranno diverse poiché l'indirizzo di destinazione dell'email di archiviazione è differente, ma il corpo dell'email sarà una replica esatta!
Se desideri una spiegazione più approfondita, ecco un collegamento alla documentazione di SparkPost su come creare copie duplicate (o archiviate) di un'email. Gli esempi degli header X-MSYS-API per questo progetto sono mostrati più avanti in questo blog.
C'è una nota su questo approccio; mentre tutte le informazioni sugli eventi nell'email originale sono collegate insieme da un transmission_id e da un message_id, non c'è nessuna informazione nell'evento di inoltro inbound (il meccanismo per ottenere e diffondere l'email di archivio) per l'email duplicata che si ricolleghi a uno di quei due id e quindi alle informazioni dell'email originale. Questo significa che dobbiamo inserire i dati nel corpo dell'email e nell'intestazione dell'email originale come un modo per collegare tutte le informazioni di SparkPost dall'email originale e dall'email di archivio.
Per creare il codice inserito nel corpo dell'email, ho utilizzato il seguente processo nell'applicazione di creazione delle email.
Da qualche parte nel corpo dell'email, ho inserito la seguente voce di input:<input name="ArchiveCode" type="hidden" value="<<UID>>">
Poi ho creato un codice unico e ho sostituito il campo <<UID>>:$uid = md5(uniqid(rand(), true)); $emailBody = str_replace(“<<UID>>,$uid,$emailBody);
Ecco un esempio di output:
<input name="ArchiveCode" type="hidden" value="00006365263145">
Successivamente, ho assicurato di aggiungere il $UID al blocco meta_data dell'header X-MSYS-API. Questo passaggio assicura che il UID sia incorporato in ogni output dell'evento per l'email originale:
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>" } }
Ora abbiamo un modo per collegare tutti i dati dall'email originale al corpo dell'email dell'archivio.
Ottenere la versione Archive
Ottenere l'email duplicata in una struttura JSON
Nella prima fase di questo progetto, tutto ciò che sto memorizzando è il formato email rfc822 in S3 e alcuni campi di descrizione ad alto livello in una tabella SQL per la ricerca. Poiché SparkPost invierà i dati delle email in una struttura JSON alla mia piattaforma di archiviazione tramite flussi di dati webhook, ho costruito un'applicazione (spesso chiamata collector) che accetta il flusso di dati Relay_Webhook.
Ciascun pacchetto da SparkPost Relay_Webhook conterrà le informazioni di un'email duplicata alla volta, quindi suddividere la struttura JSON nei componenti mirati per questo progetto è piuttosto semplice. Nel mio codice PHP, ottenere l'email formattata in rfc822 è stato facile con le seguenti poche righe di codice:
if ($verb == "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'];}
Alcune delle informazioni che voglio memorizzare nella mia tabella SQL risiedono in un array di campi di intestazione. Quindi ho scritto una piccola funzione che accettava l'array di intestazioni e lo percorreva in un ciclo per ottenere i dati che mi interessava memorizzare:
function get_important_headers($headers, &$original_to, &$headerDate, &$subject, &$from) { foreach ($headers as $key => $value) { foreach ($value as $key_sub => $value_sub) { if ($key_sub == 'To') $original_to = $value_sub; if ($key_sub == 'Date') $headerDate = $value_sub; if ($key_sub == 'Subject') $subject = $value_sub; if ($key_sub == 'From') $from = $value_sub; } } }
Ora che ho i dati, sono pronto a memorizzare il corpo in S3.
Memorizzare l'email duplicata in S3
Mi dispiace deluderti, ma non ti darò un tutorial passo dopo passo su come creare un bucket S3 per memorizzare l'email né descriverò come creare la chiave di accesso necessaria per la tua applicazione per caricare contenuti nel tuo bucket; ci sono tutorial migliori su questo argomento di quelli che potrei mai scrivere. Ecco un paio di articoli che potrebbero aiutarti:
https://docs.aws.amazon.com/quickstarts/latest/s3backup/step-1-create-bucket.html
https://aws.amazon.com/blogs/security/wheres-my-secret-access-key/
Quello che farò è evidenziare alcune delle impostazioni che ho scelto che riguardano un progetto come questo.
Controllo dell'Accesso. Non solo è necessario impostare la sicurezza per il bucket, ma bisogna impostare i permessi per gli elementi stessi. Nel mio progetto, utilizzo una politica molto aperta di lettura pubblica perché i dati di esempio non sono personali e volevo un facile accesso ai dati. Probabilmente vorrai un set di politiche ACL molto più rigoroso. Ecco un bel articolo sulle impostazioni ACL: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html
Archiviazione dell'Archivio. In S3 c'è qualcosa chiamato Gestione del Ciclo di Vita. Questo ti permette di spostare i dati da un tipo di classe di archiviazione S3 a un'altra. Le diverse classi di archiviazione rappresentano la quantità di accesso di cui hai bisogno ai dati memorizzati con costi inferiori associati allo storage che utilizzi di meno. Una buona scrittura delle diverse classi e della loro transizione può essere trovata in una guida AWS chiamata, Transitioning Objects. Nel mio caso, ho scelto di creare un ciclo di vita che spostasse ogni oggetto da Standard a Glacier dopo un anno. L'accesso a Glacier è molto più economico rispetto all'archiviazione standard di S3 e mi farà risparmiare sui costi di archiviazione.
Una volta creato il bucket S3 e impostate le mie impostazioni, S3 è pronto per me per caricare l'email conforme a rfc822 che ho ottenuto dal flusso di dati SparkPost Relay Webhook. Ma prima di caricare il payload email rfc822 su S3, devo creare un nome file univoco che userò per memorizzare quell'email.
Per il nome file univoco, cercherò nel corpo dell'email l'id nascosto che l'applicazione mittente ha inserito nell'email e userò quell'id come nome del file. Ci sono modi più eleganti per estrarre il connectorId dal corpo html, ma per semplicità e chiarezza userò il seguente codice:
$start = strpos($htmlbody, $inputField); $start = strpos($htmlbody, "value=", $start) + 7; $end = strpos($htmlbody, ">", $start) - 1; $length = $end - $start; $UID = substr($html, $start, $length);
* supponiamo che $inputField contenga il valore “ArchiveCode” e sia stato trovato nel mio file config.php.
Con l'UID, possiamo quindi creare il nome del file che verrà utilizzato in S3:
$fileName = $ArchiveDirectory . '/' . $UID . '.eml';
Ora posso aprire la mia connessione a S3 e caricare il file. Se guardi il file s3.php nel repository GitHub vedrai che ci vuole pochissimo codice per caricare il file.
L'ultimo passo è registrare questa voce nella tabella MYSQL.
Memorizzare i Meta Data in MySQL
Abbiamo raccolto tutti i dati necessari in un passaggio precedente, quindi il passaggio di archiviazione è semplice. In questa prima fase, ho scelto di costruire una tabella con i seguenti campi:
Un campo di inserimento automatico per data/ora
L'indirizzo email di destinazione (RCPT_TO)
Il timestamp dall'intestazione DATA dell'email
L'intestazione SUBJECT
L'intestazione dell'indirizzo email FROM
La directory utilizzata nel bucket S3
Il nome del file S3 per l'email archiviata
La funzione chiamata MySQLLog all'interno del file di applicazione upload.php esegue i passaggi necessari per aprire il collegamento a MySQL, inserire la nuova riga, testare i risultati e chiudere il collegamento. Aggiungo un altro passaggio per buona misura ed è quello di registrare questi dati in un file di testo. Devo eseguire molti più log per gli errori? Sì. Ma voglio mantenere questo codice leggero per permettergli di funzionare estremamente veloce. A volte, questo codice verrà chiamato centinaia di volte al minuto e deve essere il più efficiente possibile. In futuri aggiornamenti, aggiungerò codice ausiliario che elaborerà i fallimenti e invierà tali fallimenti a un amministratore per il monitoraggio.
Concludendo
Quindi in pochi passaggi abbastanza semplici, siamo stati in grado di attraversare la prima fase della costruzione di un sistema di archiviazione email robusto che conserva il duplicato delle email in S3 e incrocia i dati in una tabella MySQL. Questo ci darà una base per il resto del progetto che verrà affrontato in diversi post futuri.
In future revisioni di questo progetto, mi aspetterei di:
Memorizzare tutti gli eventi di log dell'email originale
Inviare errori di archiviazione a un amministratore quando si verifica un errore di caricamento o di log
Minimizzare la complessità del collettore.
Aggiungere un'interfaccia utente per visualizzare tutti i dati
Supportare la possibilità di rinviare l'email
Nel frattempo, spero che questo progetto sia stato interessante e utile per te; buon invio.