MasterDraco 2dcd4becef Fix update functionality and improve documentation
- Fixed update detection in install scripts
- Added Git availability checks to update system
- Improved error handling for update endpoint
- Added detailed Git requirements to README
- Added troubleshooting section for update issues

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-03-10 16:57:47 +00:00

243 lines
9.4 KiB
JavaScript

/**
* 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 = '<i class="fas fa-check-circle text-success"></i> Connected';
} else {
transmissionStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> 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 = '<i class="fas fa-circle-notch fa-spin"></i> 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';
// Set a timeout to detect network issues
const timeoutId = setTimeout(() => {
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check timed out';
showNotification('Update check timed out. Please try again later.', 'warning');
}, 10000); // 10 second timeout
fetch(url, {
headers: authHeaders(),
// Add a fetch timeout as well
signal: AbortSignal.timeout(15000) // 15 second timeout (available in modern browsers)
})
.then(response => {
clearTimeout(timeoutId);
// Better error checking
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.message || `Server error: ${response.status}`);
}).catch(e => {
if (e instanceof SyntaxError) {
throw new Error(`Server error: ${response.status}`);
}
throw e;
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
if (data.data && data.data.updateAvailable) {
updateStatusElement.innerHTML = '<i class="fas fa-exclamation-circle text-warning"></i> 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 = '<i class="fas fa-check-circle text-success"></i> Up to date';
}
} else {
// Error status but with a response
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check failed';
showNotification(data.message || 'Failed to check for updates', 'danger');
}
})
.catch(error => {
clearTimeout(timeoutId);
console.error('Error checking for updates:', error);
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check failed';
// More specific error message based on the error type
if (error.name === 'AbortError') {
showNotification('Update check timed out. Please try again later.', 'warning');
} else if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
showNotification('Network error. Please check your connection and try again.', 'danger');
} else {
showNotification(error.message || '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 = '<i class="fas fa-circle-notch fa-spin"></i> Updating...';
showNotification('Applying update. Please wait...', 'info');
// Set a timeout for the update process
const updateTimeoutId = setTimeout(() => {
updateButton.disabled = false;
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
showNotification('Update process timed out. Please try again or check server logs.', 'warning');
}, 60000); // 60 second timeout for the entire update process
fetch('/api/system/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...authHeaders()
},
signal: AbortSignal.timeout(45000) // 45 second timeout
})
.then(response => {
// Better error checking
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.message || `Server error: ${response.status}`);
}).catch(e => {
if (e instanceof SyntaxError) {
throw new Error(`Server error: ${response.status}`);
}
throw e;
});
}
return response.json();
})
.then(data => {
clearTimeout(updateTimeoutId);
if (data.status === 'success') {
showNotification('Update applied successfully. The page will reload in 30 seconds.', 'success');
// Show a countdown to reload
let secondsLeft = 30;
updateButton.innerHTML = `<i class="fas fa-sync"></i> Reloading in ${secondsLeft}s...`;
const countdownInterval = setInterval(() => {
secondsLeft--;
updateButton.innerHTML = `<i class="fas fa-sync"></i> Reloading in ${secondsLeft}s...`;
if (secondsLeft <= 0) {
clearInterval(countdownInterval);
window.location.reload();
}
}, 1000);
// Set a timer to reload the page after the service has time to restart
setTimeout(() => {
clearInterval(countdownInterval);
window.location.reload();
}, 30000);
} else {
updateButton.disabled = false;
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
showNotification(data.message || 'Failed to apply update', 'danger');
}
})
.catch(error => {
clearTimeout(updateTimeoutId);
console.error('Error applying update:', error);
updateButton.disabled = false;
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
// More specific error message based on the error type
if (error.name === 'AbortError') {
showNotification('Update request timed out. The server might still be processing the update.', 'warning');
} else if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
showNotification('Network error. Please check your connection and try again.', 'danger');
} else {
showNotification(error.message || '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);
}