executeRequest($method, $endpoint, $params); $responseTime = (int)((microtime(true) - $startTime) * 1000); // Log successful request $this->logApiRequest($source, $endpoint, $method, $params, $response, $responseTime, true); return [ 'success' => true, 'data' => $response->json(), 'status_code' => $response->status(), 'response_time' => $responseTime, ]; } catch (\Exception $e) { $lastException = $e; // Log failed attempt Log::warning("API request failed (attempt {$attempt}/{$maxRetries})", [ 'source' => $source->name, 'endpoint' => $endpoint, 'error' => $e->getMessage(), ]); if ($attempt < $maxRetries) { // Exponential backoff: wait 2^attempt seconds sleep(pow(2, $attempt)); } } } $responseTime = (int)((microtime(true) - $startTime) * 1000); // Log final failure $this->logApiRequest( $source, $endpoint, $method, $params, null, $responseTime, false, $lastException->getMessage() ?? 'Unknown error' ); return [ 'success' => false, 'error' => $lastException->getMessage() ?? 'Unknown error', 'response_time' => $responseTime, ]; } /** * Execute the HTTP request */ private function executeRequest(string $method, string $endpoint, array $params): \Illuminate\Http\Client\Response { $http = Http::timeout(30) ->retry(1, 100) ->withHeaders([ 'User-Agent' => 'JobifyGarden/1.0', 'Accept' => 'application/json', ]); return match (strtoupper($method)) { 'GET' => $http->get($endpoint, $params), 'POST' => $http->post($endpoint, $params), 'PUT' => $http->put($endpoint, $params), 'DELETE' => $http->delete($endpoint, $params), default => throw new \InvalidArgumentException("Unsupported HTTP method: {$method}"), }; } /** * Log API request to database */ private function logApiRequest( JobSource $source, string $endpoint, string $method, array $params, $response, int $responseTime, bool $success, ?string $errorMessage = null ): void { try { ApiLog::create([ 'job_source_id' => $source->id, 'endpoint' => $endpoint, 'method' => $method, 'status_code' => $response?->status(), 'response_time_ms' => $responseTime, 'success' => $success, 'error_message' => $errorMessage, 'request_params' => !empty($params) ? $params : null, 'response_data' => $success && $response ? [ 'status' => $response->status(), 'size' => strlen($response->body()), ] : null, ]); } catch (\Exception $e) { Log::error('Failed to log API request', [ 'error' => $e->getMessage(), 'endpoint' => $endpoint, ]); } } /** * Check if source is rate limited */ public function isRateLimited(JobSource $source): bool { if (!$source->rate_limit) { return false; } // Check requests in the last hour $recentRequests = ApiLog::where('job_source_id', $source->id) ->where('created_at', '>=', now()->subHour()) ->count(); return $recentRequests >= $source->rate_limit; } /** * Get rate limit info for a source */ public function getRateLimitInfo(JobSource $source): array { $recentRequests = ApiLog::where('job_source_id', $source->id) ->where('created_at', '>=', now()->subHour()) ->count(); return [ 'limit' => $source->rate_limit, 'used' => $recentRequests, 'remaining' => max(0, $source->rate_limit - $recentRequests), 'reset_at' => now()->addHour()->startOfHour(), ]; } }