// data_fetchers.js
import { API_BASE_URL, configStore } from './global.js';
import { showConfigModal, switchTab } from './modals.js';
import { listListeners } from './listeners.js'; // Will be imported later
// =========================================================================
// YAML UTILITY FUNCTION
// =========================================================================
/**
* Extracts the YAML section for a specific domain from the filterChains array.
* NOTE: This is a complex utility that you may decide is no longer needed
* given the new approach in showDomainConfig (generating from JSON).
* However, since it was in the original code, we keep it here.
* @param {string} yamlData - The full YAML string containing the listener configuration.
* @param {string} domainName - The domain name to search for.
* @returns {string | null} The YAML string for the matching filterChain.
*/
export function extractFilterChainByDomain(yamlData, domainName) {
if (typeof require === 'undefined' && typeof jsyaml === 'undefined') {
console.error("Error: YAML parser (e.g., js-yaml) is required but not found.");
return null;
}
// ... (rest of the original extractFilterChainByDomain function logic) ...
let fullConfig;
try {
const yaml = (typeof require !== 'undefined') ? require('js-yaml') : jsyaml;
const allDocs = yaml.loadAll(yamlData);
// Find the main configuration object (usually the first object document)
fullConfig = allDocs.find(doc => doc && typeof doc === 'object');
} catch (e) {
console.error("Error parsing YAML data:", e);
return null;
}
if (!fullConfig || !Array.isArray(fullConfig.filterChains)) {
console.warn("Input YAML does not contain a 'filterChains' array, or the main document was not found.");
return null;
}
let matchingChain
if (domainName === "*" && fullConfig.filterChains.length > 0) {
matchingChain = fullConfig.filterChains.find(chain => {
return chain.filterChainMatch == null
});
} else {
matchingChain = fullConfig.filterChains.find(chain => {
const serverNames = chain.filterChainMatch?.serverNames;
return serverNames && serverNames.includes(domainName);
});
}
if (!matchingChain) {
console.log(`No filterChain found for domain: ${domainName}`);
return null;
}
try {
const yaml = (typeof require !== 'undefined') ? require('js-yaml') : jsyaml;
const outputYaml = yaml.dump(matchingChain, {
indent: 2,
lineWidth: -1,
flowLevel: -1
});
return outputYaml.trim();
} catch (e) {
console.error("Error dumping YAML data:", e);
return null;
}
}
// =========================================================================
// CONFIG-SPECIFIC MODAL LAUNCHERS
// =========================================================================
/**
* Handles showing the configuration for an individual FilterChain/Domain.
* This function loads the JSON from memory and generates the YAML from it.
* @param {HTMLElement} element - The DOM element that triggered the function.
*/
export async function showDomainConfig(element) {
const title = element.getAttribute('data-title');
const listenerName = element.getAttribute('data-listener-name');
const chainIndex = parseInt(element.getAttribute('data-chain-index'), 10);
if (!listenerName || isNaN(chainIndex)) {
console.error("Missing required data attributes or invalid chain index for domain config.");
return;
}
// 1. Get the full Listener JSON config from memory
const listenerConfig = configStore.listeners[listenerName];
if (!listenerConfig || !listenerConfig.filterChains || !listenerConfig.filterChains[chainIndex]) {
console.error(`Listener or FilterChain at index ${chainIndex} not found in memory for ${listenerName}.`);
showConfigModal(`🚨 Error: Domain Config Not Found`, { name: title, error: `FilterChain index ${chainIndex} not found for listener ${listenerName}.` }, 'Error: JSON configuration missing.');
return;
}
// The JSON data for the specific filter chain
const jsonData = listenerConfig.filterChains[chainIndex];
// 2. Fetch the full YAML for the listener if not already in memory
let fullListenerYaml = listenerConfig.yaml || 'Loading YAML...';
if (fullListenerYaml === 'Loading YAML...') {
try {
const response = await fetch(`${API_BASE_URL}/get-listener?name=${listenerName}&format=yaml`);
if (!response.ok) {
fullListenerYaml = `Error fetching YAML: ${response.status} ${response.statusText}`;
} else {
fullListenerYaml = await response.text();
configStore.listeners[listenerName].yaml = fullListenerYaml; // Store YAML
}
} catch (error) {
console.error("Failed to fetch YAML listener config:", error);
fullListenerYaml = `Network Error fetching YAML: ${error.message}`;
}
}
let yamlData;
// 3. Extract the specific filterChain YAML using the utility function.
// Use the domain name from the title as a proxy, or the first server_name from the JSON.
const domainName = jsonData.filter_chain_match?.server_names?.[0] || "*";
if (fullListenerYaml.startsWith('Error') || fullListenerYaml.startsWith('Network Error')) {
yamlData = fullListenerYaml; // Pass the error message
} else {
// Use the utility function to extract the specific chain's YAML
yamlData = extractFilterChainByDomain(fullListenerYaml, domainName);
if (yamlData === null) {
// As a fallback if the utility fails or doesn't find it, dump the JSON directly
try {
const yaml = (typeof require !== 'undefined') ? require('js-yaml') : jsyaml;
yamlData = yaml.dump(jsonData, { indent: 2, lineWidth: -1, flowLevel: -1 }).trim();
} catch (e) {
yamlData = `Could not extract or dump YAML for chain: ${domainName}. Error: ${e.message}`;
}
}
}
// 4. Show the modal
showConfigModal(title, jsonData, yamlData, 'json'); // Default to 'json' since it's guaranteed from memory
}
export async function showClusterConfigModal(clusterName) {
const config = configStore.clusters[clusterName];
if (!config) {
showConfigModal(`🚨 Error: Cluster Not Found`, { name: clusterName, error: 'Configuration data missing from memory.' }, 'Error: Cluster not in memory.');
return;
}
let yamlData = configStore.clusters[clusterName]?.yaml || 'Loading YAML...';
if (yamlData === 'Loading YAML...') {
try {
const response = await fetch(`${API_BASE_URL}/get-cluster?name=${clusterName}&format=yaml`);
if (!response.ok) {
yamlData = `Error fetching YAML: ${response.status} ${response.statusText}`;
} else {
yamlData = await response.text();
configStore.clusters[clusterName].yaml = yamlData; // Store YAML
}
} catch (error) {
console.error("Failed to fetch YAML cluster config:", error);
yamlData = `Network Error fetching YAML: ${error.message}`;
}
}
showConfigModal(`Full Config for Cluster: ${clusterName}`, config, yamlData);
}
export async function showListenerConfigModal(listenerName) {
const config = configStore.listeners[listenerName];
if (!config) {
showConfigModal(`🚨 Error: Listener Not Found`, { name: listenerName, error: 'Configuration data missing from memory.' }, 'Error: Listener not in memory.');
return;
}
let yamlData = configStore.listeners[listenerName]?.yaml || 'Loading YAML...';
if (yamlData === 'Loading YAML...') {
try {
const response = await fetch(`${API_BASE_URL}/get-listener?name=${listenerName}&format=yaml`);
if (!response.ok) {
yamlData = `Error fetching YAML: ${response.status} ${response.statusText}`;
} else {
yamlData = await response.text();
configStore.listeners[listenerName].yaml = yamlData; // Store YAML
}
} catch (error) {
console.error("Failed to fetch YAML listener config:", error);
yamlData = `Network Error fetching YAML: ${error.message}`;
}
}
showConfigModal(`Full Config for Listener: ${listenerName}`, config, yamlData);
}
// =========================================================================
// FILTER CHAIN ADDITION LOGIC (NEW)
// =========================================================================
/**
* Handles the submission of the new filter chain YAML.
*/
export async function submitNewFilterChain() {
const listenerName = document.getElementById('add-fc-listener-name').value;
const yamlData = document.getElementById('add-fc-yaml-input').value.trim();
if (!yamlData) {
alert("Please paste the filter chain YAML configuration.");
return;
}
if (!listenerName) {
alert("Listener name is missing. Cannot submit.");
return;
}
const payload = {
listener_name: listenerName,
yaml: yamlData
};
try {
const response = await fetch(`${API_BASE_URL}/append-filter-chain`, {
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}`);
}
alert(`Successfully appended new filter chain to '${listenerName}'.`);
// Close modal and refresh listener list
document.getElementById('addFilterChainModal').style.display = 'none';
listListeners();
} catch (error) {
console.error(`Failed to append filter chain to '${listenerName}':`, error);
alert(`Failed to append filter chain. Check console for details. Error: ${error.message}`);
}
}