Add in Font Awesome Icons for Actions
This commit is contained in:
parent
4753207c02
commit
76825acb15
570
app.js
570
app.js
@ -46,15 +46,15 @@ addConnectionForm.addEventListener('submit', (e) => {
|
|||||||
|
|
||||||
// Function to add a new connection
|
// Function to add a new connection
|
||||||
function addConnection(topicHex) {
|
function addConnection(topicHex) {
|
||||||
const topic = b4a.from(topicHex, 'hex');
|
const topic = b4a.from(topicHex, 'hex');
|
||||||
const topicId = topicHex.substring(0, 12);
|
const topicId = topicHex.substring(0, 12);
|
||||||
|
|
||||||
console.log(`[INFO] Adding connection with topic: ${topicHex}`);
|
console.log(`[INFO] Adding connection with topic: ${topicHex}`);
|
||||||
|
|
||||||
const connectionItem = document.createElement('li');
|
const connectionItem = document.createElement('li');
|
||||||
connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between';
|
connectionItem.className = 'list-group-item d-flex align-items-center justify-content-between';
|
||||||
connectionItem.dataset.topicId = topicId;
|
connectionItem.dataset.topicId = topicId;
|
||||||
connectionItem.innerHTML = `
|
connectionItem.innerHTML = `
|
||||||
<span>
|
<span>
|
||||||
<span class="connection-status status-disconnected"></span>${topicId}
|
<span class="connection-status status-disconnected"></span>${topicId}
|
||||||
</span>
|
</span>
|
||||||
@ -63,6 +63,172 @@ function addConnection(topicHex) {
|
|||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// 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 = `
|
||||||
|
<span>
|
||||||
|
<span class="connection-status ${connections[topicId].peer ? 'status-connected' : 'status-disconnected'}"></span>${topicId}
|
||||||
|
</span>
|
||||||
|
<button class="btn btn-sm btn-danger disconnect-btn">Disconnect</button>
|
||||||
|
`;
|
||||||
|
|
||||||
// Add click event to switch connection
|
// Add click event to switch connection
|
||||||
connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId));
|
connectionItem.querySelector('span').addEventListener('click', () => switchConnection(topicId));
|
||||||
|
|
||||||
@ -74,176 +240,10 @@ function addConnection(topicHex) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
connectionList.appendChild(connectionItem);
|
connectionList.appendChild(connectionItem);
|
||||||
|
});
|
||||||
|
|
||||||
connections[topicId] = { topic, peer: null, swarm: null };
|
console.log('[INFO] Connections view reset.');
|
||||||
|
}
|
||||||
// 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 = `
|
|
||||||
<span>
|
|
||||||
<span class="connection-status ${connections[topicId].peer ? 'status-connected' : 'status-disconnected'}"></span>${topicId}
|
|
||||||
</span>
|
|
||||||
<button class="btn btn-sm btn-danger disconnect-btn">Disconnect</button>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 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.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update connection status
|
// Update connection status
|
||||||
function updateConnectionStatus(topicId, isConnected) {
|
function updateConnectionStatus(topicId, isConnected) {
|
||||||
@ -255,27 +255,27 @@ function updateConnectionStatus(topicId, isConnected) {
|
|||||||
|
|
||||||
// Switch between connections
|
// Switch between connections
|
||||||
function switchConnection(topicId) {
|
function switchConnection(topicId) {
|
||||||
const connection = connections[topicId];
|
const connection = connections[topicId];
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
console.error(`[ERROR] No connection found for topicId: ${topicId}`);
|
console.error(`[ERROR] No connection found for topicId: ${topicId}`);
|
||||||
connectionTitle.textContent = 'Choose a Connection'; // Default message
|
connectionTitle.textContent = 'Choose a Connection'; // Default message
|
||||||
return;
|
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Attach switchConnection to the global window object
|
||||||
window.switchConnection = switchConnection;
|
window.switchConnection = switchConnection;
|
||||||
|
|
||||||
@ -295,16 +295,16 @@ window.sendCommand = sendCommand;
|
|||||||
|
|
||||||
// Render the container list
|
// Render the container list
|
||||||
function renderContainers(containers) {
|
function renderContainers(containers) {
|
||||||
console.log(`[INFO] Rendering ${containers.length} containers`);
|
console.log(`[INFO] Rendering ${containers.length} containers`);
|
||||||
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 from container names
|
const name = container.Names[0].replace(/^\//, ''); // Remove leading slash from container names
|
||||||
const image = container.Image;
|
const image = container.Image;
|
||||||
const containerId = container.Id;
|
const containerId = container.Id;
|
||||||
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}</td>
|
||||||
@ -312,23 +312,33 @@ function renderContainers(containers) {
|
|||||||
<td class="memory">0</td>
|
<td class="memory">0</td>
|
||||||
<td class="ip-address">-</td>
|
<td class="ip-address">-</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-success btn-sm action-start" ${container.State === 'running' ? 'disabled' : ''}>Start</button>
|
<button class="btn btn-success btn-sm action-start" ${container.State === 'running' ? 'disabled' : ''}>
|
||||||
<button class="btn btn-warning btn-sm action-stop" ${container.State !== 'running' ? 'disabled' : ''}>Stop</button>
|
<i class="fas fa-play"></i>
|
||||||
<button class="btn btn-danger btn-sm action-remove">Remove</button>
|
</button>
|
||||||
<button class="btn btn-primary btn-sm action-terminal" ${container.State !== 'running' ? 'disabled' : ''}>Terminal</button>
|
<button class="btn btn-warning btn-sm action-stop" ${container.State !== 'running' ? 'disabled' : ''}>
|
||||||
<button class="btn btn-secondary btn-sm action-duplicate">Duplicate</button>
|
<i class="fas fa-stop"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger btn-sm action-remove">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary btn-sm action-terminal" ${container.State !== 'running' ? 'disabled' : ''}>
|
||||||
|
<i class="fas fa-terminal"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary btn-sm action-duplicate">
|
||||||
|
<i class="fas fa-clone"></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
`;
|
`;
|
||||||
containerList.appendChild(row);
|
containerList.appendChild(row);
|
||||||
|
|
||||||
// Add event listeners for action buttons
|
// Add event listeners for action buttons
|
||||||
addActionListeners(row, container);
|
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 to action buttons
|
// Add event listeners to action buttons
|
||||||
@ -357,15 +367,15 @@ 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}`);
|
||||||
let row = containerList.querySelector(`tr[data-container-id="${stats.id}"]`);
|
let row = containerList.querySelector(`tr[data-container-id="${stats.id}"]`);
|
||||||
|
|
||||||
if (!row) {
|
if (!row) {
|
||||||
console.warn(`[WARN] No row found for container ID: ${stats.id}. Adding a placeholder.`);
|
console.warn(`[WARN] No row found for container ID: ${stats.id}. Adding a placeholder.`);
|
||||||
// Create a placeholder row if it doesn't exist
|
// Create a placeholder row if it doesn't exist
|
||||||
row = document.createElement('tr');
|
row = document.createElement('tr');
|
||||||
row.dataset.containerId = stats.id;
|
row.dataset.containerId = stats.id;
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>Unknown</td>
|
<td>Unknown</td>
|
||||||
<td>-</td>
|
<td>-</td>
|
||||||
<td>-</td>
|
<td>-</td>
|
||||||
@ -374,80 +384,80 @@ function updateContainerStats(stats) {
|
|||||||
<td class="ip-address">-</td>
|
<td class="ip-address">-</td>
|
||||||
<td>-</td>
|
<td>-</td>
|
||||||
`;
|
`;
|
||||||
containerList.appendChild(row);
|
containerList.appendChild(row);
|
||||||
}
|
|
||||||
|
|
||||||
row.querySelector('.cpu').textContent = stats.cpu.toFixed(2);
|
|
||||||
row.querySelector('.memory').textContent = (stats.memory / (1024 * 1024)).toFixed(2);
|
|
||||||
row.querySelector('.ip-address').textContent = stats.ip || '-';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row.querySelector('.cpu').textContent = stats.cpu.toFixed(2);
|
||||||
|
row.querySelector('.memory').textContent = (stats.memory / (1024 * 1024)).toFixed(2);
|
||||||
|
row.querySelector('.ip-address').textContent = stats.ip || '-';
|
||||||
|
}
|
||||||
|
|
||||||
// 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}`);
|
||||||
|
|
||||||
// Send a command to get container configurations
|
// Send a command to get container configurations
|
||||||
sendCommand('inspectContainer', { id: container.Id });
|
sendCommand('inspectContainer', { id: container.Id });
|
||||||
|
|
||||||
// Listen for the response
|
// Listen for the response
|
||||||
window.inspectContainerCallback = (config) => {
|
window.inspectContainerCallback = (config) => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
alert('Failed to retrieve container configuration.');
|
alert('Failed to retrieve container configuration.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("TESTER: " + config.HostConfig?.CpusetCpus)
|
console.log("TESTER: " + config.HostConfig?.CpusetCpus)
|
||||||
let CPUs = config.HostConfig?.CpusetCpus.split(",");
|
let CPUs = config.HostConfig?.CpusetCpus.split(",");
|
||||||
|
|
||||||
// Populate the modal fields with the current configurations
|
// Populate the modal fields with the current configurations
|
||||||
document.getElementById('container-name').value = config.Name.replace(/^\//, '');
|
document.getElementById('container-name').value = config.Name.replace(/^\//, '');
|
||||||
document.getElementById('container-hostname').value = config.Config.Hostname.replace(/^\//, '');
|
document.getElementById('container-hostname').value = config.Config.Hostname.replace(/^\//, '');
|
||||||
document.getElementById('container-image').value = config.Config.Image;
|
document.getElementById('container-image').value = config.Config.Image;
|
||||||
document.getElementById('container-netmode').value = config.HostConfig?.NetworkMode;
|
document.getElementById('container-netmode').value = config.HostConfig?.NetworkMode;
|
||||||
document.getElementById('container-cpu').value = CPUs.length;
|
document.getElementById('container-cpu').value = CPUs.length;
|
||||||
document.getElementById('container-memory').value = Math.round(config.HostConfig?.Memory / (1024 * 1024));
|
document.getElementById('container-memory').value = Math.round(config.HostConfig?.Memory / (1024 * 1024));
|
||||||
document.getElementById('container-config').value = JSON.stringify(config, null, 2);
|
document.getElementById('container-config').value = JSON.stringify(config, null, 2);
|
||||||
|
|
||||||
// Show the modal
|
// Show the modal
|
||||||
duplicateModal.show();
|
duplicateModal.show();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Handle the Duplicate Container Form Submission
|
// Handle the Duplicate Container Form Submission
|
||||||
duplicateContainerForm.addEventListener('submit', (e) => {
|
duplicateContainerForm.addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const name = document.getElementById('container-name').value.trim();
|
const name = document.getElementById('container-name').value.trim();
|
||||||
const hostname = document.getElementById('container-hostname').value.trim();
|
const hostname = document.getElementById('container-hostname').value.trim();
|
||||||
const image = document.getElementById('container-image').value.trim();
|
const image = document.getElementById('container-image').value.trim();
|
||||||
const netmode = document.getElementById('container-netmode').value.trim();
|
const netmode = document.getElementById('container-netmode').value.trim();
|
||||||
const cpu = document.getElementById('container-cpu').value.trim();
|
const cpu = document.getElementById('container-cpu').value.trim();
|
||||||
const memory = document.getElementById('container-memory').value.trim();
|
const memory = document.getElementById('container-memory').value.trim();
|
||||||
const configJSON = document.getElementById('container-config').value.trim();
|
const configJSON = document.getElementById('container-config').value.trim();
|
||||||
|
|
||||||
let config;
|
let config;
|
||||||
try {
|
try {
|
||||||
config = JSON.parse(configJSON);
|
config = JSON.parse(configJSON);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('Invalid JSON in configuration.');
|
alert('Invalid JSON in configuration.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[INFO] Sending duplicateContainer command for name: ${name}`);
|
console.log(`[INFO] Sending duplicateContainer command for name: ${name}`);
|
||||||
|
|
||||||
// Send the duplicate command to the server
|
// Send the duplicate command to the server
|
||||||
sendCommand('duplicateContainer', { name, image, hostname, netmode, cpu, memory, config });
|
sendCommand('duplicateContainer', { name, image, hostname, netmode, cpu, memory, config });
|
||||||
|
|
||||||
// Close the modal
|
// Close the modal
|
||||||
duplicateModal.hide();
|
duplicateModal.hide();
|
||||||
|
|
||||||
// Trigger container list update after a short delay
|
// Trigger container list update after a short delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('[INFO] Fetching updated container list after duplication');
|
console.log('[INFO] Fetching updated container list after duplication');
|
||||||
sendCommand('listContainers');
|
sendCommand('listContainers');
|
||||||
}, 2000); // Wait for duplication to complete
|
}, 2000); // Wait for duplication to complete
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Attach startTerminal to the global window object
|
// Attach startTerminal to the global window object
|
||||||
|
Loading…
Reference in New Issue
Block a user