// secrets.js import { API_BASE_URL, configStore, cleanupConfigStore } from './global.js'; // ========================================================================= // SECRET UTILITIES // ========================================================================= /** * Extracts a concise description of the secret type (e.g., TlsCertificate). * @param {object} secret - The secret configuration object. * @returns {string} A string describing the secret type. */ function getSecretTypeDetails(secret) { try { const secretType = secret.Type; if (!secretType) return '<span style="color: gray;">(Unknown Type)</span>'; // Find the key that is not 'name' or 'Type' itself const typeKeys = Object.keys(secretType); const typeName = typeKeys.find(key => key !== 'name'); if (typeName) { // Convert 'TlsCertificate' to 'TLS Certificate' return typeName.replace(/([A-Z])/g, ' $1').trim(); } return '<span style="color: gray;">(Generic)</span>'; } catch { return '<span style="color: gray;">(Config Error)</span>'; } } // ========================================================================= // SECRET CORE LOGIC (listSecrets) // ========================================================================= export async function listSecrets() { const tableBody = document.getElementById('secret-table-body'); if (!tableBody) { console.error("Could not find element with ID 'secret-table-body'."); return; } tableBody.innerHTML = '<tr><td colspan="4" style="text-align: center; padding: 20px;">Loading...</td></tr>'; try { // The API operations show /list-secrets returns enabled and disabled lists const response = await fetch(`${API_BASE_URL}/list-secrets`); if (!response.ok) throw new Error(response.statusText); const secretResponse = await response.json(); // Combine enabled and disabled secrets for display const allSecrets = [ ...(secretResponse.enabled || []).map(s => ({ ...s, status: 'Enabled', configData: s })), ...(secretResponse.disabled || []).map(s => ({ ...s, status: 'Disabled', configData: s })) ]; if (!allSecrets.length) { tableBody.innerHTML = '<tr><td colspan="4" style="text-align: center; color: var(--secondary-color);">No secrets found.</td></tr>'; configStore.secrets = {}; return; } cleanupConfigStore(); // Store full configs in memory by name configStore.secrets = allSecrets.reduce((acc, s) => { const existingYaml = acc[s.name]?.yaml; acc[s.name] = { ...s.configData, yaml: existingYaml }; return acc; }, configStore.secrets); tableBody.innerHTML = ''; allSecrets.forEach(secret => { const row = tableBody.insertRow(); if (secret.status === 'Disabled') row.classList.add('disabled-row'); let actionButtons = ''; // NOTE: Assuming the API supports enable/disable for secrets like clusters if (secret.status === 'Enabled') { actionButtons = `<button class="action-button disable" onclick="window.disableSecret('${secret.name}', event)">Disable</button>`; } else { actionButtons = ` <button class="action-button enable" onclick="window.enableSecret('${secret.name}', event)">Enable</button> <button class="action-button remove" onclick="window.removeSecret('${secret.name}', event)">Remove</button> `; } // Secret Name Hyperlink (uses showSecretConfigModal, which must be imported from global.js or defined globally) const secretNameCell = row.insertCell(); secretNameCell.innerHTML = `<a href="#" onclick="event.preventDefault(); window.showSecretConfigModal('${secret.name}')"><span class="secret-name">${secret.name}</span></a>`; row.insertCell().textContent = secret.status; row.insertCell().innerHTML = getSecretTypeDetails(secret); row.insertCell().innerHTML = actionButtons; }); } catch (error) { tableBody.innerHTML = `<tr><td colspan="4" class="error" style="text-align: center;">🚨 Secret Error: ${error.message}</td></tr>`; console.error("Secret Fetch/Parse Error:", error); } } // ========================================================================= // SECRET ENABLE/DISABLE/REMOVE LOGIC (toggleSecretStatus) // ========================================================================= async function toggleSecretStatus(secretName, action) { // API endpoints are assumed to be /remove-secret, /enable-secret, /disable-secret let url = (action === 'remove') ? `${API_BASE_URL}/remove-secret` : `${API_BASE_URL}/${action}-secret`; const payload = { name: secretName }; 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(`Secret '${secretName}' successfully ${action}d.`); cleanupConfigStore(); listSecrets(); } catch (error) { console.error(`Failed to ${action} secret '${secretName}':`, error); alert(`Failed to ${action} secret '${secretName}'. Check console for details.`); } } // Expose these functions globally for inline HTML onclick handlers export function disableSecret(secretName, event) { event.stopPropagation(); if (confirm(`Are you sure you want to DISABLE secret: ${secretName}?`)) { toggleSecretStatus(secretName, 'disable'); } } export function enableSecret(secretName, event) { event.stopPropagation(); if (confirm(`Are you sure you want to ENABLE secret: ${secretName}?`)) { toggleSecretStatus(secretName, 'enable'); } } export function removeSecret(secretName, event) { event.stopPropagation(); if (confirm(`⚠️ WARNING: Are you absolutely sure you want to PERMANENTLY REMOVE secret: ${secretName}? This action cannot be undone.`)) { toggleSecretStatus(secretName, 'remove'); } } // ========================================================================= // ADD SECRET LOGIC (showAddSecretModal, hideAddSecretModal, submitNewSecret) // ========================================================================= /** * Shows the modal for adding a new secret. */ export function showAddSecretModal() { document.getElementById('add-secret-yaml-input').value = ''; document.getElementById('addSecretModal').style.display = 'block'; } /** * Hides the modal for adding a new secret. */ export function hideAddSecretModal() { const modal = document.getElementById('addSecretModal'); if (modal) { modal.style.display = 'none'; document.getElementById('add-secret-yaml-input').value = ''; } } /** * Submits the new secret YAML to the /add-secret endpoint. */ export async function submitNewSecret() { const yamlInput = document.getElementById('add-secret-yaml-input'); const secretYaml = yamlInput.value.trim(); if (!secretYaml) { alert('Please paste the secret YAML configuration.'); return; } try { // The /add-secret endpoint expects a JSON body with a 'YAML' key containing the stringified YAML. const payload = { YAML: secretYaml }; const url = `${API_BASE_URL}/add-secret`; 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(`New secret successfully added.`); alert('Secret successfully added! The dashboard will now refresh.'); yamlInput.value = ''; hideAddSecretModal(); cleanupConfigStore(); listSecrets(); } catch (error) { console.error(`Failed to add new secret:`, error); alert(`Failed to add new secret. Check console for details. Error: ${error.message}`); } }