190 lines
5.4 KiB
PHP
190 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Actions\ConfigureWebhookEndpointAction;
|
|
use App\Actions\DeleteWebhookRequestAction;
|
|
use App\Models\User;
|
|
use App\Models\WebhookEndpoint;
|
|
use App\Models\WebhookRequest;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Gate;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Illuminate\View\View;
|
|
use Livewire\Attributes\Locked;
|
|
use Livewire\Attributes\Url;
|
|
use Livewire\Component;
|
|
use Livewire\WithPagination;
|
|
|
|
class Inspector extends Component
|
|
{
|
|
use WithPagination;
|
|
|
|
#[Locked]
|
|
public string $endpointId = '';
|
|
|
|
#[Url]
|
|
public string $search = '';
|
|
|
|
#[Url]
|
|
public string $methodFilter = 'ALL';
|
|
|
|
#[Url]
|
|
public ?string $selectedRequestId = null;
|
|
|
|
public int $responseStatus = 200;
|
|
|
|
public string $responseHeadersJson = '{}';
|
|
|
|
public string $responseBody = '{}';
|
|
|
|
public bool $responseSaved = false;
|
|
|
|
public function mount(WebhookEndpoint $endpoint): void
|
|
{
|
|
Gate::authorize('view', $endpoint);
|
|
|
|
$this->endpointId = (string) $endpoint->getKey();
|
|
$this->responseStatus = $endpoint->response_status;
|
|
$this->responseHeadersJson = json_encode(
|
|
$endpoint->response_headers ?? [],
|
|
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
|
) ?: '{}';
|
|
$this->responseBody = $endpoint->response_body ?? '{}';
|
|
}
|
|
|
|
/**
|
|
* @return array<string, string>
|
|
*/
|
|
public function getListeners(): array
|
|
{
|
|
$endpoint = $this->endpoint();
|
|
$prefix = $endpoint->is_public ? 'echo' : 'echo-private';
|
|
|
|
return [
|
|
$prefix.':webhooks.'.$this->endpointId.',.webhook.request.received' => 'requestReceived',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $event
|
|
*/
|
|
public function requestReceived(array $event = []): void
|
|
{
|
|
if (($event['endpointId'] ?? null) === $this->endpointId) {
|
|
$this->resetPage();
|
|
}
|
|
}
|
|
|
|
public function updatedSearch(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function updatedMethodFilter(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function selectRequest(string $requestId): void
|
|
{
|
|
$webhookRequest = $this->endpoint()->requests()->findOrFail($requestId);
|
|
Gate::authorize('view', $webhookRequest);
|
|
$this->selectedRequestId = $webhookRequest->getKey();
|
|
}
|
|
|
|
public function clearSelectedRequest(): void
|
|
{
|
|
$this->selectedRequestId = null;
|
|
}
|
|
|
|
public function deleteRequest(string $requestId, DeleteWebhookRequestAction $deleteWebhookRequest): void
|
|
{
|
|
$webhookRequest = $this->endpoint()->requests()->findOrFail($requestId);
|
|
$deleteWebhookRequest->handle(Auth::user(), $webhookRequest);
|
|
|
|
if ($this->selectedRequestId === $requestId) {
|
|
$this->selectedRequestId = null;
|
|
}
|
|
}
|
|
|
|
public function saveResponse(ConfigureWebhookEndpointAction $configureWebhookEndpoint): void
|
|
{
|
|
$this->validate([
|
|
'responseStatus' => ['required', 'integer', 'between:100,599'],
|
|
'responseBody' => ['string'],
|
|
'responseHeadersJson' => ['required', 'json'],
|
|
]);
|
|
|
|
$headers = json_decode($this->responseHeadersJson, true);
|
|
|
|
if (! is_array($headers)) {
|
|
throw ValidationException::withMessages([
|
|
'responseHeadersJson' => 'Response headers must be a JSON object.',
|
|
]);
|
|
}
|
|
|
|
/** @var User|null $user */
|
|
$user = Auth::user();
|
|
$configureWebhookEndpoint->handle(
|
|
$user,
|
|
$this->endpoint(),
|
|
$this->responseStatus,
|
|
$headers,
|
|
$this->responseBody,
|
|
);
|
|
|
|
$this->responseSaved = true;
|
|
}
|
|
|
|
public function deleteEndpoint(): void
|
|
{
|
|
$endpoint = $this->endpoint();
|
|
Gate::authorize('delete', $endpoint);
|
|
$endpoint->delete();
|
|
$this->redirectRoute('home');
|
|
}
|
|
|
|
public function render(): View
|
|
{
|
|
$endpoint = $this->endpoint();
|
|
$requestQuery = $endpoint->requests()
|
|
->select(['id', 'method', 'request_uri', 'content_type', 'body_size', 'ip_address', 'received_at'])
|
|
->when($this->methodFilter !== 'ALL', fn ($query) => $query->where('method', $this->methodFilter))
|
|
->when($this->search !== '', function ($query): void {
|
|
$search = '%'.$this->search.'%';
|
|
|
|
$query->where(function ($query) use ($search): void {
|
|
$query->where('request_uri', 'like', $search)
|
|
->orWhere('ip_address', 'like', $search)
|
|
->orWhere('user_agent', 'like', $search);
|
|
});
|
|
})
|
|
->latest('received_at');
|
|
|
|
$selectedRequest = null;
|
|
|
|
if ($this->selectedRequestId !== null) {
|
|
$selectedRequest = $endpoint->requests()->find($this->selectedRequestId);
|
|
|
|
if ($selectedRequest instanceof WebhookRequest) {
|
|
Gate::authorize('view', $selectedRequest);
|
|
}
|
|
}
|
|
|
|
return view('livewire.inspector', [
|
|
'endpoint' => $endpoint,
|
|
'webhookRequests' => $requestQuery->paginate((int) config('webhooks.page_size', 25)),
|
|
'selectedRequest' => $selectedRequest,
|
|
]);
|
|
}
|
|
|
|
private function endpoint(): WebhookEndpoint
|
|
{
|
|
$endpoint = WebhookEndpoint::query()->findOrFail($this->endpointId);
|
|
Gate::authorize('view', $endpoint);
|
|
|
|
return $endpoint;
|
|
}
|
|
}
|