This commit is contained in:
Raven Scott
2025-11-24 22:55:02 -05:00
parent aa45d86efe
commit e6032533e9
2 changed files with 120 additions and 15 deletions
+41 -7
View File
@@ -527,6 +527,10 @@ function updateSystemInfo(systemInfo) {
// Update volumes store if volumes are present in systemInfo // Update volumes store if volumes are present in systemInfo
if (systemInfo.volumes && Array.isArray(systemInfo.volumes)) { if (systemInfo.volumes && Array.isArray(systemInfo.volumes)) {
volumesStore.set(systemInfo.volumes); volumesStore.set(systemInfo.volumes);
// If we're on the volumes view, refresh the display
if (currentView === 'volumes') {
renderVolumes(systemInfo.volumes);
}
} }
const dockerInfoEl = document.getElementById('docker-info-content'); const dockerInfoEl = document.getElementById('docker-info-content');
@@ -1047,11 +1051,24 @@ function setupDeployStackHandler() {
} }
} }
// Subscription for volumes store to auto-update UI
let volumesStoreSubscription = null;
function loadVolumes() { function loadVolumes() {
if (!window.activePeer) { if (!window.activePeer) {
return; return;
} }
// Set up subscription to auto-update UI when volumes change
if (!volumesStoreSubscription) {
volumesStoreSubscription = volumesStore.subscribe((volumes) => {
// Only auto-update if we're on the volumes view
if (currentView === 'volumes') {
renderVolumes(volumes);
}
});
}
// Check cache first - if fresh, use it; otherwise load from server // Check cache first - if fresh, use it; otherwise load from server
if (!volumesStore.isStale() && volumesStore.get().length > 0) { if (!volumesStore.isStale() && volumesStore.get().length > 0) {
renderVolumes(volumesStore.get()); renderVolumes(volumesStore.get());
@@ -1103,7 +1120,8 @@ function renderVolumes(volumes) {
const volumeName = btn.dataset.volumeName; const volumeName = btn.dataset.volumeName;
showConfirmModal('Are you sure you want to remove this volume? This cannot be undone.', () => { showConfirmModal('Are you sure you want to remove this volume? This cannot be undone.', () => {
sendCommand('removeVolume', { name: volumeName }); sendCommand('removeVolume', { name: volumeName });
setTimeout(() => loadVolumes(), 1000); // The server will broadcast the updated volumes list, which will trigger the subscription
// But we can also proactively refresh if needed
}); });
}); });
}); });
@@ -3805,11 +3823,21 @@ function handlePeerData(data, topicId, peer) {
case 'volumes': case 'volumes':
console.log('[INFO] Handling volumes list...'); console.log('[INFO] Handling volumes list...');
// Store in cache and render // Store in cache and render
let volumesToRender = null;
if (response.data && Array.isArray(response.data)) { if (response.data && Array.isArray(response.data)) {
renderVolumes(response.data); volumesToRender = response.data;
} else if (response.volumes && Array.isArray(response.volumes)) { } else if (response.volumes && Array.isArray(response.volumes)) {
// Fallback for old format // Fallback for old format
renderVolumes(response.volumes); volumesToRender = response.volumes;
}
if (volumesToRender !== null) {
// Update store (this will trigger subscriptions)
volumesStore.set(volumesToRender);
// Render if on volumes view
if (currentView === 'volumes') {
renderVolumes(volumesToRender);
}
} }
break; break;
@@ -3902,17 +3930,23 @@ function handlePeerData(data, topicId, peer) {
// Handle volumes responses - update cache and route to handlers if needed // Handle volumes responses - update cache and route to handlers if needed
// Check for volumes in response (both new format with type and old format) // Check for volumes in response (both new format with type and old format)
// Note: This is a fallback for responses that weren't handled in the switch statement above
// The 'volumes' case in the switch should have already handled type: 'volumes' responses
let volumesArray = null; let volumesArray = null;
if (response && response.type === 'volumes' && Array.isArray(response.data)) { if (response && response.type !== 'volumes' && response.success === true && Array.isArray(response.volumes)) {
volumesArray = response.data; // Old format volumes response that wasn't caught by switch
} else if (response && response.success === true && Array.isArray(response.volumes)) {
volumesArray = response.volumes; volumesArray = response.volumes;
} }
if (volumesArray !== null) { if (volumesArray !== null) {
// Always update the cache first // Always update the cache first (this will trigger subscriptions)
volumesStore.set(volumesArray); volumesStore.set(volumesArray);
// Render if on volumes view
if (currentView === 'volumes') {
renderVolumes(volumesArray);
}
// Route to active volume selectors if they exist (for deploy modal) // Route to active volume selectors if they exist (for deploy modal)
if (window.activeVolumeHandlers && window.activeVolumeHandlers.size > 0) { if (window.activeVolumeHandlers && window.activeVolumeHandlers.size > 0) {
for (const [volumeId, handlerInfo] of window.activeVolumeHandlers.entries()) { for (const [volumeId, handlerInfo] of window.activeVolumeHandlers.entries()) {
+72 -1
View File
@@ -1170,6 +1170,28 @@ swarm.on('connection', (peer) => {
const volume = await docker.createVolume(volumeConfig); const volume = await docker.createVolume(volumeConfig);
response = { success: true, message: `Volume "${args.name}" created successfully`, data: volume.name }; response = { success: true, message: `Volume "${args.name}" created successfully`, data: volume.name };
// Broadcast updated volumes list to all peers
try {
const volumesResult = await docker.listVolumes();
const volumesList = volumesResult.Volumes || [];
const update = {
type: 'volumes',
data: volumesList,
success: true,
volumes: volumesList
};
for (const connectedPeer of connectedPeers) {
try {
connectedPeer.write(JSON.stringify(update));
} catch (peerErr) {
console.error(`[ERROR] Failed to send volume update to peer: ${peerErr.message}`);
}
}
} catch (volErr) {
console.warn(`[WARN] Failed to broadcast volume update: ${volErr.message}`);
}
} catch (error) { } catch (error) {
console.error(`[ERROR] Failed to create volume: ${error.message}`); console.error(`[ERROR] Failed to create volume: ${error.message}`);
response = { error: `Failed to create volume: ${error.message}` }; response = { error: `Failed to create volume: ${error.message}` };
@@ -1182,6 +1204,28 @@ swarm.on('connection', (peer) => {
const volume = docker.getVolume(parsedData.args.name); const volume = docker.getVolume(parsedData.args.name);
await volume.remove(); await volume.remove();
response = { success: true, message: `Volume ${parsedData.args.name} removed` }; response = { success: true, message: `Volume ${parsedData.args.name} removed` };
// Broadcast updated volumes list to all peers
try {
const volumesResult = await docker.listVolumes();
const volumesList = volumesResult.Volumes || [];
const update = {
type: 'volumes',
data: volumesList,
success: true,
volumes: volumesList
};
for (const connectedPeer of connectedPeers) {
try {
connectedPeer.write(JSON.stringify(update));
} catch (peerErr) {
console.error(`[ERROR] Failed to send volume update to peer: ${peerErr.message}`);
}
}
} catch (volErr) {
console.warn(`[WARN] Failed to broadcast volume update: ${volErr.message}`);
}
} catch (error) { } catch (error) {
console.error(`[ERROR] Failed to remove volume: ${error.message}`); console.error(`[ERROR] Failed to remove volume: ${error.message}`);
response = { error: `Failed to remove volume: ${error.message}` }; response = { error: `Failed to remove volume: ${error.message}` };
@@ -1527,8 +1571,10 @@ async function initializeDockerEventStream() {
try { try {
const event = JSON.parse(chunk.toString()); const event = JSON.parse(chunk.toString());
if (event.status === "undefined") return; if (event.status === "undefined") return;
logger.info('Docker event received', { status: event.status, id: event.id }); logger.info('Docker event received', { status: event.status, id: event.id, type: event.Type });
// Handle container events
if (event.Type === 'container') {
// Get updated container list and broadcast it to all connected peers // Get updated container list and broadcast it to all connected peers
const containers = await docker.listContainers({ all: true }); const containers = await docker.listContainers({ all: true });
const update = { type: 'containers', data: containers }; const update = { type: 'containers', data: containers };
@@ -1540,6 +1586,31 @@ async function initializeDockerEventStream() {
logger.error('Failed to send update to peer', { error: peerErr.message }); logger.error('Failed to send update to peer', { error: peerErr.message });
} }
} }
}
// Handle volume events (create, destroy)
if (event.Type === 'volume' && (event.Action === 'create' || event.Action === 'destroy')) {
try {
const volumesResult = await docker.listVolumes();
const volumesList = volumesResult.Volumes || [];
const update = {
type: 'volumes',
data: volumesList,
success: true,
volumes: volumesList
};
for (const peer of connectedPeers) {
try {
peer.write(JSON.stringify(update));
} catch (peerErr) {
logger.error('Failed to send volume update to peer', { error: peerErr.message });
}
}
} catch (volErr) {
logger.error('Failed to fetch volumes after event', { error: volErr.message });
}
}
} catch (err) { } catch (err) {
logger.error('Failed to process Docker event', { error: err.message }); logger.error('Failed to process Docker event', { error: err.message });
} }