Teren-app/app/Console/Commands/GenerateMissingPreviews.php
2025-09-28 22:36:47 +02:00

99 lines
3.3 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Jobs\GenerateDocumentPreview;
use App\Models\Document;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class GenerateMissingPreviews extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'documents:generate-previews {--now : Run the preview job synchronously instead of queueing it} {--limit=100 : Max documents to process}';
/**
* The console command description.
*/
protected $description = 'Queue or run preview generation for DOC/DOCX documents that are missing a generated preview.';
/**
* Execute the console command.
*/
public function handle(): int
{
$limit = (int) $this->option('limit');
$now = (bool) $this->option('now');
$docs = Document::query()
->whereNull('deleted_at')
->where(function ($q) {
$q->whereRaw("lower(extension) in ('doc','docx')");
})
->orderByDesc('updated_at')
->limit($limit * 5)
->get();
if ($docs->isEmpty()) {
$this->info('No documents requiring preview generation.');
return self::SUCCESS;
}
$this->info('Scanning '.$docs->count().' candidate document(s) for (re)generation...');
$dispatched = 0;
foreach ($docs as $doc) {
// Verify source file exists on disk or under public before dispatching
$disk = $doc->disk ?: 'public';
$relPath = ltrim($doc->path ?? '', '/\\');
if (str_starts_with($relPath, 'public/')) {
$relPath = substr($relPath, 7);
}
$has = Storage::disk($disk)->exists($relPath);
if (! $has) {
$publicFull = public_path($relPath);
$real = @realpath($publicFull);
$publicRoot = @realpath(public_path());
$realN = $real ? str_replace('\\\\', '/', $real) : null;
$rootN = $publicRoot ? str_replace('\\\\', '/', $publicRoot) : null;
$has = $realN && $rootN && str_starts_with($realN, $rootN) && is_file($real);
}
if (! $has) {
$this->warn('Skipping doc '.$doc->id.' (source file missing): '.$doc->path);
continue;
}
// Determine if (re)generation is required
$needs = false;
$previewDisk = config('files.preview_disk', 'public');
if (empty($doc->preview_path)) {
$needs = true;
} else {
$existsPreview = Storage::disk($previewDisk)->exists($doc->preview_path);
if (! $existsPreview) {
$needs = true;
} elseif ($doc->preview_generated_at && $doc->updated_at && $doc->updated_at->gt($doc->preview_generated_at)) {
$needs = true;
}
}
if (! $needs) {
continue;
}
if ($now) {
GenerateDocumentPreview::dispatchSync($doc->id);
} else {
GenerateDocumentPreview::dispatch($doc->id);
}
$dispatched++;
}
$this->info(($now ? 'Ran' : 'Queued').' preview generation for '.$dispatched.' document(s).');
return self::SUCCESS;
}
}