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/be.naniguide.com/app/Http/Middleware/Ads/EmergencyKillSwitchCheck.php
<?php

namespace App\Http\Middleware\Ads;

use App\Model\AdPlatform;
use App\Services\Ads\KillSwitchStore;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use Symfony\Component\HttpFoundation\Response;

/**
 * R11.1 — guards `/rui/ads/*` against requests that would touch a platform
 * (or customer) currently held in emergency.
 *
 * Three short-circuit conditions:
 *   1. Customer freeze — admin engaged a customer-level switch via
 *      `/rui/admin/ads/emergency`. Every customer-scoped ads route returns
 *      503 with the freeze reason. Read-only routes (the activity log,
 *      retention status) stay accessible so the customer can see *why* they
 *      are frozen.
 *   2. Platform-scoped route (e.g. `/rui/ads/platforms/{uid}/*`) where the
 *      resolved AdPlatform belongs to a killed platform. Returns 503.
 *   3. Globally-killed platforms always render the suspension banner via
 *      the shared `$adKillSwitch` view payload. Banner is informational —
 *      the customer can still navigate the rest of /rui/ads/.
 *
 * Env-based config switches (`ADS_KILL_META=true` etc) and admin-toggled
 * runtime switches (Setting table) are both honored — `KillSwitchStore`
 * already merges the two sources.
 */
class EmergencyKillSwitchCheck
{
    /** Routes that stay open even when the customer is frozen. */
    private const FREEZE_BYPASS_ROUTE_NAMES = [
        'refactor.ads.activity.index',
        'refactor.ads.activity.listing',
        'refactor.ads.activity.mark_read',
    ];

    public function __construct(private readonly KillSwitchStore $store)
    {
    }

    public function handle(Request $request, Closure $next): Response
    {
        $snapshot = $this->store->platformSnapshot();

        // Always share so the layout banner can render on every /rui/ads/* page.
        View::share('adKillSwitch', [
            'platforms' => $snapshot,
            'has_killed' => $this->anyKilled($snapshot),
        ]);

        $customer = optional($request->user())->customer;
        $customerLocal = $customer?->local();
        $customerId = $customerLocal?->id;

        // 1. Customer-level freeze.
        if ($customerId && $this->store->isCustomerFrozen((int) $customerId)) {
            $routeName = optional($request->route())->getName();
            if (!in_array($routeName, self::FREEZE_BYPASS_ROUTE_NAMES, true)) {
                return $this->frozenResponse($request, $this->store->customerReason((int) $customerId));
            }
        }

        // 2. Platform-scoped routes (resolve {uid} to an AdPlatform).
        $platformParam = optional($request->route())->parameter('uid');
        if (is_string($platformParam) && $this->isAdsPlatformsRoute($request) && $customerId) {
            $platform = AdPlatform::query()
                ->where('customer_id', $customerId)
                ->where('uid', $platformParam)
                ->first();
            if ($platform && $this->store->isPlatformKilled($platform->platform)) {
                return $this->platformKilledResponse(
                    $request,
                    $platform->platform,
                    $this->store->platformReason($platform->platform)
                );
            }
        }

        return $next($request);
    }

    /** @param array<string, array{killed: bool, reason: ?string, source: string}> $snapshot */
    private function anyKilled(array $snapshot): bool
    {
        foreach ($snapshot as $entry) {
            if ($entry['killed']) {
                return true;
            }
        }
        return false;
    }

    private function isAdsPlatformsRoute(Request $request): bool
    {
        return str_starts_with(ltrim($request->path(), '/'), 'rui/ads/platforms/');
    }

    private function frozenResponse(Request $request, ?string $reason): Response
    {
        $message = trans('refactor/ads_emergency.frozen.message');
        if ($request->expectsJson()) {
            return response()->json([
                'status' => 'frozen',
                'message' => $message,
                'reason' => $reason,
            ], 503);
        }
        return response()->view('refactor.pages.ads._suspended', [
            'kind' => 'frozen',
            'reason' => $reason,
            'message' => $message,
        ], 503);
    }

    private function platformKilledResponse(Request $request, string $platform, ?string $reason): Response
    {
        $label = AdPlatform::PLATFORM_LABELS[$platform] ?? ucfirst($platform);
        $message = trans('refactor/ads_emergency.platform_killed.message', ['platform' => $label]);
        if ($request->expectsJson()) {
            return response()->json([
                'status' => 'platform_suspended',
                'platform' => $platform,
                'message' => $message,
                'reason' => $reason,
            ], 503);
        }
        return response()->view('refactor.pages.ads._suspended', [
            'kind' => 'platform',
            'platform' => $label,
            'reason' => $reason,
            'message' => $message,
        ], 503);
    }
}