Enable true one-click panel update from Settings (v2.5.1).

Download release ZIP from git.evilfox.cc when git is unavailable, add upgrade_panel API, and show a primary Update panel button.
This commit is contained in:
orohi
2026-07-28 09:41:51 +03:00
parent 77c6f0dab7
commit 599e0a487c
9 changed files with 350 additions and 83 deletions
+55 -9
View File
@@ -622,11 +622,15 @@
</div>
<div style="display: flex; gap: var(--space-sm); margin-top: var(--space-sm); flex-wrap: wrap;">
<button type="button" class="btn btn-primary" onclick="upgradePanel()" id="upgradePanelBtn" style="flex:1; min-width:180px;">
<span id="upgradePanelBtnText">⬆ {{ _('upgrade_panel') }}</span>
<div class="spinner hidden" id="upgradePanelSpinner" style="width:14px; height:14px;"></div>
</button>
<button type="button" class="btn btn-secondary" onclick="checkForUpdates()" id="checkUpdateBtn" style="flex:1; min-width:140px;">
<span id="checkUpdateBtnText">🔄 {{ _('check_updates') }}</span>
<div class="spinner hidden" id="checkUpdateSpinner" style="width:14px; height:14px;"></div>
</button>
<button type="button" class="btn btn-primary hidden" onclick="applyPanelUpdate()" id="applyUpdateBtn" style="flex:1; min-width:140px;">
<button type="button" class="btn btn-secondary hidden" onclick="applyPanelUpdate()" id="applyUpdateBtn" style="flex:1; min-width:140px;">
<span id="applyUpdateBtnText">⬆ {{ _('apply_update') }}</span>
<div class="spinner hidden" id="applyUpdateSpinner" style="width:14px; height:14px;"></div>
</button>
@@ -1614,16 +1618,26 @@
statusDiv.classList.remove('hidden');
if (modeHint) {
const mode = data.update_mode || (data.mode && data.mode.mode) || '';
if (data.can_auto_update) {
modeHint.textContent = "{{ _('auto_update_ready')|default('Git auto-update is available for this install.') }}";
} else if (mode === 'docker') {
modeHint.textContent = "{{ _('auto_update_docker')|default('Docker install: rebuild/pull image, or use a git checkout for one-click update.') }}";
const method = data.update_mode || (data.mode && data.mode.update_method) || (data.mode && data.mode.mode) || '';
if (!data.can_auto_update) {
if (method === 'docker') {
modeHint.textContent = "{{ _('auto_update_docker')|default('Docker: on the host run git pull && docker compose up -d --build.') }}";
} else {
modeHint.textContent = "{{ _('auto_update_manual')|default('One-click update needs a writable install directory or git clone.') }}";
}
} else if (method === 'archive') {
modeHint.textContent = "{{ _('auto_update_archive')|default('One-click update downloads the release ZIP from git.evilfox.cc and restarts the panel.') }}";
} else {
modeHint.textContent = "{{ _('auto_update_manual')|default('One-click update needs a git clone. You can still download a release.') }}";
modeHint.textContent = "{{ _('auto_update_ready')|default('Git auto-update is available for this install.') }}";
}
}
const upgradeBtn = document.getElementById('upgradePanelBtn');
if (upgradeBtn) {
upgradeBtn.disabled = !data.can_auto_update;
upgradeBtn.title = data.can_auto_update ? '' : (modeHint ? modeHint.textContent : '');
}
if (data.update_available && latestVersion) {
statusDiv.innerHTML = `<span style="color: var(--success); font-weight: bold;">{{ _('update_available')|default('Update available') }}: ${latestVersion}</span>`;
statusDiv.style.border = '1px solid var(--success)';
@@ -1656,7 +1670,7 @@
showToast("{{ _('update_no_target')|default('No target version. Check for updates first.') }}", 'error');
return;
}
if (!confirm("{{ _('apply_update_confirm')|default('Install update from git and restart the panel?') }} " + target)) {
if (!confirm("{{ _('apply_update_confirm')|default('Install update from git.evilfox.cc and restart the panel?') }} " + target)) {
return;
}
applyBtn.disabled = true;
@@ -1665,7 +1679,7 @@
try {
const data = await apiCall('/api/settings/apply_update', 'POST', {
target_version: target,
allow_dirty: false,
allow_dirty: true,
});
showToast(data.message || "{{ _('update_restarting')|default('Updated. Restarting…') }}", 'success');
setTimeout(() => { window.location.reload(); }, 3500);
@@ -1677,6 +1691,38 @@
}
}
async function upgradePanel() {
const btn = document.getElementById('upgradePanelBtn');
const text = document.getElementById('upgradePanelBtnText');
const spinner = document.getElementById('upgradePanelSpinner');
const latest = (latestUpdateInfo && latestUpdateInfo.latest_version) || '';
const confirmMsg = latest
? ("{{ _('upgrade_panel_confirm')|default('Download and install') }} " + latest + "?")
: "{{ _('upgrade_panel_confirm_latest')|default('Check for updates and install the latest release?') }}";
if (!confirm(confirmMsg)) return;
btn.disabled = true;
spinner.classList.remove('hidden');
text.textContent = "{{ _('upgrade_panel_working')|default('Updating panel…') }}";
try {
const data = await apiCall('/api/settings/upgrade_panel', 'POST', {});
if (data.up_to_date) {
showToast("{{ _('upgrade_panel_up_to_date')|default('Already on the latest version') }}", 'info');
await checkForUpdates(true);
return;
}
showToast(data.message || "{{ _('update_restarting')|default('Updated. Restarting…') }}", 'success');
setTimeout(() => { window.location.reload(); }, 4000);
} catch (err) {
showToast(_('error') + ': ' + err.message, 'error');
await checkForUpdates(true);
} finally {
btn.disabled = false;
spinner.classList.add('hidden');
text.textContent = "⬆ {{ _('upgrade_panel')|default('Update panel') }}";
}
}
// Auto-check once on Settings load
setTimeout(() => checkForUpdates(true), 800);