package html import ( _ "embed" "net/http" "os" "path" "text/template" // do not use html/template here, we sanitize the message before passing it to the template "codeberg.org/codeberg/pages/server/context" "github.com/microcosm-cc/bluemonday" "github.com/rs/zerolog/log" ) //go:embed templates/error.html var errorPage string var ( errorTemplate = template.Must(template.New("error").Parse(loadCustomTemplateOrDefault())) sanitizer = createBlueMondayPolicy() ) type TemplateContext struct { StatusCode int StatusText string Message string } // ReturnErrorPage sets the response status code and writes the error page to the response body. // The error page contains a sanitized version of the message and the statusCode both in text and numeric form. // // Currently, only the following html tags are supported: func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) { ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8") ctx.RespWriter.WriteHeader(statusCode) templateContext := TemplateContext{ StatusCode: statusCode, StatusText: http.StatusText(statusCode), Message: sanitizer.Sanitize(msg), } err := errorTemplate.Execute(ctx.RespWriter, templateContext) if err != nil { log.Err(err).Str("message", msg).Int("status", statusCode).Msg("could not write response") } } func createBlueMondayPolicy() *bluemonday.Policy { p := bluemonday.NewPolicy() p.AllowElements("code") return p } func loadCustomTemplateOrDefault() string { contents, err := os.ReadFile("custom/error.html") if err != nil { if !os.IsNotExist(err) { wd, wdErr := os.Getwd() if wdErr != nil { log.Err(err).Msg("could not load custom error page 'custom/error.html'") } else { log.Err(err).Msgf("could not load custom error page '%v'", path.Join(wd, "custom/error.html")) } } return errorPage } return string(contents) }