HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/hi.naniguide.com/app/Http/Controllers/Tracking/V2TrackingController.php
<?php

namespace App\Http\Controllers\Tracking;

use App\Http\Controllers\Controller;
use App\Models\CampaignRecipient;
use App\Models\CampaignTracking;
use App\Models\SuppressionList;
use App\Services\AutoResponderTriggerService;
use App\Services\AutomationTriggerService;
use App\Services\Tracking\TrackingDomainResolver;
use App\Services\Tracking\TrackingHasher;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class V2TrackingController extends Controller
{
    public function __construct(
        private readonly TrackingDomainResolver $domainResolver,
        private readonly TrackingHasher $hasher,
    ) {
    }

    public function open(Request $request, string $campaignHash, string $subscriberHash): Response
    {
        $trackingDomain = $this->domainResolver->resolve($request);

        $campaign = $this->hasher->decodeCampaign($campaignHash);
        if (!$campaign) {
            abort(404);
        }

        $subscriber = $this->hasher->decodeSubscriber($subscriberHash, $campaign);
        if (!$subscriber) {
            abort(404);
        }

        // Suppression check
        if (SuppressionList::isSuppressed($subscriber->email, $campaign->customer_id)) {
            return $this->pixelResponse();
        }

        // Idempotent opens: check if already opened
        $existing = CampaignTracking::where('campaign_id', $campaign->id)
            ->where('subscriber_id', $subscriber->id)
            ->where('event_type', 'opened')
            ->first();

        if ($existing) {
            return $this->pixelResponse();
        }

        try {
            CampaignTracking::create([
                'campaign_id' => $campaign->id,
                'subscriber_id' => $subscriber->id,
                'email' => $subscriber->email,
                'event_type' => 'opened',
                'ip_address' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'event_at' => now(),
            ]);

            $recipient = CampaignRecipient::where('campaign_id', $campaign->id)
                ->where('email', $subscriber->email)
                ->first();
            if ($recipient) {
                $recipient->markAsOpened();
            }

            $campaign->incrementOpenedCount();

            try {
                app(AutoResponderTriggerService::class)->triggerSubscriberEvent('mail_opened', $subscriber);

                app(AutomationTriggerService::class)->triggerSubscriberEvent('campaign_opened', $subscriber, [
                    'campaign_id' => $campaign->id,
                ]);
            } catch (\Throwable $e) {
                Log::warning('Failed to trigger autoresponder on mail_opened', [
                    'campaign_id' => $campaign->id,
                    'subscriber_id' => $subscriber->id,
                    'error' => $e->getMessage(),
                ]);
            }
        } catch (\Throwable $e) {
            Log::warning('Failed to log open event', [
                'campaign_id' => $campaign->id,
                'subscriber_id' => $subscriber->id,
                'error' => $e->getMessage(),
            ]);
        }

        return $this->pixelResponse();
    }

    public function click(Request $request, string $campaignHash, string $subscriberHash)
    {
        $trackingDomain = $this->domainResolver->resolve($request);

        $campaign = $this->hasher->decodeCampaign($campaignHash);
        if (!$campaign) {
            abort(404);
        }

        $subscriber = $this->hasher->decodeSubscriber($subscriberHash, $campaign);
        if (!$subscriber) {
            abort(404);
        }

        $encodedUrl = $request->query('r');
        $originalUrl = $this->decodeRedirectUrl($encodedUrl);

        if (!$originalUrl) {
            $fallback = $campaign->settings['default_url'] ?? url('/');
            return redirect($fallback);
        }

        // Suppression check: redirect without logging
        if (SuppressionList::isSuppressed($subscriber->email, $campaign->customer_id)) {
            return redirect($originalUrl);
        }

        try {
            CampaignTracking::create([
                'campaign_id' => $campaign->id,
                'subscriber_id' => $subscriber->id,
                'email' => $subscriber->email,
                'event_type' => 'clicked',
                'url' => $originalUrl,
                'ip_address' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'event_at' => now(),
            ]);

            $recipient = CampaignRecipient::where('campaign_id', $campaign->id)
                ->where('email', $subscriber->email)
                ->first();
            if ($recipient && !$recipient->isClicked()) {
                $recipient->markAsClicked();
            }

            $campaign->incrementClickedCount();

            try {
                app(AutoResponderTriggerService::class)->triggerSubscriberEvent('mail_clicked', $subscriber, [
                    'url' => $originalUrl,
                    'campaign_id' => $campaign->id,
                ]);

                app(AutomationTriggerService::class)->triggerSubscriberEvent('campaign_clicked', $subscriber, [
                    'url' => $originalUrl,
                    'campaign_id' => $campaign->id,
                ]);
            } catch (\Throwable $e) {
                Log::warning('Failed to trigger autoresponder on mail_clicked', [
                    'campaign_id' => $campaign->id,
                    'subscriber_id' => $subscriber->id,
                    'error' => $e->getMessage(),
                ]);
            }
        } catch (\Throwable $e) {
            Log::warning('Failed to log click event', [
                'campaign_id' => $campaign->id,
                'subscriber_id' => $subscriber->id,
                'error' => $e->getMessage(),
            ]);
        }

        return redirect($originalUrl);
    }

    protected function decodeRedirectUrl(?string $encoded): ?string
    {
        if (!$encoded) {
            return null;
        }

        $decoded = base64_decode($encoded, true);
        if (!$decoded) {
            return null;
        }

        $decoded = filter_var($decoded, FILTER_VALIDATE_URL);
        if (!$decoded) {
            return null;
        }

        $scheme = parse_url($decoded, PHP_URL_SCHEME);
        if (!in_array($scheme, ['http', 'https'], true)) {
            return null;
        }

        return $decoded;
    }

    protected function pixelResponse(): Response
    {
        // 1x1 transparent PNG
        $png = base64_decode(
            'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=='
        );

        return response($png, 200)
            ->header('Content-Type', 'image/png')
            ->header('Cache-Control', 'no-cache, no-store, must-revalidate')
            ->header('Pragma', 'no-cache')
            ->header('Expires', '0');
    }
}