Update: version 1.2.0, new version UI features, CSP hash auto-update, bug fixes

This commit is contained in:
Krystie
2026-05-14 21:04:35 -07:00
parent a216dd882d
commit 947007438f
12 changed files with 385 additions and 44 deletions
+172 -33
View File
@@ -8,7 +8,7 @@
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'sha256-6JZtsKK/PZthh+stCmmCvC2QxCiyk6SwZCBjXE+kYr0='; style-src 'self' 'unsafe-inline'; img-src 'self' https://cdn.jsdelivr.net data:; connect-src 'self' https://api.open-meteo.com https://geocoding-api.open-meteo.com; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'sha256-Nv8xzCSztfdYOL663VgPKWQn6v0lnM0ACWxkxpFfcfY='; style-src 'self' 'unsafe-inline'; img-src 'self' https://cdn.jsdelivr.net data:; connect-src 'self' https://api.open-meteo.com https://geocoding-api.open-meteo.com; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'">
<link rel="icon" href="/assets/dashcaddy-favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/icon-192.png">
@@ -90,16 +90,18 @@
</button>
<button id="theme-customize-btn" class="theme-customize-link" title="Customize theme colors">Customize Theme</button>
</div>
<div id="license-status-topbar" class="license-status-topbar free" title="Click to manage license">
<span id="license-topbar-icon">&#9734;</span>
<span id="license-topbar-text">FREE TIER</span>
<span id="license-topbar-time"></span>
</div>
<button id="reload-caddy-top" aria-label="Reload Caddy configuration" style="padding: 10px 20px; font-size: 0.95rem; font-weight: 600; background: linear-gradient(135deg, #3498db 0%, #2980b9 100%); border: none; border-radius: 6px; color: white; cursor: pointer; box-shadow: 0 2px 6px rgba(52, 152, 219, 0.3); transition: all 0.2s ease;">
🔄 Reload Caddy
</button>
<div class="license-version-row">
<div id="license-status-topbar" class="license-status-topbar free" title="Click to manage license">
<span id="license-topbar-icon">&#9734;</span>
<span id="license-topbar-text">FREE TIER</span>
<span id="license-topbar-time"></span>
</div>
<button id="license-topbar-version" class="dashboard-version dashboard-version-inline" type="button" title="View DashCaddy verification info" aria-label="View DashCaddy verification info"><span class="dashboard-version-bullet" id="license-topbar-version-bullet">·</span> <span id="license-topbar-version-text">Loading…</span></button>
</div>
</div>
<button id="dashboard-version" class="dashboard-version" type="button" title="View DashCaddy verification info" aria-label="View DashCaddy verification info">Version —</button>
</div>
</div>
@@ -110,7 +112,9 @@
<div id="version-info-status" class="version-info-status">Loading…</div>
<div id="version-info-grid" class="version-info-grid" style="display:none;"></div>
<div id="version-info-history" class="version-info-history" style="display:none;"></div>
<div class="weather-modal-buttons" style="margin-top: 16px;">
<div id="version-info-actions" class="weather-modal-buttons" style="margin-top: 16px; display:none; justify-content: space-between; gap: 10px;">
<button id="version-info-update">Update Now</button>
<button id="version-info-open-updates">Open Updates</button>
<button id="version-info-close">Close</button>
</div>
</div>
@@ -159,9 +163,9 @@
<div class="tools-section-items">
<button id="manage-tokens" aria-label="Manage API tokens">🔑 Tokens</button>
<button id="backup-restore-btn" aria-label="Backup and restore">💾 Backup</button>
<button id="license-btn" aria-label="License management" onclick="window.openLicenseModal && window.openLicenseModal()">🔑 License</button>
<button id="api-docs-btn" aria-label="API documentation" onclick="window.open('/api/docs', '_blank')">📖 API</button>
<button id="help-errors-btn" aria-label="Troubleshooting guide" onclick="window.open('/help-errors.html', '_blank')">❓ Help</button>
<button id="license-btn" aria-label="License management">🔑 License</button>
<button id="api-docs-btn" aria-label="API documentation">📖 API</button>
<button id="help-errors-btn" aria-label="Troubleshooting guide">❓ Help</button>
</div>
</div>
</div>
@@ -555,12 +559,18 @@
var yr = document.getElementById('footer-year');
if (yr) yr.textContent = new Date().getFullYear();
var versionEl = document.getElementById('dashboard-version');
var versionEl = document.getElementById('license-topbar-version');
var versionTextEl = document.getElementById('license-topbar-version-text');
var versionBulletEl = document.getElementById('license-topbar-version-bullet');
var versionInfoModal = document.getElementById('version-info-modal');
var versionInfoStatus = document.getElementById('version-info-status');
var versionInfoGrid = document.getElementById('version-info-grid');
var versionInfoHistory = document.getElementById('version-info-history');
var versionInfoActions = document.getElementById('version-info-actions');
var versionInfoUpdate = document.getElementById('version-info-update');
var versionInfoOpenUpdates = document.getElementById('version-info-open-updates');
var versionInfoClose = document.getElementById('version-info-close');
var latestUpdateCheck = null;
function formatValue(value) {
if (value == null || value === '') return '—';
@@ -588,71 +598,197 @@
}).join('');
}
function setVersionLabel(text) {
if (versionTextEl) versionTextEl.textContent = text;
}
function setVersionUpdateState(hasUpdate) {
if (!versionEl || !versionBulletEl) return;
versionEl.classList.toggle('has-update', !!hasUpdate);
versionEl.title = hasUpdate
? 'Update available — click for details or to update'
: 'View DashCaddy verification info';
versionEl.setAttribute('aria-label', hasUpdate
? 'DashCaddy update available — click for details or to update'
: 'View DashCaddy verification info');
versionBulletEl.textContent = hasUpdate ? '⬆' : '·';
}
function checkVersionUpdateAvailability() {
return fetch('/api/v1/system/update-check', { cache: 'no-store', credentials: 'same-origin' })
.then(function(response) {
if (response.status === 401) throw new Error('AUTH_PENDING');
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.json();
})
.then(function(data) {
latestUpdateCheck = data;
setVersionUpdateState(!!(data && data.success && data.available && data.remote));
return data;
})
.catch(function(error) {
if (!(error && error.message === 'AUTH_PENDING')) {
setVersionUpdateState(false);
}
throw error;
});
}
function loadVersionLabel(retryCount) {
if (!versionEl) return;
fetch('/api/v1/system/version', { cache: 'no-store', credentials: 'same-origin' })
.then(function(response) {
if (response.status === 401) {
throw new Error('AUTH_PENDING');
}
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.json();
})
.then(function(data) {
if (data && data.success && data.version) {
setVersionLabel('v' + data.version);
checkVersionUpdateAvailability().catch(function() {});
return;
}
setVersionLabel('version unknown');
setVersionUpdateState(false);
})
.catch(function(error) {
if (error && error.message === 'AUTH_PENDING') {
setVersionLabel('signing in…');
if ((retryCount || 0) < 12) {
setTimeout(function() {
loadVersionLabel((retryCount || 0) + 1);
}, 1500);
}
return;
}
setVersionLabel('version unavailable');
setVersionUpdateState(false);
});
}
function openUpdatesPanel() {
var updatesBtn = document.getElementById('updates-btn');
var updatesDashcaddyTab = document.getElementById('updates-dashcaddy-tab');
if (updatesBtn) updatesBtn.click();
setTimeout(function() {
if (updatesDashcaddyTab) updatesDashcaddyTab.click();
}, 50);
}
function applyVersionUpdate() {
if (window.dcApplyUpdate) {
return window.dcApplyUpdate();
}
openUpdatesPanel();
}
function openVersionInfo() {
if (!versionInfoModal) return;
versionInfoModal.classList.add('show');
versionInfoStatus.textContent = 'Loading…';
versionInfoGrid.style.display = 'none';
versionInfoHistory.style.display = 'none';
if (versionInfoActions) versionInfoActions.style.display = 'none';
if (versionInfoUpdate) {
versionInfoUpdate.disabled = true;
versionInfoUpdate.textContent = 'Update Now';
}
versionInfoGrid.innerHTML = '';
versionInfoHistory.innerHTML = '';
Promise.all([
fetch('/api/v1/system/version', { cache: 'no-store' }).then(function(response) {
fetch('/api/v1/system/version', { cache: 'no-store', credentials: 'same-origin' }).then(function(response) {
if (!response.ok) throw new Error('Version check failed');
return response.json();
}),
fetch('/api/v1/system/update-status', { cache: 'no-store' }).then(function(response) {
fetch('/api/v1/system/update-status', { cache: 'no-store', credentials: 'same-origin' }).then(function(response) {
if (!response.ok) throw new Error('Update status failed');
return response.json();
}),
fetch('/api/v1/system/update-history', { cache: 'no-store' }).then(function(response) {
fetch('/api/v1/system/update-history', { cache: 'no-store', credentials: 'same-origin' }).then(function(response) {
if (!response.ok) throw new Error('Update history failed');
return response.json();
})
}),
checkVersionUpdateAvailability().catch(function() { return latestUpdateCheck || {}; })
]).then(function(results) {
var versionData = results[0] || {};
var statusData = results[1] || {};
var historyData = results[2] || {};
var lastResult = statusData.lastResult || {};
var updateCheckData = results[3] || latestUpdateCheck || {};
var lastResult = statusData.lastResult || updateCheckData || {};
var lastPolicy = lastResult.policy || {};
var hasUpdate = !!(updateCheckData && updateCheckData.success && updateCheckData.available && updateCheckData.remote);
versionInfoStatus.textContent = 'Verification info loaded.';
versionInfoStatus.textContent = hasUpdate
? 'Update available — you can install it from here.'
: 'Verification info loaded.';
versionInfoGrid.style.display = 'grid';
versionInfoGrid.innerHTML = [
renderInfoRow('Version', versionData.version),
renderInfoRow('Commit', versionData.commit),
renderInfoRow('Updater Status', statusData.status),
renderInfoRow('Last Check', statusData.lastCheck ? new Date(statusData.lastCheck).toLocaleString() : 'Never'),
renderInfoRow('Update Available', lastResult.available),
renderInfoRow('Update Available', hasUpdate),
renderInfoRow('Available Version', updateCheckData.remote && updateCheckData.remote.version),
renderInfoRow('Eligible', lastPolicy.eligible),
renderInfoRow('Policy Reason', lastPolicy.reason),
renderInfoRow('Channel', lastPolicy.releaseChannel || (lastResult.instance && lastResult.instance.channel)),
renderInfoRow('Instance ID', lastResult.instance && lastResult.instance.instanceId)
].join('');
if (versionInfoActions) versionInfoActions.style.display = 'flex';
if (versionInfoUpdate) versionInfoUpdate.disabled = !hasUpdate;
renderHistory(historyData.history);
}).catch(function(error) {
versionInfoStatus.textContent = 'Could not load verification info: ' + error.message;
if (versionInfoActions) versionInfoActions.style.display = 'flex';
});
}
if (versionEl) {
fetch('/api/v1/system/version', { cache: 'no-store' })
.then(function(response) {
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.json();
})
.then(function(data) {
if (data && data.success && data.version) {
versionEl.textContent = 'Version ' + data.version;
}
})
.catch(function() {
versionEl.textContent = 'Version unavailable';
});
loadVersionLabel(0);
versionEl.addEventListener('click', openVersionInfo);
versionEl.addEventListener('click', function(event) {
event.stopPropagation();
openVersionInfo();
});
}
var licenseBtn = document.getElementById('license-btn');
var apiDocsBtn = document.getElementById('api-docs-btn');
var helpErrorsBtn = document.getElementById('help-errors-btn');
if (licenseBtn) {
licenseBtn.addEventListener('click', function() {
if (window.openLicenseModal) window.openLicenseModal();
});
}
if (apiDocsBtn) {
apiDocsBtn.addEventListener('click', function() {
window.open('/api/docs', '_blank');
});
}
if (helpErrorsBtn) {
helpErrorsBtn.addEventListener('click', function() {
window.open('/help-errors.html', '_blank');
});
}
if (versionInfoUpdate) {
versionInfoUpdate.addEventListener('click', function() {
applyVersionUpdate();
});
}
if (versionInfoOpenUpdates) {
versionInfoOpenUpdates.addEventListener('click', function() {
versionInfoModal.classList.remove('show');
openUpdatesPanel();
});
}
if (versionInfoClose && versionInfoModal) {
@@ -665,6 +801,9 @@
}
});
}
window.openVersionInfo = openVersionInfo;
window.applyVersionUpdate = applyVersionUpdate;
})();
</script>