[ 'title' => $mode === 'update' ? 'Aggiornamento KondoManager' : 'Installazione KondoManager', 'welcome' => $mode === 'update' ? 'Aggiornamento sistema' : 'Benvenuto in KondoManager', 'tagline' => $mode === 'update' ? 'Aggiornamento alla versione v%s' : 'Installazione nuova versione v%s', 'dont_close' => 'Non chiudere questa pagina fino al completamento.', 'error_title' => 'Impossibile procedere', 'ready_msg' => 'Tutto pronto. Scegli l\'operazione da eseguire.', 'btn_text' => $mode === 'update' ? 'Aggiorna ora' : 'Installa ora', 'progress' => 'Avanzamento', 'step_start' => 'Inizializzazione...', 'step_down' => 'Download pacchetto...', 'step_down_prog'=> 'Download: %s MB / %s MB', 'step_hash' => 'Verifica integrità (SHA256)...', 'step_backup' => 'Salvataggio dati (Solo .env)...', // TESTO MODIFICATO 'step_unzip' => 'Estrazione sicura...', 'step_install' => 'Applicazione aggiornamento...', 'step_opcache' => 'Pulizia cache PHP (OPcache)...', 'step_health' => 'Verifica integrità post-installazione...', 'step_htaccess' => 'Aggiornamento regole server (.htaccess)...', 'step_clean' => 'Pulizia cache e file temporanei...', 'step_done' => 'Operazione completata con successo!', 'step_rollback' => 'ERRORE RILEVATO! Ripristino backup in corso...', 'chk_php' => 'Versione PHP', 'chk_ext' => 'Estensioni richieste', 'chk_perm' => 'Permessi di Scrittura', 'chk_https' => 'Connessione Sicura (HTTPS)', 'chk_rewrite' => 'Modulo Apache Rewrite', 'chk_disk' => 'Spazio disco disponibile', 'chk_pkg' => 'Pacchetto remoto v%s', 'chk_ok' => 'OK', 'chk_warn' => 'Assente (Consigliato)', 'chk_err' => 'Errore', 'chk_unknown' => 'Non verificabile', 'pkg_ok' => 'Disponibile', 'pkg_err' => 'Non raggiungibile', 'err_php' => 'PHP %s+ richiesto (Rilevato: %s)', 'err_ext' => 'Estensione mancante: %s', 'err_write' => 'Cartella non scrivibile. Imposta permessi 755.', 'err_disk' => 'Spazio insufficiente (%s MB liberi, min 100 MB)', 'err_pkg' => 'Pacchetto non raggiungibile (HTTP %s)', 'err_hash_format' => 'Hash configurato non valido (SHA256 richiesto)', 'err_csrf' => 'Sessione scaduta. Ricarica la pagina.', 'err_ratelimit' => 'Troppi tentativi. Attendi 60 secondi.', 'err_hash_mismatch' => 'ERRORE SICUREZZA: Hash non corrispondente!', 'err_download' => 'Errore durante il download: %s', 'err_zip' => 'Impossibile aprire l\'archivio ZIP.', 'err_mkdir' => 'Impossibile creare cartella: %s', 'err_copy' => 'Errore durante la copia del file: %s', 'err_health' => 'Health Check fallito: File critici mancanti (%s)', 'msg_rollback_ok' => 'Backup ripristinato. Il sistema è tornato alla configurazione precedente.', 'msg_rollback_ko' => 'ROLLBACK FALLITO. Controllare manualmente la cartella _km_safe_zone.', 'pre_check_hint' => 'Verifica connessione internet, SSL o configurazione hash.', 'warn_update' => 'ATTENZIONE: Verranno sovrascritti i file di sistema. Database e storage sono al sicuro.', ], 'en' => [ 'title' => $mode === 'update' ? 'KondoManager update' : 'KondoManager installation', 'welcome' => $mode === 'update' ? 'System update' : 'Welcome to KondoManager', 'tagline' => $mode === 'update' ? 'Updating to version v%s' : 'Installing new version v%s', 'dont_close' => 'Do not close this page until completion.', 'error_title' => 'Cannot proceed', 'ready_msg' => 'Ready. Please select an action.', 'btn_text' => $mode === 'update' ? 'Update now' : 'Install now', 'progress' => 'Progress', 'step_start' => 'Initializing...', 'step_down' => 'Downloading package...', 'step_down_prog'=> 'Download: %s MB / %s MB', 'step_hash' => 'Integrity check (SHA256)...', 'step_backup' => 'Backing up data (Only .env)...', // TESTO MODIFICATO 'step_unzip' => 'Safe extraction...', 'step_install' => 'Applying update...', 'step_opcache' => 'Clearing PHP cache...', 'step_health' => 'Post-installation Health Check...', 'step_htaccess' => 'Updating server rules (.htaccess)...', 'step_clean' => 'Cleaning cache & temp files...', 'step_done' => 'Operation completed successfully!', 'step_rollback' => 'ERROR DETECTED! Restoring backup...', 'chk_php' => 'PHP Version', 'chk_ext' => 'Required extensions', 'chk_perm' => 'Write permissions', 'chk_https' => 'Secure Connection (HTTPS)', 'chk_rewrite' => 'Apache Rewrite Module', 'chk_disk' => 'Available Disk Space', 'chk_pkg' => 'Remote package v%s', 'chk_ok' => 'OK', 'chk_warn' => 'Missing (Recommended)', 'chk_err' => 'Error', 'chk_unknown' => 'Unknown', 'pkg_ok' => 'Available', 'pkg_err' => 'Not reachable', 'err_php' => 'PHP %s+ required (Detected: %s)', 'err_ext' => 'Missing extension: %s', 'err_write' => 'Folder not writable. Set permissions to 755.', 'err_disk' => 'Insufficient disk space (%s MB free, min 100 MB)', 'err_pkg' => 'Package not reachable (HTTP %s)', 'err_hash_format' => 'Invalid hash configuration (SHA256 required)', 'err_csrf' => 'Session expired. Please reload.', 'err_ratelimit' => 'Too many attempts. Wait 60 seconds.', 'err_hash_mismatch' => 'SECURITY ERROR: Hash mismatch!', 'err_download' => 'Download error: %s', 'err_zip' => 'Cannot open ZIP archive.', 'err_mkdir' => 'Cannot create folder: %s', 'err_copy' => 'File copy error: %s', 'err_health' => 'Health Check failed: Critical files missing (%s)', 'msg_rollback_ok' => 'Backup restored. System reverted to previous configuration.', 'msg_rollback_ko' => 'ROLLBACK FAILED. Please check _km_safe_zone manually.', 'pre_check_hint' => 'Check internet connection, SSL or hash configuration.', 'warn_update' => 'WARNING: System files will be overwritten. Database and storage will be preserved.', ] ]; function getLang() { $lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en'; return strpos(strtolower($lang), 'it') === 0 ? 'it' : 'en'; } function t($key, ...$args) { global $langs; $text = $langs[getLang()][$key] ?? $key; if (empty($args) && strpos($text, '%s') !== false) { $args = [APP_VERSION]; } return vsprintf($text, $args); } // ============================================================================ // 4. HELPER FUNCTIONS // ============================================================================ function logTech($msg) { @file_put_contents(LOG_FILE, '[' . date('c') . '] ' . $msg . PHP_EOL, FILE_APPEND); } function safeRmdir($dir) { if (!is_dir($dir)) return false; $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($it as $file) { $op = $file->isDir() ? 'rmdir' : 'unlink'; @$op($file->getRealPath()); } return @rmdir($dir); } function performRollback() { logTech("ROLLBACK: Avvio procedura..."); if (!is_dir(BACKUP_DIR)) return false; $items = new RecursiveIteratorIterator( new RecursiveDirectoryIterator(BACKUP_DIR, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($items as $item) { $relativePath = substr($item->getPathname(), strlen(BACKUP_DIR) + 1); $targetPath = __DIR__ . '/' . $relativePath; if ($item->isDir()) { if (!is_dir($targetPath)) @mkdir($targetPath, 0755, true); } else { @copy($item->getPathname(), $targetPath); } } return true; } function formatBytes($bytes, $precision = 2) { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, $precision) . ' ' . $units[$pow]; } // FIX 2: FUNZIONE GLOBALE CON FLUSH function sendProgress($pct, $msg, $status='current', $replace=false) { global $nonce; $pct = round($pct); $msg = addslashes($msg); $repStr = $replace ? 'true' : 'false'; echo ""; echo str_repeat(' ', 4096); if (ob_get_level() > 0) ob_flush(); flush(); } session_start(); // ============================================================================ // 6. CSS STYLES // ============================================================================ $css = <<=')) $errors[] = t('err_php', MIN_PHP_VERSION, PHP_VERSION); $requiredExt = ['zip', 'curl', 'openssl', 'mbstring', 'fileinfo']; foreach ($requiredExt as $ext) if (!extension_loaded($ext)) $errors[] = t('err_ext', $ext); if (!is_writable(__DIR__)) $errors[] = t('err_write'); if ($freeSpaceMB < 100 * 1024 * 1024) $errors[] = t('err_disk', formatBytes($freeSpaceMB)); $pkgStatus = 'unknown'; $preCheckError = ''; if (function_exists('curl_init')) { $ch = curl_init(PACKAGE_URL); curl_setopt_array($ch, [ CURLOPT_NOBODY => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_FOLLOWLOCATION => true ]); curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $pkgStatus = 'ok'; } else { $errors[] = t('err_pkg', $httpCode); $preCheckError = t('err_pkg', $httpCode); } } if (!preg_match('/^[a-f0-9]{64}$/i', PACKAGE_HASH)) { $errors[] = t('err_hash_format'); $preCheckError = t('err_hash_format'); } $allOk = empty($errors); ?> <?= t('title') ?>
Km

⚠️
⚠️
⚠️ ⚠️

<?= t('title') ?>
Km

0%
$fp, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 600, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_NOPROGRESS => false, CURLOPT_PROGRESSFUNCTION => function($r, $ds, $dld) { if ($ds > 0) { static $last_mb = 0; $current_mb = $dld / 1048576; if ($current_mb - $last_mb >= 0.5) { $pct = 10 + ($dld / $ds) * 30; $msg = sprintf("Download: %.1f MB / %.1f MB", $current_mb, $ds/1048576); global $nonce; // Necessario per la closure echo ""; echo str_repeat(' ', 4096); flush(); $last_mb = $current_mb; } } } ]); curl_exec($ch); if (curl_errno($ch)) throw new Exception(t('err_download', curl_error($ch))); fclose($fp); curl_close($ch); // 3. Hash Check sendProgress(40, t('step_hash')); $calculated = hash_file('sha256', ZIP_FILE); if (!hash_equals(PACKAGE_HASH, $calculated)) { @unlink(ZIP_FILE); throw new Exception(t('err_hash_mismatch')); } // 4. Backup (Update Mode Only) // >>> MODIFICA: BACKUP SOLO .ENV (Linea 475) if ($mode === 'update') { sendProgress(50, t('step_backup')); @mkdir(BACKUP_DIR, 0755, true); if (file_exists('.env')) { @copy('.env', BACKUP_DIR . '/.env'); $backupCreated = true; } } // 5. Unzip sendProgress(60, t('step_unzip')); $zip = new ZipArchive; if ($zip->open(ZIP_FILE) !== true) throw new Exception(t('err_zip')); if (!@mkdir(TEMP_DIR, 0755, true)) throw new Exception(t('err_mkdir', TEMP_DIR)); $zip->extractTo(TEMP_DIR); $zip->close(); // ============================================================================ // 6. INSTALL - FIX CRITICO: EXCLUDE LOGIC // ============================================================================ sendProgress(75, t('step_install')); // FIX: Lista exclude items (preserva storage senza backup/restore) $excludeItems = [ '.env', // Preservato via backup/restore 'storage', // Preservato via exclude (NON toccato!) 'public/uploads', // Preservato via exclude 'public/storage', // Symlink storage 'install.log', 'update_bridge.json', 'bootstrap/cache', '_km_safe_zone' ]; // Rileva struttura ZIP (flat vs nested) $sourceDir = TEMP_DIR; $files = scandir(TEMP_DIR); $files = array_diff($files, ['.', '..', '__MACOSX']); if (count($files) === 1 && is_dir(TEMP_DIR . '/' . reset($files))) { $sourceDir = TEMP_DIR . '/' . reset($files); logTech("SmartExtract: Nested structure detected - " . reset($files)); } else { logTech("SmartExtract: Flat structure"); } // Deploy con exclude $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); $deployed = 0; $skipped = 0; foreach ($iterator as $item) { $relativePath = substr($item->getPathname(), strlen($sourceDir) + 1); if (str_starts_with($relativePath, '__MACOSX')) continue; // FIX: Check exclude items $isExcluded = false; foreach ($excludeItems as $excluded) { if ($relativePath === $excluded || str_starts_with($relativePath, $excluded . '/')) { $isExcluded = true; break; } } if ($isExcluded) { $skipped++; if ($skipped <= 3) logTech("SKIPPED: {$relativePath}"); continue; } $targetPath = __DIR__ . '/' . $relativePath; if ($item->isDir()) { if (!is_dir($targetPath)) @mkdir($targetPath, 0755, true); } else { $targetDir = dirname($targetPath); if (!is_dir($targetDir)) @mkdir($targetDir, 0755, true); if (file_exists($targetPath)) { @chmod($targetPath, 0777); @unlink($targetPath); } if (@copy($item->getPathname(), $targetPath)) { @chmod($targetPath, 0644); $deployed++; } else { logTech("WARNING: Failed to deploy {$relativePath}"); } } } logTech("Deployment: {$deployed} files deployed, {$skipped} excluded"); // >>> FIX CRITICO: CREAZIONE CARTELLE MANCANTI (Risolve errore Cache Path) // Questo codice DEVE esserci perché il tuo ZIP non ha queste cartelle! sendProgress(80, "Verifying folder structure..."); $dirs = [ 'bootstrap/cache', 'storage/app/public', 'storage/framework/cache', 'storage/framework/sessions', 'storage/framework/views', 'storage/logs' ]; foreach($dirs as $d) { if(!is_dir(__DIR__.'/'.$d)) @mkdir(__DIR__.'/'.$d, 0777, true); } // Pulizia file cache vecchi foreach (glob(__DIR__.'/bootstrap/cache/*.php') as $f) @unlink($f); if (!file_exists(__DIR__.'/bootstrap/cache/.gitignore')) file_put_contents(__DIR__.'/bootstrap/cache/.gitignore', "*\n!.gitignore"); // 7. OPcache Reset (Critico per evitare esecuzione codice vecchio) sendProgress(85, t('step_opcache')); if (function_exists('opcache_reset')) { @opcache_reset(); logTech("OPcache reset performed."); } // 8. Health Check (Verifica fisica) sendProgress(88, t('step_health')); if (!file_exists(__DIR__ . '/artisan') || !file_exists(__DIR__ . '/public/index.php')) { throw new Exception(t('err_health', 'artisan, public/index.php')); } // 9. .htaccess (Esattamente quello richiesto) sendProgress(90, t('step_htaccess')); $htaccess = << RewriteEngine On RewriteBase / # 1. Priorità installer RewriteCond %{REQUEST_FILENAME} -f RewriteRule ^index\.php$ - [L] # 2. Authorization RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] # 3. Trailing slash RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] # 4. Blocchi sicurezza RewriteRule ^(storage|public/uploads)/.*\.php$ - [F,L,NC] RewriteRule ^(storage|bootstrap|vendor|config)/.* - [F,L,NC] # 5. Routing Laravel RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_URI} !^/public/ RewriteRule ^(.*)$ public/$1 [L] # 6. Fallback RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ public/index.php [L] Require all denied HTA; file_put_contents(__DIR__ . '/.htaccess', $htaccess); @chmod(__DIR__ . '/.htaccess', 0644); // 10. Cleanup & Self-destruct (CACHE CLEAR FIX) sendProgress(95, t('step_clean')); safeRmdir(TEMP_DIR); @unlink(ZIP_FILE); // --- FIX CACHE AVANZATO (PHP NATIVE) --- // Cancella config, route e tutte le cache di Laravel senza bisogno di shell_exec $cacheDir = __DIR__ . '/bootstrap/cache'; $files = ['config.php', 'routes.php', 'packages.php', 'services.php']; foreach ($files as $f) { if (file_exists("$cacheDir/$f")) @unlink("$cacheDir/$f"); } // Pulisce anche le view compilate per evitare conflitti UI $viewDir = __DIR__ . '/storage/framework/views'; if (is_dir($viewDir)) { foreach (glob("$viewDir/*.php") as $v) @unlink($v); } // -------------------------------------- // >>> MODIFICA: RESTORE SOLO .ENV (Linea 620) if ($backupCreated && file_exists(BACKUP_DIR . '/.env')) { @copy(BACKUP_DIR . '/.env', '.env'); logTech("Restored: .env"); } if ($backupCreated) safeRmdir(BACKUP_DIR); // Se Install Mode -> Crea .env if ($mode === 'install' && !file_exists('.env') && file_exists('.env.example')) { copy('.env.example', '.env'); $key = 'base64:' . base64_encode(random_bytes(32)); $content = file_get_contents('.env'); file_put_contents('.env', preg_replace('/^APP_KEY=.*$/m', "APP_KEY={$key}", $content)); } register_shutdown_function(function() { if (!@unlink(__FILE__)) { @file_put_contents(__FILE__, ""); } }); // 11. Final Success sendProgress(100, t('step_done'), 'success'); $redirect = ($mode === 'update') ? '/system/upgrade/finalize' : '/install'; // FIX 4: ADDED NONCE TO FINAL SCRIPT echo ""; } catch (Exception $e) { logTech("ERROR: " . $e->getMessage()); $msg = addslashes($e->getMessage()); // FIX 5: Use sendProgress instead of echo script for errors sendProgress(100, 'ERRORE: ' . $msg, 'error'); // --- GESTIONE ROLLBACK --- if ($backupCreated) { sendProgress(100, t('step_rollback'), 'error'); if (performRollback()) { echo "
" . t('msg_rollback_ok') . "
"; } else { echo "
" . t('msg_rollback_ko') . "
"; } } @unlink(ZIP_FILE); safeRmdir(TEMP_DIR); } ?>