commit21
This commit is contained in:
@@ -21,13 +21,81 @@ class ReplenishmentQueue
|
||||
public function status(User $actor): array
|
||||
{
|
||||
abort_unless(auth()->id() === $actor->id, 403);
|
||||
$observation = $this->observation();
|
||||
|
||||
return ['recoveryReady' => $this->ready(), 'scanMinutes' => config('replenishment.scan_minutes'),
|
||||
'schedulerStatus' => $observation['schedulerStatus'], 'schedulerRecent' => $observation['schedulerRecent'],
|
||||
'work' => $this->ready() ? $this->workStatus($actor->tenant_id) : null];
|
||||
}
|
||||
|
||||
/** Workdesk sources, with a bound observation required for technical monitoring.
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function health(): array
|
||||
{
|
||||
abort_unless(app()->runningInConsole() && ! auth()->check() && ! request()->hasSession(), 403);
|
||||
|
||||
return DB::transaction(function () {
|
||||
if (DB::getDriverName() === 'pgsql' && DB::transactionLevel() === 1) {
|
||||
DB::statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
||||
}
|
||||
$ready = $this->ready();
|
||||
$observation = $this->observation();
|
||||
$work = $ready ? $this->workStatus(null, true) : null;
|
||||
$reasons = [];
|
||||
if (! $ready) {
|
||||
$reasons[] = 'not_installed';
|
||||
}
|
||||
if (! $observation['schedulerRecent']) {
|
||||
$reasons[] = 'not_recent';
|
||||
}
|
||||
if (($observation['schedulerStatus']['result'] ?? null) !== 'passed') {
|
||||
$reasons[] = 'last_run_not_successful';
|
||||
}
|
||||
if (! $observation['schedulerBound']) {
|
||||
$reasons[] = 'unbound_observation';
|
||||
}
|
||||
if ((int) ($work->pending ?? 0) > 0) {
|
||||
$reasons[] = 'pending_signals';
|
||||
}
|
||||
if ((int) ($work->failed ?? 0) > 0) {
|
||||
$reasons[] = 'failed_work';
|
||||
}
|
||||
|
||||
return ['checked_at' => now()->toIso8601String(), 'ok' => $reasons === [], 'reasons' => $reasons,
|
||||
'recovery_ready' => $ready, 'scheduler_recent' => $observation['schedulerRecent'],
|
||||
'last_result' => $observation['schedulerStatus']['result'] ?? null,
|
||||
'last_finished_at' => $observation['schedulerStatus']['finished_at'] ?? null,
|
||||
'work' => $work === null ? null : ['total' => (int) $work->total, 'pending' => (int) ($work->pending ?? 0), 'failed' => (int) ($work->failed ?? 0)]];
|
||||
});
|
||||
}
|
||||
|
||||
private function workStatus(?int $tenant, bool $global = false): \stdClass
|
||||
{
|
||||
return DB::table('replenishment_work')->when(! $global, fn ($query) => $query->where('tenant_id', $tenant))
|
||||
->selectRaw('COUNT(*) as total, SUM(CASE WHEN generation > processed_generation THEN 1 ELSE 0 END) as pending, SUM(CASE WHEN failures > 0 THEN 1 ELSE 0 END) as failed, MAX(last_run_at) as last_run_at')->first() ?? throw new \LogicException('Estado técnico indisponível.');
|
||||
}
|
||||
|
||||
/** @return array{schedulerStatus:array<string,mixed>|null,schedulerRecent:bool,schedulerBound:bool} */
|
||||
private function observation(): array
|
||||
{
|
||||
$scheduler = null;
|
||||
$recent = false;
|
||||
$path = storage_path('framework/replenishment-status.json');
|
||||
if (app()->environment('payable-testing') && is_file($path)) {
|
||||
$bound = false;
|
||||
$path = storage_path('framework/replenishment-command-status.json');
|
||||
if (! is_file($path) && app()->environment('payable-testing')) {
|
||||
$path = storage_path('framework/replenishment-status.json');
|
||||
}
|
||||
if (is_file($path)) {
|
||||
$text = file_get_contents($path);
|
||||
$data = $text === false ? null : json_decode($text, true);
|
||||
if (is_array($data) && is_string($data['finished_at'] ?? null) && in_array($data['result'] ?? null, ['passed', 'retry_pending'], true)) {
|
||||
if (array_key_exists('installation', $data)) {
|
||||
if (! is_string($data['installation']) || ! hash_equals(app(ReplenishmentHeartbeat::class)->fingerprint(), $data['installation'])) {
|
||||
return ['schedulerStatus' => null, 'schedulerRecent' => false, 'schedulerBound' => false];
|
||||
}
|
||||
$bound = true;
|
||||
}
|
||||
try {
|
||||
$finished = CarbonImmutable::parse($data['finished_at']);
|
||||
$recent = $finished->greaterThanOrEqualTo(now()->subMinutes(3)) && $finished->lessThanOrEqualTo(now()->addMinute());
|
||||
@@ -37,8 +105,7 @@ class ReplenishmentQueue
|
||||
}
|
||||
}
|
||||
|
||||
return ['recoveryReady' => $this->ready(), 'scanMinutes' => config('replenishment.scan_minutes'), 'schedulerStatus' => $scheduler, 'schedulerRecent' => $recent,
|
||||
'work' => $this->ready() ? DB::table('replenishment_work')->where('tenant_id', $actor->tenant_id)->selectRaw('COUNT(*) as total, SUM(CASE WHEN generation > processed_generation THEN 1 ELSE 0 END) as pending, SUM(CASE WHEN failures > 0 THEN 1 ELSE 0 END) as failed, MAX(last_run_at) as last_run_at')->first() : null];
|
||||
return ['schedulerStatus' => $scheduler, 'schedulerRecent' => $recent, 'schedulerBound' => $bound];
|
||||
}
|
||||
|
||||
public function ready(): bool
|
||||
|
||||
Reference in New Issue
Block a user