Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6a6fe5b
feat(ResendWebhook): resend mail sent event webhook
RichardGL11 Oct 6, 2025
7b70b8b
test(ResendMiddleware): it should thrown exception if mail header was…
RichardGL11 Oct 6, 2025
056172c
test: testing another events by resend
RichardGL11 Oct 6, 2025
a4321fa
Fix styling
RichardGL11 Oct 6, 2025
de08c7c
wip
RichardGL11 Oct 6, 2025
b0bdc13
Fix styling
RichardGL11 Oct 6, 2025
9a00dbd
refactor(HasMailTrait): refactor commom methos into trait
RichardGL11 Oct 7, 2025
e28827d
style: pint
RichardGL11 Oct 7, 2025
4b60a26
Fix styling
RichardGL11 Oct 7, 2025
15e3d1a
refactor: refactoring middlewares to abstract middleware and creating…
RichardGL11 Oct 7, 2025
120284d
style: pint
RichardGL11 Oct 7, 2025
26cd415
chore: renaming interface to BetterMailDTOContract
RichardGL11 Oct 7, 2025
a88834f
feat(Middleware): resend middleware through enums
RichardGL11 Oct 7, 2025
18f66e2
feat(User-Experience): publishing migrations and config file
RichardGL11 Oct 8, 2025
a1175a0
feat: publishing views
RichardGL11 Oct 8, 2025
97ab45f
docs(readme): adding content to config section on readme
RichardGL11 Oct 8, 2025
8ea9164
docs(readme): adding more information on readme
RichardGL11 Oct 8, 2025
81dff63
fix(Middlewares): fixing middlewares that was not been registered
RichardGL11 Oct 8, 2025
6a7a400
Fix styling
RichardGL11 Oct 8, 2025
0afb9c7
chore: fixing complained contract import
RichardGL11 Oct 8, 2025
d0d7a7f
refactor(ResendMiddlewarePackage): refactoring middleware to adapter
RichardGL11 Oct 10, 2025
7f37170
Fix styling
RichardGL11 Oct 10, 2025
6f982a0
refactor: abstracting model actions to events, and renaming middlewar…
RichardGL11 Oct 13, 2025
3d8b908
style: pint
RichardGL11 Oct 13, 2025
a0a36d6
chore: removing fromEvent methods
RichardGL11 Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion config/filament-better-mails.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use Basement\BetterMails\Core\Models\BetterEmail;
use Basement\BetterMails\Core\Models\BetterEmailAttachment;
use Basement\BetterMails\Core\Models\BetterEmailEvent;
use Basement\BetterMails\Resend\Email\Middleware\VerifyResendWebhookSignature;
use Basement\BetterMails\Resend\ResendDriver;

return [
'mails' => [
Expand All @@ -26,7 +28,7 @@
],

'headers' => [
'key' => 'X-Better-Mails-Event-Id'
'key' => 'X-Better-Mails-Event-ID'
],

'logging' => [
Expand All @@ -36,5 +38,16 @@
'root' => 'mails/attachments',
],
]
],
'webhooks' => [
'provider' => env('MAILS_WEBHOOK_PROVIDER', 'resend'),

'drivers' => [
'resend' => [
'middleware' => VerifyResendWebhookSignature::class,
'driver' => ResendDriver::class,
'key_secret' => env('MAILS_WEBHOOK_SECRET', ''),
],
]
]
];
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@

namespace Basement\BetterMails\Database\Factories;

use Basement\BetterMails\Core\Enums\MailEventTypeEnum;
use Basement\BetterMails\Core\Models\BetterEmail;
use Basement\BetterMails\Core\Models\BetterEmailEvent;
use Illuminate\Database\Eloquent\Factories\Factory;

class BetterMailEventFactory extends Factory
final class BetterEmailEventFactory extends Factory
{
protected $model = BetterEmailEvent::class;

public function definition(): array
{
return [
'type' => 'delivered',
'mail_id' => BetterEmail::factory(),
'type' => MailEventTypeEnum::Sent,
'payload' => [],
];
}

public function bounce(): Factory
public function withEvent(MailEventTypeEnum $type): Factory
{
return $this->state(fn () => [
'type' => 'hard_bounced',
'type' => $type,
]);
}
}
7 changes: 7 additions & 0 deletions database/factories/BetterMailFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,11 @@ public function hasBcc(): BetterMailFactory
],
]);
}

public function withDriver(SupportedMailProvidersEnum $transport): static
{
return $this->state(fn () => [
'transport' => $transport,
]);
}
}
13 changes: 13 additions & 0 deletions routes/filament-better-mails-route.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

use Basement\BetterMails\Core\Contracts\BetterMiddlewareContract;
use Basement\BetterMails\Core\Enums\SupportedMailProvidersEnum;
use Basement\BetterMails\Core\Http\Controllers\WebhookController;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Support\Facades\Route;

Route::post('/webhook/{provider}', WebhookController::class)
->whereIn('provider', SupportedMailProvidersEnum::cases())
->middleware([BetterMiddlewareContract::class])
->withoutMiddleware(VerifyCsrfToken::class)
->name('filament-better-mails.webhook.store');
5 changes: 4 additions & 1 deletion src/Core/AbstractMailDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@

use Basement\BetterMails\Core\Contracts\BetterDriverContract;

abstract class AbstractMailDriver implements BetterDriverContract {}
abstract class AbstractMailDriver implements BetterDriverContract
{
abstract public function handle(array $data): void;
}
13 changes: 13 additions & 0 deletions src/Core/Concerns/HasMail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Basement\BetterMails\Core\Concerns;

use Basement\BetterMails\Core\Models\BetterEmail;

trait HasMail
{
public function findMail(string $uuid): BetterEmail
{
return BetterEmail::query()->where('uuid', $uuid)->firstOrFail();
}
}
8 changes: 8 additions & 0 deletions src/Core/Contracts/BetterDTOContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Basement\BetterMails\Core\Contracts;

interface BetterDTOContract
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interface BetterDTOContract
interface BetterMailDTOContract

{
public static function fromWebhook(array $dto): self;
}
5 changes: 4 additions & 1 deletion src/Core/Contracts/BetterDriverContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@

namespace Basement\BetterMails\Core\Contracts;

interface BetterDriverContract {}
interface BetterDriverContract
{
public function handle(array $data): void;
}
5 changes: 5 additions & 0 deletions src/Core/Contracts/BetterMiddlewareContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts;

interface BetterMiddlewareContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/ClickedEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface ClickedEventContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/ComplainedEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface ComplainedEventContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/DeliveredEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface DeliveredEventContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/FailedEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface FailedEventContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/HardBouncedEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface HardBouncedEventContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/OpenedEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface OpenedEventContract {}
5 changes: 5 additions & 0 deletions src/Core/Contracts/External/RecievedEventContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Basement\BetterMails\Core\Contracts\External;

interface RecievedEventContract {}
6 changes: 3 additions & 3 deletions src/Core/Events/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

## Delivery Events

- [ ] Implement "delivered" event handling
- [x] Implement "delivered" event handling
- [ ] Implement "bounced" event handling
- [ ] Implement "deferred" event handling
- [ ] Implement "blocked" event handling

## Engagement Events

- [ ] Implement "opened" event handling
- [ ] Implement "clicked" event handling
- [x] Implement "opened" event handling
- [x] Implement "clicked" event handling
- [ ] Implement "unsubscribed" event handling
- [ ] Implement "complained" (spam report) event handling

Expand Down
11 changes: 10 additions & 1 deletion src/Core/Http/Controllers/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@

namespace Basement\BetterMails\Core\Http\Controllers;

class WebhookController {}
use Basement\BetterMails\Core\Contracts\BetterDriverContract;
use Illuminate\Http\Request;

final class WebhookController
{
public function __invoke(Request $request, BetterDriverContract $driver)
{
$driver->handle($request->all());
}
}
17 changes: 17 additions & 0 deletions src/Core/Listeners/External/ClickedMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Concerns\HasMail;
use Basement\BetterMails\Core\Contracts\External\ClickedEventContract;

final class ClickedMailListener
{
use HasMail;

public function handle(ClickedEventContract $event): void
{
$mail = $this->findMail($event->dto->mailUuid);
$mail->clicked();
}
}
17 changes: 17 additions & 0 deletions src/Core/Listeners/External/ComplainedMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Concerns\HasMail;
use Basement\BetterMails\Core\Contracts\External\ComplainedEventContract;

final class ComplainedMailListener
{
use HasMail;

public function handle(ComplainedEventContract $event): void
{
$mail = $this->findMail($event->dto->mailUuid);
$mail->complained();
}
}
17 changes: 17 additions & 0 deletions src/Core/Listeners/External/DeliveredMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Concerns\HasMail;
use Basement\BetterMails\Core\Contracts\External\DeliveredEventContract;

final class DeliveredMailListener
{
use HasMail;

public function handle(DeliveredEventContract $event): void
{
$mail = $this->findMail($event->dto->mailUuid);
$mail->delivered();
}
}
10 changes: 10 additions & 0 deletions src/Core/Listeners/External/FailedMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Contracts\External\FailedEventContract;

final class FailedMailListener
{
public function handle(FailedEventContract $event): void {}
}
17 changes: 17 additions & 0 deletions src/Core/Listeners/External/HardBouncedMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Concerns\HasMail;
use Basement\BetterMails\Core\Contracts\External\HardBouncedEventContract;

final class HardBouncedMailListener
{
use HasMail;

public function handle(HardBouncedEventContract $event): void
{
$mail = $this->findMail($event->dto->mailUuid);
$mail->hardBounced();
}
}
17 changes: 17 additions & 0 deletions src/Core/Listeners/External/OpenedMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Concerns\HasMail;
use Basement\BetterMails\Core\Contracts\External\OpenedEventContract;

final class OpenedMailListener
{
use HasMail;

public function handle(OpenedEventContract $event): void
{
$mail = $this->findMail($event->dto->mailUuid);
$mail->opened();
}
}
10 changes: 10 additions & 0 deletions src/Core/Listeners/External/ReceivedMailListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Basement\BetterMails\Core\Listeners\External;

use Basement\BetterMails\Core\Contracts\External\RecievedEventContract;

final class ReceivedMailListener
{
public function handle(RecievedEventContract $event): void {}
}
Loading