*/ public function report(?string $from = null, ?string $to = null, ?int $userId = null, ?int $statusCode = null, int $top = 10): array { $baseQuery = $this->baseQuery($from, $to, $userId, $statusCode); $totalRequests = (clone $baseQuery)->count(); return [ 'generated_at' => now()->toIso8601String(), 'filters' => [ 'from' => $from, 'to' => $to, 'user_id' => $userId, 'status_code' => $statusCode, 'top' => $top, ], 'summary' => [ 'total_requests' => $totalRequests, 'unique_users' => (clone $baseQuery)->whereNotNull('user_id')->distinct('user_id')->count('user_id'), 'unique_tokens' => (clone $baseQuery)->whereNotNull('personal_access_token_id')->distinct('personal_access_token_id')->count('personal_access_token_id'), 'successful_requests' => (clone $baseQuery)->whereBetween('status_code', [200, 299])->count(), 'client_error_requests' => (clone $baseQuery)->whereBetween('status_code', [400, 499])->count(), 'server_error_requests' => (clone $baseQuery)->whereBetween('status_code', [500, 599])->count(), 'average_duration_ms' => $totalRequests > 0 ? round((float) (clone $baseQuery)->avg('duration_ms'), 2) : 0.0, ], 'top_paths' => $this->topRows($baseQuery, 'path', $top), 'status_codes' => $this->topRows($baseQuery, 'status_code', $top), 'top_users' => $this->topRows($baseQuery, 'user_id', $top), 'top_tokens' => $this->topRows($baseQuery, 'personal_access_token_id', $top), 'recent_requests' => $this->recentRequests($baseQuery, $top), ]; } private function baseQuery(?string $from, ?string $to, ?int $userId, ?int $statusCode): Builder { return ApiUsageLog::query() ->when($from !== null, fn (Builder $query) => $query->where('requested_at', '>=', $from)) ->when($to !== null, fn (Builder $query) => $query->where('requested_at', '<=', $to)) ->when($userId !== null, fn (Builder $query) => $query->where('user_id', $userId)) ->when($statusCode !== null, fn (Builder $query) => $query->where('status_code', $statusCode)); } /** * @return list */ private function topRows(Builder $baseQuery, string $column, int $limit): array { return (clone $baseQuery) ->selectRaw("{$column} as value, count(*) as requests") ->whereNotNull($column) ->groupBy($column) ->orderByDesc('requests') ->limit($limit) ->get() ->map(fn ($row): array => [ 'value' => $row->value, 'requests' => (int) $row->requests, ]) ->all(); } /** * @return list> */ private function recentRequests(Builder $baseQuery, int $limit): array { return (clone $baseQuery) ->latest('requested_at') ->latest('id') ->limit($limit) ->get([ 'id', 'user_id', 'personal_access_token_id', 'method', 'path', 'route_name', 'status_code', 'duration_ms', 'requested_at', ]) ->map(fn (ApiUsageLog $log): array => [ 'id' => $log->id, 'user_id' => $log->user_id, 'personal_access_token_id' => $log->personal_access_token_id, 'method' => $log->method, 'path' => $log->path, 'route_name' => $log->route_name, 'status_code' => $log->status_code, 'duration_ms' => $log->duration_ms, 'requested_at' => $log->requested_at?->toIso8601String(), ]) ->all(); } }