Files
2026-08-29 15:53:53 +03:00

43 lines
1.6 KiB
PHP

<?php
namespace App\Serp\Operations;
use App\Models\SerpOperationState;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\DB;
final class KillSwitch
{
public function __construct(private OperationAuditLogger $audit) {}
public function active(): bool
{
return SerpOperationState::query()->find(1)?->emergency_stopped_at !== null;
}
public function activate(string $reason, ?User $actor = null): void
{
DB::transaction(function () use ($reason, $actor) {
$s = SerpOperationState::query()->lockForUpdate()->find(1) ?: new SerpOperationState(['id' => 1]);
if (! $s->emergency_stopped_at) {
$s->forceFill(['emergency_reason_code' => $reason, 'emergency_stopped_at' => now(), 'emergency_actor_id' => $actor?->id])->save();
$this->audit->record('kill_switch_activated', ['reason' => $reason], $actor);
}
});
}
public function clear(User $actor, string $reason): void
{
if (! $actor->hasRole('super_admin') || ! $actor->hasPermission('serp_operations.emergency_stop')) {
throw new AuthorizationException;
}DB::transaction(function () use ($actor, $reason) {
$s = SerpOperationState::query()->lockForUpdate()->find(1);
if ($s?->emergency_stopped_at) {
$s->forceFill(['emergency_reason_code' => null, 'emergency_stopped_at' => null, 'emergency_actor_id' => null])->save();
$this->audit->record('kill_switch_cleared', ['reason' => $reason], $actor);
}
});
}
}