#5099 "A name containing a quote runs script when an admin clicks Delete". Inline handlers like onclick="return confirm('Delete {{ rule.name }}?')" are HTML-decoded before the JavaScript is parsed, so autoescaping did not stop a name such as x');fetch(…);// from running. - Every prompt (22 handlers, plus the docker prune and token revoke ones) is now data-confirm="…", read through dataset by one submit listener in base.html; nothing rendered is parsed as JavaScript. - The remaining inline handlers take integers only (|int), and the time range buttons pass this.dataset.range. - tests/test_inline_handlers.py fails on any {{ }} in an inline handler that is not |int, with a self-test; tests/_templates.py is the one template walker the template guards now share. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
3.4 KiB
HTML
75 lines
3.4 KiB
HTML
{# HTMX fragment — monitor rows (list page). #}
|
|
{% if monitor_data %}
|
|
{% for d in monitor_data %}
|
|
{% set m = d.monitor %}
|
|
{% set latest = d.latest %}
|
|
<div class="ping-row" {% if not m.enabled %}style="opacity:0.5;"{% endif %}>
|
|
<div class="ping-meta" style="min-width:160px;max-width:240px;">
|
|
<span class="badge" style="font-size:0.62rem;vertical-align:middle;">{{ m.type | upper }}</span>
|
|
{% if m.host_id %}
|
|
<a class="ping-name" href="/hosts/{{ m.host_id }}" title="{{ m.name }}"
|
|
style="color:inherit;text-decoration:none;">{{ m.name }}</a>
|
|
{% else %}
|
|
<span class="ping-name" title="{{ m.name }}">{{ m.name }}</span>
|
|
{% endif %}
|
|
<span class="ping-addr" title="{{ m.target }}">{{ m.target }}</span>
|
|
</div>
|
|
|
|
<div class="ping-pills">
|
|
{% for _ in range([30 - d.heartbeat|length, 0]|max) %}
|
|
<span class="pill pill-empty" title="No data"></span>
|
|
{% endfor %}
|
|
{% for h in d.heartbeat %}
|
|
<span class="pill" title="{{ h.title }}"
|
|
style="background:{{ '#1a6632' if h.state == 'up' else '#6a1515' }};"></span>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="ping-cur">
|
|
{% if latest is none %}
|
|
<span class="ping-cur-nd">—</span>
|
|
{% elif not latest.is_up %}
|
|
<span class="ping-cur-down">DOWN</span>
|
|
{% elif latest.response_ms is not none %}
|
|
<span style="{{ threshold_style(latest.response_ms, 'latency') or 'color:var(--green);' }}">
|
|
{{ "%.0f"|format(latest.response_ms) }} ms</span>
|
|
{% elif m.type == 'dns' and latest.resolved_ip %}
|
|
<span class="ping-cur-good" style="font-size:0.72rem;">{{ latest.resolved_ip }}</span>
|
|
{% else %}
|
|
<span class="ping-cur-good">UP</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div style="min-width:4.5rem;text-align:right;font-size:0.78rem;font-variant-numeric:tabular-nums;{{ threshold_style(d.uptime_pct, 'uptime') if d.uptime_pct is not none else 'color:var(--text-dim);' }}">
|
|
{% if d.uptime_pct is not none %}{{ "%.1f"|format(d.uptime_pct) }}%{% else %}—{% endif %}
|
|
<span style="color:var(--text-dim);font-size:0.7rem;margin-left:0.15rem;">{{ range_key }}</span>
|
|
</div>
|
|
|
|
{% if d.tls_days is not none %}
|
|
<div title="TLS certificate expiry" style="min-width:4rem;text-align:right;font-size:0.72rem;{{ 'color:var(--red);' if d.tls_days < 14 else ('color:var(--yellow);' if d.tls_days < 30 else 'color:var(--text-dim);') }}">
|
|
cert {{ d.tls_days|round|int }}d
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if session.user_role in ['operator', 'admin'] %}
|
|
<div style="display:flex;gap:0.35rem;flex-shrink:0;">
|
|
<a href="/monitors/{{ m.id }}/edit" class="btn btn-sm" style="padding:0.2rem 0.5rem;font-size:0.72rem;">Edit</a>
|
|
<form method="post" action="/monitors/{{ m.id }}/toggle" style="display:inline;">{{ csrf_field() }}
|
|
<button type="submit" class="btn btn-sm" style="padding:0.2rem 0.5rem;font-size:0.72rem;">
|
|
{{ "Pause" if m.enabled else "Resume" }}</button>
|
|
</form>
|
|
<form method="post" action="/monitors/{{ m.id }}/delete" style="display:inline;"
|
|
data-confirm="Delete monitor {{ m.name }}?">{{ csrf_field() }}
|
|
<button type="submit" class="btn btn-sm" style="padding:0.2rem 0.5rem;font-size:0.72rem;color:var(--red);">✕</button>
|
|
</form>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<p style="color:var(--text-muted);font-size:0.9rem;padding:1rem 0;">
|
|
No monitors yet. Add one above, or attach checks to a host from the
|
|
<a href="/hosts/" style="color:var(--accent);">Hosts</a> page.
|
|
</p>
|
|
{% endif %}
|