error('Illustration delivery is disabled'); return self::FAILURE; } $dir = dirname(config('illustrations.local_manifest')); if (! is_dir($dir)) { mkdir($dir, 0700, true); } $lock = fopen($dir.'/delivery.lock', 'c'); if (! $lock) { return self::FAILURE; } if (! flock($lock, LOCK_EX | LOCK_NB)) { fclose($lock); return self::SUCCESS; } try { $pending = array_slice(IllustrationPool::pendingDeliveries(), 0, 3); // Bound metadata calls below the consumer rate limit. if (! $pending) { $this->line('No pending illustration deliveries'); return self::SUCCESS; } $snapshots = []; $images = []; foreach ($pending as $batch) { foreach ($batch['assets'] ?? [] as $asset) { $pool = $asset['pool'] ?? ''; if (! in_array($pool, config('illustrations.categories'), true)) { throw new \RuntimeException('invalid_pool'); } if (! isset($snapshots[$pool])) { $snapshots[$pool] = IllustrationPool::sourceSnapshot($pool); } $match = null; foreach ($snapshots[$pool]['items'] as $image) { if (($image['asset_id'] ?? '') === ($asset['asset_id'] ?? null)) { $match = $image; } } if (! $match) { throw new \RuntimeException('delivery_asset_unavailable'); } $images[$asset['asset_id']] = $match; } } app(LocalIllustrations::class)->cacheDeliveryAssets(array_values($images)); $bytes = file_get_contents(config('illustrations.local_manifest')); $manifest = json_decode($bytes, true, 512, JSON_THROW_ON_ERROR); foreach ($pending as $batch) { if (($batch['state'] ?? '') !== 'awaiting_sync' || empty($batch['assets'])) { throw new \RuntimeException('invalid_delivery'); } foreach ($batch['assets'] as $asset) { $local = $manifest['assets'][$asset['asset_id']] ?? null; $sha = $asset['sha256'] ?? ''; if (! preg_match('/\A[a-f0-9]{64}\z/D', $sha) || ! $local || ($local['sha256'] ?? '') !== $sha || ($local['mime'] ?? '') !== 'image/webp') { throw new \RuntimeException('missing_copy'); } $path = rtrim(config('illustrations.local_directory'), '/').'/'.$sha.'.webp'; if (! is_file($path) || is_link($path) || hash_file('sha256', $path) !== $sha) { throw new \RuntimeException('invalid_copy'); } } $ack = IllustrationPool::acknowledgeDelivery($batch['id'], ['fingerprint' => $batch['fingerprint'], 'manifest_sha256' => hash('sha256', $bytes)]); if (($ack['state'] ?? '') !== 'completed') { throw new \RuntimeException('ack_failed'); } $this->line('Completed illustration delivery '.$batch['id']); } return self::SUCCESS; } catch (\Throwable) { $this->error('Illustration delivery sync incomplete; will retry without a completion ACK.'); return self::FAILURE; } finally { flock($lock, LOCK_UN); fclose($lock); } } }