panel-redesign: the spam filter's socket is one of the milter sockets
The owner's decision of 2026-10-10, as the plan has it (Who sees what:
for the global administrator the filter's socket is one of the milter
sockets on Health and Overview). Written by Sonnet, reviewed by Opus.
- internal/health: CheckMilter probes an address in Postfix's two
forms. unix:/path is the existing stat; inet:host:port is a TCP
dial with a 1 s cap, closed at once, nothing written. An address in
neither form is reported as not checkable, never as fine. Details
are plain sentences ("No answer from antispam:11332 within 1 s.",
"Connection to ... refused.", "The name ... could not be
resolved.") and never the Go error text.
- Health: with the filter on, a third row "Spam filter - inbound"
with the configured address; it counts in the socket status and in
Overview's Milter sockets card. A filter that does not answer is an
error when INBOUND_ANTISPAM_MILTER_ACTION is tempfail (inbound mail
is deferred) and a warning otherwise (mail goes through
unfiltered), and the row says which. With the filter off nothing
changes.
- Both pages are global-only; a test checks a domain administrator
never receives the address.
The dial runs with the other checks on every request and poll of
Overview and Health (5 s active, 30 s idle, none while the tab is
hidden): a dead filter costs a poll at most 1 s, and a live one sees a
connect-and-close at that rhythm per open tab.
Not changed: fixtures and screenshots (the mockup draws two sockets),
help texts. The CHANGELOG line comes with the documentation step, which
is editing that file now.
go test -count=1 ./internal/health/... ./internal/web/... ./cmd/panel
ok (seven packages)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -167,7 +167,7 @@ func healthPage(m view.Meta, c map[string]any, flash string) *view.Health {
|
||||
|
||||
page.Sockets.End = view.Verdict(string(c["SocketStatus"].(health.Status)))
|
||||
for _, s := range c["Sockets"].([]health.Socket) {
|
||||
row := view.SocketRow{Name: s.Name, Path: s.Path, State: view.Tag{Status: string(s.Status)}}
|
||||
row := view.SocketRow{Name: s.Name, Note: s.Note, Path: s.Path, State: view.Tag{Status: string(s.Status)}}
|
||||
if s.Status != health.StatusOK {
|
||||
row.Detail = s.Detail
|
||||
}
|
||||
@@ -351,6 +351,9 @@ func (h *Handlers) healthChecks() map[string]any {
|
||||
health.CheckSocket("OpenDKIM", h.cfg.OpenDKIMSocket, true),
|
||||
health.CheckSocket("send-log", h.cfg.JournalSocket, false),
|
||||
}
|
||||
if on, milter, action := h.inboundFilter(); on {
|
||||
sockets = append(sockets, filterSocket(milter, action))
|
||||
}
|
||||
socketStatus := health.StatusUnknown
|
||||
for _, sock := range sockets {
|
||||
socketStatus = health.Worst(socketStatus, sock.Status)
|
||||
@@ -375,6 +378,26 @@ func (h *Handlers) healthChecks() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
// filterSocket is the inbound spam filter's row among the milter sockets. A
|
||||
// filter that is down costs what the server's setting says: with the action
|
||||
// tempfail Postfix defers inbound mail (an error, like OpenDKIM), otherwise it
|
||||
// accepts the mail unfiltered (a warning, like the send log). The address is the
|
||||
// one in INBOUND_ANTISPAM_MILTER as configured; Health and Overview, the only
|
||||
// pages that show it, are for the global administrator.
|
||||
func filterSocket(milter, action string) health.Socket {
|
||||
tempfail := action == "tempfail"
|
||||
s := health.CheckMilter("Spam filter", milter, tempfail)
|
||||
s.Note = "inbound"
|
||||
switch {
|
||||
case s.Status != health.StatusError && s.Status != health.StatusWarn:
|
||||
case tempfail:
|
||||
s.Detail += " Inbound mail is deferred until it is back."
|
||||
default:
|
||||
s.Detail += " Inbound mail goes through unfiltered."
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func queueSummary(out string) string {
|
||||
lines := strings.Split(strings.TrimSpace(out), "\n")
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
|
||||
Reference in New Issue
Block a user