Compare commits
3 Commits
1dc39decf7
...
d9549454e1
Author | SHA1 | Date | |
---|---|---|---|
|
d9549454e1 | ||
|
3177976845 | ||
|
b8c6f30d32 |
145
app.js
145
app.js
@ -20,9 +20,10 @@ const connections = {};
|
|||||||
window.openTerminals = {};
|
window.openTerminals = {};
|
||||||
let activePeer = null;
|
let activePeer = null;
|
||||||
window.activePeer = null; // Expose to other modules
|
window.activePeer = null; // Expose to other modules
|
||||||
hideStatusIndicator();
|
|
||||||
let statsInterval = null; // Global variable to hold the interval
|
|
||||||
|
|
||||||
|
hideStatusIndicator();
|
||||||
|
let statsInterval = null;
|
||||||
|
let lastStatsUpdate = Date.now();
|
||||||
function stopStatsInterval() {
|
function stopStatsInterval() {
|
||||||
if (statsInterval) {
|
if (statsInterval) {
|
||||||
clearInterval(statsInterval);
|
clearInterval(statsInterval);
|
||||||
@ -31,21 +32,41 @@ function stopStatsInterval() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function startStatsInterval() {
|
function startStatsInterval() {
|
||||||
// Clear any existing interval
|
|
||||||
if (statsInterval) {
|
if (statsInterval) {
|
||||||
clearInterval(statsInterval);
|
clearInterval(statsInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a new interval to request stats every second
|
|
||||||
statsInterval = setInterval(() => {
|
statsInterval = setInterval(() => {
|
||||||
if (window.activePeer) {
|
if (window.activePeer) {
|
||||||
console.log('[INFO] Requesting container stats...');
|
const now = Date.now();
|
||||||
sendCommand('stats', {}); // Adjust the command if specific arguments are needed
|
if (now - lastStatsUpdate >= 500) { // Ensure at least 500ms between updates
|
||||||
|
sendCommand('stats', {}); // Adjust command if necessary
|
||||||
|
lastStatsUpdate = now;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('[WARN] No active peer; skipping stats request.');
|
console.warn('[WARN] No active peer; skipping stats request.');
|
||||||
}
|
}
|
||||||
}, 1000); // 1 second interval
|
}, 500); // Poll every 100ms for better reactivity
|
||||||
|
}
|
||||||
|
const smoothedStats = {}; // Container-specific smoothing storage
|
||||||
|
|
||||||
|
|
||||||
|
function smoothStats(containerId, newStats, smoothingFactor = 0.2) {
|
||||||
|
if (!smoothedStats[containerId]) {
|
||||||
|
smoothedStats[containerId] = { cpu: 0, memory: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
smoothedStats[containerId].cpu =
|
||||||
|
smoothedStats[containerId].cpu * (1 - smoothingFactor) +
|
||||||
|
newStats.cpu * smoothingFactor;
|
||||||
|
|
||||||
|
smoothedStats[containerId].memory =
|
||||||
|
smoothedStats[containerId].memory * (1 - smoothingFactor) +
|
||||||
|
newStats.memory * smoothingFactor;
|
||||||
|
|
||||||
|
return smoothedStats[containerId];
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshContainerStats() {
|
function refreshContainerStats() {
|
||||||
@ -261,43 +282,73 @@ collapseSidebarBtn.addEventListener('click', () => {
|
|||||||
|
|
||||||
function handlePeerData(data, topicId, peer) {
|
function handlePeerData(data, topicId, peer) {
|
||||||
try {
|
try {
|
||||||
|
// Parse the incoming data
|
||||||
const response = JSON.parse(data.toString());
|
const response = JSON.parse(data.toString());
|
||||||
console.log(`[DEBUG] Received data from peer (topic: ${topicId}): ${JSON.stringify(response)}`);
|
console.log(`[DEBUG] Received data from peer (topic: ${topicId}): ${JSON.stringify(response)}`);
|
||||||
|
|
||||||
// Ensure the data is for the active connection
|
// Ensure the data is for the active connection
|
||||||
if (!connections[topicId] || peer !== window.activePeer) {
|
if (!connections[topicId]) {
|
||||||
console.warn(`[WARN] Ignoring data from inactive peer or topic: ${topicId}`);
|
console.warn(`[WARN] No connection found for topic: ${topicId}. Ignoring data.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the response based on its type
|
if (peer !== connections[topicId].peer) {
|
||||||
|
console.warn(`[WARN] Ignoring data from a non-active peer for topic: ${topicId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle errors in the response
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.error(`[ERROR] Server error: ${response.error}`);
|
console.error(`[ERROR] Server error received: ${response.error}`);
|
||||||
showAlert('danger', response.error);
|
showAlert('danger', response.error);
|
||||||
hideStatusIndicator();
|
hideStatusIndicator();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.type === 'containers') {
|
// Delegate handling based on the response type
|
||||||
renderContainers(response.data, topicId); // Scope containers to this topic
|
switch (response.type) {
|
||||||
} else if (response.type === 'stats') {
|
case 'containers':
|
||||||
response.data.topicId = topicId; // Attach the topicId to the stats
|
console.log('[INFO] Processing container list...');
|
||||||
updateContainerStats(response.data); // Update stats for specific containers
|
renderContainers(response.data, topicId); // Render containers specific to this topic
|
||||||
} else if (response.type === 'terminalOutput') {
|
break;
|
||||||
appendTerminalOutput(response.data, response.containerId, response.encoding);
|
|
||||||
} else if (response.type === 'containerConfig') {
|
case 'stats':
|
||||||
if (window.inspectContainerCallback) {
|
console.log('[INFO] Updating container stats...');
|
||||||
window.inspectContainerCallback(response.data);
|
if (!response.data.id) {
|
||||||
window.inspectContainerCallback = null; // Reset the callback
|
console.warn('[WARN] Stats response is missing container ID. Skipping update.');
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
response.data.topicId = topicId; // Attach the topicId to the stats
|
||||||
|
updateContainerStats(response.data); // Update stats for the specified container
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'terminalOutput':
|
||||||
|
console.log('[INFO] Appending terminal output...');
|
||||||
|
appendTerminalOutput(response.data, response.containerId, response.encoding);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'containerConfig':
|
||||||
|
console.log('[INFO] Handling container configuration...');
|
||||||
|
if (window.inspectContainerCallback) {
|
||||||
|
window.inspectContainerCallback(response.data);
|
||||||
|
window.inspectContainerCallback = null; // Reset the callback
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`[WARN] Unhandled response type: ${response.type}`);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle peer response callback if defined
|
||||||
if (typeof window.handlePeerResponse === 'function') {
|
if (typeof window.handlePeerResponse === 'function') {
|
||||||
window.handlePeerResponse(response);
|
window.handlePeerResponse(response);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// Catch and log any parsing or processing errors
|
||||||
console.error(`[ERROR] Failed to process peer data: ${err.message}`);
|
console.error(`[ERROR] Failed to process peer data: ${err.message}`);
|
||||||
showAlert('danger', 'Failed to process peer data.');
|
console.error(`[DEBUG] Raw data received: ${data.toString()}`);
|
||||||
|
showAlert('danger', 'Failed to process peer data. Check the console for details.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,6 +356,7 @@ function handlePeerData(data, topicId, peer) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add a new connection
|
// Add a new connection
|
||||||
addConnectionForm.addEventListener('submit', (e) => {
|
addConnectionForm.addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -346,6 +398,7 @@ function addConnection(topicHex) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
disconnectConnection(topicId, connectionItem);
|
disconnectConnection(topicId, connectionItem);
|
||||||
});
|
});
|
||||||
|
refreshContainerStats();
|
||||||
|
|
||||||
connectionList.appendChild(connectionItem);
|
connectionList.appendChild(connectionItem);
|
||||||
|
|
||||||
@ -370,12 +423,13 @@ function addConnection(topicHex) {
|
|||||||
window.activePeer = null;
|
window.activePeer = null;
|
||||||
dashboard.classList.add('hidden');
|
dashboard.classList.add('hidden');
|
||||||
containerList.innerHTML = '';
|
containerList.innerHTML = '';
|
||||||
|
stopStatsInterval(); // Stop stats polling
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!window.activePeer) {
|
if (!window.activePeer) {
|
||||||
switchConnection(topicId);
|
switchConnection(topicId);
|
||||||
}
|
}
|
||||||
|
startStatsInterval();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collapse the sidebar after adding a connection
|
// Collapse the sidebar after adding a connection
|
||||||
@ -398,6 +452,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const topicHex = savedConnections[topicId].topic;
|
const topicHex = savedConnections[topicId].topic;
|
||||||
addConnection(topicHex);
|
addConnection(topicHex);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Object.keys(connections).length > 0) {
|
||||||
|
hideWelcomePage();
|
||||||
|
startStatsInterval(); // Start stats polling for active peers
|
||||||
|
} else {
|
||||||
|
showWelcomePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertVisibility();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -769,31 +832,35 @@ function addActionListeners(row, container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Function to update container statistics
|
function updateStatsUI(row, stats) {
|
||||||
function updateContainerStats(stats) {
|
requestIdleCallback(() => {
|
||||||
console.log(`[DEBUG] Updating stats for container ID: ${stats.id}, Topic ID: ${stats.topicId}`);
|
row.querySelector('.cpu').textContent = stats.cpu.toFixed(2) || '0.00';
|
||||||
|
row.querySelector('.memory').textContent = (stats.memory / (1024 * 1024)).toFixed(2) || '0.00';
|
||||||
|
row.querySelector('.ip-address').textContent = stats.ip || 'No IP Assigned';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure stats belong to the active connection
|
function updateContainerStats(stats) {
|
||||||
if (!window.activePeer || !connections[stats.topicId] || window.activePeer !== connections[stats.topicId].peer) {
|
if (!stats || !stats.id || typeof stats.cpu === 'undefined' || typeof stats.memory === 'undefined') {
|
||||||
console.warn(`[WARN] Stats received for inactive or unknown connection. Skipping.`);
|
console.error('[ERROR] Invalid stats object:', stats);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the row for the container by its ID
|
console.log(`[DEBUG] Updating stats for container ID: ${stats.id}`);
|
||||||
|
|
||||||
const row = containerList.querySelector(`tr[data-container-id="${stats.id}"]`);
|
const row = containerList.querySelector(`tr[data-container-id="${stats.id}"]`);
|
||||||
if (!row) {
|
if (!row) {
|
||||||
console.warn(`[WARN] No matching row for container ID: ${stats.id}. Skipping stats update.`);
|
console.warn(`[WARN] No matching row for container ID: ${stats.id}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the container statistics in the UI
|
const smoothed = smoothStats(stats.id, stats);
|
||||||
row.querySelector('.cpu').textContent = stats.cpu.toFixed(2) || '0.00';
|
updateStatsUI(row, smoothed);
|
||||||
row.querySelector('.memory').textContent = (stats.memory / (1024 * 1024)).toFixed(2) || '0.00';
|
|
||||||
row.querySelector('.ip-address').textContent = stats.ip || 'No IP Assigned';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to open the Duplicate Modal with container configurations
|
// Function to open the Duplicate Modal with container configurations
|
||||||
function openDuplicateModal(container) {
|
function openDuplicateModal(container) {
|
||||||
console.log(`[INFO] Opening Duplicate Modal for container: ${container.Id}`);
|
console.log(`[INFO] Opening Duplicate Modal for container: ${container.Id}`);
|
||||||
|
Loading…
Reference in New Issue
Block a user