Implements Spec 114 System Console Control Tower pages, widgets, triage actions, directory views, and enterprise polish (badges, repair workspace owners table, health indicator).
87 lines
2.8 KiB
PHP
87 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\SystemConsole;
|
|
|
|
use App\Models\OperationRun;
|
|
use App\Support\OperationRunStatus;
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
final class StuckRunClassifier
|
|
{
|
|
public function __construct(
|
|
private readonly int $queuedThresholdMinutes = 0,
|
|
private readonly int $runningThresholdMinutes = 0,
|
|
) {}
|
|
|
|
public function queuedThresholdMinutes(): int
|
|
{
|
|
if ($this->queuedThresholdMinutes > 0) {
|
|
return $this->queuedThresholdMinutes;
|
|
}
|
|
|
|
return max(1, (int) config('tenantpilot.system_console.stuck_thresholds.queued_minutes', 15));
|
|
}
|
|
|
|
public function runningThresholdMinutes(): int
|
|
{
|
|
if ($this->runningThresholdMinutes > 0) {
|
|
return $this->runningThresholdMinutes;
|
|
}
|
|
|
|
return max(1, (int) config('tenantpilot.system_console.stuck_thresholds.running_minutes', 30));
|
|
}
|
|
|
|
public function apply(Builder $query, ?CarbonImmutable $now = null): Builder
|
|
{
|
|
$now ??= CarbonImmutable::now();
|
|
|
|
$queuedCutoff = $now->subMinutes($this->queuedThresholdMinutes());
|
|
$runningCutoff = $now->subMinutes($this->runningThresholdMinutes());
|
|
|
|
return $query->where(function (Builder $stuckQuery) use ($queuedCutoff, $runningCutoff): void {
|
|
$stuckQuery
|
|
->where(function (Builder $queuedQuery) use ($queuedCutoff): void {
|
|
$queuedQuery
|
|
->where('status', OperationRunStatus::Queued->value)
|
|
->whereNull('started_at')
|
|
->where('created_at', '<=', $queuedCutoff);
|
|
})
|
|
->orWhere(function (Builder $runningQuery) use ($runningCutoff): void {
|
|
$runningQuery
|
|
->where('status', OperationRunStatus::Running->value)
|
|
->where('started_at', '<=', $runningCutoff);
|
|
});
|
|
});
|
|
}
|
|
|
|
public function classify(OperationRun $run, ?CarbonImmutable $now = null): ?string
|
|
{
|
|
$now ??= CarbonImmutable::now();
|
|
|
|
if ($run->status === OperationRunStatus::Queued->value) {
|
|
if ($run->started_at !== null) {
|
|
return null;
|
|
}
|
|
|
|
if ($run->created_at === null) {
|
|
return null;
|
|
}
|
|
|
|
return $run->created_at->lte($now->subMinutes($this->queuedThresholdMinutes()))
|
|
? OperationRunStatus::Queued->value
|
|
: null;
|
|
}
|
|
|
|
if ($run->status === OperationRunStatus::Running->value && $run->started_at !== null) {
|
|
return $run->started_at->lte($now->subMinutes($this->runningThresholdMinutes()))
|
|
? OperationRunStatus::Running->value
|
|
: null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|