feat(traefik): Routers widget shows current routers only, one header row, certs on the routers they serve (#5254)
#5254 "Traefik — Routers: current routers only, header row, certs on the router they serve". - Router hosts come from Traefik's API (/api/http|tcp/routers), read best-effort on each scrape. An optional api_url overrides it; the default is /api on the metrics host. A certificate's expiry then shows in a TLS column on the router whose host it covers. An expiring one makes that router a problem, graded by Settings → Thresholds. - Only current: routers and certificates must have appeared in the last three scrapes. Certificates are upserted by serial and never deleted, so renewed or retired ones lingered on the widget. - Widget order: verdict, problem routers with the reason, then the busiest healthy routers under one header (Router / Req/s / Bandwidth / p95 / TLS). A missing value shows "—". - If the API is unreachable, expiring certificates are listed after the routers, not above them. - The page splits certificates into served (with the routers using each, plus SANs) and "No longer served". - Latest row per router: an index walk (recursive CTE + LATERAL) replaces DISTINCT plus a query per router. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% if problems or healthy or gone or certs %}
|
||||
{% if problems or healthy or gone or certs or old_certs %}
|
||||
{# ── Routers that need attention ─────────────────────────────────────────── #}
|
||||
{% if problems %}
|
||||
<div style="column-width:340px;column-gap:1rem;">
|
||||
@@ -94,22 +94,43 @@
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{# ── Certificates, soonest expiry first ──────────────────────────────────── #}
|
||||
{% if certs %}
|
||||
{# ── Certificates Traefik still serves, soonest expiry first; then the ones
|
||||
it no longer reports (renewed or retired), kept here but off the
|
||||
dashboard (#5254). #}
|
||||
{% if certs or old_certs %}
|
||||
<div class="card">
|
||||
<div class="section-title" style="margin-bottom:0.75rem;">TLS certificates</div>
|
||||
{% if certs %}
|
||||
<table class="table" style="font-size:0.85rem;">
|
||||
<thead><tr><th>Name</th><th>Expires</th><th style="text-align:right;">Left</th></tr></thead>
|
||||
<thead><tr><th>Name</th><th>Routers</th><th>Expires</th><th style="text-align:right;">Left</th></tr></thead>
|
||||
<tbody>
|
||||
{% for cert in certs %}
|
||||
<tr>
|
||||
<td style="word-break:break-all;">{{ cert.cn }}</td>
|
||||
<td style="word-break:break-all;">{{ cert.cn }}{% if cert.sans %}<div class="field-hint">also {{ cert.sans|join(", ") }}</div>{% endif %}</td>
|
||||
<td class="field-hint">{% if cert.routers is none %}—{% elif cert.routers %}{{ cert.routers|join(", ") }}{% else %}not in use{% endif %}</td>
|
||||
<td class="field-hint">{{ cert.not_after.strftime("%Y-%m-%d") }}</td>
|
||||
<td style="text-align:right;white-space:nowrap;" class="tone-{{ status_info(status_class(cert.days_remaining, 'tls_days'))[0] }}">{{ cert_left(cert.days_remaining) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if old_certs %}
|
||||
<details style="margin-top:0.75rem;">
|
||||
<summary class="field-hint">No longer served ({{ old_certs|length }}) — renewed or retired; Traefik stopped reporting them</summary>
|
||||
<table class="table" style="font-size:0.85rem;margin-top:0.5rem;">
|
||||
<thead><tr><th>Name</th><th>Expires</th></tr></thead>
|
||||
<tbody>
|
||||
{% for cert in old_certs %}
|
||||
<tr style="color:var(--text-muted);">
|
||||
<td style="word-break:break-all;">{{ cert.cn }}</td>
|
||||
<td class="field-hint">{{ cert.not_after.strftime("%Y-%m-%d") }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user