changes 0328092025
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ClientCase;
|
||||
use App\Models\Contract;
|
||||
use App\Models\Document;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DebugDocumentView extends Command
|
||||
{
|
||||
protected $signature = 'debug:document {doc_uuid} {case_uuid}';
|
||||
|
||||
protected $description = 'Diagnose why document view returns 404 (binding, ownership, file presence)';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$docUuid = (string) $this->argument('doc_uuid');
|
||||
$caseUuid = (string) $this->argument('case_uuid');
|
||||
|
||||
$case = ClientCase::where('uuid', $caseUuid)->first();
|
||||
if (! $case) {
|
||||
$this->error('ClientCase not found by uuid: '.$caseUuid);
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
$this->info('ClientCase found: id='.$case->id.' uuid='.$case->uuid);
|
||||
|
||||
$doc = Document::withTrashed()->where('uuid', $docUuid)->first();
|
||||
if (! $doc) {
|
||||
$this->error('Document not found by uuid (including trashed): '.$docUuid);
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
$this->info('Document found: id='.$doc->id.' uuid='.$doc->uuid.' trashed='.(int) ($doc->deleted_at !== null));
|
||||
$this->line(' documentable_type='.$doc->documentable_type.' documentable_id='.$doc->documentable_id);
|
||||
$this->line(' disk='.$doc->disk.' path='.$doc->path);
|
||||
$this->line(' preview_path='.(string) $doc->preview_path.' preview_mime='.(string) $doc->preview_mime);
|
||||
|
||||
// Ownership check like in controller
|
||||
$belongsToCase = $doc->documentable_type === ClientCase::class && $doc->documentable_id === $case->id;
|
||||
$belongsToContractOfCase = false;
|
||||
if ($doc->documentable_type === Contract::class) {
|
||||
$belongsToContractOfCase = Contract::withTrashed()
|
||||
->where('id', $doc->documentable_id)
|
||||
->where('client_case_id', $case->id)
|
||||
->exists();
|
||||
}
|
||||
$this->line('Ownership: belongsToCase='.(int) $belongsToCase.' belongsToContractOfCase='.(int) $belongsToContractOfCase);
|
||||
|
||||
// File existence checks
|
||||
$disk = $doc->disk ?: 'public';
|
||||
$relPath = ltrim($doc->path ?? '', '/\\');
|
||||
if (str_starts_with($relPath, 'public/')) {
|
||||
$relPath = substr($relPath, 7);
|
||||
}
|
||||
$existsOnDisk = Storage::disk($disk)->exists($relPath);
|
||||
$this->line('Source exists on disk='.$existsOnDisk.' (disk='.$disk.' relPath='.$relPath.')');
|
||||
if (! $existsOnDisk) {
|
||||
$publicFull = public_path($relPath);
|
||||
$this->line('Public candidate='.$publicFull.' exists='.(int) is_file($publicFull));
|
||||
}
|
||||
|
||||
$previewDisk = config('files.preview_disk', 'public');
|
||||
$previewExists = $doc->preview_path ? Storage::disk($previewDisk)->exists($doc->preview_path) : false;
|
||||
$this->line('Preview exists on previewDisk='.$previewExists.' (disk='.$previewDisk.' path='.(string) $doc->preview_path.')');
|
||||
|
||||
$this->info('Done. Compare with controller logic to pin the 404 branch.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user