From dc15bee980ed823960b3ce97e6ed21191ab79e28 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 10 Oct 2025 18:07:04 +0200 Subject: [PATCH] feat: implement actual webhook delivery with Ray debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added actual HTTP POST delivery for webhook notifications and comprehensive Ray debugging for development. Changes: - Updated Team model to implement SendsWebhook interface - Added routeNotificationForWebhook() method to Team - Enhanced SendWebhookJob with Ray logging for request/response - Added Ray debugging to WebhookChannel for dispatch tracking - Added Ray debugging to Webhook Livewire component 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/Jobs/SendWebhookJob.php | 17 +++++++++++++- app/Livewire/Notifications/Webhook.php | 16 ++++++++++++++ app/Models/Team.php | 8 ++++++- app/Notifications/Channels/WebhookChannel.php | 12 ++++++++++ package-lock.json | 22 ++++++++----------- 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/app/Jobs/SendWebhookJob.php b/app/Jobs/SendWebhookJob.php index 865dd9493..607fda3fe 100644 --- a/app/Jobs/SendWebhookJob.php +++ b/app/Jobs/SendWebhookJob.php @@ -40,6 +40,21 @@ public function __construct( */ public function handle(): void { - Http::post($this->webhookUrl, $this->payload); + if (isDev()) { + ray('Sending webhook notification', [ + 'url' => $this->webhookUrl, + 'payload' => $this->payload, + ]); + } + + $response = Http::post($this->webhookUrl, $this->payload); + + if (isDev()) { + ray('Webhook response', [ + 'status' => $response->status(), + 'body' => $response->body(), + 'successful' => $response->successful(), + ]); + } } } diff --git a/app/Livewire/Notifications/Webhook.php b/app/Livewire/Notifications/Webhook.php index 491fc4b07..cf4e71105 100644 --- a/app/Livewire/Notifications/Webhook.php +++ b/app/Livewire/Notifications/Webhook.php @@ -159,6 +159,14 @@ public function saveModel() { $this->syncData(true); refreshSession(); + + if (isDev()) { + ray('Webhook settings saved', [ + 'webhook_enabled' => $this->settings->webhook_enabled, + 'webhook_url' => $this->settings->webhook_url, + ]); + } + $this->dispatch('success', 'Settings saved.'); } @@ -166,6 +174,14 @@ public function sendTestNotification() { try { $this->authorize('sendTest', $this->settings); + + if (isDev()) { + ray('Sending test webhook notification', [ + 'team_id' => $this->team->id, + 'webhook_url' => $this->settings->webhook_url, + ]); + } + $this->team->notify(new Test(channel: 'webhook')); $this->dispatch('success', 'Test notification sent.'); } catch (\Throwable $e) { diff --git a/app/Models/Team.php b/app/Models/Team.php index 0bdda9272..211ea8ade 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -7,6 +7,7 @@ use App\Notifications\Channels\SendsEmail; use App\Notifications\Channels\SendsPushover; use App\Notifications\Channels\SendsSlack; +use App\Notifications\Channels\SendsWebhook; use App\Traits\HasNotificationSettings; use App\Traits\HasSafeStringAttribute; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -36,7 +37,7 @@ ] )] -class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, SendsSlack +class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, SendsSlack, SendsWebhook { use HasFactory, HasNotificationSettings, HasSafeStringAttribute, Notifiable; @@ -166,6 +167,11 @@ public function routeNotificationForPushover() ]; } + public function routeNotificationForWebhook() + { + return data_get($this, 'webhook_url', null); + } + public function getRecipients(): array { $recipients = $this->members()->pluck('email')->toArray(); diff --git a/app/Notifications/Channels/WebhookChannel.php b/app/Notifications/Channels/WebhookChannel.php index 757e5e934..129e0628b 100644 --- a/app/Notifications/Channels/WebhookChannel.php +++ b/app/Notifications/Channels/WebhookChannel.php @@ -15,11 +15,23 @@ public function send(SendsWebhook $notifiable, Notification $notification): void $webhookSettings = $notifiable->webhookNotificationSettings; if (! $webhookSettings || ! $webhookSettings->isEnabled() || ! $webhookSettings->webhook_url) { + if (isDev()) { + ray('Webhook notification skipped - not enabled or no URL configured'); + } + return; } $payload = $notification->toWebhook(); + if (isDev()) { + ray('Dispatching webhook notification', [ + 'notification' => get_class($notification), + 'url' => $webhookSettings->webhook_url, + 'payload' => $payload, + ]); + } + SendWebhookJob::dispatch($payload, $webhookSettings->webhook_url); } } diff --git a/package-lock.json b/package-lock.json index 56e48288c..210747b4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -888,8 +888,7 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@tailwindcss/forms": { "version": "0.5.10", @@ -1404,7 +1403,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/asynckit": { "version": "0.4.0", @@ -1567,7 +1567,6 @@ "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", @@ -1582,7 +1581,6 @@ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1601,7 +1599,6 @@ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" } @@ -2376,6 +2373,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2452,6 +2450,7 @@ "integrity": "sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "tweetnacl": "^1.0.3" } @@ -2534,7 +2533,6 @@ "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -2551,7 +2549,6 @@ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2570,7 +2567,6 @@ "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -2585,7 +2581,6 @@ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2634,7 +2629,8 @@ "version": "4.1.10", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.2.2", @@ -2700,6 +2696,7 @@ "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -2799,6 +2796,7 @@ "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.16", "@vue/compiler-sfc": "3.5.16", @@ -2821,7 +2819,6 @@ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -2843,7 +2840,6 @@ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", "dev": true, - "peer": true, "engines": { "node": ">=0.4.0" }