diff --git a/app/Mail/Drip/FeedbackEmail.php b/app/Mail/Drip/FeedbackEmail.php index 7431292d..b2029c40 100644 --- a/app/Mail/Drip/FeedbackEmail.php +++ b/app/Mail/Drip/FeedbackEmail.php @@ -4,16 +4,21 @@ namespace App\Mail\Drip; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Queue\SerializesModels; -class FeedbackEmail extends Mailable +class FeedbackEmail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; - public function __construct(public User $user) {} + public function __construct(public User $user) + { + $this->onQueue('emails'); + } public function envelope(): Envelope { @@ -31,4 +36,14 @@ class FeedbackEmail extends Mailable ], ); } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [new RateLimited('emails', releaseAfter: 1)]; + } } diff --git a/app/Mail/Drip/ImportHelpEmail.php b/app/Mail/Drip/ImportHelpEmail.php index b905efad..7293a74e 100644 --- a/app/Mail/Drip/ImportHelpEmail.php +++ b/app/Mail/Drip/ImportHelpEmail.php @@ -4,16 +4,21 @@ namespace App\Mail\Drip; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Queue\SerializesModels; -class ImportHelpEmail extends Mailable +class ImportHelpEmail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; - public function __construct(public User $user) {} + public function __construct(public User $user) + { + $this->onQueue('emails'); + } public function envelope(): Envelope { @@ -31,4 +36,14 @@ class ImportHelpEmail extends Mailable ], ); } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [new RateLimited('emails', releaseAfter: 1)]; + } } diff --git a/app/Mail/Drip/OnboardingReminderEmail.php b/app/Mail/Drip/OnboardingReminderEmail.php index 7affed34..c0555405 100644 --- a/app/Mail/Drip/OnboardingReminderEmail.php +++ b/app/Mail/Drip/OnboardingReminderEmail.php @@ -4,16 +4,21 @@ namespace App\Mail\Drip; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Queue\SerializesModels; -class OnboardingReminderEmail extends Mailable +class OnboardingReminderEmail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; - public function __construct(public User $user) {} + public function __construct(public User $user) + { + $this->onQueue('emails'); + } public function envelope(): Envelope { @@ -31,4 +36,14 @@ class OnboardingReminderEmail extends Mailable ], ); } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [new RateLimited('emails', releaseAfter: 1)]; + } } diff --git a/app/Mail/Drip/PromoCodeEmail.php b/app/Mail/Drip/PromoCodeEmail.php index c73db43f..8f2c4326 100644 --- a/app/Mail/Drip/PromoCodeEmail.php +++ b/app/Mail/Drip/PromoCodeEmail.php @@ -4,16 +4,21 @@ namespace App\Mail\Drip; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Queue\SerializesModels; -class PromoCodeEmail extends Mailable +class PromoCodeEmail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; - public function __construct(public User $user) {} + public function __construct(public User $user) + { + $this->onQueue('emails'); + } public function envelope(): Envelope { @@ -32,4 +37,14 @@ class PromoCodeEmail extends Mailable ], ); } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [new RateLimited('emails', releaseAfter: 1)]; + } } diff --git a/app/Mail/Drip/WelcomeEmail.php b/app/Mail/Drip/WelcomeEmail.php index 9fce4999..d49d831f 100644 --- a/app/Mail/Drip/WelcomeEmail.php +++ b/app/Mail/Drip/WelcomeEmail.php @@ -4,16 +4,21 @@ namespace App\Mail\Drip; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Queue\SerializesModels; -class WelcomeEmail extends Mailable +class WelcomeEmail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; - public function __construct(public User $user) {} + public function __construct(public User $user) + { + $this->onQueue('emails'); + } public function envelope(): Envelope { @@ -31,4 +36,14 @@ class WelcomeEmail extends Mailable ], ); } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [new RateLimited('emails', releaseAfter: 1)]; + } } diff --git a/app/Mail/UserLeadInvitation.php b/app/Mail/UserLeadInvitation.php index c694205a..f073c998 100644 --- a/app/Mail/UserLeadInvitation.php +++ b/app/Mail/UserLeadInvitation.php @@ -8,6 +8,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Queue\SerializesModels; class UserLeadInvitation extends Mailable implements ShouldQueue @@ -19,7 +20,7 @@ class UserLeadInvitation extends Mailable implements ShouldQueue */ public function __construct(public UserLead $lead) { - // + $this->onQueue('emails'); } /** @@ -51,4 +52,14 @@ class UserLeadInvitation extends Mailable implements ShouldQueue { return []; } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [new RateLimited('emails', releaseAfter: 1)]; + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 56f8e97d..69cbe358 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,8 @@ namespace App\Providers; use App\Http\Responses\RegisterResponse; +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\ServiceProvider; use Laravel\Fortify\Contracts\RegisterResponse as RegisterResponseContract; @@ -21,6 +23,8 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + RateLimiter::for('emails', function (object $job): Limit { + return Limit::perSecond(1); + }); } } diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php index 778bbe0c..66293ad6 100644 --- a/tests/Feature/Auth/RegistrationTest.php +++ b/tests/Feature/Auth/RegistrationTest.php @@ -1,5 +1,7 @@ get(route('register')); @@ -7,6 +9,8 @@ test('registration screen can be rendered', function () { }); test('new users can register', function () { + Queue::fake(); + $response = $this->post(route('register.store'), [ 'name' => 'Test User', 'email' => 'test@example.com', diff --git a/tests/Feature/Jobs/Drip/SendFeedbackEmailJobTest.php b/tests/Feature/Jobs/Drip/SendFeedbackEmailJobTest.php index 06f57bd8..43e8608b 100644 --- a/tests/Feature/Jobs/Drip/SendFeedbackEmailJobTest.php +++ b/tests/Feature/Jobs/Drip/SendFeedbackEmailJobTest.php @@ -17,7 +17,7 @@ test('feedback email is sent to non-subscribed users', function () { SendFeedbackEmailJob::dispatchSync($user); - Mail::assertSent(FeedbackEmail::class, function ($mail) use ($user) { + Mail::assertQueued(FeedbackEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); @@ -39,7 +39,7 @@ test('feedback email is not sent to subscribed users', function () { SendFeedbackEmailJob::dispatchSync($user); - Mail::assertNotSent(FeedbackEmail::class); + Mail::assertNotQueued(FeedbackEmail::class); }); test('feedback email is not sent if already received', function () { @@ -49,5 +49,5 @@ test('feedback email is not sent if already received', function () { SendFeedbackEmailJob::dispatchSync($user); - Mail::assertNotSent(FeedbackEmail::class); + Mail::assertNotQueued(FeedbackEmail::class); }); diff --git a/tests/Feature/Jobs/Drip/SendImportHelpEmailJobTest.php b/tests/Feature/Jobs/Drip/SendImportHelpEmailJobTest.php index cab7e3c3..dd6d1b5b 100644 --- a/tests/Feature/Jobs/Drip/SendImportHelpEmailJobTest.php +++ b/tests/Feature/Jobs/Drip/SendImportHelpEmailJobTest.php @@ -18,7 +18,7 @@ test('import help email is sent to onboarded users without transactions who are SendImportHelpEmailJob::dispatchSync($user); - Mail::assertSent(ImportHelpEmail::class, function ($mail) use ($user) { + Mail::assertQueued(ImportHelpEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); @@ -33,7 +33,7 @@ test('import help email is not sent to non-onboarded users', function () { SendImportHelpEmailJob::dispatchSync($user); - Mail::assertNotSent(ImportHelpEmail::class); + Mail::assertNotQueued(ImportHelpEmail::class); }); test('import help email is not sent to users with transactions', function () { @@ -42,7 +42,7 @@ test('import help email is not sent to users with transactions', function () { SendImportHelpEmailJob::dispatchSync($user); - Mail::assertNotSent(ImportHelpEmail::class); + Mail::assertNotQueued(ImportHelpEmail::class); }); test('import help email is not sent to subscribed users', function () { @@ -57,7 +57,7 @@ test('import help email is not sent to subscribed users', function () { SendImportHelpEmailJob::dispatchSync($user); - Mail::assertNotSent(ImportHelpEmail::class); + Mail::assertNotQueued(ImportHelpEmail::class); }); test('import help email is not sent if already received', function () { @@ -67,5 +67,5 @@ test('import help email is not sent if already received', function () { SendImportHelpEmailJob::dispatchSync($user); - Mail::assertNotSent(ImportHelpEmail::class); + Mail::assertNotQueued(ImportHelpEmail::class); }); diff --git a/tests/Feature/Jobs/Drip/SendOnboardingReminderEmailJobTest.php b/tests/Feature/Jobs/Drip/SendOnboardingReminderEmailJobTest.php index ddc97278..602942c8 100644 --- a/tests/Feature/Jobs/Drip/SendOnboardingReminderEmailJobTest.php +++ b/tests/Feature/Jobs/Drip/SendOnboardingReminderEmailJobTest.php @@ -16,7 +16,7 @@ test('onboarding reminder email is sent to non-onboarded users', function () { SendOnboardingReminderEmailJob::dispatchSync($user); - Mail::assertSent(OnboardingReminderEmail::class, function ($mail) use ($user) { + Mail::assertQueued(OnboardingReminderEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); @@ -31,7 +31,7 @@ test('onboarding reminder email is not sent to onboarded users', function () { SendOnboardingReminderEmailJob::dispatchSync($user); - Mail::assertNotSent(OnboardingReminderEmail::class); + Mail::assertNotQueued(OnboardingReminderEmail::class); $this->assertDatabaseMissing('user_mail_logs', [ 'user_id' => $user->id, @@ -46,5 +46,5 @@ test('onboarding reminder email is not sent if already received', function () { SendOnboardingReminderEmailJob::dispatchSync($user); - Mail::assertNotSent(OnboardingReminderEmail::class); + Mail::assertNotQueued(OnboardingReminderEmail::class); }); diff --git a/tests/Feature/Jobs/Drip/SendPromoCodeEmailJobTest.php b/tests/Feature/Jobs/Drip/SendPromoCodeEmailJobTest.php index 5e2644eb..3325c4f1 100644 --- a/tests/Feature/Jobs/Drip/SendPromoCodeEmailJobTest.php +++ b/tests/Feature/Jobs/Drip/SendPromoCodeEmailJobTest.php @@ -19,7 +19,7 @@ test('promo code email is sent to onboarded users with transactions who are not SendPromoCodeEmailJob::dispatchSync($user); - Mail::assertSent(PromoCodeEmail::class, function ($mail) use ($user) { + Mail::assertQueued(PromoCodeEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); @@ -35,7 +35,7 @@ test('promo code email is not sent to non-onboarded users', function () { SendPromoCodeEmailJob::dispatchSync($user); - Mail::assertNotSent(PromoCodeEmail::class); + Mail::assertNotQueued(PromoCodeEmail::class); }); test('promo code email is not sent to users without transactions', function () { @@ -43,7 +43,7 @@ test('promo code email is not sent to users without transactions', function () { SendPromoCodeEmailJob::dispatchSync($user); - Mail::assertNotSent(PromoCodeEmail::class); + Mail::assertNotQueued(PromoCodeEmail::class); }); test('promo code email is not sent to subscribed users', function () { @@ -59,7 +59,7 @@ test('promo code email is not sent to subscribed users', function () { SendPromoCodeEmailJob::dispatchSync($user); - Mail::assertNotSent(PromoCodeEmail::class); + Mail::assertNotQueued(PromoCodeEmail::class); }); test('promo code email is not sent if already received', function () { @@ -70,5 +70,5 @@ test('promo code email is not sent if already received', function () { SendPromoCodeEmailJob::dispatchSync($user); - Mail::assertNotSent(PromoCodeEmail::class); + Mail::assertNotQueued(PromoCodeEmail::class); }); diff --git a/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php b/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php index fd88a77c..bdb409df 100644 --- a/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php +++ b/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php @@ -16,7 +16,7 @@ test('welcome email is sent and logged', function () { SendWelcomeEmailJob::dispatchSync($user); - Mail::assertSent(WelcomeEmail::class, function ($mail) use ($user) { + Mail::assertQueued(WelcomeEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); @@ -33,5 +33,5 @@ test('welcome email is not sent if already received', function () { SendWelcomeEmailJob::dispatchSync($user); - Mail::assertNotSent(WelcomeEmail::class); + Mail::assertNotQueued(WelcomeEmail::class); });