From 5ecc689135fada1ca5c257c5495e31cfab4b1adf Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 29 Sep 2026 10:19:52 +0200 Subject: [PATCH] fix(help): show an error when feedback cannot be delivered Without SMTP or Resend configured, the Help form posts to the Cloud feedback API but ignored the response, so it always reported success. Check the response and show an error on failure. The Cloud endpoint now returns 502 when the Discord webhook fails. --- app/Http/Controllers/Api/OtherController.php | 16 ++++-- app/Livewire/Help.php | 16 ++++-- tests/Feature/FeedbackEndpointTest.php | 11 +++++ tests/Feature/HelpFeedbackTest.php | 52 ++++++++++++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 tests/Feature/HelpFeedbackTest.php diff --git a/app/Http/Controllers/Api/OtherController.php b/app/Http/Controllers/Api/OtherController.php index 392b1fd4c0..8fa3125aec 100644 --- a/app/Http/Controllers/Api/OtherController.php +++ b/app/Http/Controllers/Api/OtherController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; @@ -281,10 +282,17 @@ class OtherController extends Controller $webhook_url = config('constants.webhooks.feedback_discord_webhook'); if ($webhook_url) { - Http::timeout(5)->post($webhook_url, [ - 'content' => $data['content'], - 'allowed_mentions' => ['parse' => []], - ]); + try { + $response = Http::timeout(5)->post($webhook_url, [ + 'content' => $data['content'], + 'allowed_mentions' => ['parse' => []], + ]); + } catch (ConnectionException) { + $response = null; + } + if (! $response?->successful()) { + return response()->json(['message' => 'Feedback could not be delivered.'], 502); + } } return response()->json(['message' => 'Feedback sent.'], 200); diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php index 421e50bcca..9c18e21d30 100644 --- a/app/Livewire/Help.php +++ b/app/Livewire/Help.php @@ -3,6 +3,7 @@ namespace App\Livewire; use DanHarrin\LivewireRateLimiting\WithRateLimiting; +use Illuminate\Http\Client\ConnectionException; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Support\Facades\Http; use Livewire\Attributes\Validate; @@ -38,9 +39,18 @@ class Help extends Component // Sending feedback through Cloud API if (blank($type)) { $url = 'https://app.coolify.io/api/feedback'; - Http::post($url, [ - 'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`', - ]); + try { + $response = Http::timeout(10)->post($url, [ + 'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`', + ]); + } catch (ConnectionException) { + $response = null; + } + if (! $response?->successful()) { + $this->dispatch('error', 'Feedback could not be sent.', 'Please try again later or contact us on Discord.'); + + return; + } } else { send_user_an_email($mail, auth()->user()?->email, 'feedback@coollabs.io'); } diff --git a/tests/Feature/FeedbackEndpointTest.php b/tests/Feature/FeedbackEndpointTest.php index a2c603defb..ef050ca62b 100644 --- a/tests/Feature/FeedbackEndpointTest.php +++ b/tests/Feature/FeedbackEndpointTest.php @@ -94,3 +94,14 @@ it('disables discord mention parsing regardless of content', function () { return $request['allowed_mentions'] === ['parse' => []]; }); }); + +it('returns an error when the discord webhook fails', function () { + config()->set('constants.webhooks.feedback_discord_webhook', 'https://webhook.example.com/failing'); + Http::fake(['webhook.example.com/*' => Http::response([], 500)]); + + $response = $this->postJson('/api/feedback', [ + 'content' => 'This is a valid feedback message for testing purposes.', + ]); + + $response->assertStatus(502); +}); diff --git a/tests/Feature/HelpFeedbackTest.php b/tests/Feature/HelpFeedbackTest.php new file mode 100644 index 0000000000..0b34f29b24 --- /dev/null +++ b/tests/Feature/HelpFeedbackTest.php @@ -0,0 +1,52 @@ + 0]); + $this->actingAs(User::factory()->create()); +}); + +it('shows success when the cloud api accepts feedback without email configured', function () { + Http::fake(['app.coolify.io/*' => Http::response(['message' => 'Feedback sent.'], 200)]); + + Livewire::test(Help::class) + ->set('subject', 'Test subject') + ->set('description', 'This is a detailed feedback description.') + ->call('submit') + ->assertDispatched('success') + ->assertNotDispatched('error'); + + Http::assertSent(fn ($request) => $request->url() === 'https://app.coolify.io/api/feedback'); +}); + +it('shows an error when the cloud api rejects feedback without email configured', function (int $status) { + Http::fake(['app.coolify.io/*' => Http::response([], $status)]); + + Livewire::test(Help::class) + ->set('subject', 'Test subject') + ->set('description', 'This is a detailed feedback description.') + ->call('submit') + ->assertDispatched('error') + ->assertNotDispatched('success') + ->assertSet('subject', 'Test subject'); +})->with([422, 429, 500, 502]); + +it('shows an error when the cloud api is unreachable', function () { + Http::fake(fn () => throw new ConnectionException('Connection refused')); + + Livewire::test(Help::class) + ->set('subject', 'Test subject') + ->set('description', 'This is a detailed feedback description.') + ->call('submit') + ->assertDispatched('error') + ->assertNotDispatched('success'); +});