handle multiple topics correctly

This commit is contained in:
Raven Scott 2024-11-30 02:42:07 -05:00
parent c078751561
commit 0e94f047c0

212
app.js
View File

@ -143,12 +143,12 @@ document.addEventListener('DOMContentLoaded', () => {
// Restore saved connections // Restore saved connections
Object.keys(savedConnections).forEach((topicId) => { Object.keys(savedConnections).forEach((topicId) => {
let topicHex = savedConnections[topicId].topic; let topicHex = savedConnections[topicId].topic;
// Ensure topicHex is a string // Ensure topicHex is a string
if (typeof topicHex !== 'string') { if (typeof topicHex !== 'string') {
topicHex = b4a.toString(topicHex, 'hex'); topicHex = b4a.toString(topicHex, 'hex');
} }
addConnection(topicHex); addConnection(topicHex);
}); });
@ -233,6 +233,13 @@ function handlePeerData(data, topicId, peer) {
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
if (!connections[topicId] || peer !== window.activePeer) {
console.warn(`[WARN] Ignoring data from inactive peer or topic: ${topicId}`);
return;
}
// Process the response based on its type
if (response.error) { if (response.error) {
console.error(`[ERROR] Server error: ${response.error}`); console.error(`[ERROR] Server error: ${response.error}`);
showAlert('danger', response.error); showAlert('danger', response.error);
@ -241,12 +248,10 @@ function handlePeerData(data, topicId, peer) {
} }
if (response.type === 'containers') { if (response.type === 'containers') {
if (window.activePeer === peer) { renderContainers(response.data, topicId); // Scope containers to this topic
renderContainers(response.data);
}
} else if (response.type === 'stats') { } else if (response.type === 'stats') {
console.log(`[DEBUG] Updating stats for container: ${response.data.id}`); response.data.topicId = topicId; // Attach the topicId to the stats
updateContainerStats(response.data); // Call the stats update function updateContainerStats(response.data); // Update stats for specific containers
} else if (response.type === 'terminalOutput') { } else if (response.type === 'terminalOutput') {
appendTerminalOutput(response.data, response.containerId, response.encoding); appendTerminalOutput(response.data, response.containerId, response.encoding);
} else if (response.type === 'containerConfig') { } else if (response.type === 'containerConfig') {
@ -266,6 +271,9 @@ 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();
@ -369,67 +377,61 @@ function disconnectConnection(topicId, connectionItem) {
return; return;
} }
// Close and clean up open terminals associated with this connection // Clean up terminals
if (window.openTerminals[topicId]) { if (window.openTerminals[topicId]) {
console.log(`[INFO] Closing terminals for topic: ${topicId}`); console.log(`[INFO] Closing terminals for topic: ${topicId}`);
window.openTerminals[topicId].forEach((containerId) => { window.openTerminals[topicId].forEach((terminalId) => {
try { try {
cleanUpTerminal(containerId); // Use the terminal.js cleanup logic cleanUpTerminal(terminalId);
} catch (err) { } catch (err) {
console.error(`[ERROR] Failed to clean up terminal for container ${containerId}: ${err.message}`); console.error(`[ERROR] Failed to clean up terminal ${terminalId}: ${err.message}`);
} }
}); });
delete window.openTerminals[topicId]; delete window.openTerminals[topicId];
} }
// Hide the terminal modal if it's active // Destroy the peer and swarm
const terminalModal = document.getElementById('terminal-modal');
if (terminalModal && terminalModal.style.display === 'flex') {
console.log(`[INFO] Hiding terminal modal for disconnected topic: ${topicId}`);
terminalModal.style.display = 'none';
}
// Disconnect the peer and destroy the swarm
if (connection.peer) { if (connection.peer) {
connection.peer.destroy(); connection.peer.destroy();
connection.peer = null;
} }
if (connection.swarm) { if (connection.swarm) {
connection.swarm.destroy(); connection.swarm.destroy();
connection.swarm = null;
} }
// Remove the connection from the global connections object // Remove from global connections
delete connections[topicId]; delete connections[topicId];
// Remove the connection item from the list // Remove the connection item from the UI
if (connectionItem) { if (connectionItem) {
connectionList.removeChild(connectionItem); connectionList.removeChild(connectionItem);
} }
console.log(`[INFO] Disconnected and removed connection: ${topicId}`); // Reset the connection title if this was the active peer
// Reset active peer if it was the disconnected connection
if (window.activePeer === connection.peer) { if (window.activePeer === connection.peer) {
window.activePeer = null; window.activePeer = null;
connectionTitle.textContent = 'Choose a Connection'; // Reset title
dashboard.classList.add('hidden'); const connectionTitle = document.getElementById('connection-title');
containerList.innerHTML = ''; // Clear the container list if (connectionTitle) {
connectionTitle.textContent = 'Choose a Connection'; // Reset the title
}
const dashboard = document.getElementById('dashboard');
if (dashboard) {
dashboard.classList.add('hidden');
}
resetContainerList(); // Clear containers
} }
// Check if no connections remain, and show the welcome page // Show welcome page if no connections remain
if (Object.keys(connections).length === 0) { if (Object.keys(connections).length === 0) {
console.log('[DEBUG] All connections removed. Showing welcome page.');
showWelcomePage(); showWelcomePage();
} }
// Ensure the container list is cleared regardless of the active connection console.log(`[INFO] Disconnected and removed connection: ${topicId}`);
resetContainerList();
// Refresh the connections view
resetConnectionsView();
} }
// Function to reset the container list // Function to reset the container list
function resetContainerList() { function resetContainerList() {
containerList.innerHTML = ''; // Clear the existing list containerList.innerHTML = ''; // Clear the existing list
@ -480,31 +482,21 @@ function updateConnectionStatus(topicId, isConnected) {
// Switch between connections // Switch between connections
function switchConnection(topicId) { function switchConnection(topicId) {
const connection = connections[topicId]; const connection = connections[topicId];
const connectionTitle = document.getElementById('connection-title');
if (!connection || !connection.peer) { if (!connection || !connection.peer) {
console.error('[ERROR] No connection found or no active peer.'); console.error('[ERROR] No connection found or no active peer.');
showWelcomePage();
// Update title if element exists
if (connectionTitle) {
connectionTitle.textContent = '󠀠';
connectionTitle.classList.add('hidden');
}
showWelcomePage(); // Show welcome page
return; return;
} }
// Set active peer and update UI // Update the active peer
window.activePeer = connection.peer; window.activePeer = connection.peer;
if (connectionTitle) { // Clear container list before loading new data
connectionTitle.textContent = `Connection: ${topicId}`; resetContainerList();
connectionTitle.classList.remove('hidden');
}
hideWelcomePage(); // Hide the welcome page console.log(`[INFO] Switched to connection: ${topicId}`);
sendCommand('listContainers'); // Request container list sendCommand('listContainers'); // Request containers for the new connection
} }
// Attach switchConnection to the global window object // Attach switchConnection to the global window object
@ -525,58 +517,62 @@ function sendCommand(command, args = {}) {
window.sendCommand = sendCommand; window.sendCommand = sendCommand;
// Render the container list // Render the container list
function renderContainers(containers) { function renderContainers(containers, topicId) {
console.log(`[INFO] Rendering ${containers.length} containers`); if (!window.activePeer || !connections[topicId] || window.activePeer !== connections[topicId].peer) {
console.warn('[WARN] Active peer mismatch or invalid connection. Skipping container rendering.');
return;
}
console.log(`[INFO] Rendering ${containers.length} containers for topic: ${topicId}`);
containerList.innerHTML = ''; // Clear the current list containerList.innerHTML = ''; // Clear the current list
containers.forEach((container) => { containers.forEach((container) => {
const name = container.Names[0].replace(/^\//, ''); // Remove leading slash const name = container.Names[0]?.replace(/^\//, '') || 'Unknown'; // Avoid undefined Names
const image = container.Image; const image = container.Image || '-';
const containerId = container.Id; const containerId = container.Id;
const ipAddress = container.ipAddress || '-'; // Use the IP address field const ipAddress = container.ipAddress || 'No IP Assigned';
const row = document.createElement('tr'); const row = document.createElement('tr');
row.dataset.containerId = containerId; // Store container ID for reference row.dataset.containerId = containerId; // Store container ID for reference
row.innerHTML = ` row.innerHTML = `
<td>${name}</td> <td>${name}</td>
<td>${image}</td> <td>${image}</td>
<td>${container.State}</td> <td>${container.State || 'Unknown'}</td>
<td class="cpu">0</td> <td class="cpu">0</td>
<td class="memory">0</td> <td class="memory">0</td>
<td class="ip-address">${ipAddress}</td> <td class="ip-address">${ipAddress}</td>
<td> <td>
<button class="btn btn-success btn-sm action-start" ${container.State === 'running' ? 'disabled' : ''}> <button class="btn btn-success btn-sm action-start" ${container.State === 'running' ? 'disabled' : ''}>
<i class="fas fa-play"></i> <i class="fas fa-play"></i>
</button> </button>
<button class="btn btn-info btn-sm action-restart" ${container.State !== 'running' ? 'disabled' : ''}> <button class="btn btn-info btn-sm action-restart" ${container.State !== 'running' ? 'disabled' : ''}>
<i class="fas fa-redo"></i> <i class="fas fa-redo"></i>
</button> </button>
<button class="btn btn-warning btn-sm action-stop" ${container.State !== 'running' ? 'disabled' : ''}> <button class="btn btn-warning btn-sm action-stop" ${container.State !== 'running' ? 'disabled' : ''}>
<i class="fas fa-stop"></i> <i class="fas fa-stop"></i>
</button> </button>
<button class="btn btn-danger btn-sm action-remove"> <button class="btn btn-danger btn-sm action-remove">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
<button class="btn btn-primary btn-sm action-terminal" ${container.State !== 'running' ? 'disabled' : ''}> <button class="btn btn-primary btn-sm action-terminal" ${container.State !== 'running' ? 'disabled' : ''}>
<i class="fas fa-terminal"></i> <i class="fas fa-terminal"></i>
</button> </button>
<button class="btn btn-secondary btn-sm action-duplicate"> <button class="btn btn-secondary btn-sm action-duplicate">
<i class="fas fa-clone"></i> <i class="fas fa-clone"></i>
</button> </button>
</td>
</td> `;
`;
containerList.appendChild(row); containerList.appendChild(row);
// Add event listeners for action buttons
addActionListeners(row, container);
// Add event listener for duplicate button // Add event listener for duplicate button
const duplicateBtn = row.querySelector('.action-duplicate'); const duplicateBtn = row.querySelector('.action-duplicate');
duplicateBtn.addEventListener('click', () => openDuplicateModal(container)); duplicateBtn.addEventListener('click', () => openDuplicateModal(container));
// Add event listeners for action buttons
addActionListeners(row, container);
}); });
} }
function addActionListeners(row, container) { function addActionListeners(row, container) {
const startBtn = row.querySelector('.action-start'); const startBtn = row.querySelector('.action-start');
const stopBtn = row.querySelector('.action-stop'); const stopBtn = row.querySelector('.action-stop');
@ -730,31 +726,29 @@ function addActionListeners(row, container) {
// Function to update container statistics // Function to update container statistics
function updateContainerStats(stats) { function updateContainerStats(stats) {
console.log(`[DEBUG] Updating stats for container ID: ${stats.id}`); console.log(`[DEBUG] Updating stats for container ID: ${stats.id}, Topic ID: ${stats.topicId}`);
let row = containerList.querySelector(`tr[data-container-id="${stats.id}"]`);
if (!row) { // Ensure stats belong to the active connection
console.warn(`[WARN] No row found for container ID: ${stats.id}. Adding a placeholder.`); if (!window.activePeer || !connections[stats.topicId] || window.activePeer !== connections[stats.topicId].peer) {
// Create a placeholder row if it doesn't exist console.warn(`[WARN] Stats received for inactive or unknown connection. Skipping.`);
row = document.createElement('tr'); return;
row.dataset.containerId = stats.id;
row.innerHTML = `
<td>Unknown</td>
<td>-</td>
<td>-</td>
<td class="cpu">0</td>
<td class="memory">0</td>
<td class="ip-address">-</td>
<td>-</td>
`;
containerList.appendChild(row);
} }
row.querySelector('.cpu').textContent = stats.cpu.toFixed(2); // Find the row for the container by its ID
row.querySelector('.memory').textContent = (stats.memory / (1024 * 1024)).toFixed(2); const row = containerList.querySelector(`tr[data-container-id="${stats.id}"]`);
row.querySelector('.ip-address').textContent = stats.ip || '-'; if (!row) {
console.warn(`[WARN] No matching row for container ID: ${stats.id}. Skipping stats update.`);
return;
}
// Update the container statistics in the UI
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';
} }
// 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}`);