import {API_BASE_URL, configStore} from './global.js' // ========================================================================= // CLUSTER UTILITIES // ========================================================================= function getClusterEndpointDetails(cluster) { try { const endpoints = cluster.load_assignment?.endpoints; if (!endpoints?.length) return '<span style="color: gray;">(No Endpoints)</span>'; const lbEndpoints = endpoints[0].lb_endpoints; if (!lbEndpoints?.length) return '<span style="color: gray;">(No LB Endpoints)</span>'; // NOTE: The original logic for endpoint extraction is complex; simplify/validate path: // assuming standard Envoy configuration structure: const endpointObj = lbEndpoints[0].HostIdentifier?.Endpoint || lbEndpoints[0].endpoint; const address = endpointObj.address.Address.SocketAddress.address; const port = endpointObj.address.Address.SocketAddress.PortSpecifier.PortValue; const tls = cluster.transport_socket ? '<span class="tls-badge">TLS/SSL</span>' : ''; return `${address}:${port} ${tls}`; } catch { return '<span style="color: gray;">(Config Error)</span>'; } } // ========================================================================= // CLUSTER LISTING // ========================================================================= /** * Fetches and lists all clusters, populating the DOM table. */ export async function listClusters() { const tableBody = document.getElementById('cluster-table-body'); if (!tableBody) { console.error("Could not find element with ID 'cluster-table-body'."); return; } tableBody.innerHTML = '<tr><td colspan="5" style="text-align: center; padding: 20px;">Loading...</td></tr>'; try { const response = await fetch(`${API_BASE_URL}/list-clusters`); if (!response.ok) throw new Error(response.statusText); const clusterResponse = await response.json(); const allClusters = [ ...(clusterResponse.enabled || []).map(c => ({ ...c, status: 'Enabled', configData: c })), ...(clusterResponse.disabled || []).map(c => ({ ...c, status: 'Disabled', configData: c })) ]; if (!allClusters.length) { tableBody.innerHTML = '<tr><td colspan="5" style="text-align: center; color: var(--secondary-color);">No clusters found.</td></tr>'; configStore.clusters = {}; return; } // Store full configs in memory by name configStore.clusters = allClusters.reduce((acc, c) => { const existingYaml = acc[c.name]?.yaml; acc[c.name] = { ...c.configData, yaml: existingYaml }; return acc; }, configStore.clusters); tableBody.innerHTML = ''; allClusters.forEach(cluster => { const row = tableBody.insertRow(); if (cluster.status === 'Disabled') row.classList.add('disabled-row'); let actionButtons = ''; if (cluster.status === 'Enabled') { actionButtons = `<button class="action-button disable" onclick="window.disableCluster('${cluster.name}', event)">Disable</button>`; } else { actionButtons = ` <button class="action-button enable" onclick="window.enableCluster('${cluster.name}', event)">Enable</button> <button class="action-button remove" onclick="window.removeCluster('${cluster.name}', event)">Remove</button> `; } const clusterNameCell = row.insertCell(); clusterNameCell.innerHTML = `<a href="#" onclick="event.preventDefault(); window.showClusterConfigModal('${cluster.name}')"><span class="cluster-name">${cluster.name}</span></a>`; row.insertCell().textContent = cluster.status; row.insertCell().innerHTML = getClusterEndpointDetails(cluster); row.insertCell().textContent = `${cluster.connect_timeout?.seconds || 0}.${(cluster.connect_timeout?.nanos / 1e6 || 0).toFixed(0).padStart(3, '0')}s`; row.insertCell().innerHTML = actionButtons; }); } catch (error) { tableBody.innerHTML = `<tr><td colspan="5" class="error" style="text-align: center;">🚨 Cluster Error: ${error.message}</td></tr>`; console.error("Cluster Fetch/Parse Error:", error); } } // ========================================================================= // CLUSTER ENABLE/DISABLE/REMOVE LOGIC // ========================================================================= async function toggleClusterStatus(clusterName, action) { let url = ''; if (action === 'remove') { url = `${API_BASE_URL}/remove-cluster`; } else { url = `${API_BASE_URL}/${action}-cluster`; } const payload = { name: clusterName }; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { const errorBody = await response.text(); throw new Error(`HTTP Error ${response.status}: ${errorBody}`); } console.log(`Cluster '${clusterName}' successfully ${action}d.`); listClusters(); } catch (error) { console.error(`Failed to ${action} cluster '${clusterName}':`, error); alert(`Failed to ${action} cluster '${clusterName}'. Check console for details.`); } } // Exported functions must be attached to 'window' if called from inline HTML attributes export function disableCluster(clusterName, event) { event.stopPropagation(); if (confirm(`Are you sure you want to DISABLE cluster: ${clusterName}?`)) { toggleClusterStatus(clusterName, 'disable'); } } export function enableCluster(clusterName, event) { event.stopPropagation(); if (confirm(`Are you sure you want to ENABLE cluster: ${clusterName}?`)) { toggleClusterStatus(clusterName, 'enable'); } } export function removeCluster(clusterName, event) { event.stopPropagation(); if (confirm(`⚠️ WARNING: Are you absolutely sure you want to PERMANENTLY REMOVE cluster: ${clusterName}? This action cannot be undone.`)) { toggleClusterStatus(clusterName, 'remove'); } }