*/ public array $backoff = [60, 300, 900]; public bool $failOnTimeout = true; public bool $deleteWhenMissingModels = true; public function __construct(public ExternalUserSource $source) { } public function uniqueId(): string { return (string) $this->source->getKey(); } public static function statusKey(int $sourceId): string { return 'external-user-sync-status-' . $sourceId; } public function handle(ExternalUserSyncService $syncService): void { $source = $this->source->fresh(); if (!$source || $source->type !== 'ldap' || !$source->active) { $this->storeStatus( 'cancelled', __('LDAP synchronization was cancelled because the source is no longer active.') ); return; } $lock = Cache::lock('external-user-sync-' . $this->source->id, self::LOCK_SECONDS); if (!$lock->get()) { $this->storeStatus('waiting', __('Another sync is still running. The queued sync will retry.')); $this->release(60); return; } $this->storeStatus('running', __('LDAP synchronization is running.')); try { $result = $syncService->syncSource($source); $this->storeStatus('completed', __('LDAP synchronization completed.'), $result); } catch (Throwable $exception) { $this->storeStatus( 'retrying', __('LDAP synchronization failed and will be retried.'), null, $exception::class . ': ' . $exception->getMessage() ); throw $exception; } finally { $lock->release(); } } public function failed(?Throwable $exception): void { $this->storeStatus( 'failed', __('LDAP synchronization failed. Please check the logs.'), null, $exception === null ? null : $exception::class . ': ' . $exception->getMessage() ); } /** @param array{total:int, synced:int, skipped:int}|null $result */ private function storeStatus( string $status, string $message, ?array $result = null, ?string $error = null ): void { Cache::put(self::statusKey($this->source->id), [ 'status' => $status, 'message' => $message, 'result' => $result, 'error' => $error, 'updated_at' => now()->toIso8601String(), ], now()->addDay()); } }