Add database notifications (#817)

* add database notifications to all panels

* add successful param to Installed event

* add listener for Installed event

* create event for subuser creation

* add listener for SubUserAdded event

* always send Installed event

* create event for subuser removal

* add listener for SubUserRemoved event

* add prefix to server name

* remove view action from SubUserRemoved notification
This commit is contained in:
Boy132 2024-12-12 14:38:45 +01:00 committed by GitHub
parent eb819032bc
commit d09227659e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 176 additions and 45 deletions

View File

@ -13,5 +13,5 @@ class Installed extends Event
/**
* Create a new event instance.
*/
public function __construct(public Server $server) {}
public function __construct(public Server $server, public bool $successful, public bool $initialInstall) {}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Subuser;
use Illuminate\Queue\SerializesModels;
class SubUserAdded extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Subuser $subuser) {}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use App\Models\User;
use Illuminate\Queue\SerializesModels;
class SubUserRemoved extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server, public User $user) {}
}

View File

@ -35,13 +35,11 @@ class ServerInstallController extends Controller
{
$status = null;
// Make sure the type of failure is accurate
if (!$request->boolean('successful')) {
$status = ServerState::InstallFailed;
$successful = $request->boolean('successful');
if ($request->boolean('reinstall')) {
$status = ServerState::ReinstallFailed;
}
// Make sure the type of failure is accurate
if (!$successful) {
$status = $request->boolean('reinstall') ? ServerState::ReinstallFailed : ServerState::InstallFailed;
}
// Keep the server suspended if it's already suspended
@ -55,16 +53,8 @@ class ServerInstallController extends Controller
$server->installed_at = now();
$server->save();
// If the server successfully installed, fire installed event.
// This logic allows individually disabling install and reinstall notifications separately.
$isInitialInstall = is_null($previouslyInstalledAt);
if ($isInitialInstall && config()->get('panel.email.send_install_notification', true)) {
event(new ServerInstalled($server));
}
if (!$isInitialInstall && config()->get('panel.email.send_reinstall_notification', true)) {
event(new ServerInstalled($server));
}
event(new ServerInstalled($server, $successful, $isInitialInstall));
return new JsonResponse([], Response::HTTP_NO_CONTENT);
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Listeners\Server;
use App\Events\Server\Installed;
use App\Filament\Server\Pages\Console;
use Filament\Notifications\Actions\Action;
use Filament\Notifications\Notification;
class ServerInstalledListener
{
public function handle(Installed $event): void
{
$event->server->loadMissing('user');
Notification::make()
->status($event->successful ? 'success' : 'danger')
->title('Server ' . ($event->initialInstall ? 'Installation' : 'Reinstallation') . ' ' . ($event->successful ? 'completed' : 'failed'))
->body('Server Name: ' . $event->server->name)
->actions([
Action::make('view')
->button()
->label('Open Server')
->markAsRead()
->url(fn () => Console::getUrl(panel: 'server', tenant: $event->server)),
])
->sendToDatabase($event->server->user);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Listeners\Server;
use App\Events\Server\SubUserAdded;
use App\Filament\Server\Pages\Console;
use Filament\Notifications\Actions\Action;
use Filament\Notifications\Notification;
class SubUserAddedListener
{
public function handle(SubUserAdded $event): void
{
$event->subuser->loadMissing('server');
$event->subuser->loadMissing('user');
Notification::make()
->title('Added to Server')
->body('You have been added as a subuser to ' . $event->subuser->server->name . '.')
->actions([
Action::make('view')
->button()
->label('Open Server')
->markAsRead()
->url(fn () => Console::getUrl(panel: 'server', tenant: $event->subuser->server)),
])
->sendToDatabase($event->subuser->user);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Listeners\Server;
use App\Events\Server\SubUserRemoved;
use Filament\Notifications\Notification;
class SubUserRemovedListener
{
public function handle(SubUserRemoved $event): void
{
Notification::make()
->title('Removed from Server')
->body('You have been removed as a subuser from ' . $event->server->name . '.')
->sendToDatabase($event->user);
}
}

View File

@ -2,7 +2,12 @@
namespace App\Notifications;
use App\Events\Server\SubUserAdded;
use App\Models\Server;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Container\Container;
use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
@ -11,14 +16,22 @@ class AddedToServer extends Notification implements ShouldQueue
{
use Queueable;
public object $server;
public Server $server;
public User $user;
/**
* Create a new notification instance.
* Handle a direct call to this notification from the subuser added event. This is configured
* in the event service provider.
*/
public function __construct(array $server)
public function handle(SubUserAdded $event): void
{
$this->server = (object) $server;
$this->server = $event->subuser->server;
$this->user = $event->subuser->user;
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.
Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this);
}
/**
@ -35,7 +48,7 @@ class AddedToServer extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
return (new MailMessage())
->greeting('Hello ' . $this->server->user . '!')
->greeting('Hello ' . $this->user->username . '!')
->line('You have been added as a subuser for the following server, allowing you certain control over the server.')
->line('Server Name: ' . $this->server->name)
->action('Visit Server', url('/server/' . $this->server->uuid_short));

View File

@ -2,7 +2,12 @@
namespace App\Notifications;
use App\Events\Server\SubUserRemoved;
use App\Models\Server;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Container\Container;
use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
@ -11,14 +16,22 @@ class RemovedFromServer extends Notification implements ShouldQueue
{
use Queueable;
public object $server;
public Server $server;
public User $user;
/**
* Create a new notification instance.
* Handle a direct call to this notification from the subuser removed event. This is configured
* in the event service provider.
*/
public function __construct(array $server)
public function handle(SubUserRemoved $event): void
{
$this->server = (object) $server;
$this->server = $event->server;
$this->user = $event->user;
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.
Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this);
}
/**
@ -36,7 +49,7 @@ class RemovedFromServer extends Notification implements ShouldQueue
{
return (new MailMessage())
->error()
->greeting('Hello ' . $this->server->user . '.')
->greeting('Hello ' . $this->user->username . '.')
->line('You have been removed as a subuser for the following server.')
->line('Server Name: ' . $this->server->name)
->action('Visit Panel', route('index'));

View File

@ -4,7 +4,6 @@ namespace App\Notifications;
use App\Models\User;
use Illuminate\Bus\Queueable;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Container\Container;
use App\Events\Server\Installed;
@ -27,14 +26,24 @@ class ServerInstalled extends Notification implements ShouldQueue
*/
public function handle(Installed $event): void
{
$event->server->loadMissing('user');
if ($event->initialInstall && !config()->get('panel.email.send_install_notification', true)) {
return;
}
$this->server = $event->server;
$this->user = $event->server->user;
if (!$event->initialInstall && !config()->get('panel.email.send_reinstall_notification', true)) {
return;
}
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.
Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this);
if ($event->successful) {
$event->server->loadMissing('user');
$this->server = $event->server;
$this->user = $event->server->user;
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.
Container::getInstance()->make(Dispatcher::class)->sendNow($this->user, $this);
}
}
/**

View File

@ -55,6 +55,7 @@ class AdminPanelProvider extends PanelProvider
->spa()
->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources')
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
->databaseNotifications()
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,

View File

@ -46,6 +46,7 @@ class AppPanelProvider extends PanelProvider
->visible(fn (): bool => auth()->user()->canAccessPanel(Filament::getPanel('admin'))),
])
->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\\Filament\\App\\Resources')
->databaseNotifications()
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,

View File

@ -65,6 +65,7 @@ class ServerPanelProvider extends PanelProvider
->discoverResources(in: app_path('Filament/Server/Resources'), for: 'App\\Filament\\Server\\Resources')
->discoverPages(in: app_path('Filament/Server/Pages'), for: 'App\\Filament\\Server\\Pages')
->discoverWidgets(in: app_path('Filament/Server/Widgets'), for: 'App\\Filament\\Server\\Widgets')
->databaseNotifications()
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,

View File

@ -2,8 +2,8 @@
namespace App\Services\Subusers;
use App\Events\Server\SubUserAdded;
use App\Models\User;
use App\Notifications\AddedToServer;
use Illuminate\Support\Str;
use App\Models\Server;
use App\Models\Subuser;
@ -63,11 +63,7 @@ class SubuserCreationService
'permissions' => array_unique($permissions),
]);
$subuser->user->notify(new AddedToServer([
'user' => $subuser->user->name_first,
'name' => $subuser->server->name,
'uuid_short' => $subuser->server->uuid_short,
]));
event(new SubUserAdded($subuser));
return $subuser;
});

View File

@ -2,11 +2,11 @@
namespace App\Services\Subusers;
use App\Events\Server\SubUserRemoved;
use App\Exceptions\Http\Connection\DaemonConnectionException;
use App\Facades\Activity;
use App\Models\Server;
use App\Models\Subuser;
use App\Notifications\RemovedFromServer;
use App\Repositories\Daemon\DaemonServerRepository;
class SubuserDeletionService
@ -25,10 +25,7 @@ class SubuserDeletionService
$log->transaction(function ($instance) use ($server, $subuser) {
$subuser->delete();
$subuser->user->notify(new RemovedFromServer([
'user' => $subuser->user->name_first,
'name' => $subuser->server->name,
]));
event(new SubUserRemoved($subuser->server, $subuser->user));
try {
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);

View File

@ -185,7 +185,7 @@ class ProcessWebhooksTest extends TestCase
$server = $this->createServer();
event(new Installed($server));
event(new Installed($server, true, true));
$this->assertDatabaseCount(Webhook::class, 1);
$this->assertDatabaseHas(Webhook::class, [