Manage webhooks from the panel
Some checks failed
Tests / lint (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / test-postgres (push) Has been cancelled
Tests / mcp (push) Has been cancelled

Create one and see its secret once, send a test, change the secret, and read
each delivery with what the other end answered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 11:44:11 +02:00
parent d80ed1a3e8
commit d4cf4a66b7
14 changed files with 797 additions and 26 deletions

View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace App\Filament\Resources\Webhooks\RelationManagers;
use App\Models\WebhookDelivery;
use App\Support\Webhooks\Webhooks;
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\HtmlString;
/**
* What was sent, and what the other end answered.
*/
class DeliveriesRelationManager extends RelationManager
{
protected static string $relationship = 'deliveries';
public static function getTitle(Model $ownerRecord, string $pageClass): string
{
return __('panel.webhooks.deliveries.title');
}
public function isReadOnly(): bool
{
return true;
}
public function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->poll('10s')
->columns([
TextColumn::make('event')
->label(__('panel.webhooks.deliveries.event'))
->badge()
->color('gray'),
TextColumn::make('status')
->label(__('panel.webhooks.deliveries.status'))
->badge()
->formatStateUsing(fn (string $state) => __("panel.webhooks.deliveries.statuses.{$state}"))
->color(fn (string $state) => match ($state) {
WebhookDelivery::SUCCEEDED => 'success',
WebhookDelivery::FAILED => 'danger',
default => 'warning',
}),
TextColumn::make('response_status')
->label(__('panel.webhooks.deliveries.response'))
->placeholder('—'),
TextColumn::make('attempts')
->label(__('panel.webhooks.deliveries.attempts')),
TextColumn::make('error')
->label(__('panel.webhooks.deliveries.error'))
->limit(60)
->tooltip(fn (WebhookDelivery $record) => $record->error)
->placeholder('—')
->color('danger'),
TextColumn::make('created_at')
->label(__('panel.webhooks.deliveries.created_at'))
->since()
->dateTimeTooltip(),
])
->filters([
SelectFilter::make('status')
->label(__('panel.webhooks.deliveries.status'))
->options(collect([WebhookDelivery::PENDING, WebhookDelivery::SUCCEEDED, WebhookDelivery::FAILED])
->mapWithKeys(fn (string $s) => [$s => __("panel.webhooks.deliveries.statuses.{$s}")])
->all()),
])
->recordActions([
Action::make('payload')
->label(__('panel.webhooks.deliveries.payload'))
->icon('heroicon-o-code-bracket')
->color('gray')
->modalHeading(fn (WebhookDelivery $record) => $record->event.' · '.$record->uuid)
->modalSubmitAction(false)
->modalContent(fn (WebhookDelivery $record) => new HtmlString(
'<pre style="white-space: pre-wrap; overflow-wrap: anywhere; font-size: .8125rem;">'
.e((string) json_encode($record->payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE))
.'</pre>'
)),
Action::make('redeliver')
->label(__('panel.webhooks.deliveries.redeliver'))
->icon('heroicon-o-arrow-path')
->requiresConfirmation()
->authorize(fn () => $this->getOwnerRecord() && auth()->user()?->can('update', $this->getOwnerRecord()))
->action(function (WebhookDelivery $record): void {
Webhooks::redeliver($record);
Notification::make()->title(__('panel.webhooks.deliveries.redelivered'))->success()->send();
}),
]);
}
}