diff --git a/public/css/styles.css b/public/css/styles.css index f300848..a22459a 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -625,6 +625,21 @@ header { font-size: 0.875rem; } +/* Testing Controls */ +.testing-controls { + opacity: 0.5; + font-size: 0.8rem; +} + +.testing-controls:hover { + opacity: 1; +} + +.testing-controls a { + text-decoration: underline; + color: var(--text-color); +} + /* Utilities */ .text-center { text-align: center; } .text-right { text-align: right; } diff --git a/public/index.html b/public/index.html index 72d2893..74aa175 100644 --- a/public/index.html +++ b/public/index.html @@ -133,6 +133,10 @@ Checking... + +
+ Toggle Test Update +
diff --git a/public/js/system-status.js b/public/js/system-status.js new file mode 100644 index 0000000..1c881ea --- /dev/null +++ b/public/js/system-status.js @@ -0,0 +1,163 @@ +/** + * Transmission RSS Manager - System Status Module + * @description Functionality for system status display and updates + */ + +// System status and updates functionality +function initSystemStatus() { + // Elements + const versionElement = document.getElementById('system-version'); + const uptimeElement = document.getElementById('uptime'); + const transmissionStatusElement = document.getElementById('transmission-status'); + const updateStatusElement = document.getElementById('update-status'); + const updateAvailableDiv = document.getElementById('update-available'); + const updateButton = document.getElementById('btn-update-now'); + const refreshButton = document.getElementById('btn-refresh-status'); + + // Load system status + function loadSystemStatus() { + fetch('/api/system/status', { + headers: authHeaders() + }) + .then(handleResponse) + .then(data => { + if (data.status === 'success') { + versionElement.textContent = data.data.version; + uptimeElement.textContent = data.data.uptime; + + // Update transmission status with icon + if (data.data.transmissionStatus === 'Connected') { + transmissionStatusElement.innerHTML = ' Connected'; + } else { + transmissionStatusElement.innerHTML = ' Disconnected'; + } + } else { + showNotification('Failed to load system status', 'danger'); + } + }) + .catch(error => { + console.error('Error fetching system status:', error); + showNotification('Failed to connect to server', 'danger'); + }); + } + + // Check for updates + function checkForUpdates() { + updateStatusElement.innerHTML = ' Checking...'; + updateAvailableDiv.classList.add('d-none'); + + // Add test=true parameter to force update availability for testing + const testMode = localStorage.getItem('showUpdateButton') === 'true'; + const url = testMode ? '/api/system/check-updates?test=true' : '/api/system/check-updates'; + + fetch(url, { + headers: authHeaders() + }) + .then(handleResponse) + .then(data => { + if (data.status === 'success') { + if (data.data.updateAvailable) { + updateStatusElement.innerHTML = ' Update available'; + updateAvailableDiv.classList.remove('d-none'); + updateAvailableDiv.querySelector('span').textContent = + `A new version is available: ${data.data.currentVersion} → ${data.data.remoteVersion}`; + } else { + updateStatusElement.innerHTML = ' Up to date'; + } + } else { + updateStatusElement.innerHTML = ' Check failed'; + showNotification(data.message || 'Failed to check for updates', 'danger'); + } + }) + .catch(error => { + console.error('Error checking for updates:', error); + updateStatusElement.innerHTML = ' Check failed'; + showNotification('Failed to connect to server', 'danger'); + }); + } + + // Apply update + function applyUpdate() { + // Show confirmation dialog + if (!confirm('Are you sure you want to update the application? The service will restart.')) { + return; + } + + // Show loading state + updateButton.disabled = true; + updateButton.innerHTML = ' Updating...'; + showNotification('Applying update. Please wait...', 'info'); + + fetch('/api/system/update', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...authHeaders() + } + }) + .then(handleResponse) + .then(data => { + if (data.status === 'success') { + showNotification('Update applied successfully. The page will reload in 30 seconds.', 'success'); + // Set a timer to reload the page after the service has time to restart + setTimeout(() => { + window.location.reload(); + }, 30000); + } else { + updateButton.disabled = false; + updateButton.innerHTML = ' Update Now'; + showNotification(data.message || 'Failed to apply update', 'danger'); + } + }) + .catch(error => { + console.error('Error applying update:', error); + updateButton.disabled = false; + updateButton.innerHTML = ' Update Now'; + showNotification('Failed to connect to server', 'danger'); + }); + } + + // Event listeners + if (refreshButton) { + refreshButton.addEventListener('click', () => { + loadSystemStatus(); + checkForUpdates(); + }); + } + + if (updateButton) { + updateButton.addEventListener('click', applyUpdate); + } + + // Test mode toggle (for developers) + const testToggle = document.getElementById('toggle-test-update-button'); + if (testToggle) { + // Initialize based on current localStorage setting + const isTestMode = localStorage.getItem('showUpdateButton') === 'true'; + + // Update toggle text + testToggle.innerText = isTestMode ? 'Disable Test Update' : 'Enable Test Update'; + + // Add click handler + testToggle.addEventListener('click', (e) => { + e.preventDefault(); + const currentSetting = localStorage.getItem('showUpdateButton') === 'true'; + const newSetting = !currentSetting; + + localStorage.setItem('showUpdateButton', newSetting); + testToggle.innerText = newSetting ? 'Disable Test Update' : 'Enable Test Update'; + + // Re-check for updates with new setting + checkForUpdates(); + + showNotification(`Test update button ${newSetting ? 'enabled' : 'disabled'}`, 'info'); + }); + } + + // Initialize + loadSystemStatus(); + checkForUpdates(); + + // Set interval to refresh uptime every minute + setInterval(loadSystemStatus, 60000); +} \ No newline at end of file diff --git a/server-endpoints.js b/server-endpoints.js index 2a3c422..d2a6d57 100644 --- a/server-endpoints.js +++ b/server-endpoints.js @@ -64,7 +64,24 @@ app.get('/api/system/check-updates', async (req, res) => { const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')); const currentVersion = packageJson.version; - // Fetch latest updates without applying them + // Check for test mode flag which forces update availability for testing + const testMode = req.query.test === 'true'; + + if (testMode) { + // In test mode, always return that an update is available + return res.json({ + status: 'success', + data: { + updateAvailable: true, + currentVersion, + remoteVersion: '2.1.0-test', + commitsBehind: 1, + testMode: true + } + }); + } + + // Normal mode - fetch latest updates without applying them await execAsync('git fetch'); // Check if we're behind the remote repository