Fix notifications and document setup
This commit is contained in:
@@ -172,7 +172,7 @@ class Waf extends Page implements HasForms, HasTable
|
|||||||
$entries = [];
|
$entries = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->auditEntries = $this->markWhitelisted($entries);
|
$this->auditEntries = $this->normalizeAuditEntries($this->markWhitelisted($entries));
|
||||||
$this->auditLoaded = true;
|
$this->auditLoaded = true;
|
||||||
|
|
||||||
if ($notify) {
|
if ($notify) {
|
||||||
@@ -254,6 +254,21 @@ class Waf extends Page implements HasForms, HasTable
|
|||||||
return $entries;
|
return $entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function normalizeAuditEntries(array $entries): array
|
||||||
|
{
|
||||||
|
return array_map(function (array $entry): array {
|
||||||
|
$entry['__key'] = md5(implode('|', [
|
||||||
|
(string) ($entry['timestamp'] ?? ''),
|
||||||
|
(string) ($entry['rule_id'] ?? ''),
|
||||||
|
(string) ($entry['uri'] ?? ''),
|
||||||
|
(string) ($entry['remote_ip'] ?? ''),
|
||||||
|
(string) ($entry['host'] ?? ''),
|
||||||
|
]));
|
||||||
|
|
||||||
|
return $entry;
|
||||||
|
}, $entries);
|
||||||
|
}
|
||||||
|
|
||||||
protected function matchesWhitelist(array $entry, array $rules): bool
|
protected function matchesWhitelist(array $entry, array $rules): bool
|
||||||
{
|
{
|
||||||
$ruleId = (string) ($entry['rule_id'] ?? '');
|
$ruleId = (string) ($entry['rule_id'] ?? '');
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use Filament\Forms\Components\TextInput;
|
|||||||
use Filament\Forms\Concerns\InteractsWithForms;
|
use Filament\Forms\Concerns\InteractsWithForms;
|
||||||
use Filament\Forms\Contracts\HasForms;
|
use Filament\Forms\Contracts\HasForms;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
|
use Filament\Support\Contracts\TranslatableContentDriver;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Concerns\InteractsWithTable;
|
use Filament\Tables\Concerns\InteractsWithTable;
|
||||||
use Filament\Tables\Contracts\HasTable;
|
use Filament\Tables\Contracts\HasTable;
|
||||||
@@ -40,6 +41,11 @@ class WafWhitelistTable extends Component implements HasTable, HasForms, HasActi
|
|||||||
return view('livewire.admin.waf-whitelist-table');
|
return view('livewire.admin.waf-whitelist-table');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function makeFilamentTranslatableContentDriver(): ?TranslatableContentDriver
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function table(Table $table): Table
|
public function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table
|
return $table
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace App\Providers;
|
|||||||
|
|
||||||
use App\Models\Domain;
|
use App\Models\Domain;
|
||||||
use App\Observers\DomainObserver;
|
use App\Observers\DomainObserver;
|
||||||
use Filament\Support\Facades\FilamentView;
|
use App\Support\Notifications\Notification as LivewireNotification;
|
||||||
use Filament\View\PanelsRenderHook;
|
use Filament\Notifications\Notification;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
@@ -15,7 +15,7 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
//
|
$this->app->bind(Notification::class, LivewireNotification::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,10 +27,5 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
// Note: AuthEventListener is auto-discovered by Laravel 11+
|
// Note: AuthEventListener is auto-discovered by Laravel 11+
|
||||||
// Do not manually subscribe - it causes duplicate audit log entries
|
// Do not manually subscribe - it causes duplicate audit log entries
|
||||||
|
|
||||||
FilamentView::registerRenderHook(
|
|
||||||
PanelsRenderHook::SCRIPTS_AFTER,
|
|
||||||
fn (): string => view('filament.partials.notifications-refresh')->render(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
app/Support/Notifications/Notification.php
Normal file
27
app/Support/Notifications/Notification.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Support\Notifications;
|
||||||
|
|
||||||
|
use Filament\Notifications\Notification as BaseNotification;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
class Notification extends BaseNotification
|
||||||
|
{
|
||||||
|
public function send(): static
|
||||||
|
{
|
||||||
|
$payload = $this->toArray();
|
||||||
|
|
||||||
|
if (Livewire::isLivewireRequest()) {
|
||||||
|
$component = Livewire::current();
|
||||||
|
if ($component) {
|
||||||
|
$component->dispatch('notificationSent', $payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session()->push('filament.notifications', $payload);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,3 +36,19 @@ After install, systemd services are enabled and started:
|
|||||||
- `jabali-agent`
|
- `jabali-agent`
|
||||||
- `jabali-queue`
|
- `jabali-queue`
|
||||||
- `jabali-health-monitor`
|
- `jabali-health-monitor`
|
||||||
|
|
||||||
|
## Panel notifications (admin + user)
|
||||||
|
|
||||||
|
Jabali ships with a hardened Filament notifications setup that prevents Livewire
|
||||||
|
success hooks from breaking after the first toast.
|
||||||
|
|
||||||
|
What is included:
|
||||||
|
|
||||||
|
- `public/js/filament/notifications/notifications.js` is patched to guard the
|
||||||
|
animation callback (prevents `TypeError: e is not a function`).
|
||||||
|
- `resources/views/vendor/filament-notifications/notifications.blade.php` adds
|
||||||
|
a lightweight `wire:poll.2s` so toasts keep flowing even if a Livewire event
|
||||||
|
is dropped.
|
||||||
|
|
||||||
|
If you update or rebuild assets, keep the guard in place and hard‑refresh the
|
||||||
|
browser (Ctrl+Shift+R) after deployment.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,57 +0,0 @@
|
|||||||
<script data-navigate-once>
|
|
||||||
(function () {
|
|
||||||
let hookRegistered = false;
|
|
||||||
|
|
||||||
const registerHook = () => {
|
|
||||||
if (hookRegistered || !window.Livewire?.hook || !window.Livewire?.dispatch) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hookRegistered = true;
|
|
||||||
|
|
||||||
window.Livewire.hook('commit', ({ component, succeed }) => {
|
|
||||||
if (component?.name === 'notifications') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
succeed(() => {
|
|
||||||
const livewire = window.Livewire;
|
|
||||||
const allComponents = typeof livewire?.all === 'function' ? livewire.all() : [];
|
|
||||||
const notificationsComponent = allComponents.find((component) => {
|
|
||||||
const name = component?.name ?? '';
|
|
||||||
return name === 'notifications' || name.includes('notifications');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (notificationsComponent?.$wire?.pullNotificationsFromSession) {
|
|
||||||
try {
|
|
||||||
notificationsComponent.$wire.pullNotificationsFromSession();
|
|
||||||
return;
|
|
||||||
} catch (error) {
|
|
||||||
// fall back to dispatch-based refresh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dispatchTo = livewire?.dispatchTo;
|
|
||||||
if (dispatchTo) {
|
|
||||||
['notifications', 'filament.notifications', 'filament-notifications'].forEach((name) => {
|
|
||||||
try {
|
|
||||||
dispatchTo(name, 'notificationsSent');
|
|
||||||
} catch (error) {
|
|
||||||
// ignore missing component names
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
livewire?.dispatch?.('notificationsSent');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('livewire:init', registerHook);
|
|
||||||
document.addEventListener('livewire:navigated', registerHook);
|
|
||||||
|
|
||||||
if (window.Livewire?.hook) {
|
|
||||||
registerHook();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
43
resources/views/vendor/filament-notifications/notifications.blade.php
vendored
Normal file
43
resources/views/vendor/filament-notifications/notifications.blade.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
@php
|
||||||
|
use Filament\Support\Enums\Alignment;
|
||||||
|
use Filament\Support\Enums\VerticalAlignment;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div wire:poll.2s="pullNotificationsFromSession">
|
||||||
|
<div
|
||||||
|
@class([
|
||||||
|
'fi-no',
|
||||||
|
'fi-align-' . static::$alignment->value,
|
||||||
|
'fi-vertical-align-' . static::$verticalAlignment->value,
|
||||||
|
])
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
@foreach ($notifications as $notification)
|
||||||
|
{{ $notification }}
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($broadcastChannel = $this->getBroadcastChannel())
|
||||||
|
@script
|
||||||
|
<script>
|
||||||
|
window.addEventListener('EchoLoaded', () => {
|
||||||
|
window.Echo.private(@js($broadcastChannel)).notification(
|
||||||
|
(notification) => {
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
$wire.handleBroadcastNotification(
|
||||||
|
notification,
|
||||||
|
),
|
||||||
|
500,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (window.Echo) {
|
||||||
|
window.dispatchEvent(new CustomEvent('EchoLoaded'))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@endscript
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user