]> BookStack Code Mirror - bookstack/blob - app/Activity/DispatchWebhookJob.php
Started aligning app-wide outbound http calling behaviour
[bookstack] / app / Activity / DispatchWebhookJob.php
1 <?php
2
3 namespace BookStack\Activity;
4
5 use BookStack\Activity\Models\Loggable;
6 use BookStack\Activity\Models\Webhook;
7 use BookStack\Activity\Tools\WebhookFormatter;
8 use BookStack\Facades\Theme;
9 use BookStack\Http\HttpRequestService;
10 use BookStack\Theming\ThemeEvents;
11 use BookStack\Users\Models\User;
12 use BookStack\Util\SsrUrlValidator;
13 use Illuminate\Bus\Queueable;
14 use Illuminate\Contracts\Queue\ShouldQueue;
15 use Illuminate\Foundation\Bus\Dispatchable;
16 use Illuminate\Queue\InteractsWithQueue;
17 use Illuminate\Queue\SerializesModels;
18 use Illuminate\Support\Facades\Log;
19 use Psr\Http\Client\ClientExceptionInterface;
20
21 class DispatchWebhookJob implements ShouldQueue
22 {
23     use Dispatchable;
24     use InteractsWithQueue;
25     use Queueable;
26     use SerializesModels;
27
28     protected Webhook $webhook;
29     protected User $initiator;
30     protected int $initiatedTime;
31     protected array $webhookData;
32
33     /**
34      * Create a new job instance.
35      *
36      * @return void
37      */
38     public function __construct(Webhook $webhook, string $event, Loggable|string $detail)
39     {
40         $this->webhook = $webhook;
41         $this->initiator = user();
42         $this->initiatedTime = time();
43
44         $themeResponse = Theme::dispatch(ThemeEvents::WEBHOOK_CALL_BEFORE, $event, $this->webhook, $detail, $this->initiator, $this->initiatedTime);
45         $this->webhookData =  $themeResponse ?? WebhookFormatter::getDefault($event, $this->webhook, $detail, $this->initiator, $this->initiatedTime)->format();
46     }
47
48     /**
49      * Execute the job.
50      *
51      * @return void
52      */
53     public function handle(HttpRequestService $http)
54     {
55         $lastError = null;
56
57         try {
58             (new SsrUrlValidator())->ensureAllowed($this->webhook->endpoint);
59
60             $client = $http->buildClient($this->webhook->timeout, [
61                 'connect_timeout' => 10,
62                 'allow_redirects' => ['strict' => true],
63             ]);
64
65             $response = $client->sendRequest($http->jsonRequest('POST', $this->webhook->endpoint, $this->webhookData));
66             $statusCode = $response->getStatusCode();
67
68             if ($statusCode >= 400) {
69                 $lastError = "Response status from endpoint was {$statusCode}";
70                 Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with status {$statusCode}");
71             }
72         } catch (ClientExceptionInterface $error) {
73             $lastError = $error->getMessage();
74             Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with error \"{$lastError}\"");
75         }
76
77         $this->webhook->last_called_at = now();
78         if ($lastError) {
79             $this->webhook->last_errored_at = now();
80             $this->webhook->last_error = $lastError;
81         }
82
83         $this->webhook->save();
84     }
85 }