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:
		@@ -3,6 +3,8 @@
 | 
				
			|||||||
namespace App\Livewire;
 | 
					namespace App\Livewire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
 | 
					use App\Notifications\Test;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\RateLimiter;
 | 
				
			||||||
use Livewire\Attributes\Validate;
 | 
					use Livewire\Attributes\Validate;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -13,6 +15,15 @@ class SettingsEmail extends Component
 | 
				
			|||||||
    #[Validate(['boolean'])]
 | 
					    #[Validate(['boolean'])]
 | 
				
			||||||
    public bool $smtpEnabled = false;
 | 
					    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'])]
 | 
					    #[Validate(['nullable', 'string'])]
 | 
				
			||||||
    public ?string $smtpHost = null;
 | 
					    public ?string $smtpHost = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,29 +31,26 @@ class SettingsEmail extends Component
 | 
				
			|||||||
    public ?int $smtpPort = null;
 | 
					    public ?int $smtpPort = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
 | 
					    #[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
 | 
				
			||||||
    public ?string $smtpEncryption = null;
 | 
					    public ?string $smtpEncryption = 'tls';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['nullable', 'string'])]
 | 
					    #[Validate(['nullable', 'string'])]
 | 
				
			||||||
    public ?string $smtpUsername = null;
 | 
					    public ?string $smtpUsername = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['nullable'])]
 | 
					    #[Validate(['nullable', 'string'])]
 | 
				
			||||||
    public ?string $smtpPassword = null;
 | 
					    public ?string $smtpPassword = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['nullable', 'numeric'])]
 | 
					    #[Validate(['nullable', 'numeric'])]
 | 
				
			||||||
    public ?int $smtpTimeout = null;
 | 
					    public ?int $smtpTimeout = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['nullable', 'email'])]
 | 
					 | 
				
			||||||
    public ?string $smtpFromAddress = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[Validate(['nullable', 'string'])]
 | 
					 | 
				
			||||||
    public ?string $smtpFromName = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[Validate(['boolean'])]
 | 
					    #[Validate(['boolean'])]
 | 
				
			||||||
    public bool $resendEnabled = false;
 | 
					    public bool $resendEnabled = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['nullable', 'string'])]
 | 
					    #[Validate(['nullable', 'string'])]
 | 
				
			||||||
    public ?string $resendApiKey = null;
 | 
					    public ?string $resendApiKey = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[Validate(['nullable', 'email'])]
 | 
				
			||||||
 | 
					    public ?string $testEmailAddress = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (isInstanceAdmin() === false) {
 | 
					        if (isInstanceAdmin() === false) {
 | 
				
			||||||
@@ -90,7 +98,7 @@ class SettingsEmail extends Component
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $this->resetErrorBag();
 | 
					            $this->resetErrorBag();
 | 
				
			||||||
            $this->syncData(true);
 | 
					            $this->syncData(true);
 | 
				
			||||||
            $this->dispatch('success', 'Settings saved.');
 | 
					            $this->dispatch('success', 'Transactional email settings updated.');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -99,19 +107,120 @@ class SettingsEmail extends Component
 | 
				
			|||||||
    public function instantSave(string $type)
 | 
					    public function instantSave(string $type)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					            $this->resetErrorBag();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($type === 'SMTP') {
 | 
					            if ($type === 'SMTP') {
 | 
				
			||||||
                $this->resendEnabled = false;
 | 
					                $this->submitSmtp();
 | 
				
			||||||
            } else {
 | 
					            } elseif ($type === 'Resend') {
 | 
				
			||||||
                $this->smtpEnabled = false;
 | 
					                $this->submitResend();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $this->syncData(true);
 | 
					
 | 
				
			||||||
            if ($this->smtpEnabled || $this->resendEnabled) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
                $this->dispatch('success', "{$type} enabled.");
 | 
					            if ($type === 'SMTP') {
 | 
				
			||||||
            } else {
 | 
					                $this->smtpEnabled = false;
 | 
				
			||||||
                $this->dispatch('success', "{$type} disabled.");
 | 
					            } 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) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function isEmailEnabled(): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->settings->smtp_enabled || $this->settings->resend_enabled;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,17 +9,26 @@
 | 
				
			|||||||
                <x-forms.button type="submit">
 | 
					                <x-forms.button type="submit">
 | 
				
			||||||
                    Save
 | 
					                    Save
 | 
				
			||||||
                </x-forms.button>
 | 
					                </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>
 | 
				
			||||||
        <div class="pb-4 ">Email settings for password resets, invitations, etc.</div>
 | 
					            <div class="pb-4">Instance wide email settings for password resets, invitations, etc.</div>
 | 
				
			||||||
            <div class="flex gap-4">
 | 
					            <div class="flex gap-4">
 | 
				
			||||||
                <x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
 | 
					                <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" />
 | 
					                <x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
        <div class="flex flex-col gap-4">
 | 
					        <div class="flex flex-col gap-4">
 | 
				
			||||||
            <div class="p-4 border dark:border-coolgray-300">
 | 
					            <div class="p-4 border dark:border-coolgray-300">
 | 
				
			||||||
            <form wire:submit='submit' class="flex flex-col">
 | 
					                <form wire:submit.prevent="submitSmtp" class="flex flex-col">
 | 
				
			||||||
                    <div class="flex gap-2">
 | 
					                    <div class="flex gap-2">
 | 
				
			||||||
                        <h3>SMTP Server</h3>
 | 
					                        <h3>SMTP Server</h3>
 | 
				
			||||||
                        <x-forms.button type="submit">
 | 
					                        <x-forms.button type="submit">
 | 
				
			||||||
@@ -41,15 +50,14 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
					                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
				
			||||||
                            <x-forms.input id="smtpUsername" label="SMTP Username" />
 | 
					                            <x-forms.input id="smtpUsername" label="SMTP Username" />
 | 
				
			||||||
                        <x-forms.input id="smtpPassword" type="password" label="SMTP Password"
 | 
					                            <x-forms.input id="smtpPassword" type="password" label="SMTP Password" autocomplete="new-password" />
 | 
				
			||||||
                            autocomplete="new-password" />
 | 
					 | 
				
			||||||
                            <x-forms.input id="smtpTimeout" helper="Timeout value for sending emails." label="Timeout" />
 | 
					                            <x-forms.input id="smtpTimeout" helper="Timeout value for sending emails." label="Timeout" />
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </form>
 | 
					                </form>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="p-4 border dark:border-coolgray-300">
 | 
					            <div class="p-4 border dark:border-coolgray-300">
 | 
				
			||||||
            <form wire:submit='submit' class="flex flex-col">
 | 
					                <form wire:submit.prevent="submitResend" class="flex flex-col">
 | 
				
			||||||
                    <div class="flex gap-2">
 | 
					                    <div class="flex gap-2">
 | 
				
			||||||
                        <h3>Resend</h3>
 | 
					                        <h3>Resend</h3>
 | 
				
			||||||
                        <x-forms.button type="submit">
 | 
					                        <x-forms.button type="submit">
 | 
				
			||||||
@@ -61,8 +69,7 @@
 | 
				
			|||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="flex flex-col gap-4">
 | 
					                    <div class="flex flex-col gap-4">
 | 
				
			||||||
                        <div class="flex flex-col w-full gap-2 xl:flex-row">
 | 
					                        <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"
 | 
					                            <x-forms.input type="password" id="resendApiKey" placeholder="API key" required label="API Key" autocomplete="new-password" />
 | 
				
			||||||
                            autocomplete="new-password" />
 | 
					 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </form>
 | 
					                </form>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user