Update: version 1.2.0, new version UI features, CSP hash auto-update, bug fixes
This commit is contained in:
+172
-33
@@ -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">☆</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">☆</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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user