find($this->restoreRunId); if (! $restoreRun) { return; } if ($restoreRun->status !== RestoreRunStatus::Queued->value) { return; } $this->notifyStatus($restoreRun, 'queued'); app(SyncRestoreRunToOperationRun::class)->handle($restoreRun); $tenant = $restoreRun->tenant; $backupSet = $restoreRun->backupSet; if (! $tenant || ! $backupSet || $backupSet->trashed()) { $restoreRun->update([ 'status' => RestoreRunStatus::Failed->value, 'failure_reason' => 'Backup set is archived or unavailable.', 'completed_at' => CarbonImmutable::now(), ]); app(SyncRestoreRunToOperationRun::class)->handle($restoreRun->refresh()); $this->notifyStatus($restoreRun->refresh(), 'failed'); if ($tenant) { $auditLogger->log( tenant: $tenant, action: 'restore.failed', context: [ 'metadata' => [ 'restore_run_id' => $restoreRun->id, 'backup_set_id' => $restoreRun->backup_set_id, 'reason' => 'Backup set is archived or unavailable.', ], ], actorEmail: $this->actorEmail, actorName: $this->actorName, resourceType: 'restore_run', resourceId: (string) $restoreRun->id, status: 'failed', ); } return; } $restoreRun->update([ 'status' => RestoreRunStatus::Running->value, 'started_at' => CarbonImmutable::now(), 'failure_reason' => null, ]); // Keep the canonical Monitoring/Operations adapter row in sync even if downstream // code performs restore-run updates without firing model events. app(SyncRestoreRunToOperationRun::class)->handle($restoreRun->refresh()); $this->notifyStatus($restoreRun->refresh(), 'running'); $auditLogger->log( tenant: $tenant, action: 'restore.started', context: [ 'metadata' => [ 'restore_run_id' => $restoreRun->id, 'backup_set_id' => $backupSet->id, ], ], actorEmail: $this->actorEmail, actorName: $this->actorName, resourceType: 'restore_run', resourceId: (string) $restoreRun->id, status: 'success', ); try { $restoreService->executeForRun( restoreRun: $restoreRun, tenant: $tenant, backupSet: $backupSet, actorEmail: $this->actorEmail, actorName: $this->actorName, ); app(SyncRestoreRunToOperationRun::class)->handle($restoreRun->refresh()); $this->notifyStatus($restoreRun->refresh(), (string) $restoreRun->status); } catch (Throwable $throwable) { $restoreRun->refresh(); $safeReason = RunFailureSanitizer::sanitizeMessage($throwable->getMessage()); if ($restoreRun->status === RestoreRunStatus::Running->value) { $restoreRun->update([ 'status' => RestoreRunStatus::Failed->value, 'failure_reason' => $safeReason, 'completed_at' => CarbonImmutable::now(), ]); } app(SyncRestoreRunToOperationRun::class)->handle($restoreRun->refresh()); $this->notifyStatus($restoreRun->refresh(), (string) $restoreRun->status); if ($tenant) { $auditLogger->log( tenant: $tenant, action: 'restore.failed', context: [ 'metadata' => [ 'restore_run_id' => $restoreRun->id, 'backup_set_id' => $backupSet->id, 'reason' => $safeReason, ], ], actorEmail: $this->actorEmail, actorName: $this->actorName, resourceType: 'restore_run', resourceId: (string) $restoreRun->id, status: 'failed', ); } throw $throwable; } } private function notifyStatus(RestoreRun $restoreRun, string $status): void { $email = $this->actorEmail; if (! is_string($email) || $email === '') { $email = is_string($restoreRun->requested_by) ? $restoreRun->requested_by : null; } if (! is_string($email) || $email === '') { return; } $user = User::query()->where('email', $email)->first(); if (! $user) { return; } $metadata = is_array($restoreRun->metadata) ? $restoreRun->metadata : []; $counts = []; foreach (['total', 'succeeded', 'failed', 'skipped'] as $key) { if (array_key_exists($key, $metadata) && is_numeric($metadata[$key])) { $counts[$key] = (int) $metadata[$key]; } } $payload = [ 'tenant_id' => (int) $restoreRun->tenant_id, 'run_type' => 'restore', 'run_id' => (int) $restoreRun->getKey(), 'status' => $status, ]; if ($counts !== []) { $payload['counts'] = $counts; } $user->notify(new RunStatusChangedNotification($payload)); } }