get()) { throw new SyncAlreadyRunningException; } try { return $this->runSync(); } finally { $lock->release(); } } private function runSync(): SyncRun { $startedAt = now(); $run = SyncRun::query()->create([ 'type' => 'assets_full', 'started_at' => $startedAt, 'status' => SyncRunStatus::Running, ]); $processed = 0; $created = 0; $updated = 0; try { foreach ($this->client->hardwarePages() as $page) { DB::transaction(function () use ($page, $run, $startedAt, &$processed, &$created, &$updated): void { foreach ($page->rows as $row) { $assetData = $this->mapper->map($row); $hash = $assetData->contentHash(); $asset = SnipeAsset::query() ->where('snipe_asset_id', $assetData->snipeAssetId) ->first(); $attributes = [ ...$assetData->toModelAttributes(), 'synced_at' => $startedAt, 'last_seen_sync_run_id' => $run->id, 'content_hash' => $hash, 'active' => true, ]; if ($asset === null) { SnipeAsset::query()->create($attributes); $created++; } else { $changed = $asset->content_hash !== $hash || ! $asset->active; $asset->update($attributes); if ($changed) { $updated++; } } $processed++; } }); } $disabled = DB::transaction(fn (): int => SnipeAsset::query() ->where('active', true) ->where(fn ($query) => $query ->whereNull('last_seen_sync_run_id') ->orWhere('last_seen_sync_run_id', '!=', $run->id)) ->update(['active' => false])); $run->update([ 'finished_at' => now(), 'status' => SyncRunStatus::Succeeded, 'records_processed' => $processed, 'records_created' => $created, 'records_updated' => $updated, 'records_disabled' => $disabled, ]); return $run->refresh(); } catch (Throwable $exception) { $message = preg_replace('/Bearer\s+\S+/i', 'Bearer [redacted]', $exception->getMessage()) ?? 'Falha desconhecida durante a sincronização.'; $run->update([ 'finished_at' => now(), 'status' => SyncRunStatus::Failed, 'records_processed' => $processed, 'records_created' => $created, 'records_updated' => $updated, 'error_message' => Str::limit($message, 2000), ]); throw $exception; } } }