Add /config page showing current server configuration
Adds a GET /config route that renders a simple table with port,
executor, sentry status, GitHub mirror token presence, and secret
names (values are never exposed).
change
commit 38f223b34e674879bec0d5fccb88e6c9c399e17b
author Claude <noreply@anthropic.com>
date
parent 3cca3ec7
diff --git a/quire-server/src/quire/web/handlers.rs b/quire-server/src/quire/web/handlers.rs
index 8ad708c..58fc415 100644
--- a/quire-server/src/quire/web/handlers.rs
+++ b/quire-server/src/quire/web/handlers.rs
@@ -280,6 +280,22 @@ pub async fn run_detail(
     render(&tmpl)
 }
 
+pub async fn config(State(quire): State<Quire>) -> Response {
+    let cfg = &quire.config;
+    let mut secret_names: Vec<String> = cfg.secrets.keys().cloned().collect();
+    secret_names.sort();
+
+    let tmpl = ConfigTemplate {
+        crumbs: vec![Crumb::new("config")],
+        port: cfg.port,
+        sentry_enabled: cfg.sentry.is_some(),
+        secret_names,
+        executor: format!("{:?}", cfg.ci.executor).to_lowercase(),
+        github_mirror_token: cfg.github.mirror_token.is_some(),
+    };
+    render(&tmpl)
+}
+
 #[cfg(test)]
 mod tests {
     use axum::body::Body;
diff --git a/quire-server/src/quire/web/mod.rs b/quire-server/src/quire/web/mod.rs
index 5ece1af..408b1fe 100644
--- a/quire-server/src/quire/web/mod.rs
+++ b/quire-server/src/quire/web/mod.rs
@@ -28,5 +28,6 @@ pub fn router(quire: Quire) -> axum::Router {
             "/{repo}/ci/{run_id}",
             axum::routing::get(handlers::run_detail),
         )
+        .route("/config", axum::routing::get(handlers::config))
         .with_state(quire)
 }
diff --git a/quire-server/src/quire/web/templates.rs b/quire-server/src/quire/web/templates.rs
index 93e7f50..4eca0f3 100644
--- a/quire-server/src/quire/web/templates.rs
+++ b/quire-server/src/quire/web/templates.rs
@@ -227,6 +227,25 @@ impl DetailShEvent {
     }
 }
 
+// ── Config ─────────────────────────────────────────────────────────
+
+#[derive(Template)]
+#[template(path = "config.html")]
+pub struct ConfigTemplate {
+    pub crumbs: Vec<Crumb>,
+    pub port: u16,
+    pub sentry_enabled: bool,
+    pub secret_names: Vec<String>,
+    pub executor: String,
+    pub github_mirror_token: bool,
+}
+
+impl ConfigTemplate {
+    pub fn version(&self) -> &'static str {
+        pkg_version()
+    }
+}
+
 // ── Error ──────────────────────────────────────────────────────────
 
 #[derive(Template)]
diff --git a/quire-server/templates/config.html b/quire-server/templates/config.html
new file mode 100644
index 0000000..5f2ca87
--- /dev/null
+++ b/quire-server/templates/config.html
@@ -0,0 +1,62 @@
+{% extends "_base.html" %}
+
+{% block title %}config{% endblock %}
+
+{% block nav %}
+<nav class="page-nav">
+  <div class="nav-bar">
+    <a class="nav-wordmark" href="/" aria-label="quire home">
+      <svg class="q-mark" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
+        <rect x="2" y="2" width="12" height="12" rx="1.2" fill="none" stroke="currentColor" stroke-width="1.2"/>
+        <line x1="4.5" y1="6"  x2="11.5" y2="6"  stroke="currentColor" stroke-width="0.8"/>
+        <line x1="4.5" y1="8"  x2="11.5" y2="8"  stroke="currentColor" stroke-width="0.8"/>
+        <line x1="4.5" y1="10" x2="9"    y2="10" stroke="currentColor" stroke-width="0.8"/>
+        <circle cx="11" cy="11" r="3" fill="none" stroke="currentColor" stroke-width="0.8" stroke-dasharray="1.2 1.2" opacity="0.35"/>
+      </svg>
+      <span class="nav-wordmark-text">quire</span>
+    </a>
+    {% for crumb in crumbs %}
+    <span class="sep">/</span>
+    {% if let Some(href) = crumb.href %}
+    <a class="nav-crumb" href="{{ href }}">{{ crumb.label }}</a>
+    {% else %}
+    <span class="nav-crumb">{{ crumb.label }}</span>
+    {% endif %}
+    {% endfor %}
+  </div>
+</nav>
+{% endblock %}
+
+{% block content %}
+<h2 class="ci-heading">config</h2>
+<table class="ci-table">
+  <tbody>
+    <tr>
+      <th>port</th>
+      <td>{{ port }}</td>
+    </tr>
+    <tr>
+      <th>executor</th>
+      <td>{{ executor }}</td>
+    </tr>
+    <tr>
+      <th>sentry</th>
+      <td>{% if sentry_enabled %}enabled{% else %}disabled{% endif %}</td>
+    </tr>
+    <tr>
+      <th>github mirror token</th>
+      <td>{% if github_mirror_token %}set{% else %}not set{% endif %}</td>
+    </tr>
+    <tr>
+      <th>secrets</th>
+      <td>
+        {% if secret_names.is_empty() %}
+        <span class="empty">none</span>
+        {% else %}
+        {% for name in secret_names %}{{ name }}{% if !loop.last %}, {% endif %}{% endfor %}
+        {% endif %}
+      </td>
+    </tr>
+  </tbody>
+</table>
+{% endblock %}