route())->getName(); $limiterName = match ($routeName) { 'forum.topic.store' => 'forum-thread-create', default => 'forum-post-write', }; try { return $this->throttleRequests->handle($request, $next, $limiterName); } catch (ThrottleRequestsException $exception) { $maxAttempts = (int) ($exception->getHeaders()['X-RateLimit-Limit'] ?? 0); $this->botProtectionService->recordRateLimitViolation( $request, $this->resolveActionName($routeName), [ 'limiter' => $limiterName, 'bucket' => $this->resolveBucket($limiterName, $maxAttempts), 'max_attempts' => $maxAttempts, 'retry_after' => (int) ($exception->getHeaders()['Retry-After'] ?? 0), 'reason' => sprintf('Forum write rate limit exceeded on %s.', $routeName !== '' ? $routeName : 'unknown route'), ], ); throw $exception; } } private function resolveActionName(string $routeName): string { return match ($routeName) { 'forum.topic.store' => 'forum_topic_create', 'forum.post.update' => 'forum_post_update', default => 'forum_reply_create', }; } private function resolveBucket(string $limiterName, int $maxAttempts): string { return $maxAttempts <= $this->minuteLimitThreshold($limiterName) ? 'minute' : 'hour'; } private function minuteLimitThreshold(string $limiterName): int { return match ($limiterName) { 'forum-thread-create', 'forum-post-write' => 3, default => PHP_INT_MAX, }; } }