updates mailgun webhook loading message data on runtime

This commit is contained in:
paulwer 2024-03-19 12:56:39 +01:00
parent 0aaaf27314
commit 73bcf928e4
4 changed files with 48 additions and 63 deletions

View File

@ -1,47 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\IngresMail\Transformer;
use App\Services\IngresEmail\IngresEmail;
use App\Utils\TempFile;
use Illuminate\Support\Carbon;
class MailgunInboundWebhookTransformer
{
public function transform($data)
{
$ingresEmail = new IngresEmail();
$ingresEmail->from = $data["sender"]; // TODO: maybe a fallback have to be used to extract email from $data["From"]
$ingresEmail->to = $data["recipient"]; // TODO: maybe a fallback have to be used to extract email from $data["To"]
$ingresEmail->subject = $data["Subject"];
$ingresEmail->body = $data["body-html"];
$ingresEmail->text_body = $data["body-plain"];
$ingresEmail->date = Carbon::createFromTimestamp((int) $data["timestamp"]);
// parse documents as UploadedFile from webhook-data
foreach (json_decode($data["attachments"]) as $attachment) {
// prepare url with credentials before downloading :: https://github.com/mailgun/mailgun.js/issues/24
$url = $attachment->url;
$credentials = config('services.mailgun.domain') . ":" . config('services.mailgun.secret') . "@";
$url = str_replace("http://", "http://" . $credentials, $url);
$url = str_replace("https://", "https://" . $credentials, $url);
// download file and save to tmp dir
$ingresEmail->documents[] = TempFile::UploadedFileFromUrl($url, $attachment->name, $attachment->{"content-type"});
}
return $ingresEmail;
}
}

View File

@ -115,19 +115,24 @@ class MailgunController extends BaseController
{ {
$input = $request->all(); $input = $request->all();
if (!array_key_exists('recipient', $input) || !array_key_exists('message-url', $input)) {
Log::info('Failed: Message could not be parsed, because required parameters are missing. Please ensure contacting this api-endpoint with a store & notify operation instead of a forward operation!');
return response()->json(['message' => 'Failed. Missing Parameters'], 400);
}
if (!array_key_exists('attachments', $input) || count(json_decode($input['attachments'])) == 0) { if (!array_key_exists('attachments', $input) || count(json_decode($input['attachments'])) == 0) {
Log::info('Message ignored because of missing attachments. Please ensure contacting this api-endpoint with a store & notify operation instead of a forward operation'); Log::info('Message ignored because of missing attachments. No Actions would have been taken...');
return response()->json(['message' => 'Sucess. Soft Fail. Missing Attachments.'], 200); return response()->json(['message' => 'Sucess. Soft Fail. Missing Attachments.'], 200);
} }
if (\abs(\time() - (int) $request['timestamp']) > 150) { if (\abs(\time() - (int) $input['timestamp']) > 150) {
Log::info('Message ignored because of request body is too old.'); Log::info('Message ignored because of request body is too old.');
return response()->json(['message' => 'Success. Soft Fail. Message too old.'], 200); return response()->json(['message' => 'Success. Soft Fail. Message too old.'], 200);
} }
// @turbo124 TODO: how to check for services.mailgun.webhook_signing_key on company level, when custom credentials are defined // @turbo124 TODO: how to check for services.mailgun.webhook_signing_key on company level, when custom credentials are defined
if (\hash_equals(\hash_hmac('sha256', $input['timestamp'] . $input['token'], config('services.mailgun.webhook_signing_key')), $input['signature'])) { if (\hash_equals(\hash_hmac('sha256', $input['timestamp'] . $input['token'], config('services.mailgun.webhook_signing_key')), $input['signature'])) {
ProcessMailgunInboundWebhook::dispatch($input)->delay(10); ProcessMailgunInboundWebhook::dispatch($input["recipient"] . "|" . $input["message-url"])->delay(10);
return response()->json(['message' => 'Success'], 201); return response()->json(['message' => 'Success'], 201);
} }

View File

@ -11,9 +11,11 @@
namespace App\Jobs\Mailgun; namespace App\Jobs\Mailgun;
use App\Helpers\IngresMail\Transformer\MailgunInboundWebhookTransformer;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Services\IngresEmail\IngresEmail;
use App\Services\IngresEmail\IngresEmailEngine; use App\Services\IngresEmail\IngresEmailEngine;
use App\Utils\TempFile;
use Illuminate\Support\Carbon;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
@ -29,9 +31,9 @@ class ProcessMailgunInboundWebhook implements ShouldQueue
/** /**
* Create a new job instance. * Create a new job instance.
* * $input consists of 2 informations: recipient|messageUrl
*/ */
public function __construct(private array $request) public function __construct(private string $input)
{ {
} }
@ -43,21 +45,48 @@ class ProcessMailgunInboundWebhook implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
if (!array_key_exists('To', $this->request) || !array_key_exists('attachments', $this->request) || !array_key_exists('timestamp', $this->request) || !array_key_exists('Subject', $this->request) || !(array_key_exists('body-html', $this->request) || array_key_exists('body-plain', $this->request))) $recipient = explode("|", $this->input)[0];
throw new \Exception('invalid body');
// match company // match company
$company = MultiDB::findAndSetDbByExpenseMailbox($this->request["To"]); $company = MultiDB::findAndSetDbByExpenseMailbox($recipient);
if (!$company) { if (!$company) {
Log::info('unknown Expense Mailbox occured while handling an inbound email from mailgun: ' . $this->request["To"]); Log::info('unknown Expense Mailbox occured while handling an inbound email from mailgun: ' . $recipient);
return; return;
} }
// prepare // fetch message from mailgun-api
$ingresMail = (new MailgunInboundWebhookTransformer())->transform($this->request); $mailgun_domain = $company->settings?->email_sending_method === 'client_mailgun' && $company->settings?->mailgun_domain ? $company->settings?->mailgun_domain : config('services.mailgun.domain');
Log::info(json_encode($ingresMail)); $mailgun_secret = $company->settings?->email_sending_method === 'client_mailgun' && $company->settings?->mailgun_secret ? $company->settings?->mailgun_secret : config('services.mailgun.secret');
$credentials = $mailgun_domain . ":" . $mailgun_secret . "@";
$messageUrl = explode("|", $this->input)[1];
$messageUrl = str_replace("http://", "http://" . $credentials, $messageUrl);
$messageUrl = str_replace("https://", "https://" . $credentials, $messageUrl);
$mail = json_decode(file_get_contents($messageUrl));
// prepare data for ingresEngine
$ingresEmail = new IngresEmail();
$ingresEmail->from = $mail->sender;
$ingresEmail->to = $recipient; // usage of data-input, because we need a single email here
$ingresEmail->subject = $mail->Subject;
$ingresEmail->body = $mail->{"body-html"};
$ingresEmail->text_body = $mail->{"body-plain"};
$ingresEmail->date = Carbon::createFromTimeString($mail->Date);
// parse documents as UploadedFile from webhook-data
foreach ($mail->attachments as $attachment) {
// prepare url with credentials before downloading :: https://github.com/mailgun/mailgun.js/issues/24
$url = $attachment->url;
$url = str_replace("http://", "http://" . $credentials, $url);
$url = str_replace("https://", "https://" . $credentials, $url);
// download file and save to tmp dir
$ingresEmail->documents[] = TempFile::UploadedFileFromUrl($url, $attachment->name, $attachment->{"content-type"});
}
// perform // perform
(new IngresEmailEngine($ingresMail))->handle(); (new IngresEmailEngine($ingresEmail))->handle();
} }
} }

View File

@ -182,8 +182,6 @@ class IngresEmailEngine
$expense->saveQuietly(); $expense->saveQuietly();
Log::info(json_encode($documents));
$this->saveDocuments($documents, $expense); $this->saveDocuments($documents, $expense);
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(null))); // @turbo124 please check, I copied from API-Controller event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(null))); // @turbo124 please check, I copied from API-Controller