diff --git a/public/assets/app.js b/public/assets/app.js index 75e228e..5b3ecbe 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -4945,28 +4945,93 @@ async function createBackupNow() { } } -async function confirmRestoreBackup(filename) { - const ok = confirm(`⚠️ ATTENTION — RESTAURATION COMPLÈTE DU SYSTÈME NEXUSPBX ⚠️\n\nVoulez-vous restaurer le système depuis l'archive :\n${filename} ?\n\nCette action va :\n1. Remplacer la base de données SQLite\n2. Restaurer tous les fichiers sons, annonces et musiques\n3. Restaurer toutes les boîtes vocales, salutations et PINs\n4. Recharger l'intégralité du PBX Asterisk\n\nConfirmez-vous la restauration immédiate ?`); - if (!ok) return; +let pendingRestoreAction = null; +let pendingRestoreInput = null; - showToast("Restauration du système en cours... Veuillez patienter.", "info"); +function showRestoreModal({ title, filename, badgeText, onConfirm, inputEl = null }) { + const titleEl = document.getElementById('modal-restore-title'); + if (titleEl) titleEl.textContent = title || "Restauration Complète du Système"; + + const nameEl = document.getElementById('restore-target-name'); + if (nameEl) nameEl.textContent = filename || "Archive de sauvegarde"; + + const badgeEl = document.getElementById('restore-target-badge'); + if (badgeEl) badgeEl.textContent = badgeText || "Archive"; + + const confirmBtn = document.getElementById('btn-confirm-restore'); + const cancelBtn = document.getElementById('btn-cancel-restore'); + if (confirmBtn) { + confirmBtn.disabled = false; + confirmBtn.innerHTML = ` + + Confirmer la Restauration + `; + } + if (cancelBtn) cancelBtn.disabled = false; + + pendingRestoreAction = onConfirm; + pendingRestoreInput = inputEl; + + openModal('modal-restore-backup'); +} + +function closeRestoreModal() { + closeModal('modal-restore-backup'); + if (pendingRestoreInput) { + pendingRestoreInput.value = ''; + pendingRestoreInput = null; + } + pendingRestoreAction = null; +} + +async function executePendingRestore() { + if (!pendingRestoreAction) return; + const confirmBtn = document.getElementById('btn-confirm-restore'); + const cancelBtn = document.getElementById('btn-cancel-restore'); + if (confirmBtn) { + confirmBtn.disabled = true; + confirmBtn.innerHTML = ` + + Restauration en cours... + `; + } + if (cancelBtn) cancelBtn.disabled = true; try { - const res = await apiCall('restore_backup', { filename }); - showToast(res.message || "Restauration terminée avec succès !", 'success'); - setTimeout(() => { - window.location.reload(); - }, 2000); + await pendingRestoreAction(); } catch (err) { - showToast("Échec de la restauration : " + err.message, 'error'); + showToast("Erreur inattendue : " + err.message, "error"); + closeRestoreModal(); } } +function confirmRestoreBackup(filename) { + showRestoreModal({ + title: "Restauration du Système NexusPBX", + filename: filename, + badgeText: "Archive Locale", + onConfirm: async () => { + showToast("Restauration du système en cours... Veuillez patienter.", "info"); + try { + const res = await apiCall('restore_backup', { filename }); + closeRestoreModal(); + showToast(res.message || "Restauration terminée avec succès !", 'success'); + setTimeout(() => { + window.location.reload(); + }, 2000); + } catch (err) { + closeRestoreModal(); + showToast("Échec de la restauration : " + err.message, 'error'); + } + } + }); +} + function triggerBackupUpload() { document.getElementById('backup-upload-input')?.click(); } -async function handleBackupUpload(input) { +function handleBackupUpload(input) { const file = input?.files?.[0]; if (!file) return; @@ -4977,35 +5042,38 @@ async function handleBackupUpload(input) { return; } - const ok = confirm(`⚠️ RESTAURATION DISASTER RECOVERY VIA FICHIER TÉLÉVERSÉ ⚠️\n\nFichier sélectionné : ${file.name} (${(file.size / (1024 * 1024)).toFixed(2)} Mo)\n\nVoulez-vous restaurer immédiatement le système NexusPBX avec cette archive ?\nToutes les données actuelles seront remplacées.`); - if (!ok) { - input.value = ''; - return; - } + const fileSizeMb = (file.size / (1024 * 1024)).toFixed(2); - showToast("Téléversement et restauration de l'archive en cours...", "info"); + showRestoreModal({ + title: "Disaster Recovery — Archive Téléversée", + filename: `${file.name} (${fileSizeMb} Mo)`, + badgeText: "Fichier Téléversé", + inputEl: input, + onConfirm: async () => { + showToast("Téléversement et restauration de l'archive en cours...", "info"); + const fd = new FormData(); + fd.append('backup_file', file); - const fd = new FormData(); - fd.append('backup_file', file); - - try { - const response = await fetch('/api.php?action=upload_restore_backup', { - method: 'POST', - body: fd - }); - const res = await response.json(); - if (!res.success) { - throw new Error(res.message || "Erreur lors de la restauration"); + try { + const response = await fetch('/api.php?action=upload_restore_backup', { + method: 'POST', + body: fd + }); + const res = await response.json(); + closeRestoreModal(); + if (!res.success) { + throw new Error(res.message || "Erreur lors de la restauration"); + } + showToast(res.message, 'success'); + setTimeout(() => { + window.location.reload(); + }, 2000); + } catch (err) { + closeRestoreModal(); + showToast("Échec de la restauration : " + err.message, 'error'); + } } - showToast(res.message, 'success'); - setTimeout(() => { - window.location.reload(); - }, 2000); - } catch (err) { - showToast("Échec de la restauration : " + err.message, 'error'); - } finally { - input.value = ''; - } + }); } function downloadBackup(filename) { diff --git a/public/index.php b/public/index.php index 80b786c..5c82f21 100644 --- a/public/index.php +++ b/public/index.php @@ -4628,6 +4628,94 @@ $currentUser = Auth::getUsername(); + + + +
+ diff --git a/version.json b/version.json index c0a8e35..654ed30 100644 --- a/version.json +++ b/version.json @@ -1,12 +1,12 @@ { - "version": "1.3.5", + "version": "1.3.6", "codename": "Helios", "release_date": "2026-11-04", "min_php_version": "8.2", "changelog": [ - "Désactivation du bac à sable ProtectSystem=full sur PHP-FPM pour autoriser les écritures système lors des mises à jour", - "Sécurisation non-bloquante de la création des liens symboliques /usr/local/bin/ dans le script de mise à jour", - "Ajout automatique du paquet sshpass dans install.sh et le processus de mise à jour pour les sauvegardes distantes", - "Autorisation sudoers www-data automatique pour les mises à jour 1-clic depuis l'interface Web" + "Nouvelle modale de confirmation de restauration système alignée sur le thème sombre NexusPBX", + "Remplacement des popups d'alerte natives du navigateur lors des restaurations locales et téléversées", + "Affichage détaillé des éléments restaurés (base SQLite, sons, messageries vocales, rechargement Asterisk)", + "Désactivation du bac à sable ProtectSystem=full sur PHP-FPM pour fiabiliser les mises à jour" ] }