<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Envoy Configuration Dashboard</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js"></script>
</head>
<body>
<div class="container">
<h1>Envoy Configuration Dashboard ⚙️</h1>
<div id="consistency-status-container">
<button id="consistency-button" class="status-indicator-button" title="System Consistency Status"
onclick="showConsistencyModal()" disabled>
Loading...
</button>
</div>
<div class="toolbar">
<div class="toolbar-left-group">
<button onclick="listClusters()">🔁 Reload Clusters</button>
<button onclick="listSecrets()">🔁 Reload Secrets</button>
<button onclick="listListeners()">🔁 Reload Listeners</button>
<button onclick="loadAllData()">Refresh All Data 🌐</button>
</div>
<div class="toolbar-right-group">
<button onclick="window.showAddClusterModal()">Add/Update
Cluster</button>
<button onclick="window.showAddSecretModal()">Add/Update
Secret</button>
<button onclick="window.showAddListenerModal()">Add/Update
Listener</button>
</div>
</div>
<h2>Existing Clusters (Click a row for full JSON/YAML details)</h2>
<table id="clusterTable" class="config-table">
<thead>
<tr>
<th>Cluster Name</th>
<th>Status</th>
<th>Primary Endpoint</th>
<th>Connect Timeout</th>
<th>Action</th>
</tr>
</thead>
<tbody id="cluster-table-body">
<tr>
<td colspan="4" style="text-align: center;">Loading cluster
data...</td>
</tr>
</tbody>
</table>
<div style="display: flex; align-items: center; justify-content: space-between;">
<h2>Existing Secrets (Click a row for full JSON/YAML details)</h2>
<a href="/tools/cert_issuer.html" class="cert-issuer-button"
title="Certificate Issuer Tool: Issue New Certificate 🛡️">
<span class="cert-issuer-button-text">Issue New Certificate</span>
<span class="cert-issuer-button-icon">➕</span>
</a>
</div>
<table id="secretTable" class="config-table">
<thead>
<tr>
<th>Secret Name</th>
<th>Status</th>
<th>Secret Type</th>
<th>Action</th>
</tr>
</thead>
<tbody id="secret-table-body">
<tr>
<td colspan="4" style="text-align: center;">Loading secret
data...</td>
</tr>
</tbody>
</table>
<h2>Existing Listeners (Click a domain/filter for details)</h2>
<table id="listenerTable" class="config-table">
<thead>
<tr>
<th>Listener Name</th>
<th>Status</th>
<th>Address:Port</th>
<th>Domains / Filters</th>
<th>Action</th>
</tr>
</thead>
<tbody id="listener-table-body">
<tr>
<td colspan="4" style="text-align: center;">Loading listener
data...</td>
</tr>
</tbody>
</table>
</div>
<div id="configModal" class="modal">
<div class="modal-content">
<span class="close" onclick="hideModal()">×</span>
<h2 id="modal-title"></h2>
<div class="tab-controls">
<button class="tab-button active" data-tab="json">JSON</button>
<button class="tab-button" data-tab="yaml">YAML</button>
<button id="download-yaml-btn" class="tab-button download-button" onclick="downloadYaml()">
⬇️ Download YAML
</button>
</div>
<div class="tab-content">
<pre id="modal-json-content" class="code-block"></pre>
<pre id="modal-yaml-content" class="code-block" style="display: none;"></pre>
</div>
</div>
</div>
<div id="consistencyModal" class="modal" onclick="hideConsistencyModal()">
<div class="modal-content" onclick="event.stopPropagation()">
<div class="modal-header">
<h3>⚠️ Consistency Conflict Detected</h3>
<span class="close-btn" onclick="hideConsistencyModal()">×</span>
</div>
<p>The in-memory cache and the persistent database are out of sync.
Please choose a resolution strategy.</p>
<h4>Inconsistency Details:</h4>
<div id="inconsistency-details-content">
<p><strong>Cache-Only Resources (DB Missing):</strong> <span id="cache-only-count">0</span></p>
<pre id="cache-only-data" class="conflict-list"></pre>
<p><strong>DB-Only Resources (Cache Missing):</strong> <span id="db-only-count">0</span></p>
<pre id="db-only-data" class="conflict-list"></pre>
</div>
<div class="modal-actions">
<button class="action-button enable" onclick="resolveConsistency('flush')">
Flush (Cache → DB)
<br> <small>Override DB with Cache (Risk of DB data
loss)</small>
</button>
<button class="action-button disable" onclick="resolveConsistency('rollback')">
Rollback (DB → Cache)
<br> <small>Override Cache with DB (Risk of in-memory data
loss)</small>
</button>
</div>
</div>
</div>
<div id="addFilterChainModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="add-fc-modal-title">Add New Filter Chain</h3>
<span class="close-btn" onclick="hideAddFilterChainModal()">×</span>
</div>
<p>Paste the <b>YAML configuration</b> for the new filter chain
below.
</p>
<p>
Need help composing a valid configuration? Use the external:
<a href="tools/filter_chain.html" target="_blank" class="text-blue-600 hover:text-blue-800 font-medium">
Envoy Filter Chain Composer ↗
</a>
</p>
<form id="add-filter-chain-form">
<input type="hidden" id="add-fc-listener-name" value="">
<label for="add-fc-yaml-input">Filter Chain YAML:</label>
<textarea id="add-fc-yaml-input" rows="15" placeholder="Paste YAML here..."></textarea>
<div class="modal-actions">
<button type="button" class="action-button add" onclick="submitNewFilterChain()">
Submit Filter Chain
</button>
<button type="button" class="action-button disable" onclick="hideAddFilterChainModal()">
Cancel
</button>
</div>
</form>
</div>
</div>
<div id="addListenerModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Add/Update Listener</h3>
<span class="close-btn" onclick="hideAddListenerModal()">×</span>
</div>
<p>Paste the full <b>YAML configuration</b> for the new listener(s)
below and submit. You can provide a single listener object, or a
list of listeners under the <code>resources</code> key to load
multiple at once:</p>
<pre>resources:
- <listener 1>
- <listener 2></pre>
<p>Make sure every listener includes the required type identifier:
<code>"@type": type.googleapis.com/envoy.config.listener.v3.Listener</code>,
which allows the parser to recognize your configuration.
</p>
<form id="add-listener-form">
<label for="add-listener-yaml-input">Listener YAML:</label>
<textarea id="add-listener-yaml-input" rows="20"
placeholder="Paste full listener YAML here..."></textarea>
<div class="modal-actions">
<button type="button" class="action-button add" onclick="submitNewListener()">
Submit Listener
</button>
<button type="button" class="action-button disable" onclick="hideAddListenerModal()">
Cancel
</button>
</div>
</form>
</div>
</div>
<div id="addClusterModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Add/Update Cluster</h3>
<span class="close-btn" onclick="hideAddClusterModal()">×</span>
</div>
<p>Paste the full <b>YAML configuration</b> for the new cluster(s)
below and submit. You can provide a single cluster object, or a
list of clusters under the <code>resources</code> key to load
multiple at once:</p>
<pre>resources:
- <cluster 1>
- <cluster 2></pre>
<p>Or:</p>
<pre><cluster 1></pre>
<p>Make sure every cluster includes the required type identifier:
<code> "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster</code>,
which allows the parser to recognize your configuration.
</p>
<form id="add-cluster-form">
<label for="add-cluster-yaml-input">Cluster YAML:</label>
<textarea id="add-cluster-yaml-input" rows="20"
placeholder="Paste full cluster YAML here..."></textarea>
<div class="modal-actions">
<button type="button" class="action-button add" onclick="submitNewCluster()">
Submit Cluster
</button>
<button type="button" class="action-button disable" onclick="hideAddClusterModal()">
Cancel
</button>
</div>
</form>
</div>
</div>
<div id="addSecretModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Add/Update Secret</h3>
<span class="close-btn" onclick="hideAddSecretModal()">×</span>
</div>
<p>Paste the full <b>YAML configuration</b> for the new secret(s)
below and submit. You can provide a single secret object, or a
list of secrets under the <code>resources</code> key to load
multiple at once:</p>
<pre>resources:
- <secret 1>
- <secret 2></pre>
<p>Make sure every secret includes the required type identifier:
<code>"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret</code>,
which allows the parser to recognize your configuration.
</p>
<form id="add-secret-form">
<label for="add-secret-yaml-input">Secret YAML:</label>
<textarea id="add-secret-yaml-input" rows="20" placeholder="Paste full secret YAML here..."></textarea>
<div class="modal-actions">
<button type="button" class="action-button add" onclick="submitNewSecret()">
Submit Secret
</button>
<button type="button" class="action-button disable" onclick="hideAddSecretModal()">
Cancel
</button>
</div>
</form>
</div>
</div>
<div id="certificateDetailsModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="cert-modal-title">TLS Certificate Details</h3>
<span class="close-btn"
onclick="document.getElementById('certificateDetailsModal').style.display='none'">×</span>
</div>
<div id="certificate-details-content">
<p style="text-align: center;">Loading certificate data...</p>
</div>
<div class="modal-actions">
<button type="button" class="action-button disable"
onclick="document.getElementById('certificateDetailsModal').style.display='none'">
Close
</button>
</div>
</div>
</div>
<script type="module" src="global.js"></script>
<script type="module" src="modals.js"></script>
<script type="module" src="data_fetchers.js"></script>
<script type="module" src="clusters.js"></script>
<script type="module" src="secrets.js"></script>
<script type="module" src="listeners.js"></script>
<script type="module" src="consistency.js"></script>
<script type="module" src="data_loader.js"></script>
</body>
</html>