Traefik: routers are graded from their latest scrape (router_state: crit on any real 5xx share, warn on p95 past the latency critical cutoff or a high 4xx share). Failing and slow routers lead as cards that say why; healthy ones fold into one table that stays open or shut across polls. Proxy counts are stat tiles; certificates are graded by Settings -> Thresholds (tls_days). UniFi: WAN health is a stat strip (latency graded by the thresholds), devices carry status dots, event categories and firewall actions are badges, the alarm count reuses .count-badge (renamed from .nav-count). SNMP: each device wears a status badge and its readings are stat tiles. Agent fleet: the table comes first, with a Status column (Reporting / Silent / Never reported, by the host page's stale_after_seconds), silent agents first, hosts linked, last seen as |ago. Audit: entries name their entity (one query per type, steward/audit/ service.py) and link it; something deleted since keeps the name its entry recorded and says "(deleted)". Tokens: charts read colours through chart-colors.js (stewardColor resolves a token to the rgb the browser computed; Chart.js defaults take the page's text and border colours), so host, history, Traefik and SNMP charts drop their hex. Template hex colours reach 0 (HEX_CEILING = 0), the last rgba pills go, and heavy weights (600/700/bold, 60 of them) become 500, the house pair being 400/500; a guard keeps templates there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
2.1 KiB
HTML
75 lines
2.1 KiB
HTML
{# traefik/widget_request_chart.html — Chart.js request rate + error % over time #}
|
|
{% if not labels_json or labels_json == '[]' %}
|
|
<div style="color:var(--text-muted);font-size:0.85rem;padding:0.5rem 0;">No data for last {{ hours }}h.</div>
|
|
{% else %}
|
|
<div style="position:relative;height:160px;">
|
|
<canvas id="chart-traefik-req-{{ widget_id }}"></canvas>
|
|
</div>
|
|
<script>
|
|
(function() {
|
|
var ctx = document.getElementById("chart-traefik-req-{{ widget_id }}");
|
|
if (!ctx || !window.Chart) return;
|
|
var existing = Chart.getChart(ctx);
|
|
if (existing) existing.destroy();
|
|
new Chart(ctx.getContext("2d"), {
|
|
type: "line",
|
|
data: {
|
|
labels: {{ labels_json | safe }},
|
|
datasets: [
|
|
{
|
|
label: "Req/s",
|
|
data: {{ req_rate_json | safe }},
|
|
borderColor: stewardColor("--blue"),
|
|
backgroundColor: stewardColor("--blue-dim"),
|
|
fill: true,
|
|
tension: 0.3,
|
|
pointRadius: 0,
|
|
borderWidth: 1.5,
|
|
yAxisID: "y",
|
|
},
|
|
{
|
|
label: "5xx %",
|
|
data: {{ error_rate_json | safe }},
|
|
borderColor: stewardColor("--red"),
|
|
fill: false,
|
|
tension: 0.3,
|
|
pointRadius: 0,
|
|
borderWidth: 1.5,
|
|
yAxisID: "y2",
|
|
},
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
animation: false,
|
|
plugins: {
|
|
legend: {
|
|
labels: { boxWidth: 10, font: { size: 10 } }
|
|
}
|
|
},
|
|
scales: {
|
|
x: { display: false },
|
|
y: {
|
|
min: 0,
|
|
ticks: { font: { size: 10 } },
|
|
title: { display: true, text: "req/s", font: { size: 9 } },
|
|
},
|
|
y2: {
|
|
position: "right",
|
|
min: 0,
|
|
max: 100,
|
|
grid: { drawOnChartArea: false },
|
|
ticks: { color: stewardColor("--red"), font: { size: 10 } },
|
|
title: { display: true, text: "5xx%", color: stewardColor("--red"), font: { size: 9 } },
|
|
},
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
<div style="font-size:0.72rem;color:var(--text-muted);margin-top:0.35rem;text-align:right;">
|
|
last {{ hours }}h
|
|
</div>
|
|
{% endif %}
|