Following the design commit df96690.
- Two columns (owner, 2026-10-14): sender_check_days.messages (migration
0009, not deployed) holds the messages deferred mid-message, from
cleanup's warning; deferred stays the connections smtpd turned away.
AddSenderCheckDeferred takes which; the counter passes cleanup's as a
message. The card has "Connections turned away" and "Messages deferred";
it warns on either within 24 hours (one mark, as before).
- Supervisord silent at start: postfixStarted asks again every 2 s for 30 s
(postfixState: started, running, known). With no answer the downtime is
the time since the alive mark - an upper bound - booked with estimated = 1
(sender_check_days.estimated, MAX over a day); the card prefixes such a
period's time with "≈ " and its note says what ≈ means. Before, such a
start counted nothing and the number the owner decides by read low.
- Help topic sender-check and the log topic word for word as the mockup
(help.go).
Tests: store (connections and messages apart, a period with an estimated
day); logtail (cleanup's warnings are messages); cmd/panel (supervisord
silent: the time since the mark, estimated); view fixture and the Health
page at four columns with ≈; e2e reads the row's two counts (panel down:
connections; panel hung: messages). Schema at 0009 with the columns.
schema-migrations row 9, CHANGELOG [Unreleased], audit plan lines ticked
(two), roadmap 28/62.
Evidence: go vet ./... clean; go test of store, view, cmd/panel ok locally,
logtail but for the Windows-only test; the server run follows before the
push.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
4.1 KiB
HTML
84 lines
4.1 KiB
HTML
{{/* The polled part of Health, in the page and in its fragment
|
|
(GET /server/health/fragment) alike. The left stack is the element that
|
|
polls (data-poll, outerHTML): all four of its boxes are readings. The right
|
|
stack ends in the static Configuration box, so its three readings are
|
|
swapped into place one by one, each box by its own id, when the fragment
|
|
marks them out of band (Health.AsFragment) - a fragment can replace only
|
|
the element that asked for it. */}}
|
|
{{define "health_body"}}{{template "health_stack" .}}
|
|
{{template "health_sockets" .}}
|
|
{{template "health_certificate" .}}
|
|
{{template "health_mail" .}}{{end}}
|
|
|
|
{{/* health_stack is the left stack: Processes, Sender check, Hostname and
|
|
reverse DNS, Machine. */}}
|
|
{{define "health_stack"}}<div class="column is-6" id="health-stack" data-poll aria-live="polite" hx-get="/server/health/fragment" hx-trigger="load" hx-swap="outerHTML">
|
|
{{template "box_open" .Processes}}
|
|
{{if .ProcessError}}
|
|
{{template "body_open"}}
|
|
<p class="has-text-danger">Could not ask supervisord for the process list.</p>
|
|
{{template "body_close"}}
|
|
{{else}}
|
|
<div class="table-container"><table class="table is-fullwidth">
|
|
<thead><tr><th>Program</th><th>State</th><th>Detail</th></tr></thead>
|
|
<tbody>{{range .ProcessRows}}
|
|
<tr><td>{{.Name}}</td><td>{{template "tag" .State}}</td><td class="sp-muted">{{.Detail}}</td></tr>{{end}}
|
|
</tbody>
|
|
</table></div>
|
|
{{end}}
|
|
{{template "box_close"}}
|
|
{{template "box_open" .SenderCheck}}
|
|
<div class="table-container"><table class="table is-fullwidth">
|
|
<thead><tr><th>Period</th><th>Panel not answering</th><th>Connections turned away</th><th>Messages deferred</th></tr></thead>
|
|
<tbody>{{range .SenderRows}}
|
|
<tr><td>{{.Period}}</td><td>{{.Down}}</td><td>{{.Deferred}}</td><td>{{.Messages}}</td></tr>{{end}}
|
|
</tbody>
|
|
</table></div>
|
|
{{template "body_open"}}
|
|
<p class="sp-muted">{{template "inline" .SenderNote}}</p>
|
|
{{template "body_close"}}
|
|
{{template "box_close"}}
|
|
{{template "box_open" .Hostname}}
|
|
{{template "body_open"}}
|
|
{{template "facts" .HostFacts}}
|
|
{{template "body_close"}}
|
|
{{template "box_close"}}
|
|
{{template "box_open" .Machine}}
|
|
<div class="table-container"><table class="table is-fullwidth">
|
|
<thead><tr><th>Resource</th><th>Usage</th><th>Detail</th></tr></thead>
|
|
<tbody>{{range .MachineRows}}
|
|
<tr><td>{{.Resource}}</td><td>{{if .Gauge}}<progress class="progress is-small {{.Gauge.Bar}} mb-1" value="{{.Gauge.Percent}}" max="100"></progress>{{.Gauge.Text}}{{else if .Usage}}<span class="sp-nowrap">{{.Usage}}</span>{{else}}<span class="sp-muted">—</span>{{end}}</td><td class="sp-muted">{{range .Detail}}<div>{{.}}</div>{{end}}</td></tr>{{end}}
|
|
</tbody>
|
|
</table></div>
|
|
{{template "box_close"}}
|
|
</div>{{end}}
|
|
|
|
{{define "health_sockets"}}{{template "box_open" .Sockets}}
|
|
<div class="table-container"><table class="table is-fullwidth">
|
|
<thead><tr><th>Milter</th><th>State</th></tr></thead>
|
|
<tbody>{{range .SocketRows}}
|
|
<tr><td>{{.Name}}{{if .Note}} <span class="sp-muted">· {{.Note}}</span>{{end}}<br><span class="sp-muted sp-mono sp-small">{{.Path}}</span>{{if .Detail}}<br><span class="sp-muted sp-small">{{.Detail}}</span>{{end}}</td><td>{{template "tag" .State}}</td></tr>{{end}}
|
|
</tbody>
|
|
</table></div>
|
|
{{template "box_close"}}{{end}}
|
|
|
|
{{define "health_certificate"}}{{template "box_open" .Certificate}}
|
|
{{template "body_open"}}
|
|
{{if .CertFacts}}{{template "facts" .CertFacts}}{{end}}
|
|
<p class="mt-4 {{if .CertProblem}}has-text-danger{{else}}sp-muted{{end}}">{{.CertDetail}}</p>
|
|
{{template "body_close"}}
|
|
{{template "box_close"}}{{end}}
|
|
|
|
{{/* health_mail is the Notification mail card. */}}
|
|
{{define "health_mail"}}{{template "box_open" .NotifyMail}}
|
|
<div class="table-container"><table class="table is-fullwidth">
|
|
<thead><tr><th>Record</th><th>State</th></tr></thead>
|
|
<tbody>{{range .NotifyRows}}
|
|
<tr><td>{{.Name}}<br><span class="sp-muted sp-mono sp-small">{{.Host}}</span></td><td>{{template "tag" .State}}</td></tr>{{end}}
|
|
</tbody>
|
|
</table></div>
|
|
{{template "body_open"}}
|
|
<p class="sp-muted">{{template "inline" .NotifyNote}}</p>
|
|
{{template "body_close"}}
|
|
{{template "box_close"}}{{end}}
|