feat: add resend as transactional emails

This commit is contained in:
Andras Bacsai
2023-08-31 13:10:39 +02:00
parent 87dd819ae4
commit 2538890b52
6 changed files with 200 additions and 75 deletions

View File

@@ -20,6 +20,9 @@ class Email extends Component
'settings.smtp_timeout' => 'nullable', 'settings.smtp_timeout' => 'nullable',
'settings.smtp_from_address' => 'required|email', 'settings.smtp_from_address' => 'required|email',
'settings.smtp_from_name' => 'required', 'settings.smtp_from_name' => 'required',
'settings.resend_enabled' => 'nullable|boolean',
'settings.resend_api_key' => 'nullable'
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'settings.smtp_from_address' => 'From Address', 'settings.smtp_from_address' => 'From Address',
@@ -30,48 +33,76 @@ class Email extends Component
'settings.smtp_encryption' => 'Encryption', 'settings.smtp_encryption' => 'Encryption',
'settings.smtp_username' => 'Username', 'settings.smtp_username' => 'Username',
'settings.smtp_password' => 'Password', 'settings.smtp_password' => 'Password',
'settings.smtp_timeout' => 'Timeout',
'settings.resend_api_key' => 'Resend API Key'
]; ];
public function mount() public function mount()
{ {
$this->decrypt();
$this->emails = auth()->user()->email; $this->emails = auth()->user()->email;
} }
private function decrypt() public function submitFromFields() {
{ try {
if (data_get($this->settings, 'smtp_password')) { $this->resetErrorBag();
try { $this->validate([
$this->settings->smtp_password = decrypt($this->settings->smtp_password); 'settings.smtp_from_address' => 'required|email',
} catch (\Exception $e) { 'settings.smtp_from_name' => 'required',
} ]);
$this->settings->save();
$this->emit('success', 'Settings saved successfully.');
} catch (\Exception $e) {
return general_error_handler($e, $this);
}
}
public function instantSaveResend()
{
try {
$this->submitResend();
} catch (\Exception $e) {
return general_error_handler($e, $this);
}
}
public function submitResend() {
try {
$this->resetErrorBag();
$this->validate([
'settings.resend_api_key' => 'required'
]);
$this->settings->smtp_enabled = false;
$this->settings->save();
$this->emit('success', 'Settings saved successfully.');
} catch (\Exception $e) {
$this->settings->resend_enabled = false;
return general_error_handler($e, $this);
} }
} }
public function instantSave() public function instantSave()
{ {
try { try {
$this->submit(); $this->submit();
$this->emit('success', 'Settings saved successfully.');
} catch (\Exception $e) { } catch (\Exception $e) {
$this->settings->smtp_enabled = false; return general_error_handler($e, $this);
$this->validate();
} }
} }
public function submit() public function submit()
{ {
$this->resetErrorBag(); try {
$this->validate(); $this->resetErrorBag();
if ($this->settings->smtp_password) { $this->validate([
$this->settings->smtp_password = encrypt($this->settings->smtp_password); 'settings.smtp_host' => 'required',
} else { 'settings.smtp_port' => 'required|numeric',
$this->settings->smtp_password = null; 'settings.smtp_encryption' => 'nullable',
'settings.smtp_username' => 'nullable',
'settings.smtp_password' => 'nullable',
'settings.smtp_timeout' => 'nullable',
]);
$this->settings->resend_enabled = false;
$this->settings->save();
$this->emit('success', 'Settings saved successfully.');
} catch (\Exception $e) {
return general_error_handler($e, $this);
} }
$this->settings->save();
$this->emit('success', 'Transaction email settings updated successfully.');
$this->decrypt();
} }
public function sendTestNotification() public function sendTestNotification()

View File

@@ -13,6 +13,7 @@ class InstanceSettings extends Model implements SendsEmail
protected $guarded = []; protected $guarded = [];
protected $casts = [ protected $casts = [
'resale_license' => 'encrypted', 'resale_license' => 'encrypted',
'smtp_password' => 'encrypted',
]; ];
public static function get() public static function get()

View File

@@ -6,10 +6,10 @@ use Exception;
use Illuminate\Mail\Message; use Illuminate\Mail\Message;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class EmailChannel class EmailChannel
{ {
private bool $isResend = false;
public function send(SendsEmail $notifiable, Notification $notification): void public function send(SendsEmail $notifiable, Notification $notification): void
{ {
$this->bootConfigs($notifiable); $this->bootConfigs($notifiable);
@@ -20,29 +20,52 @@ class EmailChannel
} }
$mailMessage = $notification->toMail($notifiable); $mailMessage = $notification->toMail($notifiable);
Mail::send( if ($this->isResend) {
[], foreach($recepients as $receipient) {
[], Mail::send(
fn (Message $message) => $message [],
->from( [],
data_get($notifiable, 'smtp_from_address'), fn (Message $message) => $message
data_get($notifiable, 'smtp_from_name'), ->from(
) data_get($notifiable, 'smtp_from_address'),
->bcc($recepients) data_get($notifiable, 'smtp_from_name'),
->subject($mailMessage->subject) )
->html((string)$mailMessage->render()) ->to($receipient)
); ->subject($mailMessage->subject)
->html((string)$mailMessage->render())
);
}
} else {
Mail::send(
[],
[],
fn (Message $message) => $message
->from(
data_get($notifiable, 'smtp_from_address'),
data_get($notifiable, 'smtp_from_name'),
)
->bcc($recepients)
->subject($mailMessage->subject)
->html((string)$mailMessage->render())
);
}
} }
private function bootConfigs($notifiable): void private function bootConfigs($notifiable): void
{ {
$password = data_get($notifiable, 'smtp_password'); if (data_get($notifiable, 'resend_enabled')) {
if ($password) $password = decrypt($password); $resendAPIKey = data_get($notifiable, 'resend_api_key');
if ($resendAPIKey) {
if (Str::contains(data_get($notifiable, 'smtp_host'),'resend.com')) { $this->isResend = true;
config()->set('mail.default', 'resend'); config()->set('mail.default', 'resend');
config()->set('resend.api_key', $password); config()->set('resend.api_key', $resendAPIKey);
} else { }
}
if (data_get($notifiable, 'smtp_enabled')) {
$password = data_get($notifiable, 'smtp_password');
if ($password) $password = decrypt($password);
config()->set('mail.default', 'smtp'); config()->set('mail.default', 'smtp');
config()->set('mail.mailers.smtp', [ config()->set('mail.mailers.smtp', [
"transport" => "smtp", "transport" => "smtp",

View File

@@ -17,7 +17,7 @@ class Checkbox extends Component
public string|null $value = null, public string|null $value = null,
public string|null $label = null, public string|null $label = null,
public string|null $helper = null, public string|null $helper = null,
public bool $instantSave = false, public string|bool $instantSave = false,
public bool $disabled = false, public bool $disabled = false,
public string $defaultClass = "toggle toggle-xs toggle-warning rounded disabled:bg-coolgray-200 disabled:opacity-50 placeholder:text-neutral-700" public string $defaultClass = "toggle toggle-xs toggle-warning rounded disabled:bg-coolgray-200 disabled:opacity-50 placeholder:text-neutral-700"
) { ) {

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('instance_settings', function (Blueprint $table) {
$table->boolean('resend_enabled')->default(false);
$table->text('resend_api_key')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('instance_settings', function (Blueprint $table) {
$table->dropColumn('resend_enabled');
$table->dropColumn('resend_api_key');
});
}
};

View File

@@ -10,40 +10,80 @@
<button>close</button> <button>close</button>
</form> </form>
</dialog> </dialog>
<form wire:submit.prevent='submit' class="flex flex-col pb-10"> <div class="flex items-center gap-2">
<div class="flex items-center gap-2"> <h2>Transactional Emails</h2>
<h2>Transactional Emails</h2> </div>
<div class="pb-4 ">SMTP settings for password resets, invitations, etc.</div>
<form wire:submit.prevent='submitFromFields' class="pb-4">
<div class="flex flex-col items-end w-full gap-2 xl:flex-row">
<x-forms.input required id="settings.smtp_from_name" helper="Name used in emails." label="From Name" />
<x-forms.input required id="settings.smtp_from_address" helper="Email address used in emails."
label="From Address" />
<x-forms.button type="submit"> <x-forms.button type="submit">
Save Save
</x-forms.button> </x-forms.button>
@if ($settings->smtp_enabled) @if ($settings->resend_enabled || $settings->smtp_enabled)
<x-forms.button onclick="sendTestEmail.showModal()" <x-forms.button onclick="sendTestEmail.showModal()"
class="text-white normal-case btn btn-xs no-animation btn-primary"> class="text-white normal-case btn btn-xs no-animation btn-primary">
Send Test Email Send Test Email
</x-forms.button> </x-forms.button>
@endif @endif
</div>
<div class="pb-4 ">SMTP settings for password resets, invitations, etc.</div>
<div class="w-32 pb-4">
<x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled" />
</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input required id="settings.smtp_host" placeholder="smtp.mailgun.org" label="Host" />
<x-forms.input required id="settings.smtp_port" placeholder="587" label="Port" />
<x-forms.input id="settings.smtp_encryption" helper="If SMTP uses SSL, set it to 'tls'."
placeholder="tls" label="Encryption" />
</div>
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input id="settings.smtp_username" label="SMTP Username" />
<x-forms.input id="settings.smtp_password" type="password" label="SMTP Password" />
<x-forms.input id="settings.smtp_timeout" helper="Timeout value for sending emails." label="Timeout" />
</div>
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input required id="settings.smtp_from_name" helper="Name used in emails." label="From Name" />
<x-forms.input required id="settings.smtp_from_address" helper="Email address used in emails."
label="From Address" />
</div>
</div> </div>
</form> </form>
<div class="flex flex-col gap-4">
<details class="border rounded collapse border-coolgray-500 collapse-arrow ">
<summary class="text-xl collapse-title">
<div>SMTP Server</div>
<div class="w-32">
<x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled" />
</div>
</summary>
<div class="collapse-content">
<form wire:submit.prevent='submit' class="flex flex-col">
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input required id="settings.smtp_host" placeholder="smtp.mailgun.org"
label="Host" />
<x-forms.input required id="settings.smtp_port" placeholder="587" label="Port" />
<x-forms.input id="settings.smtp_encryption" helper="If SMTP uses SSL, set it to 'tls'."
placeholder="tls" label="Encryption" />
</div>
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input id="settings.smtp_username" label="SMTP Username" />
<x-forms.input id="settings.smtp_password" type="password" label="SMTP Password" />
<x-forms.input id="settings.smtp_timeout" helper="Timeout value for sending emails."
label="Timeout" />
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
</details>
<details class="border rounded collapse border-coolgray-500 collapse-arrow ">
<summary class="text-xl collapse-title">
<div>Resend</div>
<div class="w-32">
<x-forms.checkbox instantSave='instantSaveResend' id="settings.resend_enabled" label="Enabled" />
</div>
</summary>
<div class="collapse-content">
<form wire:submit.prevent='submitResend' class="flex flex-col">
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input type="password" id="settings.resend_api_key" placeholder="API key" label="Host" />
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
</details>
</div>
</div> </div>