handle multiple topics correctly
This commit is contained in:
parent
c078751561
commit
0e94f047c0
212
app.js
212
app.js
@ -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}`);
|
||||||
|
Loading…
Reference in New Issue
Block a user