This commit is contained in:
Ovidiu U
2026-05-12 09:47:26 +01:00
parent 3d103f19e1
commit 759e4f2784
183 changed files with 20094 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers\Api;
use App\Enums\ResponseStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\ContactEnquiryRequest;
use App\Models\ApiRequest;
use App\Models\Website;
use Illuminate\Http\JsonResponse;
class ContactEnquiryController extends Controller
{
public function __invoke(ContactEnquiryRequest $request): JsonResponse
{
$website = $request->user();
$registrationNumber = strtoupper(str_replace(' ', '', $request->validated('registration_number')));
ApiRequest::create([
'website_id' => $website->id,
'registration_number' => $registrationNumber,
'ip_address' => $request->ip(),
'contact_data' => $request->validated('contact_data'),
'response_status' => ResponseStatus::ContactSubmitted,
'metadata' => [
'user_agent' => $request->userAgent(),
'referer' => $request->header('referer'),
],
'created_at' => now(),
]);
return response()->json(['success' => true]);
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace App\Http\Controllers\Api;
use App\Enums\ResponseStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\VehicleEnquiryRequest;
use App\Models\ApiRequest;
use App\Models\VehicleDataSource;
use App\Models\VehicleRecord;
use App\Models\Website;
use App\Services\DvlaService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class VehicleEnquiryController extends Controller
{
public function __construct(
private readonly DvlaService $dvlaService
) {
}
public function __invoke(VehicleEnquiryRequest $request): JsonResponse
{
$startTime = microtime(true);
$website = $request->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);
}
}