#5509 "Delivery you can see: each alert's email state in history, and outgoing email on Settings -> System" (milestone 501). - Alerts -> History: an alert's email reads from the outbox (#5506): "gathering" with when it sends, "sending", "retrying" with the attempt, next try and the server's last answer, "sent", or "failed" naming each reader it didn't reach and why (outbox.delivery / email_states). Events from before the outbox keep what they recorded. The Notified column shows once an alert has a queued email. - Settings -> System gets "Outgoing email": every email of the last 7 days not yet sent, failures first, then the longest retrying, with recipient, kind, state and last error, and a count of what was sent. Failures and anything still retrying after 5 minutes lead the page's verdict (outbox.outgoing). - get_alert_history (MCP) adds `email`: the same delivery state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
4.9 KiB
HTML
84 lines
4.9 KiB
HTML
{# alerts/_history_page.html — one page of alert episodes (#5316), then the
|
|
sentinel that loads the next page as the reader nears it (core.paging,
|
|
pref 172). An episode is one stretch out of quiet: what it reached, for how
|
|
long, its worst reading, who was told; its transitions fold under it. #}
|
|
{% from "_macros.html" import status_badge, scroll_more, scroll_end %}
|
|
{% set cols = 3 + (0 if chosen else 1) + (1 if show_notified else 0) + (1 if show_action else 0) %}
|
|
{% for ep in episodes %}
|
|
<tr>
|
|
<td class="nowrap">
|
|
{%- if ep.began_here %}{{ ep.start|ago }}{% else %}<span class="tone-dim" title="It began before this part of the list">earlier</span>{% endif -%}
|
|
</td>
|
|
{% if not chosen %}
|
|
<td class="cell-name">
|
|
<a href="/alerts/history?rule={{ ep.rule.id }}&range={{ current_range }}" title="{{ ep.rule.name }}">{{ ep.rule.name }}</a>
|
|
<div class="cell-sub">{{ source_label(ep.rule.source_module) }} · {{ ep.rule.resource_name }}</div>
|
|
</td>
|
|
{% endif %}
|
|
<td>
|
|
{% if not ep.began_here and ep.steps|length == 1 %}
|
|
{%- set s = ep.steps[0] %}{{ status_badge(s.from_state) }} <span class="tone-dim">→</span> {{ status_badge(s.to_state) }}
|
|
{% else %}
|
|
{{ status_badge(ep.worst) }}
|
|
{%- if ep.ongoing %} <span class="tone-crit">{% if ep.began_here %}for {{ ep.duration|duration }}, {% endif %}still going</span>
|
|
{%- elif ep.continues %} <span class="tone-muted">cleared later (above)</span>
|
|
{%- elif ep.began_here %} for {{ ep.duration|duration }}<span class="tone-dim"> · cleared {{ ep.end|ago }}</span>
|
|
{%- else %} <span class="tone-dim">cleared {{ ep.end|ago }}</span>{% endif %}
|
|
{% endif %}
|
|
{% for n in ep.notes %}<div class="cell-note">{{ n }}</div>{% endfor %}
|
|
{% if ep.steps|length > 1 %}
|
|
<details class="episode-steps">
|
|
<summary>{{ ep.steps|length }} changes of state</summary>
|
|
<ol>
|
|
{% for s in ep.steps %}
|
|
<li>{{ status_badge(s.from_state) }} <span class="tone-dim">→</span> {{ status_badge(s.to_state) }}
|
|
<span class="tone-muted">{{ s.value }}</span> <span class="tone-dim">· {{ s.at|ago }}</span></li>
|
|
{% endfor %}
|
|
</ol>
|
|
</details>
|
|
{% endif %}
|
|
</td>
|
|
<td class="nowrap">
|
|
{%- if ep.reading %}{{ ep.reading }} <span class="tone-dim">({{ ep.fires_when }})</span>
|
|
{%- else %}<span class="tone-dim">no reading</span> <span class="tone-dim">({{ ep.fires_when }})</span>{% endif -%}
|
|
</td>
|
|
{% if show_notified %}
|
|
<td>
|
|
{% if ep.muted_by %}
|
|
<span class="badge badge-yellow" title="A maintenance window muted this notification">muted · {{ ep.muted_by }}</span>
|
|
{% if ep.released %}<div class="cell-note">notified when the window ended</div>{% endif %}
|
|
{% endif %}
|
|
{# Each channel's outcome; an email through the outbox (#5509) also
|
|
says when it sends, or how its retries stand, or why it failed. #}
|
|
{% for d in ep.deliveries %}
|
|
<div>
|
|
{% if d.state == "sent" %}<span class="badge badge-green">{{ d.label }} sent</span>
|
|
{%- if d.at %} <span class="tone-dim">{{ d.at|when }}</span>{% endif %}
|
|
{% elif d.state == "held" %}<span class="badge badge-yellow">{{ d.label }} gathering</span>
|
|
<div class="cell-note">waiting for other alerts; sends {{ d.at|when }}</div>
|
|
{% elif d.state == "sending" %}<span class="badge badge-yellow">{{ d.label }} sending</span>
|
|
{% elif d.state == "retrying" %}<span class="badge badge-yellow">{{ d.label }} retrying</span>
|
|
<div class="cell-note" style="max-width:28rem;overflow-wrap:anywhere;">attempt {{ d.attempts }}
|
|
{%- if d.at %}, next {{ d.at|when }}{% endif %}{% if d.error %}: {{ d.error }}{% endif %}</div>
|
|
{% else %}<span class="badge badge-red">{{ d.label }} failed</span>
|
|
{% if d.error %}<div class="cell-note" style="max-width:28rem;overflow-wrap:anywhere;">{{ d.error }}</div>{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% if not ep.muted_by and not ep.deliveries %}<span class="tone-dim" title="No notification was sent for this">—</span>{% endif %}
|
|
</td>
|
|
{% endif %}
|
|
{% if show_action %}
|
|
<td>
|
|
{% for run_id in ep.runs %}<div><a href="/ansible/runs/{{ run_id }}">Ansible run</a></div>
|
|
{% else %}<span class="tone-dim">—</span>{% endfor %}
|
|
</td>
|
|
{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
{% if after %}
|
|
{{ scroll_more("/alerts/history?before=" ~ (after|urlencode) ~ "&range=" ~ (current_range|urlencode) ~ (("&rule=" ~ (chosen.id|urlencode)) if chosen else ""), cols=cols) }}
|
|
{% else %}
|
|
{{ scroll_end(cols=cols) }}
|
|
{% endif %}
|