From 4f132818738af988e5de323bf35ad9df861aaf9d Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 29 Nov 2024 23:30:18 -0500 Subject: [PATCH] Add restart to actions --- app.js | 105 +++++++++++++++++++++++++++++++---------------- index.html | 68 ++++++++++++++++++++++++++++++ server/server.js | 16 +++++--- 3 files changed, 148 insertions(+), 41 deletions(-) diff --git a/app.js b/app.js index 47e26d7..eb1382f 100644 --- a/app.js +++ b/app.js @@ -105,27 +105,31 @@ function hideStatusIndicator() { // Show Alert function showAlert(type, message) { const alertContainer = document.getElementById('alert-container'); + + // Create alert element const alert = document.createElement('div'); - alert.className = `alert alert-${type} alert-dismissible fade show`; - alert.role = 'alert'; + alert.className = `alert ${type}`; alert.innerHTML = ` - ${message} - + ${message} + `; + + // Add close button functionality + const closeButton = alert.querySelector('.close-btn'); + closeButton.addEventListener('click', () => { + alert.remove(); // Remove alert on close + }); + + // Append alert to container alertContainer.appendChild(alert); - // Automatically hide the status indicator for errors or success - if (type === 'danger' || type === 'success') { - hideStatusIndicator(); - } - - // Automatically remove the alert after 5 seconds + // Automatically remove alert after 5 seconds setTimeout(() => { - alert.classList.remove('show'); - setTimeout(() => alert.remove(), 300); // Bootstrap's fade-out transition duration + alert.remove(); }, 5000); } + // Collapse Sidebar Functionality const collapseSidebarBtn = document.getElementById('collapse-sidebar-btn'); collapseSidebarBtn.addEventListener('click', () => { @@ -454,30 +458,33 @@ function renderContainers(containers) { const row = document.createElement('tr'); row.dataset.containerId = containerId; // Store container ID for reference row.innerHTML = ` - ${name} - ${image} - ${container.State} - 0 - 0 - ${ipAddress} - - - - - - - - `; + ${name} + ${image} + ${container.State} + 0 + 0 + ${ipAddress} + + + + + + + + +`; containerList.appendChild(row); // Add event listeners for action buttons @@ -494,6 +501,7 @@ function addActionListeners(row, container) { const stopBtn = row.querySelector('.action-stop'); const removeBtn = row.querySelector('.action-remove'); const terminalBtn = row.querySelector('.action-terminal'); + const restartBtn = row.querySelector('.action-restart'); // Start Button startBtn.addEventListener('click', async () => { @@ -541,6 +549,31 @@ function addActionListeners(row, container) { hideStatusIndicator(); } }); + + + // Restart Button +restartBtn.addEventListener('click', async () => { + showStatusIndicator(`Restarting container "${container.Names[0]}"...`); + sendCommand('restartContainer', { id: container.Id }); + + const expectedMessageFragment = `Container ${container.Id} restarted`; + + try { + const response = await waitForPeerResponse(expectedMessageFragment); + console.log('[DEBUG] Restart container response:', response); + + showAlert('success', response.message); + + // Refresh the container list to update states + sendCommand('listContainers'); + } catch (error) { + console.error('[ERROR] Failed to restart container:', error.message); + showAlert('danger', error.message || 'Failed to restart container.'); + } finally { + console.log('[DEBUG] Hiding status indicator in restartBtn finally block'); + hideStatusIndicator(); + } +}); // Remove Button diff --git a/index.html b/index.html index c7527ef..e4402a3 100644 --- a/index.html +++ b/index.html @@ -226,6 +226,74 @@ #dashboard.hidden { display: none !important; /* Hide the dashboard completely when not needed */ +} +#alert-container { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1055; /* Ensure it overlays other elements */ + display: flex; + flex-direction: column-reverse; /* Stack alerts upwards */ + gap: 10px; /* Add space between alerts */ +} + +.alert { + display: flex; + align-items: center; + justify-content: space-between; + max-width: 80%; /* Ensure the alert doesn't stretch too wide */ + padding: 12px 20px; /* Adjust padding for a more balanced look */ + background-color: #2b2b2b; /* Slightly lighter background for better contrast */ + color: #e0e0e0; /* Softer white text for readability */ + border-radius: 6px; /* Round corners for a modern look */ + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5); /* Add subtle shadow for depth */ + font-family: Arial, sans-serif; + font-size: 14px; /* Adjust font size for readability */ + animation: fadeIn 0.3s ease-out, fadeOut 4.5s ease-in forwards; + white-space: nowrap; /* Prevent text wrapping */ + overflow: hidden; /* Hide overflow for long text */ + text-overflow: ellipsis; /* Add ellipsis for overflowed text */ +} + +.alert.success { + border-left: 6px solid #28a745; /* Green border for success */ +} + +.alert.danger { + border-left: 6px solid #dc3545; /* Red border for danger */ +} + +.alert .close-btn { + background: none; + border: none; + color: #e0e0e0; /* Use the same text color for consistency */ + font-size: 16px; + cursor: pointer; + margin-left: 15px; /* Space between text and close button */ +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeOut { + 0% { + opacity: 1; + } + 90% { + opacity: 0.3; + } + 100% { + opacity: 0; + transform: translateY(10px); + } } diff --git a/server/server.js b/server/server.js index 851b3ee..802bdec 100644 --- a/server/server.js +++ b/server/server.js @@ -60,12 +60,12 @@ swarm.on('connection', (peer) => { console.log('[INFO] Handling \'listContainers\' command'); try { const containers = await docker.listContainers({ all: true }); - + const detailedContainers = await Promise.all( containers.map(async (container) => { try { const details = await docker.getContainer(container.Id).inspect(); - + // Safely access the IP address let ipAddress = 'No IP Assigned'; if (details.NetworkSettings && details.NetworkSettings.Networks) { @@ -74,7 +74,7 @@ swarm.on('connection', (peer) => { ipAddress = networks[0].IPAddress; } } - + return { ...container, ipAddress }; // Add IP address to container data } catch (error) { console.error(`[ERROR] Failed to inspect container ${container.Id}: ${error.message}`); @@ -82,14 +82,14 @@ swarm.on('connection', (peer) => { } }) ); - + response = { type: 'containers', data: detailedContainers }; } catch (error) { console.error(`[ERROR] Failed to list containers: ${error.message}`); response = { error: 'Failed to list containers' }; } break; - + case 'inspectContainer': console.log(`[INFO] Handling 'inspectContainer' command for container: ${parsedData.args.id}`); const container = docker.getContainer(parsedData.args.id); @@ -118,6 +118,12 @@ swarm.on('connection', (peer) => { response = { success: true, message: `Container ${parsedData.args.id} stopped` }; break; + case 'restartContainer': + console.log(`[INFO] Handling 'restartContainer' command for container: ${parsedData.args.id}`); + await docker.getContainer(parsedData.args.id).restart(); + response = { success: true, message: `Container ${parsedData.args.id} restarted` }; + break; + case 'removeContainer': console.log(`[INFO] Handling 'removeContainer' command for container: ${parsedData.args.id}`); await docker.getContainer(parsedData.args.id).remove({ force: true });