Newer
Older
EnvoyControlPlane / static / extension_configs.js
// extension_configs.js
import { API_BASE_URL, configStore, cleanupConfigStore } from './global.js';

// =========================================================================
// EXTENSION CONFIG UTILITIES
// =========================================================================

/**
 * Extracts the Type URL of the inner, configured resource from the TypedExtensionConfig.
 * @param {object} extensionConfig - The ExtensionConfig object from the API.
 * @returns {string} The inner type URL or a default message.
 */
function getExtensionConfigTypeUrl(extensionConfig) {
    try {
        const typeUrl = extensionConfig.typed_config.type_url;
        if (typeUrl) {
            // Display only the last part of the type URL (e.g., Lua or JwtAuthn)
            return typeUrl.substring(typeUrl.lastIndexOf('/') + 1);
        }
        return '<span style="color: gray;">(Unknown Type)</span>';
    } catch {
        return '<span style="color: gray;">(Config Error)</span>';
    }
}

// =========================================================================
// EXTENSION CONFIG CORE LOGIC
// =========================================================================

/**
 * Fetches and lists all enabled and disabled ExtensionConfigs.
 */
export async function listExtensionConfigs() {
    const tableBody = document.getElementById('extensionconfig-table-body');
    if (!tableBody) {
        console.error("Could not find element with ID 'extensionconfig-table-body'.");
        return;
    }

    tableBody.innerHTML =
        '<tr><td colspan="4" style="text-align: center; padding: 20px;">Loading...</td></tr>';

    try {
        const response = await fetch(`${API_BASE_URL}/list-extensionconfigs`);
        if (!response.ok) throw new Error(response.statusText);

        const extensionConfigResponse = await response.json();

        const allConfigs = [
            ...(extensionConfigResponse.enabled || []).map(c => ({ ...c, status: 'Enabled', configData: c })),
            ...(extensionConfigResponse.disabled || []).map(c => ({ ...c, status: 'Disabled', configData: c }))
        ];

        if (!allConfigs.length) {
            tableBody.innerHTML =
                '<tr><td colspan="4" style="text-align: center; color: var(--secondary-color);">No ExtensionConfigs found.</td></tr>';
            configStore.extension_configs = {};
            return;
        }
        cleanupConfigStore();

        // Store full configs in memory by name
        configStore.extension_configs = allConfigs.reduce((acc, c) => {
            const existingYaml = acc[c.name]?.yaml; 
            acc[c.name] = { ...c.configData, yaml: existingYaml };
            return acc;
        }, configStore.extension_configs);


        tableBody.innerHTML = '';
        allConfigs.forEach(config => {
            const row = tableBody.insertRow();
            if (config.status === 'Disabled') row.classList.add('disabled-row');

            let actionButtons = '';
            if (config.status === 'Enabled') {
                actionButtons = `<button class="action-button disable" onclick="window.disableExtensionConfig('${config.name}', event)">Disable</button>`;
            } else {
                // When disabled, show Enable and Remove buttons
                actionButtons = `
                    <button class="action-button enable" onclick="window.enableExtensionConfig('${config.name}', event)">Enable</button>
                    <button class="action-button remove" onclick="window.removeExtensionConfig('${config.name}', event)">Remove</button>
                `;
            }

            // Config Name Hyperlink (uses showExtensionConfigModal from global.js - must be implemented there)
            const nameCell = row.insertCell();
            nameCell.innerHTML =
                `<a href="#" onclick="event.preventDefault(); window.showExtensionConfigModal('${config.name}')"><span class="config-name">${config.name}</span></a>`;

            row.insertCell().textContent = config.status;
            row.insertCell().innerHTML = getExtensionConfigTypeUrl(config); // Shows the inner type
            row.insertCell().innerHTML = actionButtons;
        });
    } catch (error) {
        tableBody.innerHTML = `<tr><td colspan="4" class="error" style="text-align: center;">🚨 ExtensionConfig Error: ${error.message}</td></tr>`;
        console.error("ExtensionConfig Fetch/Parse Error:", error);
    }
}

// =========================================================================
// EXTENSION CONFIG ENABLE/DISABLE/REMOVE LOGIC
// =========================================================================

async function toggleExtensionConfigStatus(configName, action) {
    let url = (action === 'remove') ? `${API_BASE_URL}/remove-extensionconfig` : `${API_BASE_URL}/${action}-extensionconfig`;
    const payload = { name: configName };

    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(`ExtensionConfig '${configName}' successfully ${action}d.`);
        cleanupConfigStore();
        listExtensionConfigs();
    } catch (error) {
        console.error(`Failed to ${action} ExtensionConfig '${configName}':`, error);
        alert(`Failed to ${action} ExtensionConfig '${configName}'. Check console for details.`);
    }
}

// Attach functions to the global window object so they can be called from HTML buttons
export  function disableExtensionConfig(configName, event) {
    event.stopPropagation();
    if (confirm(`Are you sure you want to DISABLE ExtensionConfig: ${configName}?`)) {
        toggleExtensionConfigStatus(configName, 'disable');
    }
}

export  function enableExtensionConfig(configName, event) {
    event.stopPropagation();
    if (confirm(`Are you sure you want to ENABLE ExtensionConfig: ${configName}?`)) {
        toggleExtensionConfigStatus(configName, 'enable');
    }
}

export  function removeExtensionConfig(configName, event) {
    event.stopPropagation();
    if (confirm(`⚠️ WARNING: Are you absolutely sure you want to PERMANENTLY REMOVE ExtensionConfig: ${configName}? This action cannot be undone.`)) {
        toggleExtensionConfigStatus(configName, 'remove');
    }
}

// =========================================================================
// ADD EXTENSION CONFIG LOGIC
// =========================================================================

/**
 * Shows the modal for adding a new ExtensionConfig.
 */
export function showAddExtensionConfigModal() {
    document.getElementById('add-extension-config-yaml-input').value = '';
    // Clear checkbox on show
    const upsertCheckbox = document.getElementById('add-extension-config-upsert-flag');
    if (upsertCheckbox) {
        upsertCheckbox.checked = false;
    }
    document.getElementById('addExtensionConfigModal').style.display = 'block';
}

/**
 * Hides the modal for adding a new ExtensionConfig.
 */
export function hideAddExtensionConfigModal() {
    const modal = document.getElementById('addExtensionConfigModal');
    if (modal) {
        modal.style.display = 'none';
        document.getElementById('add-extension-config-yaml-input').value = '';
        // Clear checkbox on hide
        const upsertCheckbox = document.getElementById('add-extension-config-upsert-flag');
        if (upsertCheckbox) {
            upsertCheckbox.checked = false;
        }
    }
}


/**
 * Submits the new ExtensionConfig YAML to the /add-extensionconfig endpoint.
 */
export async function submitNewExtensionConfig() {
    const yamlInput = document.getElementById('add-extension-config-yaml-input');
    const upsertCheckbox = document.getElementById('add-extension-config-upsert-flag');
    const configYaml = yamlInput.value.trim();

    if (!configYaml) {
        alert('Please paste the ExtensionConfig YAML configuration.');
        return;
    }

    try {
        const payload = { yaml: configYaml };

        // Add upsert flag to payload if checkbox is checked
        if (upsertCheckbox && upsertCheckbox.checked) {
            payload.upsert = true;
        }

        const url = `${API_BASE_URL}/add-extensionconfig`;

        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 ExtensionConfig successfully added.`);
        alert('ExtensionConfig successfully added! The dashboard will now refresh.');

        yamlInput.value = '';
        // Uncheck the box upon success/closing
        if (upsertCheckbox) {
            upsertCheckbox.checked = false;
        }
        hideAddExtensionConfigModal();

        cleanupConfigStore();
        listExtensionConfigs();

    } catch (error) {
        console.error(`Failed to add new ExtensionConfig:`, error);
        alert(`Failed to add new ExtensionConfig. Check console for details. Error: ${error.message}`);
    }
}