getQueryString(); return redirect('/configuratore/'.($query ? '?'.$query : '')); }); Route::get('/admin/preventivi/{preventivo}/pdf', function (Preventivo $preventivo) { if (! $preventivo->pdf_path || ! Storage::disk('local')->exists($preventivo->pdf_path)) { $preventivo->pdf_path = PreventivoPdf::genera($preventivo); $preventivo->save(); } return Storage::disk('local')->response( $preventivo->pdf_path, 'preventivo-'.($preventivo->numero ?? $preventivo->id).'.pdf' ); })->middleware(['auth'])->name('preventivi.pdf.show'); // Configuratore 3D e app cliente (preventivo) sono build Vite statiche dentro public/ (vedi build.sh): // i file (js, css, assets) li serve direttamente il web server, qui gestiamo solo l'index. // Lo slash finale è obbligatorio perché i path runtime sono relativi (./assets/...). foreach (['configuratore', 'preventivo'] as $frontend) { Route::get($frontend, function (Request $request) use ($frontend) { if (! str_ends_with($request->getPathInfo(), '/')) { $query = $request->getQueryString(); return redirect('/'.$frontend.'/'.($query ? '?'.$query : ''), 301); } $index = public_path($frontend.'/index.html'); abort_unless(is_file($index), 404, "Build di {$frontend} assente: eseguire build.sh."); return response()->file($index, ['Cache-Control' => 'no-cache']); })->name($frontend); }