All checks were successful
check / check (push) Successful in 4m16s
Expanding a delivery on the event log page now shows each recorded attempt: attempt number, outcome, status code, duration, error and response body. Previously a failure rendered as "target: failed" and diagnosing it meant opening the per-webhook SQLite file by hand. The response body is cut by SQLite via substr over a blob cast, the same projection the event body uses, so an oversized stored response never becomes a Go string. The page reports the cut with a marker. Response bodies and errors are remote content, so both go through a new delivery.Redactor that strips the target's own destination URL, path, query and userinfo, plus the values of credential-shaped request headers, before rendering. A cut body goes through RedactCut as well, which drops any tail that is a proper prefix of a secret: the remote chooses the padding in front of a credential it echoes, so it chooses where the cut falls inside that credential. Target configuration keeps reaching the template only as a TargetView. Redactors are built from an unscoped target load. Deleting a target only soft deletes the row while its deliveries survive, and a scoped load would leave exactly those deliveries rendering unredacted. The views the page lists stay scoped. Attempt loading is chunked so the IN clause cannot exceed SQLite's bound-parameter limit, its error is reported rather than discarded, and the page renders at most 20 attempts per delivery, counting what it leaves out. static/css/tailwind.css is regenerated with the repo's pinned tailwindcss for the utility classes the new markup uses.
103 lines
3.0 KiB
Go
103 lines
3.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"html/template"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
)
|
|
|
|
// SetLogForTest replaces the handler's logger, so the handlers_test
|
|
// package can assert on what a log line actually contains rather than
|
|
// on what it is meant to contain.
|
|
func (s *Handlers) SetLogForTest(log *slog.Logger) {
|
|
s.log = log
|
|
}
|
|
|
|
// MaxRenderedBodyBytesForTest exposes the event log's body cap
|
|
// to the handlers_test package.
|
|
const MaxRenderedBodyBytesForTest = maxRenderedBodyBytes
|
|
|
|
// MaxRenderedResponseBytesForTest exposes the event log's
|
|
// delivery response cap to the handlers_test package.
|
|
const MaxRenderedResponseBytesForTest = maxRenderedResponseBytes
|
|
|
|
// MaxRenderedAttemptsForTest exposes the event log's
|
|
// per-delivery attempt ceiling to the handlers_test package.
|
|
const MaxRenderedAttemptsForTest = maxRenderedAttempts
|
|
|
|
// DummyVerificationsForTest reports how many equivalent-cost
|
|
// verifications were charged for usernames that do not exist. It
|
|
// lets a test prove the anti-enumeration path ran without timing
|
|
// anything.
|
|
func (s *Handlers) DummyVerificationsForTest() uint64 {
|
|
return s.dummyVerifications.Load()
|
|
}
|
|
|
|
// TrimPartialRuneForTest exposes trimPartialRune for use in the
|
|
// handlers_test package.
|
|
func TrimPartialRuneForTest(b []byte) []byte {
|
|
return trimPartialRune(b)
|
|
}
|
|
|
|
// LoadEventLogViewsForTest exposes loadEventsWithDeliveries for
|
|
// use in the handlers_test package. Assertions on the projected
|
|
// body need the bytes as loaded: html/template rewrites invalid
|
|
// UTF-8 on the way out, so the rendered page cannot show whether
|
|
// a binary body survived the projection intact.
|
|
func (s *Handlers) LoadEventLogViewsForTest(
|
|
w http.ResponseWriter,
|
|
webhook database.Webhook,
|
|
page int,
|
|
) []EventLogView {
|
|
views, _ := s.loadEventsWithDeliveries(w, webhook, nil, page)
|
|
|
|
return views
|
|
}
|
|
|
|
// AddTemplateForTest registers a template under a page name so that
|
|
// the handlers_test package can drive the render path with a
|
|
// template of its own.
|
|
func (s *Handlers) AddTemplateForTest(
|
|
pageTemplate string,
|
|
tmpl *template.Template,
|
|
) {
|
|
s.templates[pageTemplate] = tmpl
|
|
}
|
|
|
|
// RenderTemplateForTest exposes renderTemplate for use in the
|
|
// handlers_test package.
|
|
func (s *Handlers) RenderTemplateForTest(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
pageTemplate string,
|
|
data any,
|
|
) {
|
|
s.renderTemplate(w, r, pageTemplate, data)
|
|
}
|
|
|
|
// BuildSlackTargetConfigForTest exposes buildURLTargetConfig
|
|
// with the Slack target parameters for use in the
|
|
// handlers_test package.
|
|
func (s *Handlers) BuildSlackTargetConfigForTest(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
targetURL string,
|
|
) (string, error) {
|
|
return s.buildURLTargetConfig(
|
|
w, r, targetURL, "webhookUrl",
|
|
"Webhook URL is required for Slack targets",
|
|
)
|
|
}
|
|
|
|
// BuildDatabaseTargetConfigForTest exposes
|
|
// buildDatabaseTargetConfig for use in the handlers_test
|
|
// package.
|
|
func (s *Handlers) BuildDatabaseTargetConfigForTest(
|
|
w http.ResponseWriter,
|
|
expiry string,
|
|
) (string, error) {
|
|
return s.buildDatabaseTargetConfig(w, expiry)
|
|
}
|