| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/switchyard/current/app/Jobs/ |
Upload File : |
<?php
namespace App\Jobs;
use App\Enums\DispatchStatus;
use App\Models\Destination;
use App\Models\DestinationDispatch;
use App\Models\Lead;
use App\Models\Scopes\WorkspaceScope;
use App\Services\Destinations\DestinationDispatcherResolver;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
/**
* Send one Lead to one Destination. Idempotent on (destination_id, lead_id) —
* if a `sent` DestinationDispatch row already exists, the job no-ops.
*
* Retry: 4 tries total, exp backoff (60s, 5m, 30m). 4xx terminal except 429
* (rate-limited) which is retried; 5xx + network errors are retried.
*/
class DispatchDestinationJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public int $tries = 4;
public function __construct(
public readonly string $destinationId,
public readonly string $leadId,
) {}
/** @return array<int, int> */
public function backoff(): array
{
return [60, 300, 1800];
}
public function handle(DestinationDispatcherResolver $resolver): void
{
$destination = Destination::withoutGlobalScope(WorkspaceScope::class)->find($this->destinationId);
if (! $destination) {
Log::warning('DispatchDestinationJob: destination not found', ['destination_id' => $this->destinationId]);
return;
}
if (! $destination->is_enabled) {
Log::info('DispatchDestinationJob: destination disabled, skipping', ['destination_id' => $destination->id]);
return;
}
$lead = Lead::withoutGlobalScope(WorkspaceScope::class)->find($this->leadId);
if (! $lead) {
Log::warning('DispatchDestinationJob: lead not found', ['lead_id' => $this->leadId]);
return;
}
$alreadySent = DestinationDispatch::query()
->where('destination_id', $destination->id)
->where('lead_id', $lead->id)
->where('status', DispatchStatus::Sent)
->exists();
if ($alreadySent) {
Log::info('DispatchDestinationJob: already sent, skipping', [
'destination_id' => $destination->id,
'lead_id' => $lead->id,
]);
return;
}
$dispatcher = $resolver->for($destination);
$result = $dispatcher->send($destination, $lead);
DestinationDispatch::create([
'destination_id' => $destination->id,
'lead_id' => $lead->id,
'status' => $result->ok ? DispatchStatus::Sent : DispatchStatus::Failed,
'attempt' => $this->attempts(),
'request_body' => $result->requestBody,
'response_status' => $result->responseStatus,
'response_body' => $result->responseBody,
'dispatched_at' => now(),
'completed_at' => now(),
]);
if ($result->ok) {
$destination->forceFill([
'last_dispatched_at' => now(),
'last_error' => null,
'last_error_at' => null,
])->save();
return;
}
$destination->forceFill([
'last_error' => sprintf('[%s] %s', $result->responseStatus ?? 'network', $result->responseBody ?? ''),
'last_error_at' => now(),
])->save();
if ($result->retryable) {
throw new \RuntimeException(sprintf(
'Destination dispatch retryable failure (status=%s): %s',
$result->responseStatus ?? 'network',
substr((string) $result->responseBody, 0, 200),
));
}
}
}