test / test (push) Canceled after 0s
Audit 2026-10-12, stage 2c, "Health says what is served"; the pages of
ea8b6bf's mockup.
Sockets (A-38). A unix socket's file outlives a killed OpenDKIM or panel
and refuses every connect() (unix(7)); Health said "Listening" from a
stat. CheckSocket and CheckMilter's unix case now dial: refused is
"Nothing listens on the ... socket: the program that made it has
stopped", EACCES is unknown (the panel may not connect), anything else
"does not answer". The probe closes before a byte, which no milter logs:
libmilter takes the EOF as SMFIC_EOF without a line (sendmail
8.17.1.9-2+deb12u2, the image's libmilter1.0.1, libmilter/comm.c
mi_rd_cmd(), engine.c mi_engine() - its source is now in dev/upstream),
OpenDKIM's mlfi_close() has no connection to free, go-milter v0.4.1
returns quietly on io.EOF (session.go, HandleMilterCommands()). The page
polls every 5 s while in view, so this mattered.
Certificate (A-65). Postfix serves build/postfix-config.sh's copy,
refreshed by cert-reload every TLS_RELOAD_INTERVAL_SECONDS. The panel now
reads smtpd_tls_cert_file from postconf -h at its start (as the retry
policy) and TLS_RELOAD_INTERVAL_SECONDS; CheckServedCertificate checks the
served copy and, when the mounted file differs byte for byte (cert-reload's
own cmp -s), names it as Renewal: its expiry and mtime, two facts on the
card. Waiting is fine for the interval: a nearing expiry of the served one
is then no warning (an expiry still an error); past interval + 1 h it is a
warning pointing at cert-reload's lines. The notifier sends no expiry
notice while a renewal waits on time.
Wording (D-P1, D-P3, and the same for the spam filter): Sender check
note, help topics sender-check, upstream, filter, certificate - word for
word as in the mockup; the filter row's detail too.
Tests: stale socket file (Linux) refused for CheckSocket and CheckMilter;
CheckServedCertificate (same, waiting, late, expired + waiting, no served
path); intervalText; the card's facts; the notifier quiet while a renewal
waits, speaking once it is late. Stand (2026-10-15, make e2e, exit 0):
new health_shows_what_is_served - the three sockets dialled ok by the
panel's user; a certificate mounted without its key is refused by
cert-reload ("do not belong together") and the card shows the served
expiry, "Renewal expires" with the mounted one's, "Renewal mounted", and
"Postfix still serves the certificate above."; a matching pair is then
served again. go vet and go test ./... pass on the server (24 packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
109 lines
3.2 KiB
Go
109 lines
3.2 KiB
Go
// Package handlers implements the panel's authenticated HTTP handlers.
|
|
package handlers
|
|
|
|
import (
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/mixeme/selfpost/internal/app"
|
|
"github.com/mixeme/selfpost/internal/dmarc"
|
|
"github.com/mixeme/selfpost/internal/dnscheck"
|
|
"github.com/mixeme/selfpost/internal/domain"
|
|
"github.com/mixeme/selfpost/internal/health"
|
|
"github.com/mixeme/selfpost/internal/inbound"
|
|
"github.com/mixeme/selfpost/internal/postfix"
|
|
"github.com/mixeme/selfpost/internal/store"
|
|
"github.com/mixeme/selfpost/internal/web/auth"
|
|
"github.com/mixeme/selfpost/internal/web/view"
|
|
)
|
|
|
|
// Config holds handler-specific panel configuration.
|
|
type Config struct {
|
|
Hostname string
|
|
SubmissionEnabled bool
|
|
MailLogPath string
|
|
DataDir string
|
|
DBPath string
|
|
DeployRoot string
|
|
Version string
|
|
TLSCertFile string
|
|
TLSServedCertFile string
|
|
TLSReloadInterval time.Duration
|
|
OpenDKIMSocket string
|
|
JournalSocket string
|
|
SenderSocket string
|
|
// Level-1 Postfix anvil backstop (env RATE_LIMIT_*), shown in the panel
|
|
// and used to cap domain/app level-2 ceilings (guide § Rate limiting).
|
|
RateLimitMessagesPerIP int
|
|
RateLimitWindowSeconds int
|
|
// RetryPolicy is this Postfix's deferred-mail timings, snapshotted once
|
|
// when the HTTP role starts (architecture.md). The Mail queue card and
|
|
// delivery history read it from here; they never call postconf.
|
|
RetryPolicy postfix.RetryPolicy
|
|
// InboundEnabled mirrors INBOUND_RELAY_ENABLE: the inbound panel and
|
|
// routes exist only when this is true.
|
|
InboundEnabled bool
|
|
// InboundAntispamMilter mirrors INBOUND_ANTISPAM_MILTER and
|
|
// InboundAntispamAction INBOUND_ANTISPAM_MILTER_ACTION ("accept",
|
|
// "tempfail", or "" for a value build/postfix-config.sh would refuse). The
|
|
// filter is on only when the inbound relay is on and the milter is set.
|
|
InboundAntispamMilter string
|
|
InboundAntispamAction string
|
|
// DMARCEnabled mirrors DMARC_REPORTS_ENABLE.
|
|
DMARCEnabled bool
|
|
// SendLogRetentionEnvDefault is SEND_LOG_RETENTION_DAYS at panel start; used
|
|
// as bootstrap and fallback when the settings row is missing or invalid.
|
|
SendLogRetentionEnvDefault int
|
|
// InboundSpamLogRetentionEnvDefault is INBOUND_SPAM_LOG_RETENTION_DAYS at
|
|
// panel start, the same bootstrap and fallback for the inbound journal.
|
|
InboundSpamLogRetentionEnvDefault int
|
|
}
|
|
|
|
// Handlers holds dependencies for authenticated panel routes.
|
|
type Handlers struct {
|
|
store *store.Store
|
|
domains *domain.Service
|
|
apps *app.Service
|
|
inbound *inbound.Service
|
|
lists *inbound.Lists
|
|
dmarc *dmarc.Service
|
|
cfg Config
|
|
view *view.Engine
|
|
dns *dnscheck.Checker
|
|
machine *health.MachineSampler
|
|
auth *auth.Module
|
|
}
|
|
|
|
// New builds authenticated panel handlers.
|
|
func New(
|
|
st *store.Store,
|
|
domains *domain.Service,
|
|
apps *app.Service,
|
|
inboundSvc *inbound.Service,
|
|
lists *inbound.Lists,
|
|
dmarcSvc *dmarc.Service,
|
|
cfg Config,
|
|
v *view.Engine,
|
|
dns *dnscheck.Checker,
|
|
machine *health.MachineSampler,
|
|
a *auth.Module,
|
|
) *Handlers {
|
|
return &Handlers{
|
|
store: st,
|
|
domains: domains,
|
|
apps: apps,
|
|
inbound: inboundSvc,
|
|
lists: lists,
|
|
dmarc: dmarcSvc,
|
|
cfg: cfg,
|
|
view: v,
|
|
dns: dns,
|
|
machine: machine,
|
|
auth: a,
|
|
}
|
|
}
|
|
|
|
func logf(format string, args ...any) {
|
|
log.Printf(format, args...)
|
|
}
|