pages-server/html/html.go
Gnarwhal 23a8e83e80 Allow serving custom error page (#393)
It might be useful for those self-hosting a Codeberg Pages instance to be able to serve a different error page than the one embedded with go:embed

Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/393
Co-authored-by: Gnarwhal <git.aspect893@passmail.net>
Co-committed-by: Gnarwhal <git.aspect893@passmail.net>
2024-11-17 16:28:52 +00:00

72 lines
1.9 KiB
Go

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: <code>
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)
}