false, 'error' => $message ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); exit; } // CORS if (isset($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Max-Age: 86400'); } if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) { header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); } if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) { header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"); } exit(0); } // Vérifier la méthode HTTP if ($_SERVER['REQUEST_METHOD'] !== 'POST') { sendError('Méthode non autorisée. Utilisez POST.', 405); } // Initialiser la session try { $sessionId = SessionManager::init(); Config::log("Merge API - Session : {$sessionId}"); } catch (Exception $e) { sendError('Erreur initialisation session : ' . $e->getMessage(), 500); } // Récupérer le body JSON $input = file_get_contents('php://input'); if (empty($input)) { sendError('Body JSON manquant. Envoyez la sélection au format JSON.'); } $data = json_decode($input, true); if (json_last_error() !== JSON_ERROR_NONE) { sendError('JSON invalide : ' . json_last_error_msg()); } // Vérifier la sélection if (!isset($data['selection']) || !is_array($data['selection'])) { sendError('Paramètre "selection" manquant ou invalide.'); } $selection = $data['selection']; Config::log("Sélection reçue : " . count($selection) . " entrées"); // Valider la sélection $validation = FileTree::validateSelection($selection); if (!$validation['valid']) { $conflicts = implode(', ', $validation['conflicts']); sendError("Sélection invalide. Conflits détectés : {$conflicts}"); } $uploadDir = SessionManager::getUploadDir($sessionId); $leftZipPath = $uploadDir . 'left.zip'; $rightZipPath = $uploadDir . 'right.zip'; // Vérifier que les 2 ZIP existent if (!file_exists($leftZipPath)) { sendError('ZIP gauche non uploadé.', 404); } if (!file_exists($rightZipPath)) { sendError('ZIP droite non uploadé.', 404); } // Préparer le chemin du ZIP fusionné $mergedZipPath = $uploadDir . 'merged.zip'; // Fusionner $zipHandler = new ZipHandler(); try { Config::log("Début fusion : " . count(array_filter($selection)) . " fichiers sélectionnés"); $result = $zipHandler->merge($leftZipPath, $rightZipPath, $selection, $mergedZipPath); Config::log("Fusion réussie : {$result}"); // Vérifier que le fichier a été créé if (!file_exists($mergedZipPath)) { throw new Exception("Le fichier fusionné n'a pas été créé"); } $fileSize = filesize($mergedZipPath); Config::log("ZIP fusionné créé : " . Config::formatBytes($fileSize)); // Mettre à jour le timestamp SessionManager::updateAccess($sessionId); // Envoyer le fichier en téléchargement // Nom de fichier avec timestamp $timestamp = date('Y-m-d_His'); $filename = "fuzip_merged_{$timestamp}.zip"; // Headers pour téléchargement header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Length: ' . $fileSize); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); // Stream le fichier $handle = fopen($mergedZipPath, 'rb'); if ($handle === false) { throw new Exception("Impossible d'ouvrir le fichier fusionné"); } // Envoyer par chunks pour économiser la mémoire while (!feof($handle)) { echo fread($handle, Config::STREAM_BUFFER_SIZE); flush(); } fclose($handle); Config::log("ZIP fusionné envoyé : {$filename} (" . Config::formatBytes($fileSize) . ")"); exit; } catch (Exception $e) { sendError('Erreur lors de la fusion : ' . $e->getMessage(), 500); }