configureRateLimiting(); Scramble::afterOpenApiGenerated(function (OpenApi $openApi) { $openApi->secure( SecurityScheme::http('bearer') ); }); if ($this->app->environment('production')) { URL::forceScheme('https'); } Notification::extend('expo', fn ($app) => $app->make(ExpoPushChannel::class)); Event::listen(\Laravel\Cashier\Events\WebhookReceived::class, GrantCreditsFromStripeWebhook::class); VerifyEmail::createUrlUsing(function (User $notifiable): string { $relativeUrl = URL::temporarySignedRoute( 'verification.verify', now()->addMinutes((int) config('auth.verification.expire', 60)), [ 'id' => $notifiable->getKey(), 'hash' => sha1($notifiable->getEmailForVerification()), ], absolute: false, ); return url($relativeUrl); }); VerifyEmail::toMailUsing( fn (User $notifiable, string $verificationUrl): VerifyAccount => (new VerifyAccount($notifiable, $verificationUrl)) ->to($notifiable->getEmailForVerification()) ); $passwordResetUrl = function (User $notifiable, string $token): string { return route('password.reset', [ 'email' => $notifiable->getEmailForPasswordReset(), 'token' => $token, ]); }; ResetPassword::createUrlUsing($passwordResetUrl); ResetPassword::toMailUsing( fn (User $notifiable, string $token): ResetPasswordMail => (new ResetPasswordMail($notifiable, $passwordResetUrl($notifiable, $token))) ->to($notifiable->getEmailForPasswordReset()) ); Gate::define('viewApiDocs', fn (?User $user): bool => $user?->isAdmin() === true); Health::checks([ DatabaseCheck::new(), UsedDiskSpaceCheck::new(), RedisCheck::new(), ScheduleCheck::new(), HorizonCheck::new(), OptimizedAppCheck::new(), EnvironmentCheck::new(), DebugModeCheck::new(), MeilisearchCheck::new() ->url('http://meilisearch:7700/health'), ]); } private function configureRateLimiting(): void { RateLimiter::for('auth', fn (Request $request): array => [ Limit::perMinute(20)->by('ip:'.$request->ip()), Limit::perMinute(5)->by($this->loginRateLimitKey($request)), ]); RateLimiter::for( 'account-action', fn (Request $request): Limit => Limit::perMinute(5)->by($this->userRateLimitKey($request)) ); RateLimiter::for( 'content-write', fn (Request $request): Limit => Limit::perMinute(30)->by($this->userRateLimitKey($request)) ); RateLimiter::for( 'engagement', fn (Request $request): Limit => Limit::perMinute(60)->by($this->userRateLimitKey($request)) ); RateLimiter::for('ai-analysis', fn (Request $request): array => [ Limit::perMinute(5)->by('minute:'.$this->userRateLimitKey($request)), Limit::perHour(30)->by('hour:'.$this->userRateLimitKey($request)), ]); RateLimiter::for( 'external-service', fn (Request $request): Limit => Limit::perMinute(10)->by($this->userRateLimitKey($request)) ); RateLimiter::for( 'device-token', fn (Request $request): Limit => Limit::perMinute(30)->by($this->userRateLimitKey($request)) ); RateLimiter::for( 'notifications', fn (Request $request): Limit => Limit::perMinute(3)->by($this->userRateLimitKey($request)) ); RateLimiter::for( 'reports', fn (Request $request): Limit => Limit::perMinute(10)->by($this->userRateLimitKey($request)) ); } private function loginRateLimitKey(Request $request): string { $email = strtolower((string) $request->input('email', '')); return $email !== '' ? 'email:'.$email : 'ip:'.$request->ip(); } private function userRateLimitKey(Request $request): string { $userId = $request->user()?->getAuthIdentifier(); return $userId !== null ? 'user:'.$userId : 'ip:'.$request->ip(); } }