package routes import ( "context" "errors" "fmt" "foobar/views" "net/http" "github.com/a-h/templ" ) type userError struct { error user string } type redirectURL string type postRouteRedirectFunc func(http.ResponseWriter, *http.Request) (redirectURL, error) type routeGetFunc func(http.ResponseWriter, *http.Request) (templ.Component, error) var ( errUser = errors.New("user error") ) func newUserError(msg string) *userError { return &userError{ user: msg, } } func (u *userError) Error() string { return u.user } func (u redirectURL) Valid() bool { return len(u) > 0 && u[0] == '/' } func getRouteMiddleware(fun routeGetFunc) http.HandlerFunc { // TODO: handle boosted HTMX requests gracefully return func(w http.ResponseWriter, r *http.Request) { component, err := fun(w, r) if err != nil { logInternalError(w, err) return } err = component.Render(context.Background(), w) if err != nil { logInternalError(w, err) return } } } func redirectHtmxFormMiddleware(fun postRouteRedirectFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { hxRequest := r.Header.Get("HX-Request") == "true" redirectURL, err := fun(w, r) if err != nil { w.Header().Add("HX-Retarget", "#error-target") w.Header().Add("HX-Reswap", "innerHTML") w.WriteHeader(http.StatusUnprocessableEntity) if u, ok := err.(*userError); ok { if hxRequest { err = views.ErrorBox(u.user).Render(context.Background(), w) if err != nil { logError(fmt.Errorf("%w: %s", err, r.URL.Path)) } } else { _, _ = w.Write([]byte(u.user)) } return } else { msg := "Internal error... See the server logs for more information" _ = views.ErrorBox(msg).Render(context.Background(), w) logError(err) return } } if !redirectURL.Valid() { panic("not implemented") } if hxRequest { w.Header().Add("HX-Redirect", string(redirectURL)) } else { http.Redirect(w, r, string(redirectURL), http.StatusFound) } } }