From 5d60e66518f292bd3ec026299717f0ac53fd85dc Mon Sep 17 00:00:00 2001 From: sorcier Date: Wed, 4 Nov 2026 01:20:38 +0100 Subject: [PATCH] feat(ota): add 1-click update UI & API with changelog display --- public/api.php | 58 +++++++++++++++++++ public/assets/app.js | 130 +++++++++++++++++++++++++++++++++++++++++++ public/index.php | 43 +++++++++++++- 3 files changed, 230 insertions(+), 1 deletion(-) diff --git a/public/api.php b/public/api.php index aa38c0f..d9cd532 100644 --- a/public/api.php +++ b/public/api.php @@ -2231,6 +2231,64 @@ try { echo json_encode($res); break; + case 'check_update': + $appDir = dirname(__DIR__); + $localVerJson = file_exists("{$appDir}/version.json") + ? json_decode((string)file_get_contents("{$appDir}/version.json"), true) + : ['version' => '1.2.0', 'changelog' => []]; + + $currentVer = $localVerJson['version'] ?? '1.2.0'; + $remoteVer = $currentVer; + $remoteChangelog = []; + $remoteDate = ''; + + exec("git --git-dir={$appDir}/.git fetch origin main --quiet 2>/dev/null", $output, $code); + $remoteJsonRaw = shell_exec("git --git-dir={$appDir}/.git show origin/main:version.json 2>/dev/null"); + if ($remoteJsonRaw) { + $remoteData = json_decode($remoteJsonRaw, true); + if (is_array($remoteData) && !empty($remoteData['version'])) { + $remoteVer = (string)$remoteData['version']; + $remoteChangelog = $remoteData['changelog'] ?? []; + $remoteDate = $remoteData['release_date'] ?? ''; + } + } + + $hasUpdate = version_compare($remoteVer, $currentVer, '>'); + $localHash = trim((string)shell_exec("git --git-dir={$appDir}/.git rev-parse HEAD 2>/dev/null")); + $remoteHash = trim((string)shell_exec("git --git-dir={$appDir}/.git rev-parse origin/main 2>/dev/null")); + if (!$hasUpdate && $localHash !== '' && $remoteHash !== '' && $localHash !== $remoteHash) { + $hasUpdate = true; + } + + echo json_encode([ + 'success' => true, + 'current_version' => $currentVer, + 'latest_version' => $remoteVer, + 'has_update' => $hasUpdate, + 'changelog' => $hasUpdate ? $remoteChangelog : ($localVerJson['changelog'] ?? []), + 'release_date' => $remoteDate ?: ($localVerJson['release_date'] ?? ''), + ]); + break; + + case 'run_update': + $output = []; + $code = 0; + exec("sudo /var/www/nexus-pbx/scripts/update.sh 2>&1", $output, $code); + $outputStr = implode("\n", $output); + + $appDir = dirname(__DIR__); + $newVerJson = file_exists("{$appDir}/version.json") + ? json_decode((string)file_get_contents("{$appDir}/version.json"), true) + : ['version' => '1.3.0']; + + echo json_encode([ + 'success' => ($code === 0), + 'new_version' => $newVerJson['version'] ?? '1.3.0', + 'output' => $outputStr, + 'message' => ($code === 0) ? "Mise à jour réussie !" : "Erreur lors de la mise à jour", + ]); + break; + default: http_response_code(400); echo json_encode([ diff --git a/public/assets/app.js b/public/assets/app.js index 0f24c9d..b504fae 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -144,6 +144,7 @@ async function loadState() { } renderAll(); loadBackups(); + checkSystemUpdate(false); startMetricsPolling(); } catch (err) { if (err.message !== 'Session expirée') { @@ -5636,3 +5637,132 @@ function copyYealinkUrl(inputId, btn) { showToast("Sélectionnez l'URL et appuyez sur Ctrl+C.", 'info'); }); } + +// ========================================================================= +// GESTIONNAIRE DE MISES À JOUR DU SYSTÈME NEXUSPBX (OTA) +// ========================================================================= +async function checkSystemUpdate(notify = false) { + const btnCheck = document.getElementById('btn-check-update'); + const btnRun = document.getElementById('btn-run-update'); + const statusText = document.getElementById('update-status-text'); + const lastChecked = document.getElementById('update-last-checked'); + const changelogContainer = document.getElementById('update-changelog-container'); + const changelogList = document.getElementById('update-changelog-list'); + const pingDot = document.getElementById('update-ping'); + const verTag = document.getElementById('system-version-tag'); + const appBadge = document.getElementById('app-version-badge'); + + if (btnCheck) { + btnCheck.disabled = true; + btnCheck.classList.add('opacity-50'); + } + + try { + const res = await fetch('/api.php?action=check_update'); + const data = await res.json(); + + if (lastChecked) { + const now = new Date(); + lastChecked.textContent = `Dernière vérification : ${now.toLocaleTimeString()}`; + } + + if (verTag && data.current_version) { + verTag.textContent = `v${data.current_version}`; + } + if (appBadge && data.current_version) { + appBadge.textContent = `v${data.current_version}`; + } + + if (data.has_update) { + if (statusText) { + statusText.className = 'text-amber-400 font-bold'; + statusText.innerHTML = `🚀 Nouvelle version disponible : v${escapeHtml(data.latest_version)} !`; + } + if (btnRun) { + btnRun.style.display = 'inline-flex'; + btnRun.innerHTML = `Mettre à jour vers v${escapeHtml(data.latest_version)} (1-Clic)`; + } + if (pingDot) pingDot.classList.remove('hidden'); + + if (changelogList && Array.isArray(data.changelog) && data.changelog.length > 0) { + changelogList.innerHTML = data.changelog.map(item => `
  • ${escapeHtml(item)}
  • `).join(''); + if (changelogContainer) changelogContainer.classList.remove('hidden'); + } + if (notify) { + showToast(`Nouvelle version NexusPBX v${data.latest_version} disponible !`, 'info'); + } + } else { + if (statusText) { + statusText.className = 'text-emerald-400 font-bold'; + statusText.textContent = `Votre PBX est à jour avec la dernière version officielle (v${data.current_version}).`; + } + if (btnRun) btnRun.style.display = 'none'; + if (pingDot) pingDot.classList.add('hidden'); + if (changelogContainer) changelogContainer.classList.add('hidden'); + if (notify) { + showToast("Votre système NexusPBX est parfaitement à jour !", 'success'); + } + } + } catch (e) { + if (notify) { + showToast("Erreur lors de la vérification des mises à jour : " + e.message, 'error'); + } + } finally { + if (btnCheck) { + btnCheck.disabled = false; + btnCheck.classList.remove('opacity-50'); + } + } +} + +async function runSystemUpdate() { + if (!confirm("Voulez-vous lancer la mise à jour de NexusPBX ?\n\nUne sauvegarde préventive automatique de vos configurations et de votre base de données sera créée.")) { + return; + } + + const btnRun = document.getElementById('btn-run-update'); + const statusText = document.getElementById('update-status-text'); + + if (btnRun) { + btnRun.disabled = true; + btnRun.innerHTML = '↻ Mise à jour en cours...'; + } + if (statusText) { + statusText.className = 'text-cyan-300 font-bold'; + statusText.textContent = 'Mise à jour en cours : sauvegarde, git pull, migrations SQLite et rechargement des services...'; + } + + showToast("Mise à jour en cours d'exécution en arrière-plan...", 'info'); + + try { + const res = await fetch('/api.php?action=run_update', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}) + }); + const data = await res.json(); + + if (data.success) { + showToast(`NexusPBX a été mis à jour avec succès en v${data.new_version} ! Rechargement...`, 'success'); + if (statusText) { + statusText.className = 'text-emerald-400 font-bold'; + statusText.textContent = `Mise à jour terminée avec succès (v${data.new_version}) ! Rechargement de la page...`; + } + setTimeout(() => { + window.location.reload(); + }, 2500); + } else { + alert("Erreur lors de la mise à jour :\n\n" + (data.output || data.message)); + if (btnRun) { + btnRun.disabled = false; + btnRun.innerHTML = 'Réessayer la mise à jour'; + } + } + } catch (e) { + alert("Erreur réseau lors de la mise à jour : " + e.message); + if (btnRun) { + btnRun.disabled = false; + btnRun.innerHTML = 'Réessayer la mise à jour'; + } + } +} diff --git a/public/index.php b/public/index.php index 767b69a..f095397 100644 --- a/public/index.php +++ b/public/index.php @@ -207,7 +207,7 @@ $currentUser = Auth::getUsername();
    NexusPBX
    - v1.2.0 + v1.3.0
    Moteur VoIP & Téléphonie IP
    @@ -1560,6 +1560,47 @@ $currentUser = Auth::getUsername(); + +
    +
    +
    +
    + + + + +

    Mises à Jour du Système NexusPBX

    + v1.3.0 +
    +

    Vérifiez les nouvelles versions officielles et mettez à jour votre PBX en 1 clic sans coupure de service.

    +
    +
    + + +
    +
    + + +
    +
    + Statut : Votre PBX est à jour avec la dernière version officielle. + Dernière vérification : À l'instant +
    + +
    +
    +