Files
selfpost/internal/web/handlers/handlers.go
T
mixandClaude Opus 5.5 a8fd96c752 health-setup-checks: the certificate's name, Postfix's greeting, the proxy
Decided by the owner on 2026-10-13 (in place of preflight), taken up on
2026-10-16; mockups b2dcefc, cc33890, 0feaa55 (his: the greeting on a row
of its own, the name check a tag beside Names).

- The certificate: health.Certificate keeps its names (DNS names, or the
  CN without any) and ForHost holds it to SELFPOST_HOSTNAME as a TLS
  client does - VerifyHostname over the DNS names, the CN only without
  any, as OpenSSL's X509_check_host and so a sending Postfix still take it
  (src/tls/tls_client.c:439, 513). A certificate for another name fails;
  the tag beside Names says matches / not for the hostname.
- The greeting: postconf -x -h smtpd_banner, read once at the panel's
  start like the served certificate's path (main.cf is not rewritten
  after), is what smtpd writes after "220 " (src/smtpd/smtpd.c:5979). A
  first word other than the hostname warns, with what to set. A fact may
  now take the whole row (Fact.Wide, sp-wide) and carry a tag beside its
  label (Fact.LabelTag); both on the kit page.
- The proxy: auth.Module.Seen says the address the panel counts sign-ins
  against, the peer, and whether X-Forwarded-For was read or left unread
  for an untrusted peer; the Configuration card shows "Your address" and,
  in the second case, warns that every visitor shares one sign-in limit
  and names the /32 (or /128) to put in TRUSTED_PROXY_CIDR.

Tests: TestCertificateForHost (CN-only, DNS names, a wildcard, the CN
ignored beside DNS names), TestSeenAddress, TestHealthSaysWhereItSeesTheViewer,
TestHealthGreetingAndCertificateName; e2e reads the stand's right state on
Health (matches, "220 mail.e2e.test ESMTP", a direct client). go vet + go
test (26 packages) and make e2e on the test server for this tree.

Docs: help (word for word with the mockup), guide § Overview and Health,
CHANGELOG [Unreleased] § Added, roadmap row done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 23:51:42 +03:00

111 lines
3.3 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
SMTPBanner string
SignSocket string
SignLocalSocket 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...)
}