// static/js/components/clustersTable.js
import { configStore, cleanupConfigStore } from '../store/configStore.js';
import { fetchClusters, toggleClusterStatus, fetchClusterYaml } from '../api/clustersService.js';
import { setupConfigModal } from './modals.js';
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>';
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>';
}
}
export async function renderClustersTable() {
const tableBody = document.getElementById('cluster-table-body');
if (!tableBody) return;
tableBody.innerHTML = '<tr><td colspan="5" style="text-align: center; padding: 20px;">Loading...</td></tr>';
try {
const clusterResponse = await fetchClusters();
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;
}
cleanupConfigStore();
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');
const clusterNameCell = row.insertCell();
const clusterLink = document.createElement('a');
clusterLink.href = '#';
clusterLink.innerHTML = `<span class="cluster-name">${cluster.name}</span>`;
clusterLink.addEventListener('click', (e) => {
e.preventDefault();
showClusterConfig(cluster.name);
});
clusterNameCell.appendChild(clusterLink);
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`;
const actionsCell = row.insertCell();
if (cluster.status === 'Enabled') {
const disableBtn = document.createElement('button');
disableBtn.className = 'action-button disable';
disableBtn.textContent = 'Disable';
disableBtn.onclick = (e) => {
e.stopPropagation();
if (confirm(`Are you sure you want to DISABLE cluster: ${cluster.name}?`)) {
handleStatusToggle(cluster.name, 'disable');
}
};
actionsCell.appendChild(disableBtn);
} else {
const enableBtn = document.createElement('button');
enableBtn.className = 'action-button enable';
enableBtn.textContent = 'Enable';
enableBtn.onclick = (e) => {
e.stopPropagation();
if (confirm(`Are you sure you want to ENABLE cluster: ${cluster.name}?`)) {
handleStatusToggle(cluster.name, 'enable');
}
};
actionsCell.appendChild(enableBtn);
const removeBtn = document.createElement('button');
removeBtn.className = 'action-button remove';
removeBtn.textContent = 'Remove';
removeBtn.onclick = (e) => {
e.stopPropagation();
if (confirm(`⚠️ WARNING: Are you absolutely sure you want to PERMANENTLY REMOVE cluster: ${cluster.name}? This action cannot be undone.`)) {
handleStatusToggle(cluster.name, 'remove');
}
};
actionsCell.appendChild(removeBtn);
}
});
} catch (error) {
tableBody.innerHTML = `<tr><td colspan="5" class="error" style="text-align: center;">🚨 Cluster Error: ${error.message}</td></tr>`;
}
}
async function handleStatusToggle(name, action) {
try {
await toggleClusterStatus(name, action);
cleanupConfigStore();
renderClustersTable();
} catch (error) {
alert(`Failed to ${action} cluster '${name}'. Error: ${error.message}`);
}
}
async function showClusterConfig(name) {
const config = configStore.clusters[name];
if (!config) return;
let yamlData = config.yaml || 'Loading YAML...';
if (yamlData === 'Loading YAML...') {
try {
yamlData = await fetchClusterYaml(name);
configStore.clusters[name].yaml = yamlData;
} catch (error) {
yamlData = `Error fetching YAML: ${error.message}`;
}
}
setupConfigModal(`Full Config for Cluster: ${name}`, config, yamlData);
}