Add one-click panel auto-update from git.evilfox.cc (v2.4.0).

Check Gitea releases in Settings and install the latest tag via git fetch/checkout with pip sync and restart when running from a git checkout.
This commit is contained in:
orohi
2026-07-28 09:28:15 +03:00
parent 6206697e3b
commit fd257a59f7
9 changed files with 451 additions and 17 deletions
+75 -13
View File
@@ -597,15 +597,20 @@
<!-- Updated via JS -->
</div>
<div style="display: flex; gap: var(--space-sm); margin-top: var(--space-sm);">
<button type="button" class="btn btn-secondary" onclick="checkForUpdates()" id="checkUpdateBtn" style="flex:1;">
<div style="display: flex; gap: var(--space-sm); margin-top: var(--space-sm); flex-wrap: wrap;">
<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>
<a href="https://git.evilfox.cc/test2/Amnezia-Web-Panel-main/releases" target="_blank" class="btn btn-primary hidden" id="downloadUpdateBtn" style="flex:1; text-decoration: none; justify-content: center;">
<button type="button" class="btn btn-primary 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>
<a href="https://git.evilfox.cc/test2/Amnezia-Web-Panel-main/releases" target="_blank" class="btn btn-secondary hidden" id="downloadUpdateBtn" style="flex:1; min-width:140px; text-decoration: none; justify-content: center;">
⬇️ {{ _('download_update') }}
</a>
</div>
<p class="form-hint" id="updateModeHint" style="margin:0;">{{ _('auto_update_hint') }}</p>
</div>
</div>
</div>
@@ -1482,42 +1487,99 @@
updateProtocolsForSync();
}
async function checkForUpdates() {
let latestUpdateInfo = null;
async function checkForUpdates(silent) {
const btn = document.getElementById('checkUpdateBtn');
const text = document.getElementById('checkUpdateBtnText');
const spinner = document.getElementById('checkUpdateSpinner');
const statusDiv = document.getElementById('updateStatus');
const downloadBtn = document.getElementById('downloadUpdateBtn');
btn.disabled = true;
text.textContent = "{{ _('checking_updates')|default('Checking...') }}";
spinner.classList.remove('hidden');
const applyBtn = document.getElementById('applyUpdateBtn');
const modeHint = document.getElementById('updateModeHint');
if (!silent) {
btn.disabled = true;
text.textContent = "{{ _('checking_updates')|default('Checking...') }}";
spinner.classList.remove('hidden');
}
statusDiv.classList.add('hidden');
downloadBtn.classList.add('hidden');
applyBtn.classList.add('hidden');
try {
const data = await apiCall('/api/settings/check_updates');
latestUpdateInfo = data;
const latestVersion = data.latest_version || '';
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.') }}";
} else {
modeHint.textContent = "{{ _('auto_update_manual')|default('One-click update needs a git clone. You can still download a release.') }}";
}
}
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)';
downloadBtn.href = data.html_url || data.releases_url || 'https://git.evilfox.cc/test2/Amnezia-Web-Panel-main/releases';
downloadBtn.classList.remove('hidden');
if (data.can_auto_update) {
applyBtn.classList.remove('hidden');
}
} else {
statusDiv.innerHTML = `<span style="color: var(--text-muted);">{{ _('up_to_date')|default('Up to date') }}</span>`;
statusDiv.style.border = '1px solid var(--border-color)';
}
} catch (err) {
showToast('Failed to check for updates: ' + err.message, 'error');
if (!silent) showToast('Failed to check for updates: ' + err.message, 'error');
} finally {
btn.disabled = false;
text.textContent = "🔄 {{ _('check_updates')|default('Check for updates') }}";
spinner.classList.add('hidden');
if (!silent) {
btn.disabled = false;
text.textContent = "🔄 {{ _('check_updates')|default('Check for updates') }}";
spinner.classList.add('hidden');
}
}
}
async function applyPanelUpdate() {
const applyBtn = document.getElementById('applyUpdateBtn');
const applyText = document.getElementById('applyUpdateBtnText');
const applySpinner = document.getElementById('applyUpdateSpinner');
const target = (latestUpdateInfo && latestUpdateInfo.latest_version) || '';
if (!target) {
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)) {
return;
}
applyBtn.disabled = true;
applyText.textContent = "{{ _('applying_update')|default('Updating…') }}";
applySpinner.classList.remove('hidden');
try {
const data = await apiCall('/api/settings/apply_update', 'POST', {
target_version: target,
allow_dirty: false,
});
showToast(data.message || "{{ _('update_restarting')|default('Updated. Restarting…') }}", 'success');
setTimeout(() => { window.location.reload(); }, 3500);
} catch (err) {
showToast(_('error') + ': ' + err.message, 'error');
applyBtn.disabled = false;
applyText.textContent = "⬆ {{ _('apply_update')|default('Install update') }}";
applySpinner.classList.add('hidden');
}
}
// Auto-check once on Settings load
setTimeout(() => checkForUpdates(true), 800);
/* ========== API Tokens ========== */
function fmtTokenDate(iso) {