mirror of
https://github.com/alexjustesen/speedtest-tracker.git
synced 2026-06-23 04:30:09 +00:00
Refactored database, mail and webhook notifications (#2439)
Co-authored-by: Alex Justesen <1144087+alexjustesen@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Notifications;
|
namespace App\Actions\Notifications;
|
||||||
|
|
||||||
use App\Mail\Test as TestMail;
|
use App\Mail\TestMail;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ use App\Actions\Notifications\SendSlackTestNotification;
|
|||||||
use App\Actions\Notifications\SendTelegramTestNotification;
|
use App\Actions\Notifications\SendTelegramTestNotification;
|
||||||
use App\Actions\Notifications\SendWebhookTestNotification;
|
use App\Actions\Notifications\SendWebhookTestNotification;
|
||||||
use App\Settings\NotificationSettings;
|
use App\Settings\NotificationSettings;
|
||||||
|
use CodeWithDennis\SimpleAlert\Components\SimpleAlert;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Forms\Components\Checkbox;
|
||||||
use Filament\Forms\Components\Repeater;
|
use Filament\Forms\Components\Repeater;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
@@ -22,13 +24,16 @@ use Filament\Schemas\Components\Actions;
|
|||||||
use Filament\Schemas\Components\Fieldset;
|
use Filament\Schemas\Components\Fieldset;
|
||||||
use Filament\Schemas\Components\Grid;
|
use Filament\Schemas\Components\Grid;
|
||||||
use Filament\Schemas\Components\Section;
|
use Filament\Schemas\Components\Section;
|
||||||
|
use Filament\Schemas\Components\Tabs;
|
||||||
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
use Filament\Schemas\Components\Utilities\Get;
|
use Filament\Schemas\Components\Utilities\Get;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class Notification extends SettingsPage
|
class Notification extends SettingsPage
|
||||||
{
|
{
|
||||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-bell';
|
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-bell-alert';
|
||||||
|
|
||||||
protected static string|\UnitEnum|null $navigationGroup = 'Settings';
|
protected static string|\UnitEnum|null $navigationGroup = 'Settings';
|
||||||
|
|
||||||
@@ -60,6 +65,135 @@ class Notification extends SettingsPage
|
|||||||
{
|
{
|
||||||
return $schema
|
return $schema
|
||||||
->components([
|
->components([
|
||||||
|
Tabs::make()
|
||||||
|
->schema([
|
||||||
|
Tab::make(__('settings/notifications.database'))
|
||||||
|
->icon(Heroicon::OutlinedCircleStack)
|
||||||
|
->schema([
|
||||||
|
Toggle::make('database_enabled')
|
||||||
|
->label(__('general.enable'))
|
||||||
|
->live(),
|
||||||
|
|
||||||
|
Grid::make([
|
||||||
|
'default' => 1,
|
||||||
|
])
|
||||||
|
->hidden(fn (Get $get) => $get('database_enabled') !== true)
|
||||||
|
->schema([
|
||||||
|
Fieldset::make(__('settings.triggers'))
|
||||||
|
->columns(1)
|
||||||
|
->schema([
|
||||||
|
Checkbox::make('database_on_speedtest_run')
|
||||||
|
->label(__('settings/notifications.database_on_speedtest_run')),
|
||||||
|
|
||||||
|
Checkbox::make('database_on_threshold_failure')
|
||||||
|
->label(__('settings/notifications.database_on_threshold_failure')),
|
||||||
|
]),
|
||||||
|
|
||||||
|
Actions::make([
|
||||||
|
Action::make('test database')
|
||||||
|
->label(__('settings/notifications.test_database_channel'))
|
||||||
|
->action(fn () => SendDatabaseTestNotification::run(user: Auth::user())),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
// ...
|
||||||
|
]),
|
||||||
|
|
||||||
|
Tab::make(__('settings/notifications.mail'))
|
||||||
|
->icon(Heroicon::OutlinedEnvelope)
|
||||||
|
->schema([
|
||||||
|
Toggle::make('mail_enabled')
|
||||||
|
->label(__('general.enable'))
|
||||||
|
->live(),
|
||||||
|
|
||||||
|
Grid::make([
|
||||||
|
'default' => 1,
|
||||||
|
])
|
||||||
|
->hidden(fn (Get $get) => $get('mail_enabled') !== true)
|
||||||
|
->schema([
|
||||||
|
Fieldset::make(__('settings.triggers'))
|
||||||
|
->columns(1)
|
||||||
|
->schema([
|
||||||
|
Checkbox::make('mail_on_speedtest_run')
|
||||||
|
->label(__('settings/notifications.mail_on_speedtest_run')),
|
||||||
|
|
||||||
|
Checkbox::make('mail_on_threshold_failure')
|
||||||
|
->label(__('settings/notifications.mail_on_threshold_failure')),
|
||||||
|
]),
|
||||||
|
|
||||||
|
Repeater::make('mail_recipients')
|
||||||
|
->label(__('settings/notifications.recipients'))
|
||||||
|
->schema([
|
||||||
|
TextInput::make('email_address')
|
||||||
|
->placeholder('your@email.com')
|
||||||
|
->email()
|
||||||
|
->required(),
|
||||||
|
]),
|
||||||
|
|
||||||
|
Actions::make([
|
||||||
|
Action::make('test mail')
|
||||||
|
->label(__('settings/notifications.test_mail_channel'))
|
||||||
|
->action(fn (Get $get) => SendMailTestNotification::run(recipients: $get('mail_recipients')))
|
||||||
|
->hidden(fn (Get $get) => ! count($get('mail_recipients'))),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
// ...
|
||||||
|
]),
|
||||||
|
|
||||||
|
Tab::make(__('settings/notifications.webhook'))
|
||||||
|
->icon(Heroicon::OutlinedGlobeAlt)
|
||||||
|
->schema([
|
||||||
|
Toggle::make('webhook_enabled')
|
||||||
|
->label(__('general.enable'))
|
||||||
|
->live(),
|
||||||
|
|
||||||
|
Grid::make([
|
||||||
|
'default' => 1,
|
||||||
|
])
|
||||||
|
->hidden(fn (Get $get) => $get('webhook_enabled') !== true)
|
||||||
|
->schema([
|
||||||
|
Fieldset::make(__('settings.triggers'))
|
||||||
|
->columns(1)
|
||||||
|
->schema([
|
||||||
|
Checkbox::make('webhook_on_speedtest_run')
|
||||||
|
->label(__('settings/notifications.webhook_on_speedtest_run')),
|
||||||
|
|
||||||
|
Checkbox::make('webhook_on_threshold_failure')
|
||||||
|
->label(__('settings/notifications.webhook_on_threshold_failure')),
|
||||||
|
]),
|
||||||
|
|
||||||
|
Repeater::make('webhook_urls')
|
||||||
|
->label(__('settings/notifications.recipients'))
|
||||||
|
->schema([
|
||||||
|
TextInput::make('url')
|
||||||
|
->placeholder('https://webhook.site/longstringofcharacters')
|
||||||
|
->maxLength(2000)
|
||||||
|
->required()
|
||||||
|
->url(),
|
||||||
|
]),
|
||||||
|
|
||||||
|
Actions::make([
|
||||||
|
Action::make('test webhook')
|
||||||
|
->label(__('settings/notifications.test_webhook_channel'))
|
||||||
|
->action(fn (Get $get) => SendWebhookTestNotification::run(webhooks: $get('webhook_urls')))
|
||||||
|
->hidden(fn (Get $get) => ! count($get('webhook_urls'))),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
// ...
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->columnSpanFull(),
|
||||||
|
|
||||||
|
// ! DEPRECATED CHANNELS
|
||||||
|
SimpleAlert::make('deprecation_warning')
|
||||||
|
->title('Deprecated Notification Channels')
|
||||||
|
->description('The following notification channels are deprecated and will be removed in a future release!')
|
||||||
|
->border()
|
||||||
|
->warning()
|
||||||
|
->columnSpanFull(),
|
||||||
|
|
||||||
Grid::make([
|
Grid::make([
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
'md' => 3,
|
'md' => 3,
|
||||||
@@ -70,118 +204,6 @@ class Notification extends SettingsPage
|
|||||||
'default' => 1,
|
'default' => 1,
|
||||||
])
|
])
|
||||||
->schema([
|
->schema([
|
||||||
Section::make(__('settings/notifications.database'))
|
|
||||||
->description(__('settings/notifications.database_description'))
|
|
||||||
->schema([
|
|
||||||
Toggle::make('database_enabled')
|
|
||||||
->label(__('settings/notifications.enable_database_notifications'))
|
|
||||||
->reactive()
|
|
||||||
->columnSpanFull(),
|
|
||||||
Grid::make([
|
|
||||||
'default' => 1,
|
|
||||||
])
|
|
||||||
->hidden(fn (Get $get) => $get('database_enabled') !== true)
|
|
||||||
->schema([
|
|
||||||
Fieldset::make(__('settings.triggers'))
|
|
||||||
->schema([
|
|
||||||
Toggle::make('database_on_speedtest_run')
|
|
||||||
->label(__('settings/notifications.database_on_speedtest_run'))
|
|
||||||
->columnSpanFull(),
|
|
||||||
Toggle::make('database_on_threshold_failure')
|
|
||||||
->label(__('settings/notifications.database_on_threshold_failure'))
|
|
||||||
->columnSpanFull(),
|
|
||||||
]),
|
|
||||||
Actions::make([
|
|
||||||
Action::make('test database')
|
|
||||||
->label(__('settings/notifications.test_database_channel'))
|
|
||||||
->action(fn () => SendDatabaseTestNotification::run(user: Auth::user())),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
->compact()
|
|
||||||
->columnSpan('full'),
|
|
||||||
|
|
||||||
Section::make(__('settings/notifications.mail'))
|
|
||||||
->schema([
|
|
||||||
Toggle::make('mail_enabled')
|
|
||||||
->label(__('settings/notifications.enable_mail_notifications'))
|
|
||||||
->reactive()
|
|
||||||
->columnSpanFull(),
|
|
||||||
Grid::make([
|
|
||||||
'default' => 1,
|
|
||||||
])
|
|
||||||
->hidden(fn (Get $get) => $get('mail_enabled') !== true)
|
|
||||||
->schema([
|
|
||||||
Fieldset::make(__('settings.triggers'))
|
|
||||||
->schema([
|
|
||||||
Toggle::make('mail_on_speedtest_run')
|
|
||||||
->label(__('settings/notifications.mail_on_speedtest_run'))
|
|
||||||
->columnSpanFull(),
|
|
||||||
Toggle::make('mail_on_threshold_failure')
|
|
||||||
->label(__('settings/notifications.mail_on_threshold_failure'))
|
|
||||||
->columnSpanFull(),
|
|
||||||
]),
|
|
||||||
Repeater::make('mail_recipients')
|
|
||||||
->label(__('settings/notifications.recipients'))
|
|
||||||
->schema([
|
|
||||||
TextInput::make('email_address')
|
|
||||||
->placeholder('your@email.com')
|
|
||||||
->email()
|
|
||||||
->required(),
|
|
||||||
])
|
|
||||||
->columnSpanFull(),
|
|
||||||
Actions::make([
|
|
||||||
Action::make('test mail')
|
|
||||||
->label(__('settings/notifications.test_mail_channel'))
|
|
||||||
->action(fn (Get $get) => SendMailTestNotification::run(recipients: $get('mail_recipients')))
|
|
||||||
->hidden(fn (Get $get) => ! count($get('mail_recipients'))),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
->compact()
|
|
||||||
->columnSpan('full'),
|
|
||||||
|
|
||||||
Section::make(__('settings/notifications.webhook'))
|
|
||||||
->schema([
|
|
||||||
Toggle::make('webhook_enabled')
|
|
||||||
->label(__('settings/notifications.enable_webhook_notifications'))
|
|
||||||
->reactive()
|
|
||||||
->columnSpanFull(),
|
|
||||||
Grid::make([
|
|
||||||
'default' => 1,
|
|
||||||
])
|
|
||||||
->hidden(fn (Get $get) => $get('webhook_enabled') !== true)
|
|
||||||
->schema([
|
|
||||||
Fieldset::make(__('settings.triggers'))
|
|
||||||
->schema([
|
|
||||||
Toggle::make('webhook_on_speedtest_run')
|
|
||||||
->label(__('settings/notifications.webhook_on_speedtest_run'))
|
|
||||||
->columnSpan(2),
|
|
||||||
Toggle::make('webhook_on_threshold_failure')
|
|
||||||
->label(__('settings/notifications.webhook_on_threshold_failure'))
|
|
||||||
->columnSpan(2),
|
|
||||||
]),
|
|
||||||
Repeater::make('webhook_urls')
|
|
||||||
->label(__('settings/notifications.recipients'))
|
|
||||||
->schema([
|
|
||||||
TextInput::make('url')
|
|
||||||
->placeholder('https://webhook.site/longstringofcharacters')
|
|
||||||
->maxLength(2000)
|
|
||||||
->required()
|
|
||||||
->url(),
|
|
||||||
])
|
|
||||||
->columnSpanFull(),
|
|
||||||
Actions::make([
|
|
||||||
Action::make('test webhook')
|
|
||||||
->label(__('settings/notifications.test_webhook_channel'))
|
|
||||||
->action(fn (Get $get) => SendWebhookTestNotification::run(webhooks: $get('webhook_urls')))
|
|
||||||
->hidden(fn (Get $get) => ! count($get('webhook_urls'))),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
->compact()
|
|
||||||
->columnSpan('full'),
|
|
||||||
|
|
||||||
Section::make('Pushover')
|
Section::make('Pushover')
|
||||||
->description('⚠️ Pushover is deprecated and will be removed in a future release.')
|
->description('⚠️ Pushover is deprecated and will be removed in a future release.')
|
||||||
->schema([
|
->schema([
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners\Database;
|
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Settings\NotificationSettings;
|
|
||||||
use Filament\Notifications\Notification;
|
|
||||||
|
|
||||||
class SendSpeedtestCompletedNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*/
|
|
||||||
public function handle(SpeedtestCompleted $event): void
|
|
||||||
{
|
|
||||||
$notificationSettings = new NotificationSettings;
|
|
||||||
|
|
||||||
if (! $notificationSettings->database_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $notificationSettings->database_on_speedtest_run) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (User::all() as $user) {
|
|
||||||
Notification::make()
|
|
||||||
->title(__('results.speedtest_completed'))
|
|
||||||
->success()
|
|
||||||
->sendToDatabase($user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners\Database;
|
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
|
||||||
use App\Helpers\Number;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Settings\NotificationSettings;
|
|
||||||
use App\Settings\ThresholdSettings;
|
|
||||||
use Filament\Notifications\Notification;
|
|
||||||
|
|
||||||
class SendSpeedtestThresholdNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*/
|
|
||||||
public function handle(SpeedtestCompleted $event): void
|
|
||||||
{
|
|
||||||
$notificationSettings = new NotificationSettings;
|
|
||||||
|
|
||||||
if (! $notificationSettings->database_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $notificationSettings->database_on_threshold_failure) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$thresholdSettings = new ThresholdSettings;
|
|
||||||
|
|
||||||
if (! $thresholdSettings->absolute_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_download > 0) {
|
|
||||||
$this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_upload > 0) {
|
|
||||||
$this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_ping > 0) {
|
|
||||||
$this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send database notification if absolute download threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): void
|
|
||||||
{
|
|
||||||
if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (User::all() as $user) {
|
|
||||||
Notification::make()
|
|
||||||
->title(__('results.download_threshold_breached'))
|
|
||||||
->body('Speedtest #'.$event->result->id.' breached the download threshold of '.$thresholdSettings->absolute_download.' Mbps at '.Number::toBitRate($event->result->download_bits).'.')
|
|
||||||
->warning()
|
|
||||||
->sendToDatabase($user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send database notification if absolute upload threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): void
|
|
||||||
{
|
|
||||||
if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (User::all() as $user) {
|
|
||||||
Notification::make()
|
|
||||||
->title(__('results.upload_threshold_breached'))
|
|
||||||
->body('Speedtest #'.$event->result->id.' breached the upload threshold of '.$thresholdSettings->absolute_upload.' Mbps at '.Number::toBitRate($event->result->upload_bits).'.')
|
|
||||||
->warning()
|
|
||||||
->sendToDatabase($user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send database notification if absolute upload threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): void
|
|
||||||
{
|
|
||||||
if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (User::all() as $user) {
|
|
||||||
Notification::make()
|
|
||||||
->title(__('results.ping_threshold_breached'))
|
|
||||||
->body('Speedtest #'.$event->result->id.' breached the ping threshold of '.$thresholdSettings->absolute_ping.'ms at '.$event->result->ping.'ms.')
|
|
||||||
->warning()
|
|
||||||
->sendToDatabase($user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners\Mail;
|
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
|
||||||
use App\Mail\SpeedtestCompletedMail;
|
|
||||||
use App\Settings\NotificationSettings;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
|
|
||||||
class SendSpeedtestCompletedNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*/
|
|
||||||
public function handle(SpeedtestCompleted $event): void
|
|
||||||
{
|
|
||||||
$notificationSettings = new NotificationSettings;
|
|
||||||
|
|
||||||
if (! $notificationSettings->mail_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $notificationSettings->mail_on_speedtest_run) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! count($notificationSettings->mail_recipients)) {
|
|
||||||
Log::warning('Mail recipients not found, check mail notification channel settings.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($notificationSettings->mail_recipients as $recipient) {
|
|
||||||
Mail::to($recipient)
|
|
||||||
->send(new SpeedtestCompletedMail($event->result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners\Mail;
|
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
|
||||||
use App\Helpers\Number;
|
|
||||||
use App\Mail\SpeedtestThresholdMail;
|
|
||||||
use App\Settings\NotificationSettings;
|
|
||||||
use App\Settings\ThresholdSettings;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
|
|
||||||
class SendSpeedtestThresholdNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*/
|
|
||||||
public function handle(SpeedtestCompleted $event): void
|
|
||||||
{
|
|
||||||
$notificationSettings = new NotificationSettings;
|
|
||||||
|
|
||||||
if (! $notificationSettings->mail_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $notificationSettings->mail_on_threshold_failure) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! count($notificationSettings->mail_recipients) > 0) {
|
|
||||||
Log::warning('Mail recipients not found, check mail notification channel settings.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$thresholdSettings = new ThresholdSettings;
|
|
||||||
|
|
||||||
if (! $thresholdSettings->absolute_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$failed = [];
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_download > 0) {
|
|
||||||
array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_upload > 0) {
|
|
||||||
array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_ping > 0) {
|
|
||||||
array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
$failed = array_filter($failed);
|
|
||||||
|
|
||||||
if (! count($failed)) {
|
|
||||||
Log::warning('Failed mail thresholds not found, won\'t send notification.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($notificationSettings->mail_recipients as $recipient) {
|
|
||||||
Mail::to($recipient)
|
|
||||||
->send(new SpeedtestThresholdMail($event->result, $failed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build mail notification if absolute download threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
|
|
||||||
{
|
|
||||||
if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'name' => 'Download',
|
|
||||||
'threshold' => $thresholdSettings->absolute_download.' Mbps',
|
|
||||||
'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build mail notification if absolute upload threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
|
|
||||||
{
|
|
||||||
if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'name' => 'Upload',
|
|
||||||
'threshold' => $thresholdSettings->absolute_upload.' Mbps',
|
|
||||||
'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build mail notification if absolute ping threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
|
|
||||||
{
|
|
||||||
if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'name' => 'Ping',
|
|
||||||
'threshold' => $thresholdSettings->absolute_ping.' ms',
|
|
||||||
'value' => round($event->result->ping, 2).' ms',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,13 +3,23 @@
|
|||||||
namespace App\Listeners;
|
namespace App\Listeners;
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
use App\Events\SpeedtestCompleted;
|
||||||
|
use App\Mail\CompletedSpeedtestMail;
|
||||||
use App\Models\Result;
|
use App\Models\Result;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Settings\NotificationSettings;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Spatie\WebhookServer\WebhookCall;
|
||||||
|
|
||||||
class ProcessCompletedSpeedtest
|
class ProcessCompletedSpeedtest
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
public NotificationSettings $notificationSettings,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the event.
|
* Handle the event.
|
||||||
*/
|
*/
|
||||||
@@ -17,8 +27,53 @@ class ProcessCompletedSpeedtest
|
|||||||
{
|
{
|
||||||
$result = $event->result;
|
$result = $event->result;
|
||||||
|
|
||||||
if ($result->dispatched_by && ! $result->scheduled) {
|
$result->loadMissing(['dispatchedBy']);
|
||||||
$this->notifyDispatchingUser($result);
|
|
||||||
|
// $this->notifyAppriseChannels($result);
|
||||||
|
$this->notifyDatabaseChannels($result);
|
||||||
|
$this->notifyDispatchingUser($result);
|
||||||
|
$this->notifyMailChannels($result);
|
||||||
|
$this->notifyWebhookChannels($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify Apprise channels.
|
||||||
|
*/
|
||||||
|
private function notifyAppriseChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send Apprise notification if dispatched by a user or test is unhealthy.
|
||||||
|
if (filled($result->dispatched_by) || ! $result->healthy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify database channels.
|
||||||
|
*/
|
||||||
|
private function notifyDatabaseChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send database notification if dispatched by a user or test is unhealthy.
|
||||||
|
if (filled($result->dispatched_by) || ! $result->healthy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if database notifications are enabled.
|
||||||
|
if (! $this->notificationSettings->database_enabled || ! $this->notificationSettings->database_on_speedtest_run) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (User::all() as $user) {
|
||||||
|
Notification::make()
|
||||||
|
->title(__('results.speedtest_completed'))
|
||||||
|
->actions([
|
||||||
|
Action::make('view')
|
||||||
|
->label(__('general.view'))
|
||||||
|
->url(route('filament.admin.resources.results.index')),
|
||||||
|
])
|
||||||
|
->success()
|
||||||
|
->sendToDatabase($user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +82,11 @@ class ProcessCompletedSpeedtest
|
|||||||
*/
|
*/
|
||||||
private function notifyDispatchingUser(Result $result): void
|
private function notifyDispatchingUser(Result $result): void
|
||||||
{
|
{
|
||||||
$user = User::find($result->dispatched_by);
|
if (empty($result->dispatched_by) || ! $result->healthy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$user->notify(
|
$result->dispatchedBy->notify(
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title(__('results.speedtest_completed'))
|
->title(__('results.speedtest_completed'))
|
||||||
->actions([
|
->actions([
|
||||||
@@ -41,4 +98,72 @@ class ProcessCompletedSpeedtest
|
|||||||
->toDatabase(),
|
->toDatabase(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify mail channels.
|
||||||
|
*/
|
||||||
|
private function notifyMailChannels(Result $result): void
|
||||||
|
{
|
||||||
|
if (empty($result->dispatched_by) || ! $result->healthy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->notificationSettings->mail_enabled || ! $this->notificationSettings->mail_on_speedtest_run) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! count($this->notificationSettings->mail_recipients)) {
|
||||||
|
Log::warning('Mail recipients not found, check mail notification channel settings.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->notificationSettings->mail_recipients as $recipient) {
|
||||||
|
Mail::to($recipient)
|
||||||
|
->send(new CompletedSpeedtestMail($result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify webhook channels.
|
||||||
|
*/
|
||||||
|
private function notifyWebhookChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send webhook if dispatched by a user or test is unhealthy.
|
||||||
|
if (filled($result->dispatched_by) || ! $result->healthy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if webhook notifications are enabled.
|
||||||
|
if (! $this->notificationSettings->webhook_enabled || ! $this->notificationSettings->webhook_on_speedtest_run) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if webhook urls are configured.
|
||||||
|
if (! count($this->notificationSettings->webhook_urls)) {
|
||||||
|
Log::warning('Webhook urls not found, check webhook notification channel settings.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->notificationSettings->webhook_urls as $url) {
|
||||||
|
WebhookCall::create()
|
||||||
|
->url($url['url'])
|
||||||
|
->payload([
|
||||||
|
'result_id' => $result->id,
|
||||||
|
'site_name' => config('app.name'),
|
||||||
|
'server_name' => Arr::get($result->data, 'server.name'),
|
||||||
|
'server_id' => Arr::get($result->data, 'server.id'),
|
||||||
|
'isp' => Arr::get($result->data, 'isp'),
|
||||||
|
'ping' => $result->ping,
|
||||||
|
'download' => $result->downloadBits,
|
||||||
|
'upload' => $result->uploadBits,
|
||||||
|
'packet_loss' => Arr::get($result->data, 'packetLoss'),
|
||||||
|
'speedtest_url' => Arr::get($result->data, 'result.url'),
|
||||||
|
'url' => url('/admin/results'),
|
||||||
|
])
|
||||||
|
->doNotSign()
|
||||||
|
->dispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace App\Listeners;
|
|||||||
|
|
||||||
use App\Events\SpeedtestFailed;
|
use App\Events\SpeedtestFailed;
|
||||||
use App\Models\Result;
|
use App\Models\Result;
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
|
|
||||||
@@ -17,9 +16,23 @@ class ProcessFailedSpeedtest
|
|||||||
{
|
{
|
||||||
$result = $event->result;
|
$result = $event->result;
|
||||||
|
|
||||||
if ($result->dispatched_by && ! $result->scheduled) {
|
$result->loadMissing(['dispatchedBy']);
|
||||||
$this->notifyDispatchingUser($result);
|
|
||||||
|
// $this->notifyAppriseChannels($result);
|
||||||
|
$this->notifyDispatchingUser($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify Apprise channels.
|
||||||
|
*/
|
||||||
|
private function notifyAppriseChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send Apprise notification if dispatched by a user or test is unhealthy.
|
||||||
|
if (filled($result->dispatched_by) || ! $result->healthy) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,9 +40,11 @@ class ProcessFailedSpeedtest
|
|||||||
*/
|
*/
|
||||||
private function notifyDispatchingUser(Result $result): void
|
private function notifyDispatchingUser(Result $result): void
|
||||||
{
|
{
|
||||||
$user = User::find($result->dispatched_by);
|
if (empty($result->dispatched_by)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$user->notify(
|
$result->dispatchedBy->notify(
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title(__('results.speedtest_failed'))
|
->title(__('results.speedtest_failed'))
|
||||||
->actions([
|
->actions([
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Events\SpeedtestBenchmarkFailed;
|
||||||
|
use App\Mail\UnhealthySpeedtestMail;
|
||||||
|
use App\Models\Result;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Settings\NotificationSettings;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Spatie\WebhookServer\WebhookCall;
|
||||||
|
|
||||||
|
class ProcessUnhealthySpeedtest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public NotificationSettings $notificationSettings,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*/
|
||||||
|
public function handle(SpeedtestBenchmarkFailed $event): void
|
||||||
|
{
|
||||||
|
$result = $event->result;
|
||||||
|
|
||||||
|
$result->loadMissing(['dispatchedBy']);
|
||||||
|
|
||||||
|
// $this->notifyAppriseChannels($result);
|
||||||
|
$this->notifyDatabaseChannels($result);
|
||||||
|
$this->notifyDispatchingUser($result);
|
||||||
|
$this->notifyMailChannels($result);
|
||||||
|
$this->notifyWebhookChannels($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify Apprise channels.
|
||||||
|
*/
|
||||||
|
private function notifyAppriseChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send Apprise notification if dispatched by a user.
|
||||||
|
if (filled($result->dispatched_by)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify database channels.
|
||||||
|
*/
|
||||||
|
private function notifyDatabaseChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send database notification if dispatched by a user.
|
||||||
|
if (filled($result->dispatched_by)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if database notifications are enabled.
|
||||||
|
if (! $this->notificationSettings->database_enabled || ! $this->notificationSettings->database_on_threshold_failure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (User::all() as $user) {
|
||||||
|
Notification::make()
|
||||||
|
->title(__('results.speedtest_benchmark_failed'))
|
||||||
|
->actions([
|
||||||
|
Action::make('view')
|
||||||
|
->label(__('general.view'))
|
||||||
|
->url(route('filament.admin.resources.results.index')),
|
||||||
|
])
|
||||||
|
->success()
|
||||||
|
->sendToDatabase($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the user who dispatched the speedtest.
|
||||||
|
*/
|
||||||
|
private function notifyDispatchingUser(Result $result): void
|
||||||
|
{
|
||||||
|
if (empty($result->dispatched_by)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result->dispatchedBy->notify(
|
||||||
|
Notification::make()
|
||||||
|
->title(__('results.speedtest_benchmark_failed'))
|
||||||
|
->actions([
|
||||||
|
Action::make('view')
|
||||||
|
->label(__('general.view'))
|
||||||
|
->url(route('filament.admin.resources.results.index')),
|
||||||
|
])
|
||||||
|
->warning()
|
||||||
|
->toDatabase(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify mail channels.
|
||||||
|
*/
|
||||||
|
private function notifyMailChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send webhook if dispatched by a user.
|
||||||
|
if (filled($result->dispatched_by)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mail notifications are enabled.
|
||||||
|
if (! $this->notificationSettings->mail_enabled || ! $this->notificationSettings->mail_on_threshold_failure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mail recipients are configured.
|
||||||
|
if (! count($this->notificationSettings->mail_recipients)) {
|
||||||
|
Log::warning('Mail recipients not found, check mail notification channel settings.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->notificationSettings->mail_recipients as $recipient) {
|
||||||
|
Mail::to($recipient)
|
||||||
|
->send(new UnhealthySpeedtestMail($result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify webhook channels.
|
||||||
|
*/
|
||||||
|
private function notifyWebhookChannels(Result $result): void
|
||||||
|
{
|
||||||
|
// Don't send webhook if dispatched by a user.
|
||||||
|
if (filled($result->dispatched_by)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if webhook notifications are enabled.
|
||||||
|
if (! $this->notificationSettings->webhook_enabled || ! $this->notificationSettings->webhook_on_threshold_failure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if webhook urls are configured.
|
||||||
|
if (! count($this->notificationSettings->webhook_urls)) {
|
||||||
|
Log::warning('Webhook urls not found, check webhook notification channel settings.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->notificationSettings->webhook_urls as $url) {
|
||||||
|
WebhookCall::create()
|
||||||
|
->url($url['url'])
|
||||||
|
->payload([
|
||||||
|
'result_id' => $result->id,
|
||||||
|
'site_name' => config('app.name'),
|
||||||
|
'isp' => $result->isp,
|
||||||
|
'benchmarks' => $result->benchmarks,
|
||||||
|
'speedtest_url' => $result->result_url,
|
||||||
|
'url' => url('/admin/results'),
|
||||||
|
])
|
||||||
|
->doNotSign()
|
||||||
|
->dispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners\Webhook;
|
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
|
||||||
use App\Settings\NotificationSettings;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Spatie\WebhookServer\WebhookCall;
|
|
||||||
|
|
||||||
class SendSpeedtestCompletedNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*/
|
|
||||||
public function handle(SpeedtestCompleted $event): void
|
|
||||||
{
|
|
||||||
$notificationSettings = new NotificationSettings;
|
|
||||||
|
|
||||||
if (! $notificationSettings->webhook_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $notificationSettings->webhook_on_speedtest_run) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! count($notificationSettings->webhook_urls)) {
|
|
||||||
Log::warning('Webhook urls not found, check webhook notification channel settings.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($notificationSettings->webhook_urls as $url) {
|
|
||||||
WebhookCall::create()
|
|
||||||
->url($url['url'])
|
|
||||||
->payload([
|
|
||||||
'result_id' => $event->result->id,
|
|
||||||
'site_name' => config('app.name'),
|
|
||||||
'server_name' => Arr::get($event->result->data, 'server.name'),
|
|
||||||
'server_id' => Arr::get($event->result->data, 'server.id'),
|
|
||||||
'isp' => Arr::get($event->result->data, 'isp'),
|
|
||||||
'ping' => $event->result->ping,
|
|
||||||
'download' => $event->result->downloadBits,
|
|
||||||
'upload' => $event->result->uploadBits,
|
|
||||||
'packet_loss' => Arr::get($event->result->data, 'packetLoss'),
|
|
||||||
'speedtest_url' => Arr::get($event->result->data, 'result.url'),
|
|
||||||
'url' => url('/admin/results'),
|
|
||||||
])
|
|
||||||
->doNotSign()
|
|
||||||
->dispatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners\Webhook;
|
|
||||||
|
|
||||||
use App\Events\SpeedtestCompleted;
|
|
||||||
use App\Helpers\Number;
|
|
||||||
use App\Settings\NotificationSettings;
|
|
||||||
use App\Settings\ThresholdSettings;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Spatie\WebhookServer\WebhookCall;
|
|
||||||
|
|
||||||
class SendSpeedtestThresholdNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*/
|
|
||||||
public function handle(SpeedtestCompleted $event): void
|
|
||||||
{
|
|
||||||
$notificationSettings = new NotificationSettings;
|
|
||||||
|
|
||||||
if (! $notificationSettings->webhook_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $notificationSettings->webhook_on_threshold_failure) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! count($notificationSettings->webhook_urls)) {
|
|
||||||
Log::warning('Webhook urls not found, check webhook notification channel settings.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$thresholdSettings = new ThresholdSettings;
|
|
||||||
|
|
||||||
if (! $thresholdSettings->absolute_enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$failed = [];
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_download > 0) {
|
|
||||||
array_push($failed, $this->absoluteDownloadThreshold(event: $event, thresholdSettings: $thresholdSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_upload > 0) {
|
|
||||||
array_push($failed, $this->absoluteUploadThreshold(event: $event, thresholdSettings: $thresholdSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($thresholdSettings->absolute_ping > 0) {
|
|
||||||
array_push($failed, $this->absolutePingThreshold(event: $event, thresholdSettings: $thresholdSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
$failed = array_filter($failed);
|
|
||||||
|
|
||||||
if (! count($failed)) {
|
|
||||||
Log::warning('Failed webhook thresholds not found, won\'t send notification.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($notificationSettings->webhook_urls as $url) {
|
|
||||||
WebhookCall::create()
|
|
||||||
->url($url['url'])
|
|
||||||
->payload([
|
|
||||||
'result_id' => $event->result->id,
|
|
||||||
'site_name' => config('app.name'),
|
|
||||||
'isp' => $event->result->isp,
|
|
||||||
'metrics' => $failed,
|
|
||||||
'speedtest_url' => $event->result->result_url,
|
|
||||||
'url' => url('/admin/results'),
|
|
||||||
])
|
|
||||||
->doNotSign()
|
|
||||||
->dispatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build webhook notification if absolute download threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absoluteDownloadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
|
|
||||||
{
|
|
||||||
if (! absoluteDownloadThresholdFailed($thresholdSettings->absolute_download, $event->result->download)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'name' => 'Download',
|
|
||||||
'threshold' => $thresholdSettings->absolute_download.' Mbps',
|
|
||||||
'value' => Number::toBitRate(bits: $event->result->download_bits, precision: 2),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build webhook notification if absolute upload threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absoluteUploadThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
|
|
||||||
{
|
|
||||||
if (! absoluteUploadThresholdFailed($thresholdSettings->absolute_upload, $event->result->upload)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'name' => 'Upload',
|
|
||||||
'threshold' => $thresholdSettings->absolute_upload.' Mbps',
|
|
||||||
'value' => Number::toBitRate(bits: $event->result->upload_bits, precision: 2),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build webhook notification if absolute ping threshold is breached.
|
|
||||||
*/
|
|
||||||
protected function absolutePingThreshold(SpeedtestCompleted $event, ThresholdSettings $thresholdSettings): bool|array
|
|
||||||
{
|
|
||||||
if (! absolutePingThresholdFailed($thresholdSettings->absolute_ping, $event->result->ping)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'name' => 'Ping',
|
|
||||||
'threshold' => $thresholdSettings->absolute_ping.' ms',
|
|
||||||
'value' => round($event->result->ping, 2).' ms',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ use Illuminate\Mail\Mailables\Envelope;
|
|||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class SpeedtestCompletedMail extends Mailable implements ShouldQueue
|
class CompletedSpeedtestMail extends Mailable implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class SpeedtestCompletedMail extends Mailable implements ShouldQueue
|
|||||||
public function content(): Content
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
markdown: 'emails.speedtest-completed',
|
markdown: 'mail.speedtest.completed',
|
||||||
with: [
|
with: [
|
||||||
'id' => $this->result->id,
|
'id' => $this->result->id,
|
||||||
'service' => Str::title($this->result->service->getLabel()),
|
'service' => Str::title($this->result->service->getLabel()),
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Models\Result;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Mail\Mailables\Content;
|
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class SpeedtestThresholdMail extends Mailable implements ShouldQueue
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
public Result $result,
|
|
||||||
public array $metrics,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the message envelope.
|
|
||||||
*/
|
|
||||||
public function envelope(): Envelope
|
|
||||||
{
|
|
||||||
return new Envelope(
|
|
||||||
subject: 'Speedtest Threshold Breached - #'.$this->result->id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the message content definition.
|
|
||||||
*/
|
|
||||||
public function content(): Content
|
|
||||||
{
|
|
||||||
return new Content(
|
|
||||||
markdown: 'emails.speedtest-threshold',
|
|
||||||
with: [
|
|
||||||
'id' => $this->result->id,
|
|
||||||
'service' => Str::title($this->result->service->getLabel()),
|
|
||||||
'serverName' => $this->result->server_name,
|
|
||||||
'serverId' => $this->result->server_id,
|
|
||||||
'isp' => $this->result->isp,
|
|
||||||
'speedtest_url' => $this->result->result_url,
|
|
||||||
'url' => url('/admin/results'),
|
|
||||||
'metrics' => $this->metrics,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ use Illuminate\Mail\Mailables\Content;
|
|||||||
use Illuminate\Mail\Mailables\Envelope;
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class Test extends Mailable implements ShouldQueue
|
class TestMail extends Mailable implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ class Test extends Mailable implements ShouldQueue
|
|||||||
public function content(): Content
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
markdown: 'emails.test',
|
markdown: 'mail.test',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use App\Helpers\Number;
|
||||||
|
use App\Models\Result;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class UnhealthySpeedtestMail extends Mailable implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public Result $result,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message envelope.
|
||||||
|
*/
|
||||||
|
public function envelope(): Envelope
|
||||||
|
{
|
||||||
|
return new Envelope(
|
||||||
|
subject: 'Speedtest Threshold Breached - #'.$this->result->id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message content definition.
|
||||||
|
*/
|
||||||
|
public function content(): Content
|
||||||
|
{
|
||||||
|
$benchmarks = [];
|
||||||
|
|
||||||
|
foreach ($this->result->benchmarks as $metric => $benchmark) {
|
||||||
|
$benchmarks[] = $this->formatBenchmark($metric, $benchmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Content(
|
||||||
|
markdown: 'mail.speedtest.unhealthy',
|
||||||
|
with: [
|
||||||
|
'id' => $this->result->id,
|
||||||
|
'service' => str($this->result->service->getLabel())->title(),
|
||||||
|
'isp' => $this->result->isp,
|
||||||
|
'url' => url('/admin/results'),
|
||||||
|
'benchmarks' => $benchmarks,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a benchmark for display in the email.
|
||||||
|
*/
|
||||||
|
private function formatBenchmark(string $metric, array $benchmark): array
|
||||||
|
{
|
||||||
|
$metricName = str($metric)->title();
|
||||||
|
$type = str($benchmark['type'])->title();
|
||||||
|
$thresholdValue = $benchmark['value'].' '.str($benchmark['unit'])->title();
|
||||||
|
|
||||||
|
// Get the actual result value
|
||||||
|
$resultValue = match ($metric) {
|
||||||
|
'download' => Number::toBitRate($this->result->download_bits, 2),
|
||||||
|
'upload' => Number::toBitRate($this->result->upload_bits, 2),
|
||||||
|
'ping' => round(Number::castToType($this->result->ping, 'float'), 2).' ms',
|
||||||
|
default => 'N/A',
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
'metric' => $metricName,
|
||||||
|
'type' => $type,
|
||||||
|
'threshold_value' => $thresholdValue,
|
||||||
|
'result_value' => $resultValue,
|
||||||
|
'passed' => $benchmark['passed'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
->colors([
|
->colors([
|
||||||
'primary' => Color::Amber,
|
'primary' => Color::Amber,
|
||||||
])
|
])
|
||||||
|
->viteTheme('resources/css/filament/admin/theme.css')
|
||||||
->favicon(asset('img/speedtest-tracker-icon.png'))
|
->favicon(asset('img/speedtest-tracker-icon.png'))
|
||||||
->sidebarCollapsibleOnDesktop()
|
->sidebarCollapsibleOnDesktop()
|
||||||
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||||
@@ -40,6 +41,7 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||||
->widgets([])
|
->widgets([])
|
||||||
->databaseNotifications()
|
->databaseNotifications()
|
||||||
|
->databaseNotificationsPolling('5s')
|
||||||
->maxContentWidth(config('speedtest.content_width'))
|
->maxContentWidth(config('speedtest.content_width'))
|
||||||
->middleware([
|
->middleware([
|
||||||
EncryptCookies::class,
|
EncryptCookies::class,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"chrisullyott/php-filesize": "^4.2.1",
|
"chrisullyott/php-filesize": "^4.2.1",
|
||||||
|
"codewithdennis/filament-simple-alert": "^4.0.2",
|
||||||
"dragonmantank/cron-expression": "^3.6.0",
|
"dragonmantank/cron-expression": "^3.6.0",
|
||||||
"filament/filament": "4.1.0",
|
"filament/filament": "4.1.0",
|
||||||
"filament/spatie-laravel-settings-plugin": "^4.1",
|
"filament/spatie-laravel-settings-plugin": "^4.1",
|
||||||
|
|||||||
Generated
+75
-2
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "39020dcee9d9965e781ef550aca663ac",
|
"content-hash": "3aff9923fe99afc6088082ec8c3be834",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "anourvalar/eloquent-serialize",
|
"name": "anourvalar/eloquent-serialize",
|
||||||
@@ -625,6 +625,79 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-12-20T15:40:13+00:00"
|
"time": "2023-12-20T15:40:13+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "codewithdennis/filament-simple-alert",
|
||||||
|
"version": "v4.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/CodeWithDennis/filament-simple-alert.git",
|
||||||
|
"reference": "d30b0cad908f3ade1bed153d486fd564ac312ffd"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/CodeWithDennis/filament-simple-alert/zipball/d30b0cad908f3ade1bed153d486fd564ac312ffd",
|
||||||
|
"reference": "d30b0cad908f3ade1bed153d486fd564ac312ffd",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"filament/filament": "^4.0",
|
||||||
|
"php": "^8.1",
|
||||||
|
"spatie/laravel-package-tools": "^1.15.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/pint": "^1.16",
|
||||||
|
"nunomaduro/collision": "^7.9",
|
||||||
|
"orchestra/testbench": "^8.0",
|
||||||
|
"pestphp/pest": "^2.1",
|
||||||
|
"pestphp/pest-plugin-arch": "^2.0",
|
||||||
|
"pestphp/pest-plugin-laravel": "^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"aliases": {
|
||||||
|
"SimpleAlert": "CodeWithDennis\\SimpleAlert\\Facades\\SimpleAlert"
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"CodeWithDennis\\SimpleAlert\\SimpleAlertServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"CodeWithDennis\\SimpleAlert\\": "src/",
|
||||||
|
"CodeWithDennis\\SimpleAlert\\Database\\Factories\\": "database/factories/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "CodeWithDennis",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A plugin for adding straightforward alerts to your filament pages",
|
||||||
|
"homepage": "https://github.com/codewithdennis/filament-simple-alert",
|
||||||
|
"keywords": [
|
||||||
|
"CodeWithDennis",
|
||||||
|
"filament-simple-alert",
|
||||||
|
"laravel"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/codewithdennis/filament-simple-alert/issues",
|
||||||
|
"source": "https://github.com/codewithdennis/filament-simple-alert"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/CodeWithDennis",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-06-21T18:43:06+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "danharrin/date-format-converter",
|
"name": "danharrin/date-format-converter",
|
||||||
"version": "v0.3.1",
|
"version": "v0.3.1",
|
||||||
@@ -13714,5 +13787,5 @@
|
|||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.4"
|
"php": "8.4"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.9.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ return [
|
|||||||
'view_on_speedtest_net' => 'View on Speedtest.net',
|
'view_on_speedtest_net' => 'View on Speedtest.net',
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
|
'speedtest_benchmark_passed' => 'Speedtest benchmark passed',
|
||||||
|
'speedtest_benchmark_failed' => 'Speedtest benchmark failed',
|
||||||
'speedtest_started' => 'Speedtest started',
|
'speedtest_started' => 'Speedtest started',
|
||||||
'speedtest_completed' => 'Speedtest completed',
|
'speedtest_completed' => 'Speedtest completed',
|
||||||
'speedtest_failed' => 'Speedtest failed',
|
'speedtest_failed' => 'Speedtest failed',
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ return [
|
|||||||
// Database notifications
|
// Database notifications
|
||||||
'database' => 'Database',
|
'database' => 'Database',
|
||||||
'database_description' => 'Notifications sent to this channel will show up under the 🔔 icon in the header.',
|
'database_description' => 'Notifications sent to this channel will show up under the 🔔 icon in the header.',
|
||||||
'enable_database_notifications' => 'Enable database notifications',
|
|
||||||
'database_on_speedtest_run' => 'Notify on every speedtest run',
|
'database_on_speedtest_run' => 'Notify on every speedtest run',
|
||||||
'database_on_threshold_failure' => 'Notify on threshold failures',
|
'database_on_threshold_failure' => 'Notify on threshold failures',
|
||||||
'test_database_channel' => 'Test database channel',
|
'test_database_channel' => 'Test database channel',
|
||||||
|
|
||||||
// Mail notifications
|
// Mail notifications
|
||||||
'mail' => 'Mail',
|
'mail' => 'Mail',
|
||||||
'enable_mail_notifications' => 'Enable mail notifications',
|
|
||||||
'recipients' => 'Recipients',
|
'recipients' => 'Recipients',
|
||||||
'mail_on_speedtest_run' => 'Notify on every speedtest run',
|
'mail_on_speedtest_run' => 'Notify on every speedtest run',
|
||||||
'mail_on_threshold_failure' => 'Notify on threshold failures',
|
'mail_on_threshold_failure' => 'Notify on threshold failures',
|
||||||
@@ -23,7 +21,6 @@ return [
|
|||||||
// Webhook
|
// Webhook
|
||||||
'webhook' => 'Webhook',
|
'webhook' => 'Webhook',
|
||||||
'webhooks' => 'Webhooks',
|
'webhooks' => 'Webhooks',
|
||||||
'enable_webhook_notifications' => 'Enable webhook notifications',
|
|
||||||
'webhook_on_speedtest_run' => 'Notify on every speedtest run',
|
'webhook_on_speedtest_run' => 'Notify on every speedtest run',
|
||||||
'webhook_on_threshold_failure' => 'Notify on threshold failures',
|
'webhook_on_threshold_failure' => 'Notify on threshold failures',
|
||||||
'test_webhook_channel' => 'Test webhook channel',
|
'test_webhook_channel' => 'Test webhook channel',
|
||||||
|
|||||||
Generated
+2
-2
@@ -6,10 +6,10 @@
|
|||||||
"": {
|
"": {
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"autoprefixer": "^10.4.15",
|
"autoprefixer": "^10.4.15",
|
||||||
"laravel-vite-plugin": "^1.0.0",
|
"laravel-vite-plugin": "^1.0.0",
|
||||||
"tailwindcss": "^4.1.16",
|
"tailwindcss": "^4.1.17",
|
||||||
"vite": "^6.4.1"
|
"vite": "^6.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -7,10 +7,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"autoprefixer": "^10.4.15",
|
"autoprefixer": "^10.4.15",
|
||||||
"laravel-vite-plugin": "^1.0.0",
|
"laravel-vite-plugin": "^1.0.0",
|
||||||
"tailwindcss": "^4.1.16",
|
"tailwindcss": "^4.1.17",
|
||||||
"vite": "^6.4.1"
|
"vite": "^6.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
@import '../../../../vendor/filament/filament/resources/css/theme.css';
|
||||||
|
|
||||||
|
@source '../../../../app/Filament/**/*';
|
||||||
|
@source '../../../../resources/views/filament/**/*';
|
||||||
|
|
||||||
|
/* Filament Plugins */
|
||||||
|
@source '../../../../vendor/codewithdennis/filament-simple-alert/resources/**/*.blade.php';
|
||||||
|
@source inline('animate-{spin,pulse,bounce}');
|
||||||
|
|
||||||
|
/* Additional styles */
|
||||||
.fi-topbar #dashboardAction .fi-btn-label,
|
.fi-topbar #dashboardAction .fi-btn-label,
|
||||||
.fi-topbar #speedtestAction .fi-btn-label {
|
.fi-topbar #speedtestAction .fi-btn-label {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<x-mail::message>
|
|
||||||
# Speedtest Threshold Breached - #{{ $id }}
|
|
||||||
|
|
||||||
A new speedtest was completed using **{{ $service }}** on **{{ $isp }}** but a threshold was breached.
|
|
||||||
|
|
||||||
<x-mail::table>
|
|
||||||
| **Metric** | **Threshold** | **Value** |
|
|
||||||
|:-----------|:--------------|----------:|
|
|
||||||
@foreach ($metrics as $item)
|
|
||||||
| {{ $item['name'] }} | {{ $item['threshold'] }} | {{ $item['value'] }} |
|
|
||||||
@endforeach
|
|
||||||
</x-mail::table>
|
|
||||||
|
|
||||||
<x-mail::button :url="$url">
|
|
||||||
View Results
|
|
||||||
</x-mail::button>
|
|
||||||
|
|
||||||
<x-mail::button :url="$speedtest_url">
|
|
||||||
View Results on Ookla
|
|
||||||
</x-mail::button>
|
|
||||||
|
|
||||||
Thanks,<br>
|
|
||||||
{{ config('app.name') }}
|
|
||||||
</x-mail::message>
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<x-mail::message>
|
||||||
|
# Speedtest Threshold Breached - #{{ $id }}
|
||||||
|
|
||||||
|
A new speedtest was completed using **{{ $service }}** on **{{ $isp }}** but a threshold was breached.
|
||||||
|
|
||||||
|
<x-mail::table>
|
||||||
|
| **Metric** | **Type** | **Threshold Value** | **Result Value** | **Status** |
|
||||||
|
|:-----------|:---------|:--------------------|:-----------------|:---------:|
|
||||||
|
@foreach ($benchmarks as $benchmark)
|
||||||
|
| {{ $benchmark['metric'] }} | {{ $benchmark['type'] }} | {{ $benchmark['threshold_value'] }} | {{ $benchmark['result_value'] }} | {{ $benchmark['passed'] ? '✅' : '❌' }} |
|
||||||
|
@endforeach
|
||||||
|
</x-mail::table>
|
||||||
|
|
||||||
|
<x-mail::button :url="$url">
|
||||||
|
{{ __('general.view') }}
|
||||||
|
</x-mail::button>
|
||||||
|
</x-mail::message>
|
||||||
+1
-1
@@ -7,7 +7,7 @@ import tailwindcss from "@tailwindcss/vite";
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
laravel({
|
laravel({
|
||||||
input: ['resources/css/app.css'],
|
input: ['resources/css/app.css', 'resources/css/filament/admin/theme.css'],
|
||||||
refresh: [`resources/views/**/*`],
|
refresh: [`resources/views/**/*`],
|
||||||
}),
|
}),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|||||||
Reference in New Issue
Block a user