The owner, 2026-10-17: "Показ времени отчёта по UTC принимаем. Это не проблема. Пояснения добавь. Все мелочи закрой." Designs9b33b80,2bf9056. DMARC: a report's day stays the reporter's, in UTC (RFC 7489 § 7.2); the report's Window now says so under its times (windowNote), and Settings' Time zone box and Help (#settings, #dmarc) name it as the one exception to the panel's zone. formatDMARCWindow counts a window that ends at midnight as ending the day before, so a daily report is one day, not "20 Sep - 21 Sep" (TestDMARCWindowIsTheReportersDay, in Moscow's zone). Health: with the inbound relay on, the inbound journal milter's socket is dialled (InboundJournalSocket through web and handlers Config), fail-open as the send log's, and Overview's card counts it; the send log's socket is named journal-milter, as the mockup names it. The confirmation link's sign-in state is drawn (confirm-email-signin.html); the panel already renders it. guide: /license and /server/components, the DMARC pages' paths and day, selfpost-backup -o / -i / -version, the Inbound log's retention field, PANEL_COOKIE_SECURE, the inbound journal's socket, the DMARC exception to the zone. CHANGELOG [Unreleased]. (The e2e case negative_level1_ratelimit, listed as able to pass falsely, was fixed on 2026-10-14 - the note was stale.) go vet + go test (26 packages) and make e2e on the test server for this tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
3.8 KiB
Go
125 lines
3.8 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/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
|
||
// InboundJournalSocket is the inbound journal milter's (Inbound › Log).
|
||
InboundJournalSocket 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...)
|
||
}
|