feat(mail): add MailSetting & DynamicMailConfigServiceProvider to load mail config from DB
This commit is contained in:
162
app/Filament/Pages/MailSettings.php
Normal file
162
app/Filament/Pages/MailSettings.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use BackedEnum;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Inerba\DbConfig\AbstractPageSettings;
|
||||
use Filament\Schemas\Components;
|
||||
use Filament\Schemas\Schema;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||
|
||||
class MailSettings extends AbstractPageSettings
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed> | null
|
||||
*/
|
||||
public ?array $data = [];
|
||||
|
||||
protected static ?string $title = 'Mail';
|
||||
|
||||
protected static string | BackedEnum | null $navigationIcon = 'heroicon-o-envelope-open'; // Uncomment if you want to set a custom navigation icon
|
||||
|
||||
// protected ?string $subheading = ''; // Uncomment if you want to set a custom subheading
|
||||
|
||||
// protected static ?string $slug = 'mail-settings'; // Uncomment if you want to set a custom slug
|
||||
|
||||
protected string $view = 'filament.pages.mail-settings';
|
||||
|
||||
protected function settingName(): string
|
||||
{
|
||||
return 'mail';
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default values.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'mail_driver' => 'smtp',
|
||||
'mail_encryption' => 'tls',
|
||||
];
|
||||
}
|
||||
|
||||
public function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('test_connection')
|
||||
->label('Test Connection')
|
||||
->color('gray')
|
||||
->icon('heroicon-o-paper-airplane')
|
||||
->action(fn() => $this->testSMTPConnection()),
|
||||
|
||||
...parent::getHeaderActions(),
|
||||
];
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Components\Section::make('Mail Settings')->schema([
|
||||
TextInput::make('mail_driver')
|
||||
->label('Mail Driver')
|
||||
->placeholder('smtp')
|
||||
->required(),
|
||||
TextInput::make('mail_host')
|
||||
->label('Mail Host')
|
||||
->placeholder('mail.example.com')
|
||||
->required(),
|
||||
TextInput::make('mail_port')
|
||||
->label('Mail Port')
|
||||
->integer()
|
||||
->minValue(1)
|
||||
->maxValue(65535)
|
||||
->placeholder('587')
|
||||
->required(),
|
||||
TextInput::make('mail_username')
|
||||
->label('Mail Username')
|
||||
->placeholder('username')
|
||||
->required(),
|
||||
TextInput::make('mail_password')
|
||||
->label('Mail Password')
|
||||
->placeholder('password')
|
||||
->required(),
|
||||
Select::make('mail_encryption')
|
||||
->label('Mail Encryption')
|
||||
->options([
|
||||
'tls' => 'TLS',
|
||||
'ssl' => 'SSL'
|
||||
])
|
||||
->native(false)
|
||||
->required(),
|
||||
|
||||
TextInput::make('mail_from_name')
|
||||
->label('Mail From Name')
|
||||
->placeholder('John Doe')
|
||||
->required(),
|
||||
TextInput::make('mail_from_address')
|
||||
->label('Mail From Address')
|
||||
->placeholder('john@example.com')
|
||||
->required(),
|
||||
]),
|
||||
])
|
||||
->statePath('data');
|
||||
}
|
||||
|
||||
private function testSMTPConnection(): void
|
||||
{
|
||||
$settings = $this->data;
|
||||
|
||||
// Required keys for testing connection
|
||||
$requiredFields = [
|
||||
'mail_host',
|
||||
'mail_port',
|
||||
'mail_username',
|
||||
'mail_password',
|
||||
'mail_encryption',
|
||||
];
|
||||
|
||||
// Check for missing or empty required fields
|
||||
$missingFields = collect($requiredFields)->filter(function ($field) use ($settings) {
|
||||
return empty($settings[$field]);
|
||||
});
|
||||
|
||||
if ($missingFields->isNotEmpty()) {
|
||||
Notification::make()
|
||||
->title('Missing required mail settings: ' . $missingFields->implode(', '))
|
||||
->danger()
|
||||
->send();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$transport = new \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
|
||||
$settings['mail_host'],
|
||||
(int) $settings['mail_port'],
|
||||
strtolower($settings['mail_encryption']) === 'ssl'
|
||||
);
|
||||
|
||||
$transport->setUsername($settings['mail_username']);
|
||||
$transport->setPassword($settings['mail_password']);
|
||||
|
||||
$transport->start(); // Attempt connection
|
||||
|
||||
Notification::make()
|
||||
->title('SMTP connection successful!')
|
||||
->success()
|
||||
->send();
|
||||
} catch (\Exception $e) {
|
||||
Notification::make()
|
||||
->title('SMTP connection failed: ' . $e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
app/Providers/DynamicMailConfigServiceProvider.php
Normal file
93
app/Providers/DynamicMailConfigServiceProvider.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Helper\ArrayHelper;
|
||||
use Config;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Inerba\DbConfig\DbConfig;
|
||||
use Log;
|
||||
|
||||
class DynamicMailConfigServiceProvider extends ServiceProvider
|
||||
{
|
||||
private array $mailConfig;
|
||||
|
||||
// Mail configuration properties
|
||||
private string $mailDriver;
|
||||
private ?string $mailScheme;
|
||||
private string $mailHost;
|
||||
private string $mailPort;
|
||||
private ?string $mailUsername;
|
||||
private ?string $mailPassword;
|
||||
private string $mailFromAddress;
|
||||
private string $mailFromName;
|
||||
private int $mailTimeout;
|
||||
private string $mailEHLO;
|
||||
private string $appUrl;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->loadConfiguration();
|
||||
|
||||
Config::set('mail.default', $this->mailDriver ?? config('mail.default'));
|
||||
|
||||
Config::set('mail.mailers.smtp', [
|
||||
'transport' => $this->mailDriver,
|
||||
'scheme' => $this->mailScheme,
|
||||
'host' => $this->mailHost,
|
||||
'port' => $this->mailPort,
|
||||
'username' => $this->mailUsername,
|
||||
'password' => $this->mailPassword,
|
||||
'timeout' => $this->mailTimeout,
|
||||
'local_domain' => $this->mailEHLO,
|
||||
]);
|
||||
|
||||
Config::set('mail.from', [
|
||||
'address' => $this->mailFromAddress,
|
||||
'name' => $this->mailFromName,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
private function loadConfiguration(): void
|
||||
{
|
||||
try {
|
||||
$this->mailConfig = DbConfig::getGroup('mail') ?: [];
|
||||
$this->mailConfig['app_url'] = db_config('website.site_url') ?? env('APP_URL', 'http://localhost');
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Database mail configuration unavailable; falling back to env values.');
|
||||
$this->mailConfig = ['app_url' => env('APP_URL', 'http://localhost')];
|
||||
}
|
||||
|
||||
// Load mail configuration
|
||||
$this->appUrl = $this->getConfig('app_url');
|
||||
$this->mailDriver = $this->getConfig('mail_driver', 'smtp');
|
||||
$this->mailScheme = $this->getConfig('mail_scheme', env('MAIL_SCHEME'));
|
||||
$this->mailHost = $this->getConfig('mail_host', env('MAIL_HOST', '127.0.0.1'));
|
||||
$this->mailPort = $this->getConfig('mail_port', env('MAIL_PORT', 2525));
|
||||
$this->mailUsername = $this->getConfig('mail_username', env('MAIL_USERNAME', 'username'));
|
||||
$this->mailPassword = $this->getConfig('mail_password', env('MAIL_PASSWORD', 'password'));
|
||||
$this->mailFromName = $this->getConfig('mail_from_name', env('MAIL_FROM_NAME', 'Example'));
|
||||
$this->mailFromAddress = $this->getConfig('mail_from_address', env('MAIL_FROM_ADDRESS', 'hello@example.com'));
|
||||
$this->mailTimeout = $this->getConfig('mail_timeout', env('MAIL_TIMEOUT', 15));
|
||||
$this->mailEHLO = $this->getConfig('mail_ehlo', env('MAIL_EHLO', parse_url((string) $this->appUrl, PHP_URL_HOST)));
|
||||
}
|
||||
|
||||
private function getConfig(string $key, $default = null)
|
||||
{
|
||||
return ArrayHelper::getValueFromArray($key, $this->mailConfig) ?? $default;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\DynamicMailConfigServiceProvider::class,
|
||||
App\Providers\Filament\DashPanelProvider::class,
|
||||
App\Providers\FortifyServiceProvider::class,
|
||||
];
|
||||
|
||||
8
resources/views/filament/pages/mail-settings.blade.php
Normal file
8
resources/views/filament/pages/mail-settings.blade.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<x-filament-panels::page>
|
||||
<form wire:submit="save" class="fi-page-content">
|
||||
{{ $this->form }}
|
||||
<small class="text-success">
|
||||
Last update: {{ $this->lastUpdatedAt(timezone: 'UTC', format: 'F j, Y, H:i:s') . ' UTC' ?? 'Never' }}
|
||||
</small>
|
||||
</form>
|
||||
</x-filament-panels::page>
|
||||
Reference in New Issue
Block a user