// 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}`);
}
}