diff --git a/data/config.db b/data/config.db index 8d9b397..5e7a8d5 100644 --- a/data/config.db +++ b/data/config.db Binary files differ diff --git a/data/lds.yaml b/data/lds.yaml index afa64e3..81b5897 100644 --- a/data/lds.yaml +++ b/data/lds.yaml @@ -1,5 +1,37 @@ resources: - "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: http_listener + address: + socket_address: { address: 0.0.0.0, port_value: 10000 } + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: AUTO + route_config: + name: ingress_generic_insecure + virtual_hosts: + - name: http_to_https + domains: ["*"] + routes: + - match: { prefix : "/.well-known/acme-challenge"} + route: { cluster: _acme_renewer } + - match: { prefix: "/" } + redirect: { https_redirect: true } + - name: video_insecure + domains: ["video.jerxie.com" , "video.local:10000"] + routes: + - match: { prefix : "/.well-known/acme-challenge"} + route: { cluster: _acme_renewer } + - match: { prefix : "/"} + route: { cluster: _nas_video } + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router +- "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: https_listener address: socket_address: { address: 0.0.0.0, port_value: 10001 } diff --git a/internal/snapshot/resource_io.go b/internal/snapshot/resource_io.go index 6f4154a..36316f7 100644 --- a/internal/snapshot/resource_io.go +++ b/internal/snapshot/resource_io.go @@ -8,10 +8,13 @@ clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/envoyproxy/go-control-plane/pkg/cache/types" "github.com/envoyproxy/go-control-plane/pkg/cache/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "google.golang.org/protobuf/encoding/protojson" + yaml "gopkg.in/yaml.v3" internallog "envoy-control-plane/internal/log" diff --git a/static/data_fetchers.js b/static/data_fetchers.js index 1f66740..18accfe 100644 --- a/static/data_fetchers.js +++ b/static/data_fetchers.js @@ -9,8 +9,8 @@ /** * 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). + * 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. @@ -22,11 +22,12 @@ 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); @@ -37,12 +38,19 @@ 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 + }); - const matchingChain = fullConfig.filterChains.find(chain => { - const serverNames = chain.filter_chain_match?.server_names; - return serverNames && serverNames.includes(domainName); - }); - + } 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; @@ -51,9 +59,9 @@ try { const yaml = (typeof require !== 'undefined') ? require('js-yaml') : jsyaml; const outputYaml = yaml.dump(matchingChain, { - indent: 2, - lineWidth: -1, - flowLevel: -1 + indent: 2, + lineWidth: -1, + flowLevel: -1 }); return outputYaml.trim(); @@ -65,7 +73,7 @@ // ========================================================================= -// CONFIG-SPECIFIC MODAL LAUNCHERS +// CONFIG-SPECIFIC MODAL LAUNCHERS // ========================================================================= /** @@ -76,51 +84,64 @@ export async function showDomainConfig(element) { const title = element.getAttribute('data-title'); const listenerName = element.getAttribute('data-listener-name'); - const chainIndex = element.getAttribute('data-chain-index'); + const chainIndex = parseInt(element.getAttribute('data-chain-index'), 10); - if (!listenerName || chainIndex === null) { - console.error("Missing required data attributes for domain config."); - return; - } - - const listener = configStore.listeners[listenerName]; - const jsonData = listener?.filterChains?.[parseInt(chainIndex)]; - - if (!jsonData) { - const errorMsg = 'Filter Chain configuration not found in memory.'; - console.error(errorMsg); - showConfigModal(`🚨 Error: ${title}`, { error: errorMsg }, errorMsg); + if (!listenerName || isNaN(chainIndex)) { + console.error("Missing required data attributes or invalid chain index for domain config."); return; } - let yamlData = 'Generating YAML from in-memory JSON...'; - let defaultTab = 'yaml'; + // 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; + } - try { - if (typeof require === 'undefined' && typeof jsyaml === 'undefined') { - throw new Error("YAML parser (e.g., js-yaml) is required but not found."); + // 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}`; } - - const yaml = (typeof require !== 'undefined') ? require('js-yaml') : jsyaml; - yamlData = yaml.dump(jsonData, { indent: 2, lineWidth: -1, flowLevel: -1 }); - - } catch (error) { - console.error("Failed to generate YAML from JSON. Falling back to approximation.", error); - - const yamlApproximation = JSON.stringify(jsonData, null, 2) - .replace(/[{}]/g, '') - .replace(/"(\w+)":\s*/g, '$1: ') - .replace(/,\n\s*/g, '\n') - .replace(/\[\n\s*(\s*)/g, '\n$1 - ') - .replace(/,\n\s*(\s*)/g, '\n$1- ') - .replace(/:\s*"/g, ': ') - .replace(/"/g, ''); - - yamlData = yamlApproximation + `\n\n--- WARNING: YAML is an approximation because the js-yaml library is missing or failed to parse. ---\n\n`; - defaultTab = 'json'; // Switch to JSON tab if YAML generation failed } - showConfigModal(title, jsonData, yamlData, defaultTab); + 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 } @@ -132,7 +153,7 @@ } 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`); @@ -159,7 +180,6 @@ } 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`); @@ -193,7 +213,7 @@ alert("Please paste the filter chain YAML configuration."); return; } - + if (!listenerName) { alert("Listener name is missing. Cannot submit."); return; @@ -217,10 +237,10 @@ } alert(`Successfully appended new filter chain to '${listenerName}'.`); - + // Close modal and refresh listener list document.getElementById('addFilterChainModal').style.display = 'none'; - listListeners(); + 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}`); diff --git a/static/filter_chain.html b/static/filter_chain.html deleted file mode 100644 index 46b7c9e..0000000 --- a/static/filter_chain.html +++ /dev/null @@ -1,341 +0,0 @@ - - -
- - -Configure your Envoy listener filter chain using simple inputs or switch to advanced editing mode.
- -Loading cluster data... | |||
Loading cluster + data... | +
Configure your Envoy filter chain for a Listener using simple inputs or switch to advanced editing mode.
+ +The 'DOMAIN_PLACEHOLDER' will be replaced by your **Primary Domain**.
+ +