Newer
Older
EnvoyControlPlane / static / index.html
<!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>
        <div class="utility-links">
            <h2>Utility Tools</h2>
            <p>
                Need to manually issue a certificate? Use the
                <a href="/tools/cert_issuer.html"
                    class="link-button">Certificate Issuer Tool 🛡️</a>.
            </p>
        </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>

        <h2>Existing Secrets (Click a row for full JSON/YAML details)</h2>
        <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()">&times;</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()">&times;</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 &rarr; DB)
                    <br> <small>Override DB with Cache (Risk of DB data
                        loss)</small>
                </button>
                <button class="action-button disable"
                    onclick="resolveConsistency('rollback')">
                    Rollback (DB &rarr; 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()">&times;</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()">&times;</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:
  - &lt;listener 1&gt;
  - &lt;listener 2&gt;</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()">&times;</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:
  - &lt;cluster 1&gt;
  - &lt;cluster 2&gt;</pre>
            <p>Or:</p>
            <pre>&lt;cluster 1&gt;</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()">&times;</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:
  - &lt;secret 1&gt;
  - &lt;secret 2&gt;</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'">&times;</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>