mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2025-04-25 06:16:58 +00:00
add go templating engine for error page and make errors more clear
This commit is contained in:
parent
7f0a4e5ca9
commit
9346dbfba9
15 changed files with 228 additions and 201 deletions
|
@ -1,37 +0,0 @@
|
|||
<!doctype html>
|
||||
<html class="codeberg-design">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>%status%</title>
|
||||
|
||||
<link rel="stylesheet" href="https://design.codeberg.org/design-kit/codeberg.css" />
|
||||
<link href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css" rel="stylesheet" />
|
||||
<link href="https://fonts.codeberg.org/dist/fontawesome5/css/all.min.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0; padding: 1rem; box-sizing: border-box;
|
||||
width: 100%; min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<i class="fa fa-search text-primary" style="font-size: 96px;"></i>
|
||||
<h1 class="mb-0 text-primary">
|
||||
Page not found!
|
||||
</h1>
|
||||
<h5 class="text-center" style="max-width: 25em;">
|
||||
Sorry, but this page couldn't be found or is inaccessible (%status%).<br/>
|
||||
We hope this isn't a problem on our end ;) - Make sure to check the <a href="https://docs.codeberg.org/codeberg-pages/troubleshooting/" target="_blank">troubleshooting section in the Docs</a>!
|
||||
</h5>
|
||||
<small class="text-muted">
|
||||
<img src="https://design.codeberg.org/logo-kit/icon.svg" class="align-top">
|
||||
Static pages made easy - <a href="https://codeberg.page">Codeberg Pages</a>
|
||||
</small>
|
||||
</body>
|
||||
</html>
|
|
@ -1,50 +0,0 @@
|
|||
package html
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"codeberg.org/codeberg/pages/server/context"
|
||||
)
|
||||
|
||||
// ReturnErrorPage sets the response status code and writes NotFoundPage to the response body,
|
||||
// with "%status%" and %message% replaced with the provided statusCode and msg
|
||||
func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) {
|
||||
ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
ctx.RespWriter.WriteHeader(statusCode)
|
||||
|
||||
msg = generateResponse(msg, statusCode)
|
||||
|
||||
_, _ = ctx.RespWriter.Write([]byte(msg))
|
||||
}
|
||||
|
||||
// TODO: use template engine
|
||||
func generateResponse(msg string, statusCode int) string {
|
||||
if msg == "" {
|
||||
msg = strings.ReplaceAll(NotFoundPage,
|
||||
"%status%",
|
||||
strconv.Itoa(statusCode)+" "+errorMessage(statusCode))
|
||||
} else {
|
||||
msg = strings.ReplaceAll(
|
||||
strings.ReplaceAll(ErrorPage, "%message%", template.HTMLEscapeString(msg)),
|
||||
"%status%",
|
||||
http.StatusText(statusCode))
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func errorMessage(statusCode int) string {
|
||||
message := http.StatusText(statusCode)
|
||||
|
||||
switch statusCode {
|
||||
case http.StatusMisdirectedRequest:
|
||||
message += " - domain not specified in <code>.domains</code> file"
|
||||
case http.StatusFailedDependency:
|
||||
message += " - target repo/branch doesn't exist or is private"
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<!doctype html>
|
||||
<html class="codeberg-design">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>%status%</title>
|
||||
|
||||
<link rel="stylesheet" href="https://design.codeberg.org/design-kit/codeberg.css" />
|
||||
<link href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css" rel="stylesheet" />
|
||||
<link href="https://fonts.codeberg.org/dist/fontawesome5/css/all.min.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0; padding: 1rem; box-sizing: border-box;
|
||||
width: 100%; min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<i class="fa fa-search text-primary" style="font-size: 96px;"></i>
|
||||
<h1 class="mb-0 text-primary">
|
||||
%status%!
|
||||
</h1>
|
||||
<h5 class="text-center" style="max-width: 25em;">
|
||||
Sorry, but this page couldn't be served.<br/>
|
||||
We got an <b>"%message%"</b><br/>
|
||||
We hope this isn't a problem on our end ;) - Make sure to check the <a href="https://docs.codeberg.org/codeberg-pages/troubleshooting/" target="_blank">troubleshooting section in the Docs</a>!
|
||||
</h5>
|
||||
<small class="text-muted">
|
||||
<img src="https://design.codeberg.org/logo-kit/icon.svg" class="align-top">
|
||||
Static pages made easy - <a href="https://codeberg.page">Codeberg Pages</a>
|
||||
</small>
|
||||
</body>
|
||||
</html>
|
|
@ -1,38 +0,0 @@
|
|||
package html
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidMessage(t *testing.T) {
|
||||
testString := "requested blacklisted path"
|
||||
statusCode := http.StatusForbidden
|
||||
|
||||
expected := strings.ReplaceAll(
|
||||
strings.ReplaceAll(ErrorPage, "%message%", testString),
|
||||
"%status%",
|
||||
http.StatusText(statusCode))
|
||||
actual := generateResponse(testString, statusCode)
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("generated response did not match: expected: '%s', got: '%s'", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageWithHtml(t *testing.T) {
|
||||
testString := `abc<img src=1 onerror=alert("xss");`
|
||||
escapedString := "abc<img src=1 onerror=alert("xss");"
|
||||
statusCode := http.StatusNotFound
|
||||
|
||||
expected := strings.ReplaceAll(
|
||||
strings.ReplaceAll(ErrorPage, "%message%", escapedString),
|
||||
"%status%",
|
||||
http.StatusText(statusCode))
|
||||
actual := generateResponse(testString, statusCode)
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("generated response did not match: expected: '%s', got: '%s'", expected, actual)
|
||||
}
|
||||
}
|
52
html/html.go
52
html/html.go
|
@ -1,9 +1,51 @@
|
|||
package html
|
||||
|
||||
import _ "embed"
|
||||
import (
|
||||
_ "embed"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
//go:embed 404.html
|
||||
var NotFoundPage string
|
||||
"codeberg.org/codeberg/pages/server/context"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
//go:embed error.html
|
||||
var ErrorPage string
|
||||
//go:embed templates/error.html
|
||||
var errorPage string
|
||||
|
||||
var errorTemplate = template.Must(template.New("error").Parse(errorPage))
|
||||
var 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
|
||||
}
|
||||
|
|
54
html/html_test.go
Normal file
54
html/html_test.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package html
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSanitizerSimpleString(t *testing.T) {
|
||||
str := "simple text message without any html elements"
|
||||
|
||||
assert.Equal(t, str, sanitizer.Sanitize(str))
|
||||
}
|
||||
|
||||
func TestSanitizerStringWithCodeTag(t *testing.T) {
|
||||
str := "simple text message with <code>html</code> tag"
|
||||
|
||||
assert.Equal(t, str, sanitizer.Sanitize(str))
|
||||
}
|
||||
|
||||
func TestSanitizerStringWithCodeTagWithAttribute(t *testing.T) {
|
||||
str := "simple text message with <code id=\"code\">html</code> tag"
|
||||
expected := "simple text message with <code>html</code> tag"
|
||||
|
||||
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||
}
|
||||
|
||||
func TestSanitizerStringWithATag(t *testing.T) {
|
||||
str := "simple text message with <a>a link to another page</a>"
|
||||
expected := "simple text message with a link to another page"
|
||||
|
||||
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||
}
|
||||
|
||||
func TestSanitizerStringWithATagAndHref(t *testing.T) {
|
||||
str := "simple text message with <a href=\"http://evil.site\">a link to another page</a>"
|
||||
expected := "simple text message with a link to another page"
|
||||
|
||||
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||
}
|
||||
|
||||
func TestSanitizerStringWithImgTag(t *testing.T) {
|
||||
str := "simple text message with a <img alt=\"not found\" src=\"http://evil.site\">"
|
||||
expected := "simple text message with a "
|
||||
|
||||
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||
}
|
||||
|
||||
func TestSanitizerStringWithImgTagAndOnerrorAttribute(t *testing.T) {
|
||||
str := "simple text message with a <img alt=\"not found\" src=\"http://evil.site\" onerror=\"alert(secret)\">"
|
||||
expected := "simple text message with a "
|
||||
|
||||
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||
}
|
69
html/templates/error.html
Normal file
69
html/templates/error.html
Normal file
|
@ -0,0 +1,69 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="codeberg-design">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{.StatusText}}</title>
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://design.codeberg.org/design-kit/codeberg.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css"
|
||||
/>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
code {
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
background-color: silver;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="10em"
|
||||
viewBox="0 0 24 24"
|
||||
fill="var(--blue-color)"
|
||||
>
|
||||
<path
|
||||
d="M 9 2 C 5.1458514 2 2 5.1458514 2 9 C 2 12.854149 5.1458514 16 9 16 C 10.747998 16 12.345009 15.348024 13.574219 14.28125 L 14 14.707031 L 14 16 L 19.585938 21.585938 C 20.137937 22.137937 21.033938 22.137938 21.585938 21.585938 C 22.137938 21.033938 22.137938 20.137938 21.585938 19.585938 L 16 14 L 14.707031 14 L 14.28125 13.574219 C 15.348024 12.345009 16 10.747998 16 9 C 16 5.1458514 12.854149 2 9 2 z M 9 4 C 11.773268 4 14 6.2267316 14 9 C 14 11.773268 11.773268 14 9 14 C 6.2267316 14 4 11.773268 4 9 C 4 6.2267316 6.2267316 4 9 4 z"
|
||||
/>
|
||||
</svg>
|
||||
<h1 class="mb-0 text-primary">{{.StatusText}} ({{.StatusCode}})!</h1>
|
||||
<h5 class="text-center" style="max-width: 25em">
|
||||
<p>Sorry, but this page couldn't be served.</p>
|
||||
<p><b>"{{.Message}}"</b></p>
|
||||
<p>
|
||||
We hope this isn't a problem on our end ;) - Make sure to check the
|
||||
<a
|
||||
href="https://docs.codeberg.org/codeberg-pages/troubleshooting/"
|
||||
target="_blank"
|
||||
>troubleshooting section in the Docs</a
|
||||
>!
|
||||
</p>
|
||||
</h5>
|
||||
<small class="text-muted">
|
||||
<img
|
||||
src="https://design.codeberg.org/logo-kit/icon.svg"
|
||||
class="align-top"
|
||||
/>
|
||||
Static pages made easy -
|
||||
<a href="https://codeberg.page">Codeberg Pages</a>
|
||||
</small>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue