fn_b/app/Console/Commands/SyncLocalIllustrations.php

54 lines
2.8 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Articles;
use App\Services\IllustrationPool;
use App\Services\LocalIllustrations;
use Illuminate\Console\Command;
use Spatie\ResponseCache\Facades\ResponseCache;
final class SyncLocalIllustrations extends Command
{
protected $signature = 'illustrations:sync-local {--assign-missing : Create missing Resource assignments for existing public articles}';
protected $description = 'Copy approved Resource images into deduplicated local static files and atomically reconcile article mappings';
public function handle(LocalIllustrations $local): int
{
if (! IllustrationPool::enabled()) {
$this->error('Resource binding is not enabled for this environment');
return self::FAILURE;
}
try {
$baseline = $local->selectionBaseline();
$snapshots = [];
$expected = [];
foreach (config('illustrations.categories') as $category => $pool) {
$keys = Articles::where('iCategoryId', $category)->where('iStatus', 1)->orderBy('created_at')->orderBy('id')->pluck('sSlug')->all();
IllustrationPool::forget($pool);
$snapshot = IllustrationPool::sourceSnapshot($pool);
$missing = array_values(array_diff($keys, array_keys($snapshot['selections']), array_keys($snapshot['unavailable'] ?? [])));
if ($missing && ! $this->option('assign-missing')) throw new \RuntimeException('Missing article assignments; rerun with --assign-missing using a write-scoped consumer');
foreach (array_chunk($missing, 200) as $chunk) IllustrationPool::assignSource($pool, $chunk);
if ($missing) $snapshot = IllustrationPool::sourceSnapshot($pool);
$snapshots[$pool] = $snapshot;
$expected[$pool] = $keys;
}
$result = $local->synchronize($snapshots, $baseline);
foreach ($expected as $pool => $keys) {
foreach ($keys as $key) {
if (! $local->thumbnail($pool, $key)) $result['pending'][$pool.':'.$key] ??= 'local_copy_unavailable';
}
}
ResponseCache::clear();
\Illuminate\Support\Facades\Cache::forget('article_order_likes');
$this->line(json_encode($result, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
return $result['pending'] ? self::FAILURE : self::SUCCESS;
} catch (\Throwable) {
// Never print request/credential exceptions or leave partial manifest writes.
$this->error('Local illustration sync failed; existing manifest retained if publication did not complete. Retry after checking Resource and filesystem access.');
return self::FAILURE;
}
}
}