234 lines
8.6 KiB
HTML
234 lines
8.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="stylesheet" type="text/css" href="/css/nav.css">
|
|
<title>Firmware Upgrade</title>
|
|
<style>
|
|
.status-circle {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
margin-right: 10px;
|
|
}
|
|
.connected { background-color: #4CAF50; }
|
|
.disconnected { background-color: #f44336; }
|
|
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 20px auto;
|
|
padding: 20px;
|
|
background-color: #fff;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.version-info {
|
|
margin: 20px 0;
|
|
padding: 10px;
|
|
background-color: #f5f5f5;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.progress-box {
|
|
height: calc(100vh - 500px);
|
|
min-height: 200px;
|
|
margin: 20px 0;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
overflow-y: auto;
|
|
font-family: monospace;
|
|
}
|
|
|
|
button {
|
|
background-color: #2196F3;
|
|
color: white;
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin: 5px;
|
|
}
|
|
|
|
button:disabled {
|
|
background-color: #cccccc;
|
|
cursor: not-allowed;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="navbar"></div>
|
|
<div class="container">
|
|
<h1>Firmware Upgrade</h1>
|
|
|
|
<div>
|
|
<span class="status-circle disconnected" id="status-indicator"></span>
|
|
<span id="connection-status">Disconnected</span>
|
|
</div>
|
|
|
|
<div class="version-info">
|
|
<p>Current Version: <span id="current-version">-</span></p>
|
|
<p>Latest Version: <span id="latest-version">-</span></p>
|
|
</div>
|
|
|
|
<div>
|
|
<button id="check-upgrades" onclick="checkUpdates()">Check for Upgrades</button>
|
|
<button id="start-upgrade" onclick="startUpdate()" disabled>Start upgrade</button>
|
|
</div>
|
|
|
|
<div class="progress-box" id="progress-log"></div>
|
|
</div>
|
|
|
|
<script>
|
|
// Load the navigation bar from an external HTML file and insert it into the page
|
|
fetch('/www/navbar.html')
|
|
.then(response => response.text())
|
|
.then(data => {
|
|
document.getElementById('navbar').innerHTML = data;
|
|
});
|
|
|
|
// Flag to track if a firmware upgrade is available
|
|
let updateAvailable = false;
|
|
|
|
// Updates the UI to show connection status
|
|
// Changes the color and text of the status indicator
|
|
function updateStatus(connected) {
|
|
const indicator = document.getElementById('status-indicator');
|
|
const status = document.getElementById('connection-status');
|
|
|
|
indicator.className = `status-circle ${connected ? 'connected' : 'disconnected'}`;
|
|
status.textContent = connected ? 'Connected' : 'Disconnected';
|
|
}
|
|
|
|
// Adds a message to the progress log box
|
|
// Auto-scrolls to the bottom to show latest messages
|
|
function log(message) {
|
|
const logBox = document.getElementById('progress-log');
|
|
logBox.innerHTML += `${message}<br>`;
|
|
logBox.scrollTop = logBox.scrollHeight;
|
|
}
|
|
|
|
// Checks for firmware updates by calling the server API
|
|
// Updates the UI with version information and enables/disables upgrade button
|
|
async function checkUpdates() {
|
|
const checkButton = document.getElementById('check-upgrades');
|
|
checkButton.disabled = true; // Disable button while checking
|
|
|
|
try {
|
|
const response = await fetch('/upgrade/check');
|
|
const data = await response.json();
|
|
|
|
// Upgrade version information in the UI
|
|
document.getElementById('current-version').textContent = data.currentVersion;
|
|
document.getElementById('latest-version').textContent = data.latestVersion;
|
|
|
|
// Enable/disable upgrade button based on availability
|
|
updateAvailable = data.updateAvailable;
|
|
document.getElementById('start-upgrade').disabled = !updateAvailable;
|
|
|
|
log(updateAvailable ? 'Upgrade available!' : 'No upgrades available');
|
|
} catch (error) {
|
|
log('Error checking for upgrades: ' + error);
|
|
} finally {
|
|
checkButton.disabled = false; // Re-enable button when done
|
|
}
|
|
}
|
|
|
|
// Initiates the firmware upgrade process
|
|
// Uses Websocket to receive progress updates
|
|
async function startUpdate() {
|
|
const startButton = document.getElementById('start-upgrade');
|
|
const checkButton = document.getElementById('check-upgrades');
|
|
let retryCount = 0;
|
|
const maxRetries = 3;
|
|
|
|
// Disable buttons during the update
|
|
startButton.disabled = true;
|
|
checkButton.disabled = true;
|
|
|
|
try {
|
|
// Start the upgrade process with a timeout
|
|
log('Starting update...');
|
|
const timeout = new Promise((_, reject) =>
|
|
setTimeout(() => reject(new Error("Request timed out")), 10000)
|
|
);
|
|
|
|
const response = await Promise.race([
|
|
fetch('/upgrade/start', {
|
|
method: 'POST',
|
|
credentials: 'same-origin'
|
|
}),
|
|
timeout
|
|
]);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
log('Update initiated successfully.');
|
|
|
|
// Create SSE connection
|
|
const eventSource = new EventSource('/upgrade-progress');
|
|
log('Connecting to update server...');
|
|
|
|
eventSource.onopen = () => {
|
|
console.log("EventSource connected");
|
|
log('Update connection established.');
|
|
};
|
|
|
|
eventSource.addEventListener('update', (event) => {
|
|
try {
|
|
console.log("Received message:", event.data);
|
|
const data = JSON.parse(event.data);
|
|
log(data.message); // Log the update message
|
|
|
|
// Handle completion
|
|
if (data.complete) {
|
|
eventSource.close();
|
|
log('Upgrade complete! Rebooting...');
|
|
//setTimeout(() => window.location.reload(), 5000);
|
|
}
|
|
} catch (error) {
|
|
console.error("Message parsing error:", error);
|
|
log(`Error processing update message: ${error.message}`);
|
|
}
|
|
});
|
|
|
|
|
|
eventSource.onerror = (event) => {
|
|
const errorDetails = event.error ? `Error: ${event.error}` : event.status ? `Status: ${event.status}` : 'Unknown error';
|
|
log('Connection error occurred' + errorDetails);
|
|
|
|
if (retryCount++ >= maxRetries || eventSource.readyState === EventSource.CLOSED) {
|
|
log('Max retries reached. Please refresh the page to retry.');
|
|
eventSource.close();
|
|
startButton.disabled = false;
|
|
checkButton.disabled = false;
|
|
} else {
|
|
log(`Attempting to reconnect... (${retryCount}/${maxRetries})`);
|
|
}
|
|
};
|
|
} catch (error) {
|
|
console.error("Update error:", error);
|
|
log(`Update error: ${error.message}`);
|
|
} finally {
|
|
startButton.disabled = false;
|
|
checkButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
// Poll server every 5 seconds to check if device is still connected
|
|
// Updates the status indicator accordingly
|
|
setInterval(async () => {
|
|
try {
|
|
const response = await fetch('/api/status');
|
|
updateStatus(response.ok);
|
|
} catch {
|
|
updateStatus(false);
|
|
}
|
|
}, 5000);
|
|
</script>
|
|
</body>
|
|
</html> |