Add App\Support\SafeNotifier that wraps $notifiable->notify() in a Throwable guard with a logged warning, so SMTP outages (the current Gmail 535 errors) no longer propagate as a 500 to the user. Apply to the two hot paths that were still unprotected: CommentController (new comment, reply, like) and RegisteredUserController (super-admin registration alert). User::sendEmailVerificationNotification() is wrapped inline for the same reason. Also add docs/mail-setup.md covering the three delivery options: Gmail app password (quick), Mailgun, Postmark — with the exact .env keys for each. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
44 lines
1.2 KiB
PHP
44 lines
1.2 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use Illuminate\Notifications\Notification;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Notification failures (SMTP down, Gmail 535, Mailgun 4xx, etc.) must never
|
|
* crash a user-facing request. Everywhere we previously did
|
|
*
|
|
* $user->notify(new SomeNotification(...));
|
|
*
|
|
* we now do
|
|
*
|
|
* SafeNotifier::send($user, new SomeNotification(...));
|
|
*
|
|
* which runs the same code, catches any Throwable, and writes a warning line
|
|
* so ops still has the signal.
|
|
*/
|
|
class SafeNotifier
|
|
{
|
|
/**
|
|
* @param mixed $notifiable Anything with ->notify(), usually a User.
|
|
*/
|
|
public static function send(mixed $notifiable, Notification $notification): void
|
|
{
|
|
if (! $notifiable || ! is_object($notifiable) || ! method_exists($notifiable, 'notify')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$notifiable->notify($notification);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('notification send failed', [
|
|
'notification' => $notification::class,
|
|
'notifiable' => $notifiable::class,
|
|
'notifiable_id'=> $notifiable->getKey() ?? null,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|