diff --git a/app.js b/app.js index 525ac47..30f9ab1 100644 --- a/app.js +++ b/app.js @@ -46,15 +46,15 @@ addConnectionForm.addEventListener('submit', (e) => { // Function to add a new connection function addConnection(topicHex) { - const topic = b4a.from(topicHex, 'hex'); - const topicId = topicHex.substring(0, 12); - - console.log(`[INFO] Adding connection with topic: ${topicHex}`); - - const connectionItem = document.createElement('li'); - connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between'; - connectionItem.dataset.topicId = topicId; - connectionItem.innerHTML = ` + const topic = b4a.from(topicHex, 'hex'); + const topicId = topicHex.substring(0, 12); + + console.log(`[INFO] Adding connection with topic: ${topicHex}`); + + const connectionItem = document.createElement('li'); + connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between'; + connectionItem.dataset.topicId = topicId; + connectionItem.innerHTML = ` ${topicId} @@ -62,188 +62,188 @@ function addConnection(topicHex) { `; - + + // Add click event to switch connection + connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId)); + + // Add click event to the disconnect button + const disconnectBtn = connectionItem.querySelector('.disconnect-btn'); + disconnectBtn.addEventListener('click', (e) => { + e.stopPropagation(); // Prevent triggering the switch connection event + disconnectConnection(topicId, connectionItem); + }); + + connectionList.appendChild(connectionItem); + + connections[topicId] = { topic, peer: null, swarm: null }; + + // Create a new swarm for this connection + const swarm = new Hyperswarm(); + connections[topicId].swarm = swarm; + + swarm.join(topic, { client: true, server: false }); + + swarm.on('connection', (peer) => { + console.log(`[INFO] Connected to peer for topic: ${topicHex}`); + + // Prevent duplicate connections + if (connections[topicId].peer) { + console.warn(`[WARN] Duplicate connection detected for topic: ${topicId}. Closing.`); + peer.destroy(); + return; + } + + connections[topicId].peer = peer; + updateConnectionStatus(topicId, true); + + + function handlePeerData(data, topicId, peer) { + try { + const response = JSON.parse(data.toString()); + console.log(`[DEBUG] Received data from peer (topic: ${topicId}): ${JSON.stringify(response)}`); + + if (response.type === 'containers') { + if (window.activePeer === peer) { + renderContainers(response.data); + } + } else if (response.type === 'terminalOutput') { + appendTerminalOutput(response.data, response.containerId, response.encoding); + } else if (response.type === 'containerConfig') { + if (window.inspectContainerCallback) { + window.inspectContainerCallback(response.data); + window.inspectContainerCallback = null; // Reset the callback + } + } else if (response.type === 'stats') { + updateContainerStats(response.data); + } else if (response.error) { + console.error(`[ERROR] Server error: ${response.error}`); + } + } catch (err) { + console.error(`[ERROR] Failed to parse data from peer (topic: ${topicId}): ${err.message}`); + } + } + + peer.on('data', (data) => { + // Handle incoming data + handlePeerData(data, topicId, peer); + }); + + peer.on('close', () => { + console.log(`[INFO] Disconnected from peer for topic: ${topicHex}`); + updateConnectionStatus(topicId, false); + + if (window.activePeer === peer) { + window.activePeer = null; + connectionTitle.textContent = 'Disconnected'; + dashboard.classList.add('hidden'); + containerList.innerHTML = ''; + } + }); + + // If this is the first connection, switch to it + if (!window.activePeer) { + switchConnection(topicId); + } + }); +} + +function disconnectConnection(topicId, connectionItem) { + const connection = connections[topicId]; + if (!connection) { + console.error(`[ERROR] No connection found for topicId: ${topicId}`); + return; + } + + // Close and kill any open terminals associated with this connection + if (window.openTerminals[topicId]) { + console.log(`[INFO] Closing terminals for topic: ${topicId}`); + window.openTerminals[topicId].forEach((containerId) => { + try { + cleanUpTerminal(containerId); // Use the terminal.js cleanup logic + } catch (err) { + console.error(`[ERROR] Failed to kill terminal for container ${containerId}: ${err.message}`); + } + }); + delete window.openTerminals[topicId]; + } + + // Hide the terminal modal if it is active + const terminalModal = document.getElementById('terminal-modal'); + if (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) { + connection.peer.destroy(); + connection.peer = null; + } + if (connection.swarm) { + connection.swarm.destroy(); + connection.swarm = null; + } + + // Remove the connection from the global connections object + delete connections[topicId]; + + // Remove the connection item from the list + connectionList.removeChild(connectionItem); + + console.log(`[INFO] Disconnected and removed connection: ${topicId}`); + + // Reset active peer if it was the disconnected connection + if (window.activePeer === connection.peer) { + window.activePeer = null; + connectionTitle.textContent = 'Choose a Connection'; // Reset title + dashboard.classList.add('hidden'); + containerList.innerHTML = ''; // Clear the container list + } + + // Ensure the container list is cleared regardless of the active connection + resetContainerList(); + + // Refresh the connections view + resetConnectionsView(); +} +// Function to reset the container list +function resetContainerList() { + containerList.innerHTML = ''; // Clear the existing list + console.log('[INFO] Container list cleared.'); +} + +// Function to reset the connections view +function resetConnectionsView() { + // Clear the connection list + connectionList.innerHTML = ''; + + // Re-populate the connection list from the `connections` object + Object.keys(connections).forEach((topicId) => { + const connectionItem = document.createElement('li'); + connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between'; + connectionItem.dataset.topicId = topicId; + connectionItem.innerHTML = ` + + ${topicId} + + + `; + // Add click event to switch connection connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId)); - + // Add click event to the disconnect button const disconnectBtn = connectionItem.querySelector('.disconnect-btn'); disconnectBtn.addEventListener('click', (e) => { e.stopPropagation(); // Prevent triggering the switch connection event disconnectConnection(topicId, connectionItem); }); - + connectionList.appendChild(connectionItem); - - connections[topicId] = { topic, peer: null, swarm: null }; - - // Create a new swarm for this connection - const swarm = new Hyperswarm(); - connections[topicId].swarm = swarm; - - swarm.join(topic, { client: true, server: false }); - - swarm.on('connection', (peer) => { - console.log(`[INFO] Connected to peer for topic: ${topicHex}`); - - // Prevent duplicate connections - if (connections[topicId].peer) { - console.warn(`[WARN] Duplicate connection detected for topic: ${topicId}. Closing.`); - peer.destroy(); - return; - } - - connections[topicId].peer = peer; - updateConnectionStatus(topicId, true); - + }); - function handlePeerData(data, topicId, peer) { - try { - const response = JSON.parse(data.toString()); - console.log(`[DEBUG] Received data from peer (topic: ${topicId}): ${JSON.stringify(response)}`); - - if (response.type === 'containers') { - if (window.activePeer === peer) { - renderContainers(response.data); - } - } else if (response.type === 'terminalOutput') { - appendTerminalOutput(response.data, response.containerId, response.encoding); - } else if (response.type === 'containerConfig') { - if (window.inspectContainerCallback) { - window.inspectContainerCallback(response.data); - window.inspectContainerCallback = null; // Reset the callback - } - } else if (response.type === 'stats') { - updateContainerStats(response.data); - } else if (response.error) { - console.error(`[ERROR] Server error: ${response.error}`); - } - } catch (err) { - console.error(`[ERROR] Failed to parse data from peer (topic: ${topicId}): ${err.message}`); - } - } - - peer.on('data', (data) => { - // Handle incoming data - handlePeerData(data, topicId, peer); - }); - - peer.on('close', () => { - console.log(`[INFO] Disconnected from peer for topic: ${topicHex}`); - updateConnectionStatus(topicId, false); - - if (window.activePeer === peer) { - window.activePeer = null; - connectionTitle.textContent = 'Disconnected'; - dashboard.classList.add('hidden'); - containerList.innerHTML = ''; - } - }); - - // If this is the first connection, switch to it - if (!window.activePeer) { - switchConnection(topicId); - } - }); - } - - function disconnectConnection(topicId, connectionItem) { - const connection = connections[topicId]; - if (!connection) { - console.error(`[ERROR] No connection found for topicId: ${topicId}`); - return; - } - - // Close and kill any open terminals associated with this connection - if (window.openTerminals[topicId]) { - console.log(`[INFO] Closing terminals for topic: ${topicId}`); - window.openTerminals[topicId].forEach((containerId) => { - try { - cleanUpTerminal(containerId); // Use the terminal.js cleanup logic - } catch (err) { - console.error(`[ERROR] Failed to kill terminal for container ${containerId}: ${err.message}`); - } - }); - delete window.openTerminals[topicId]; - } - - // Hide the terminal modal if it is active - const terminalModal = document.getElementById('terminal-modal'); - if (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) { - connection.peer.destroy(); - connection.peer = null; - } - if (connection.swarm) { - connection.swarm.destroy(); - connection.swarm = null; - } - - // Remove the connection from the global connections object - delete connections[topicId]; - - // Remove the connection item from the list - connectionList.removeChild(connectionItem); - - console.log(`[INFO] Disconnected and removed connection: ${topicId}`); - - // Reset active peer if it was the disconnected connection - if (window.activePeer === connection.peer) { - window.activePeer = null; - connectionTitle.textContent = 'Choose a Connection'; // Reset title - dashboard.classList.add('hidden'); - containerList.innerHTML = ''; // Clear the container list - } - - // Ensure the container list is cleared regardless of the active connection - resetContainerList(); - - // Refresh the connections view - resetConnectionsView(); - } - // Function to reset the container list - function resetContainerList() { - containerList.innerHTML = ''; // Clear the existing list - console.log('[INFO] Container list cleared.'); - } - - // Function to reset the connections view - function resetConnectionsView() { - // Clear the connection list - connectionList.innerHTML = ''; - - // Re-populate the connection list from the `connections` object - Object.keys(connections).forEach((topicId) => { - const connectionItem = document.createElement('li'); - connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between'; - connectionItem.dataset.topicId = topicId; - connectionItem.innerHTML = ` - - ${topicId} - - - `; - - // Add click event to switch connection - connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId)); - - // Add click event to the disconnect button - const disconnectBtn = connectionItem.querySelector('.disconnect-btn'); - disconnectBtn.addEventListener('click', (e) => { - e.stopPropagation(); // Prevent triggering the switch connection event - disconnectConnection(topicId, connectionItem); - }); - - connectionList.appendChild(connectionItem); - }); - - console.log('[INFO] Connections view reset.'); - } + console.log('[INFO] Connections view reset.'); +} // Update connection status function updateConnectionStatus(topicId, isConnected) { @@ -255,27 +255,27 @@ function updateConnectionStatus(topicId, isConnected) { // Switch between connections function switchConnection(topicId) { - const connection = connections[topicId]; - if (!connection) { - console.error(`[ERROR] No connection found for topicId: ${topicId}`); - connectionTitle.textContent = 'Choose a Connection'; // Default message - return; - } - - if (!connection.peer) { - console.error('[ERROR] No active peer for this connection.'); - connectionTitle.textContent = 'Choose a Connection'; // Default message - return; - } - - window.activePeer = connection.peer; - connectionTitle.textContent = `Connection: ${topicId}`; - dashboard.classList.remove('hidden'); - - console.log('[INFO] Sending "listContainers" command'); - sendCommand('listContainers'); + const connection = connections[topicId]; + if (!connection) { + console.error(`[ERROR] No connection found for topicId: ${topicId}`); + connectionTitle.textContent = 'Choose a Connection'; // Default message + return; } + if (!connection.peer) { + console.error('[ERROR] No active peer for this connection.'); + connectionTitle.textContent = 'Choose a Connection'; // Default message + return; + } + + window.activePeer = connection.peer; + connectionTitle.textContent = `Connection: ${topicId}`; + dashboard.classList.remove('hidden'); + + console.log('[INFO] Sending "listContainers" command'); + sendCommand('listContainers'); +} + // Attach switchConnection to the global window object window.switchConnection = switchConnection; @@ -295,16 +295,16 @@ window.sendCommand = sendCommand; // Render the container list function renderContainers(containers) { - console.log(`[INFO] Rendering ${containers.length} containers`); - containerList.innerHTML = ''; // Clear the current list - - containers.forEach((container) => { - const name = container.Names[0].replace(/^\//, ''); // Remove leading slash from container names - const image = container.Image; - const containerId = container.Id; - const row = document.createElement('tr'); - row.dataset.containerId = containerId; // Store container ID for reference - row.innerHTML = ` + console.log(`[INFO] Rendering ${containers.length} containers`); + containerList.innerHTML = ''; // Clear the current list + + containers.forEach((container) => { + const name = container.Names[0].replace(/^\//, ''); // Remove leading slash from container names + const image = container.Image; + const containerId = container.Id; + const row = document.createElement('tr'); + row.dataset.containerId = containerId; // Store container ID for reference + row.innerHTML = `