// 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/notify" "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 TimeZone 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 // testMail sends the panel's test message (notify.Mailer; tests // substitute one that records). testMail testMailer } // testMailer is what sends the test message: notify.Mailer.SendWithID. type testMailer interface { SendWithID(to, subject, body, id string) error } // 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, // The test message goes as notifications go (test-email). testMail: notify.NewMailer(cfg.Hostname), } } func logf(format string, args ...any) { log.Printf(format, args...) }