The owner, 2026-10-16: "Да, бери notification-mail-recheck". The records of the server's own name can be checked again where they are shown: Re- check DNS in Settings' page head (mockup98e4dfc) posts to POST /server/settings/dns-recheck (guard52a9811), global role, which runs Health's lookups - the server's address, then serverMail(true) - and returns to /server/settings?rechecked=1, "DNS re-checked.". The Notification mail box's end slot says when the records were checked ("The server's own name · checked 3 min ago", from the check's CheckedAt; the old wording while nothing was looked up). A record that has turned right comes back folded. Tests: the page head's form and the end slot, and the Settings form holding neither the re-check nor the right column (TestSettingsRechecksTheMailRecords); the re-check skips the cache, redirects and the page shows both records folded (TestSettingsRecheckAsksForNotificationMailAgain); a domain administrator gets 404 (globalOnlyRoutes). go vet + go test (26 packages) and make e2e on the test server for this tree. Docs: architecture.md routes, the panel-redesign plan's route table, guide § Settings, help (help.go word for word with the mockup's); CHANGELOG [Unreleased]; roadmap row done. The roadmap's settings-save-row note no longer claims the left-column form was needed for this - the action went to the page head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,7 @@ func NewSettings(m Meta, retention, level1 string) *Settings {
|
||||
Title: "Settings",
|
||||
Lead: Rich("What holds for the whole instance, whoever is signed in. Your own name, e-mail and password are under ",
|
||||
Link("/account", "Account"), "."),
|
||||
Actions: []Action{{Label: "Re-check DNS", Icon: "ti-refresh", Post: settingsDNSRecheck}},
|
||||
},
|
||||
Retention: Box{No: "01", Title: "Log retention", Help: topicLink(HelpSettings, "How long rows are kept")},
|
||||
RetentionDays: retention,
|
||||
@@ -117,10 +118,18 @@ func (s *Settings) WithNotifications(on bool, from string) *Settings {
|
||||
return s
|
||||
}
|
||||
|
||||
// settingsDNSRecheck is where Re-check DNS on Settings posts.
|
||||
const settingsDNSRecheck = "/server/settings/dns-recheck"
|
||||
|
||||
// WithMail sets the records of the server's own name, in the order they are
|
||||
// shown (ServerMailDKIM, ServerMailSPF).
|
||||
func (s *Settings) WithMail(records []Record) *Settings {
|
||||
// shown (ServerMailDKIM, ServerMailSPF), and how long ago they were checked
|
||||
// (FormatAge; "" when they were not).
|
||||
func (s *Settings) WithMail(records []Record, checked string) *Settings {
|
||||
s.MailRecords = records
|
||||
s.Mail.End = Plain("DNS records of the server's own name")
|
||||
if checked != "" {
|
||||
s.Mail.End = Plain("The server's own name · checked " + checked)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ func settingsFixture() *Settings {
|
||||
WithMail([]Record{
|
||||
ServerMailDKIM("sp2026", mailFixtureDKIMHost, key, DNSCheck{Status: "ok"}),
|
||||
ServerMailSPF("mail.example.org", "v=spf1 a -all", DNSCheck{Status: "error", Detail: "No SPF record is published for mail.example.org."}),
|
||||
})
|
||||
}, "3 min ago")
|
||||
}
|
||||
|
||||
// outDomainsFixture is the mockup's Outbound domains: three domains, the first
|
||||
|
||||
@@ -256,7 +256,7 @@ var serverTopics = []HelpTopic{
|
||||
Br(), Br(),
|
||||
"Each user turns notifications on or off under Account and needs an e-mail there; Server › Settings turns them off for everyone. They are the panel's own mail: not rows of the Outbound log, and not counted against any limit.",
|
||||
Br(), Br(),
|
||||
"For that mail to arrive, the server's own name needs two DNS records of its own, as each of your domains does: a DKIM key, which the panel generates and signs notifications with, and an SPF record, ", Code("v=spf1 a -all"), ". Server › Settings gives both with the values to publish, and Server › Health shows whether they are in place. Without them notifications are still sent, and a receiver may file them as spam. Mail of your domains does not depend on either."),
|
||||
"For that mail to arrive, the server's own name needs two DNS records of its own, as each of your domains does: a DKIM key, which the panel generates and signs notifications with, and an SPF record, ", Code("v=spf1 a -all"), ". Server › Settings gives both with the values to publish and whether they are in place, and its Re-check DNS checks them again once they are published; Server › Health shows them too. Without them notifications are still sent, and a receiver may file them as spam. Mail of your domains does not depend on either."),
|
||||
}},
|
||||
}
|
||||
|
||||
|
||||
@@ -368,11 +368,32 @@ func TestSettingsNotificationsSwitchAndRecords(t *testing.T) {
|
||||
s.WithMail([]Record{
|
||||
ServerMailDKIM("sp2026", mailFixtureDKIMHost, "v=DKIM1; k=rsa; p=AAAA", DNSCheck{Status: "ok"}),
|
||||
ServerMailSPF("mail.example.org", "v=spf1 a -all", DNSCheck{Status: "ok"}),
|
||||
})
|
||||
}, "")
|
||||
out = renderSignedIn(t, "settings", s)
|
||||
pageHas(t, "Settings", out, `<input type="checkbox" name="notify"> Send notification e-mails`,
|
||||
`<h3>SPF</h3><span class="tag is-success is-light">published</span>`)
|
||||
pageLacks(t, "Settings", out, `name="notify" checked`, `a receiver may file them as spam`)
|
||||
`<h3>SPF</h3><span class="tag is-success is-light">published</span>`,
|
||||
`<div class="sp-end">DNS records of the server's own name</div>`)
|
||||
pageLacks(t, "Settings", out, `name="notify" checked`, `a receiver may file them as spam`, `· checked`)
|
||||
}
|
||||
|
||||
// The records of the server's own name can be checked again from Settings,
|
||||
// where they are shown: Re-check DNS in the page head, outside the form, and
|
||||
// the box says when they were checked.
|
||||
func TestSettingsRechecksTheMailRecords(t *testing.T) {
|
||||
out := renderSignedIn(t, "settings", settingsFixture())
|
||||
pageHas(t, "Settings", out,
|
||||
`<div class="buttons"><form method="post" action="/server/settings/dns-recheck"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form></div>`,
|
||||
`<div class="sp-end">The server's own name · checked 3 min ago</div>`)
|
||||
form := out[strings.Index(out, `<form class="sp-form" method="post" action="/server/settings">`):]
|
||||
form = form[:strings.Index(form, "</form>")]
|
||||
for _, outside := range []string{"dns-recheck", `id="notification-mail"`, `id="rate-limits"`} {
|
||||
if strings.Contains(form, outside) {
|
||||
t.Errorf("the Settings form holds %s; only the fields and Save settings belong in it", outside)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(form, `name="notify"`) || !strings.Contains(form, `Save settings`) {
|
||||
t.Errorf("the Settings form lost its switch or its button:\n%s", form)
|
||||
}
|
||||
}
|
||||
|
||||
// A record of the server's own name that is wrong or missing is a warning, never
|
||||
|
||||
Reference in New Issue
Block a user