Follows the design commit before it. Written by Sonnet, reviewed by
Opus.
- view/help.go, templates/help.html: the mockup's topics, texts, ids
and menu. The page shows the groups the reader's menu shows:
Overview and Server for the global role, Outbound and Inbound by
reach (Inbound only with the feature on), Account for everyone;
boxes are numbered as shown.
- Box.Help set on the boxes the mockups mark; the inbound MX box has
its link again.
- TestEveryHelpLinkLandsOnATopic also fails on a topic that no page
links to, unless it is listed as menu-only (account) - and on a
stale exception.
Known until the change that follows: the dns, dmarc and account topics
describe the three-choice report address, which the code does not have
yet; Account's DMARC box still links to #dns.
Not verified by running: three sentences rest on Postfix behaviour
(delivery is deferred when a required TLS session cannot be made; a
milter with default_action=tempfail answers 4xx; with "any recipient"
an address the upstream refuses comes back as a bounce).
Evidence
- docs/assets/panel-redesign/evidence/help.png (1440x3600) and
in-domain.png: headless Edge, real templates served locally with
their fixtures - NOT from the container.
- go test -count=1 ./internal/web/... ./cmd/panel: ok (six packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
3.1 KiB
Go
83 lines
3.1 KiB
Go
package view
|
|
|
|
// The data of the Outbound queue (templates/out_queue.html, mockup
|
|
// out-queue.html): what Postfix is still holding, and the retry policy that
|
|
// decides how long it holds it. The queue box polls; the page and its fragment,
|
|
// GET /outbound/queue/fragment, render the same box
|
|
// (templates/out_queue_body.html).
|
|
//
|
|
// Until the queue is a table (docs/roadmap.md, parse postqueue -j) the box
|
|
// shows postqueue's own listing in a log pane, as the system log shows
|
|
// mail.log: the mockup's table is not drawn from data the panel has.
|
|
//
|
|
// A page is a typed struct that embeds Meta, built by a constructor and filled
|
|
// by With… methods; the handler reads Postfix, the view decides what is said and
|
|
// how it is worded.
|
|
|
|
import "strings"
|
|
|
|
const outboundQueue = "/outbound/queue"
|
|
|
|
// OutQueue is the data of the Outbound queue. It is for the global role.
|
|
type OutQueue struct {
|
|
Meta
|
|
Head Head
|
|
|
|
Queue Box
|
|
Lines []LogLine
|
|
// Empty is shown in place of the listing when there is none: the queue holds
|
|
// nothing, or it could not be read.
|
|
Empty EmptyState
|
|
|
|
Retries Box
|
|
Policy []Fact
|
|
}
|
|
|
|
// NewOutQueue builds the page. listing is what postqueue printed, as it was
|
|
// read, and errText is why it could not be read, empty when it could.
|
|
func NewOutQueue(m Meta, listing, errText string) *OutQueue {
|
|
m.Title, m.Section, m.Page = "Outbound queue", "outbound", "queue"
|
|
p := &OutQueue{
|
|
Meta: m,
|
|
Head: Head{
|
|
Kicker: "Outbound",
|
|
Title: "Outbound queue",
|
|
Lead: Rich("What applications handed over and the receiving server has not yet taken, as ", Code("postqueue -p"), " lists it."),
|
|
},
|
|
Queue: Box{No: "01", Title: "Waiting", ID: "out-queue", Poll: outboundQueue + "/fragment",
|
|
Help: topicLink(HelpQueue, "What waits here and how Postfix retries"),
|
|
End: Plain("Refreshes on its own")},
|
|
Empty: EmptyState{Icon: "ti-stack-2", Text: Plain("Queue is empty.")},
|
|
Retries: Box{No: "02", Title: "How retries work"},
|
|
}
|
|
if errText != "" {
|
|
p.Empty = EmptyState{Icon: "ti-alert-circle", Text: Plain(errText)}
|
|
return p
|
|
}
|
|
if strings.TrimSpace(listing) == "" {
|
|
return p
|
|
}
|
|
for _, line := range strings.Split(strings.TrimRight(listing, "\r\n"), "\n") {
|
|
p.Lines = append(p.Lines, LogLine{Text: strings.TrimRight(line, "\r")})
|
|
}
|
|
return p
|
|
}
|
|
|
|
// WithRetryPolicy fills the second box: the first retry, the cap the later ones
|
|
// double up to and how long a message is kept, each as the handler words it.
|
|
// fromDefaults says the Postfix configuration could not be read and these are
|
|
// the compiled-in values.
|
|
func (p *OutQueue) WithRetryPolicy(firstRetry, backoffCap, lifetime string, fromDefaults bool) *OutQueue {
|
|
p.Retries.End = Plain("This Postfix's policy, read at panel start")
|
|
if fromDefaults {
|
|
p.Retries.End = Plain("Compiled-in defaults: the Postfix configuration could not be read")
|
|
}
|
|
p.Policy = []Fact{
|
|
{Label: "First retry", Value: Plain(firstRetry), Big: true},
|
|
{Label: "Later retries", Value: Plain("doubling"), Big: true, Note: Plain("capped at " + backoffCap)},
|
|
{Label: "Kept in queue", Value: Plain(lifetime), Big: true},
|
|
{Label: "Then", Value: Plain("bounced"), Big: true, Note: Plain("no attempt counter, only time")},
|
|
}
|
|
return p
|
|
}
|