audit 4: log paging at its edges, NaN refused, a red rate limit, recalculation asks, reload writes all
test / test (push) Canceled after 0s

Step 4 of stage 4 (L10, L11, L13, L18, L19 and a row of
review-2026-08-followups):

- The Outbound log counts first and clamps the page to the last; a number
  too large for an int (strconv's ErrRange with the largest int) is the
  last page on both logs. Tested at the edges: first, last, past the last
  however far, nonsense, an empty log asked for page 5.
- checkAutoMultiplier is "not within", so NaN and the infinities are out.
- rate-limited is red (components.go; the mockup first, 643892a).
- data-unsaved on both recalculate buttons: panel.js asks on submit
  (ev.submitter) when a field of the page differs from what it was drawn
  with; testdata/unsaved_test.js under TestPanelJS.
- HandleReload writes what a restore's first start writes - the filter
  lists and the DMARC maps too - and a failed step is an error flash on
  Health that names it, not plain text; the success flash says what was
  written.

Tested on the test server: go vet + go test of 25 packages, make e2e (all
pass, 695 s) on this tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-15 11:20:18 +03:00
co-authored by Claude Opus 5.5
parent 643892ab91
commit ca876c1631
23 changed files with 355 additions and 48 deletions
+16 -7
View File
@@ -380,21 +380,22 @@ func (h *Handlers) outLogPage(r *http.Request) (*view.OutLog, error) {
}
}
page := parsePage(q.Get("p"))
total, err := h.store.CountSendLog(filter)
if err != nil {
return nil, err
}
rows, err := h.store.QuerySendLog(filter, sendLogPageSize, (page-1)*sendLogPageSize)
if err != nil {
return nil, err
}
lastPage := 1
if total > 0 {
lastPage = int((total + sendLogPageSize - 1) / sendLogPageSize)
}
// A page past the end is the last one, as on the Inbound log: a link
// kept while the log was pruned, or a number typed, shows rows, not an
// empty table under "Page 9 of 2" (audit 2026-10-12, L10).
page := min(parsePage(q.Get("p")), lastPage)
rows, err := h.store.QuerySendLog(filter, sendLogPageSize, (page-1)*sendLogPageSize)
if err != nil {
return nil, err
}
out := view.NewOutLog(h.shellMeta(r), h.sendLogRetentionDays(), p.IsGlobal(), domainNames, logins, filter.Domain, filter.AppLogin)
table := make([]view.OutLogRow, len(rows))
for i, row := range rows {
@@ -409,8 +410,16 @@ func (h *Handlers) outLogPage(r *http.Request) (*view.OutLog, error) {
// parsePage clamps the "p" query parameter to a valid page number, defaulting
// to 1 for anything missing or malformed rather than rejecting the request.
//
// A number too large for an int is past every last page, and is taken as
// that: strconv returns it as the largest int with ErrRange (Go 1.26,
// strconv.ParseInt: "the maximum magnitude integer of the appropriate
// bitSize and sign"), and the caller clamps it to the last page.
func parsePage(v string) int {
n, err := strconv.Atoi(v)
if errors.Is(err, strconv.ErrRange) && n > 0 {
return n
}
if err != nil || n < 1 {
return 1
}