feat(surf): branded HTML error pages for browser navigations

A browser navigation (Sec-Fetch-Mode navigate, or GET/HEAD without it; no
X-Requested-With; Accept naming text/html above every JSON type; not /api,
/_<name> or the admin prefix) that hits a framework-generated 404, 405 or
500 gets a self-contained SummerCMS page: unmatched routes and wrong
methods at the ServeMux, failed Where/WhereIn constraints, overlap-family
misses, the unpublished uploads route and recovered panics. Every other
request keeps the exact stdlib text, wire's opaque JSON 500 or the bare
raw-group 500.

Details (method, path, X-Request-Id) only with app.debug or outside
production. Applications replace the page with a published
surf.ErrorPageRenderer; http.error_pages.enabled false turns it off.
This commit is contained in:
2026-10-20 03:12:25 +02:00
parent 7c2b7fcb79
commit b1236cbfd4
14 changed files with 813 additions and 28 deletions

View File

@@ -78,14 +78,16 @@ func (c Constraint) match(value string) bool {
return false
}
func constrain(next http.Handler, cs []Constraint) http.Handler {
// constrain answers 404 (the error page for a browser navigation, pages
// permitting) when a Where/WhereIn constraint fails.
func constrain(pages *errorPages, next http.Handler, cs []Constraint) http.Handler {
if len(cs) == 0 {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for _, c := range cs {
if !c.match(r.PathValue(c.param)) {
http.NotFound(w, r)
pages.notFound(w, r)
return
}
}