user(); $registrationNumber = strtoupper(str_replace(' ', '', $request->validated('registration_number'))); $contactData = $request->validated('contact_data'); $vehicleRecord = VehicleRecord::where('registration_number', $registrationNumber)->first(); if ($vehicleRecord) { $this->logRequest( $website, $registrationNumber, $request->ip(), $contactData, ResponseStatus::CacheHit, $this->buildMetadata($request, $startTime) ); $this->incrementCacheHitRateLimit($request); return response()->json([ 'success' => true, 'data' => $this->filterByTier($vehicleRecord->data, $website), ]); } if (!$this->checkExternalApiRateLimit($website)) { return response()->json([ 'message' => 'External API rate limit exceeded', 'limit' => $website->external_api_rate_limit, 'reset_at' => now()->addHour()->startOfHour()->timestamp, ], 429); } try { $dvlaData = $this->dvlaService->getVehicleDetails($registrationNumber); if (!$dvlaData) { $this->logRequest( $website, $registrationNumber, $request->ip(), $contactData, ResponseStatus::NotFound, $this->buildMetadata($request, $startTime) ); $this->incrementExternalApiRateLimit($website); return response()->json([ 'message' => 'Vehicle not found', ], 404); } $vehicleRecord = DB::transaction(function () use ($registrationNumber, $dvlaData) { $vehicle = VehicleRecord::create([ 'registration_number' => $registrationNumber, 'data' => $dvlaData, ]); VehicleDataSource::create([ 'vehicle_record_id' => $vehicle->id, 'source_name' => 'dvla', 'source_url' => DvlaService::class.'::API_URL', 'last_fetched_at' => now(), 'cache_expires_at' => now()->addMonths(6), ]); return $vehicle; }); $this->logRequest( $website, $registrationNumber, $request->ip(), $contactData, ResponseStatus::ApiFetched, $this->buildMetadata($request, $startTime) ); $this->incrementExternalApiRateLimit($website); return response()->json([ 'success' => true, 'data' => $this->filterByTier($vehicleRecord->data, $website), ]); } catch (\Exception $e) { $this->logRequest( $website, $registrationNumber, $request->ip(), $contactData, ResponseStatus::Error, $this->buildMetadata($request, $startTime, $e->getMessage()) ); return response()->json([ 'message' => 'Failed to fetch vehicle details', ], 500); } } private function logRequest(Website $website, string $registrationNumber, string $ipAddress, ?array $contactData, ResponseStatus $status, array $metadata = []): void { ApiRequest::create([ 'website_id' => $website->id, 'registration_number' => $registrationNumber, 'ip_address' => $ipAddress, 'contact_data' => $contactData, 'response_status' => $status, 'metadata' => $metadata, 'created_at' => now(), ]); } private function buildMetadata($request, float $startTime, ?string $errorMessage = null): array { $responseTime = round((microtime(true) - $startTime) * 1000, 2); $metadata = [ 'user_agent' => $request->userAgent(), 'response_time_ms' => $responseTime, 'referer' => $request->header('referer'), 'accept' => $request->header('accept'), ]; if ($errorMessage) { $metadata['error_message'] = $errorMessage; } return $metadata; } private function incrementCacheHitRateLimit($request): void { $cacheKey = $request->attributes->get('rate_limit_key'); if ($cacheKey) { $value = Cache::increment($cacheKey); Cache::put($cacheKey, $value, 3600); } } private function checkExternalApiRateLimit(Website $website): bool { if (app()->environment('local') || $website->bypass_rate_limit) { return true; } $hour = now()->format('Y-m-d-H'); $cacheKey = "rate_limit:website:{$website->id}:external_api:{$hour}"; $attempts = Cache::get($cacheKey, 0); return $attempts < $website->external_api_rate_limit; } private function incrementExternalApiRateLimit(Website $website): void { if (app()->environment('local') || $website->bypass_rate_limit) { return; } $hour = now()->format('Y-m-d-H'); $cacheKey = "rate_limit:website:{$website->id}:external_api:{$hour}"; $value = Cache::increment($cacheKey); Cache::put($cacheKey, $value, 3600); } private function filterByTier(mixed $data, Website $website): array { return $website->tier->filterFields((array) $data); } }