Files
selfpost/internal/web/view/outbound_queue.go
T
mixandClaude Opus 5.5 4ab63d08ae panel-redesign: Help with seventeen topics; every box links to its own
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>
2026-10-10 11:58:36 +03:00

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
}