43 lines
1.6 KiB
PHP
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);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|