fix: Instance email settins

- fix: resend, smtp save button should only save respective settings
- feat: ability to send test email
This commit is contained in:
peaklabs-dev
2024-12-09 14:39:50 +01:00
parent 208766455d
commit 2aacb1dc28
2 changed files with 196 additions and 80 deletions

View File

@@ -3,6 +3,8 @@
namespace App\Livewire;
use App\Models\InstanceSettings;
use App\Notifications\Test;
use Illuminate\Support\Facades\RateLimiter;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -13,6 +15,15 @@ class SettingsEmail extends Component
#[Validate(['boolean'])]
public bool $smtpEnabled = false;
#[Validate(['nullable', 'email'])]
public ?string $smtpFromAddress = null;
#[Validate(['nullable', 'string'])]
public ?string $smtpFromName = null;
#[Validate(['nullable', 'string'])]
public ?string $smtpRecipients = null;
#[Validate(['nullable', 'string'])]
public ?string $smtpHost = null;
@@ -20,29 +31,26 @@ class SettingsEmail extends Component
public ?int $smtpPort = null;
#[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
public ?string $smtpEncryption = null;
public ?string $smtpEncryption = 'tls';
#[Validate(['nullable', 'string'])]
public ?string $smtpUsername = null;
#[Validate(['nullable'])]
#[Validate(['nullable', 'string'])]
public ?string $smtpPassword = null;
#[Validate(['nullable', 'numeric'])]
public ?int $smtpTimeout = null;
#[Validate(['nullable', 'email'])]
public ?string $smtpFromAddress = null;
#[Validate(['nullable', 'string'])]
public ?string $smtpFromName = null;
#[Validate(['boolean'])]
public bool $resendEnabled = false;
#[Validate(['nullable', 'string'])]
public ?string $resendApiKey = null;
#[Validate(['nullable', 'email'])]
public ?string $testEmailAddress = null;
public function mount()
{
if (isInstanceAdmin() === false) {
@@ -90,7 +98,7 @@ class SettingsEmail extends Component
try {
$this->resetErrorBag();
$this->syncData(true);
$this->dispatch('success', 'Settings saved.');
$this->dispatch('success', 'Transactional email settings updated.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -99,19 +107,120 @@ class SettingsEmail extends Component
public function instantSave(string $type)
{
try {
$this->resetErrorBag();
if ($type === 'SMTP') {
$this->resendEnabled = false;
} else {
$this->smtpEnabled = false;
$this->submitSmtp();
} elseif ($type === 'Resend') {
$this->submitResend();
}
$this->syncData(true);
if ($this->smtpEnabled || $this->resendEnabled) {
$this->dispatch('success', "{$type} enabled.");
} else {
$this->dispatch('success', "{$type} disabled.");
} catch (\Throwable $e) {
if ($type === 'SMTP') {
$this->smtpEnabled = false;
} elseif ($type === 'Resend') {
$this->resendEnabled = false;
}
return handleError($e, $this);
}
}
public function submitSmtp(): void
{
$this->validate([
'smtpEnabled' => 'boolean',
'smtpFromAddress' => 'required|email',
'smtpFromName' => 'required|string',
'smtpHost' => 'required|string',
'smtpPort' => 'required|numeric',
'smtpEncryption' => 'required|string|in:tls,ssl,none',
'smtpUsername' => 'nullable|string',
'smtpPassword' => 'nullable|string',
'smtpTimeout' => 'nullable|numeric',
], [
'smtpFromAddress.required' => 'From Address is required.',
'smtpFromAddress.email' => 'Please enter a valid email address.',
'smtpFromName.required' => 'From Name is required.',
'smtpHost.required' => 'SMTP Host is required.',
'smtpPort.required' => 'SMTP Port is required.',
'smtpPort.numeric' => 'SMTP Port must be a number.',
'smtpEncryption.required' => 'Encryption type is required.',
]);
$this->resendEnabled = false;
$this->settings->smtp_enabled = $this->smtpEnabled;
$this->settings->smtp_host = $this->smtpHost;
$this->settings->smtp_port = $this->smtpPort;
$this->settings->smtp_encryption = $this->smtpEncryption;
$this->settings->smtp_username = $this->smtpUsername;
$this->settings->smtp_password = $this->smtpPassword;
$this->settings->smtp_timeout = $this->smtpTimeout;
$this->settings->smtp_from_address = $this->smtpFromAddress;
$this->settings->smtp_from_name = $this->smtpFromName;
$this->settings->resend_enabled = false;
$this->settings->save();
$this->dispatch('success', 'SMTP settings updated.');
}
public function submitResend(): void
{
$this->validate([
'resendEnabled' => 'boolean',
'resendApiKey' => 'required|string',
'smtpFromAddress' => 'required|email',
'smtpFromName' => 'required|string',
], [
'resendApiKey.required' => 'Resend API Key is required.',
'smtpFromAddress.required' => 'From Address is required.',
'smtpFromAddress.email' => 'Please enter a valid email address.',
'smtpFromName.required' => 'From Name is required.',
]);
$this->smtpEnabled = false;
$this->settings->resend_enabled = $this->resendEnabled;
$this->settings->resend_api_key = $this->resendApiKey;
$this->settings->smtp_from_address = $this->smtpFromAddress;
$this->settings->smtp_from_name = $this->smtpFromName;
$this->settings->smtp_enabled = false;
$this->settings->save();
$this->dispatch('success', 'Resend settings updated.');
}
public function sendTestEmail()
{
try {
$this->validate([
'testEmailAddress' => 'required|email',
], [
'testEmailAddress.required' => 'Test email address is required.',
'testEmailAddress.email' => 'Please enter a valid email address.',
]);
$executed = RateLimiter::attempt(
'test-email:'.$this->team->id,
$perMinute = 0,
function () {
$this->team?->notify(new Test($this->testEmailAddress));
$this->dispatch('success', 'Test Email sent.');
},
$decaySeconds = 10,
);
if (! $executed) {
throw new \Exception('Too many messages sent!');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function isEmailEnabled(): bool
{
return $this->settings->smtp_enabled || $this->settings->resend_enabled;
}
}

View File

@@ -1,71 +1,78 @@
<div>
<x-slot:title>
Transactional Email | Coolify
</x-slot>
<x-settings.navbar />
<form wire:submit='submit' class="flex flex-col gap-2 pb-4">
<div class="flex items-center gap-2">
<h2>Transactional Email</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="pb-4 ">Email settings for password resets, invitations, etc.</div>
<div class="flex gap-4">
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
</div>
</form>
<div class="flex flex-col gap-4">
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit='submit' class="flex flex-col">
<div class="flex gap-2">
<h3>SMTP Server</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("SMTP")' id="smtpEnabled" 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="smtpHost" placeholder="smtp.mailgun.org" label="Host" />
<x-forms.input required id="smtpPort" placeholder="587" label="Port" />
<x-forms.select id="smtpEncryption" label="Encryption">
<option value="tls">TLS</option>
<option value="ssl">SSL</option>
<option value="none">None</option>
</x-forms.select>
</x-slot>
<x-settings.navbar />
<form wire:submit='submit' class="flex flex-col gap-2 pb-4">
<div class="flex items-center gap-2">
<h2>Transactional Email</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($this->isEmailEnabled() && auth()->user()->isAdminFromSession())
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
<form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
<x-forms.input wire:model="testEmailAddress" placeholder="test@example.com" id="testEmailAddress" label="Recipients" required />
<x-forms.button type="submit" @click="modalOpen=false">
Send Email
</x-forms.button>
</form>
</x-modal-input>
@endif
</div>
<div class="pb-4">Instance wide email settings for password resets, invitations, etc.</div>
<div class="flex gap-4">
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
</div>
</form>
<div class="flex flex-col gap-4">
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit.prevent="submitSmtp" class="flex flex-col">
<div class="flex gap-2">
<h3>SMTP Server</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input id="smtpUsername" label="SMTP Username" />
<x-forms.input id="smtpPassword" type="password" label="SMTP Password"
autocomplete="new-password" />
<x-forms.input id="smtpTimeout" helper="Timeout value for sending emails." label="Timeout" />
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("SMTP")' id="smtpEnabled" label="Enabled" />
</div>
</div>
</form>
</div>
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit='submit' class="flex flex-col">
<div class="flex gap-2">
<h3>Resend</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("Resend")' id="resendEnabled" 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 type="password" id="resendApiKey" placeholder="API key" required label="API Key"
autocomplete="new-password" />
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input required id="smtpHost" placeholder="smtp.mailgun.org" label="Host" />
<x-forms.input required id="smtpPort" placeholder="587" label="Port" />
<x-forms.select id="smtpEncryption" label="Encryption">
<option value="tls">TLS</option>
<option value="ssl">SSL</option>
<option value="none">None</option>
</x-forms.select>
</div>
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input id="smtpUsername" label="SMTP Username" />
<x-forms.input id="smtpPassword" type="password" label="SMTP Password" autocomplete="new-password" />
<x-forms.input id="smtpTimeout" helper="Timeout value for sending emails." label="Timeout" />
</div>
</div>
</div>
</form>
</form>
</div>
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit.prevent="submitResend" class="flex flex-col">
<div class="flex gap-2">
<h3>Resend</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("Resend")' id="resendEnabled" 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 type="password" id="resendApiKey" placeholder="API key" required label="API Key" autocomplete="new-password" />
</div>
</div>
</form>
</div>
</div>
</div>
</div>