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);
}
}