Skip to content

Commit 2537440

Browse files
authored
feat: Added support for extra links and rewrote the cats template (#399)
1 parent 6516f02 commit 2537440

27 files changed

Lines changed: 799 additions & 147 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ The following templates are built-in and available for use without any additiona
8787
[app-down-light]:https://github.com/user-attachments/assets/c5f42b53-51cd-47c4-a22f-553d44d2a288
8888
[app-down-dark]:https://github.com/user-attachments/assets/135bac8c-983f-461c-97ba-e653e9b9adfe
8989
[cats-link]:https://tarampampam.github.io/error-pages/cats/404.html
90-
[cats-light]:https://github.com/tarampampam/error-pages/assets/7326800/056cd00e-bc9a-4120-8325-310d7b0ebd1b
91-
[cats-dark]:https://github.com/tarampampam/error-pages/assets/7326800/5689880b-f770-406c-81dd-2d28629e6f2e
90+
[cats-light]:https://github.com/user-attachments/assets/7bea967e-a427-4ba2-a3a3-d71b9986ecfc
91+
[cats-dark]:https://github.com/user-attachments/assets/f9b945b2-3e19-44d5-842b-0c43c55d9b70
9292
[connection-link]:https://tarampampam.github.io/error-pages/connection/404.html
9393
[connection-light]:https://github.com/tarampampam/error-pages/assets/7326800/099ecc2d-e724-4d9c-b5ed-66ddabd71139
9494
[connection-dark]:https://github.com/tarampampam/error-pages/assets/7326800/3f03dc1b-c1ee-4a91-b3d7-e3b93c79020e

cmd/builder/app/app.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type App struct {
3838
customTemplate string
3939
l10nDisabled bool
4040
homepageURL string
41+
links []tpl.Link
4142
}
4243
}
4344

@@ -60,6 +61,7 @@ func NewApp(name string) *App {
6061
templateFlag = newTemplateFlag()
6162
disableL10nFlag = shared.NewDisableL10nFlag()
6263
homepageURLFlag = shared.NewHomepageURLFlag(app.opt.homepageURL)
64+
addLinksFlag = shared.NewAddLinksFlag()
6365
)
6466

6567
app.cmd.Flags = []cli.Flagger{
@@ -70,6 +72,7 @@ func NewApp(name string) *App {
7072
&templateFlag,
7173
&disableL10nFlag,
7274
&homepageURLFlag,
75+
&addLinksFlag,
7376
}
7477

7578
app.cmd.Action = func(ctx context.Context, _ *cli.Command, _ []string) error {
@@ -89,6 +92,12 @@ func NewApp(name string) *App {
8992
setIfFlagIsSet(&app.opt.customTemplate, templateFlag)
9093
setIfFlagIsSet(&app.opt.homepageURL, homepageURLFlag)
9194

95+
if addLinksFlag.Value != nil && addLinksFlag.IsSet() {
96+
if parsed, err := shared.ParseLinks(*addLinksFlag.Value); err == nil {
97+
app.opt.links = parsed
98+
}
99+
}
100+
92101
// load custom template content if a source is provided (either URL, file path, or raw template string)
93102
if src := app.opt.customTemplate; src != "" {
94103
t, err := tploader.LoadTemplateContent(ctx, src)
@@ -194,6 +203,7 @@ func (a *App) renderCustomTemplate(httpCodes codes.Codes, history map[string][]h
194203
Message: desc.Short,
195204
Description: desc.Full,
196205
HomepageURL: a.opt.homepageURL,
206+
Links: a.opt.links,
197207
Config: tpl.Config{L10nDisabled: a.opt.l10nDisabled},
198208
})
199209
if renderErr != nil {
@@ -254,6 +264,7 @@ func (a *App) renderBuiltInTemplates(httpCodes codes.Codes, history map[string][
254264
Message: desc.Short,
255265
Description: desc.Full,
256266
HomepageURL: a.opt.homepageURL,
267+
Links: a.opt.links,
257268
Config: tpl.Config{L10nDisabled: a.opt.l10nDisabled},
258269
})
259270
if renderErr != nil {

cmd/error-pages/app/app.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type App struct {
4545
templateName string
4646
rotationMode tpl.RotationMode
4747
homepageURL string
48+
links []tpl.Link
4849
customTemplates struct {
4950
html, json, xml, text string
5051
}
@@ -88,6 +89,7 @@ func NewApp(name string) *App { //nolint:funlen
8889
templateNameFlag = newTemplateNameFlag(allTemplateNames, app.opt.errorPages.templateName)
8990
rotationModeFlag = newRotationModeFlag(app.opt.errorPages.rotationMode)
9091
homepageURLFlag = shared.NewHomepageURLFlag(app.opt.errorPages.homepageURL)
92+
addLinksFlag = shared.NewAddLinksFlag()
9193
htmlTemplateFlag = newHTMLTemplateFlag()
9294
jsonTemplateFlag = newJSONTemplateFlag()
9395
xmlTemplateFlag = newXMLTemplateFlag()
@@ -109,6 +111,7 @@ func NewApp(name string) *App { //nolint:funlen
109111
&templateNameFlag,
110112
&rotationModeFlag,
111113
&homepageURLFlag,
114+
&addLinksFlag,
112115
&htmlTemplateFlag,
113116
&jsonTemplateFlag,
114117
&xmlTemplateFlag,
@@ -155,6 +158,13 @@ func NewApp(name string) *App { //nolint:funlen
155158
}
156159

157160
setIfFlagIsSet(&app.opt.errorPages.homepageURL, homepageURLFlag)
161+
162+
if addLinksFlag.Value != nil && addLinksFlag.IsSet() {
163+
if parsed, err := shared.ParseLinks(*addLinksFlag.Value); err == nil {
164+
app.opt.errorPages.links = parsed
165+
}
166+
}
167+
158168
setIfFlagIsSet(&app.opt.errorPages.customTemplates.html, htmlTemplateFlag)
159169
setIfFlagIsSet(&app.opt.errorPages.customTemplates.json, jsonTemplateFlag)
160170
setIfFlagIsSet(&app.opt.errorPages.customTemplates.xml, xmlTemplateFlag)
@@ -303,6 +313,7 @@ func (a *App) run(ctx context.Context, log *logger.Logger) error {
303313
a.opt.errorPages.showDetails,
304314
a.opt.errorPages.l10nDisabled,
305315
a.opt.errorPages.homepageURL,
316+
a.opt.errorPages.links,
306317
),
307318
httpserver.WithErrorLog(logger.NewStdLog(log, logger.ErrorLevel)),
308319
)
@@ -320,6 +331,7 @@ func (a *App) run(ctx context.Context, log *logger.Logger) error {
320331
logger.Bool("show_details", a.opt.errorPages.showDetails),
321332
logger.Strings("proxy_headers", a.opt.errorPages.proxyHeaders...),
322333
logger.String("homepage_url", a.opt.errorPages.homepageURL),
334+
logger.Int("links_count", len(a.opt.errorPages.links)),
323335
logger.Bool("l10n_disabled", a.opt.errorPages.l10nDisabled),
324336
)
325337

deploy/helm/README.tpl.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,44 @@ Override descriptions or add non-standard codes (e.g. `499`, `4**` wildcard):
107107

108108
```yaml
109109
config:
110-
addCode: |
111-
499=Client Closed Request|The client closed the connection before the server finished responding.
112-
4**=Client Error|Something went wrong on the client side.
110+
addCode:
111+
- {code: "4**", message: "Client Error", description: "Something went wrong on the client side"}
112+
- code: "499"
113+
message: "Client Closed Request"
114+
description: "The client closed the connection before the server finished responding"
115+
```
116+
117+
Via `--set` (`--set-string` is required for numeric-looking codes like `499`):
118+
119+
```shell
120+
helm install error-pages oci://ghcr.io/tarampampam/error-pages/charts/error-pages \
121+
--set-string 'config.addCode[0].code=4**' \
122+
--set 'config.addCode[0].message=Client Error' \
123+
--set-string 'config.addCode[1].code=499' \
124+
--set 'config.addCode[1].message=Client Closed Request' \
125+
--set 'config.addCode[1].description=The client closed the connection before the server finished responding'
126+
```
127+
128+
### Adding extra links
129+
130+
Display additional links (status page, contact, privacy policy, etc.) on all error pages:
131+
132+
```yaml
133+
config:
134+
addLink:
135+
- {label: "Status Page", url: "https://status.example.com"}
136+
- {label: "Contact Support", url: "https://example.com/contact"}
137+
- {label: "Privacy Policy", url: "https://example.com/privacy"}
138+
```
139+
140+
Via `--set`:
141+
142+
```shell
143+
helm install error-pages oci://ghcr.io/tarampampam/error-pages/charts/error-pages \
144+
--set 'config.addLink[0].label=Status Page' \
145+
--set 'config.addLink[0].url=https://status.example.com' \
146+
--set 'config.addLink[1].label=Contact Support' \
147+
--set 'config.addLink[1].url=https://example.com/contact'
113148
```
114149

115150
## 💊 Support

deploy/helm/templates/deployment.yaml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,15 @@ spec:
7575
- {name: DISABLE_BUILT_IN_CODES, value: "true"}
7676
{{- end }}
7777
{{- if .addCode }}
78-
- {name: ADD_CODE, value: {{ .addCode | toJson }}}
78+
{{- $parts := list -}}
79+
{{- range .addCode -}}
80+
{{- $e := printf "%s=%s" .code .message -}}
81+
{{- if .description -}}{{- $e = printf "%s|%s" $e .description -}}{{- end -}}
82+
{{- $parts = append $parts $e -}}
83+
{{- end }}
84+
- name: ADD_CODE
85+
value: |
86+
{{ join "\n" $parts | indent 16 }}
7987
{{- end }}
8088
{{- if .htmlTemplate.name }}
8189
- {name: TEMPLATE_NAME, value: "{{ .htmlTemplate.name }}"}
@@ -101,6 +109,15 @@ spec:
101109
{{- end }}
102110
{{- if .homepageUrl }}
103111
- {name: HOMEPAGE_URL, value: "{{ .homepageUrl }}"}
112+
{{- end }}
113+
{{- if .addLink }}
114+
{{- $parts := list -}}
115+
{{- range .addLink -}}
116+
{{- $parts = append $parts (printf "%s=%s" .label .url) -}}
117+
{{- end }}
118+
- name: ADD_LINK
119+
value: |
120+
{{ join "\n" $parts | indent 16 }}
104121
{{- end }}
105122
{{- if .disableL10n }}
106123
- {name: DISABLE_L10N, value: "true"}

deploy/helm/values.schema.json

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,19 @@
277277
},
278278
"addCode": {
279279
"oneOf": [
280-
{"type": "string", "minLength": 1, "examples": ["599=Custom Error|Something went wrong"]},
280+
{
281+
"type": "array",
282+
"items": {
283+
"type": "object",
284+
"properties": {
285+
"code": {"type": "string", "minLength": 1},
286+
"message": {"type": "string", "minLength": 1},
287+
"description": {"type": "string", "minLength": 1}
288+
},
289+
"required": ["code", "message"],
290+
"additionalProperties": false
291+
}
292+
},
281293
{"type": "null"}
282294
]
283295
},
@@ -355,6 +367,23 @@
355367
{"type": "null"}
356368
]
357369
},
370+
"addLink": {
371+
"oneOf": [
372+
{
373+
"type": "array",
374+
"items": {
375+
"type": "object",
376+
"properties": {
377+
"label": {"type": "string", "minLength": 1},
378+
"url": {"type": "string", "minLength": 1}
379+
},
380+
"required": ["label", "url"],
381+
"additionalProperties": false
382+
}
383+
},
384+
{"type": "null"}
385+
]
386+
},
358387
"disableL10n": {
359388
"oneOf": [
360389
{"type": "boolean"},

deploy/helm/values.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,12 @@ config:
127127
proxyHeaders: null
128128
# -- (bool/null) Disable built-in HTTP status code descriptions
129129
disableBuiltInCodes: null
130-
# -- (string/null) Add or override HTTP status codes. Format: `CODE=MESSAGE[\|DESCRIPTION]` (`CODE` supports wildcards like `4**`).
131-
# Separate multiple entries with newlines
130+
# -- ([]object/null) Add or override HTTP status codes. Each entry must have `code` and `message` (required), and
131+
# optionally `description`. `code` supports wildcards like `4**`.
132132
# @default -- *all built-in codes*
133-
addCode: null
133+
addCode: null # Array<{code: string, message: string, description?: string}>
134+
# -- ([]object/null) Extra links to display on error pages. Each entry must have `label` and `url` (both required).
135+
addLink: null # Array<{label: string, url: string}>
134136

135137
htmlTemplate:
136138
# -- (string/null) Built-in HTML template name (**ignored when `custom` is set**).

docs/CLI.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Options:
2727
--template-name="…" Name of the built-in HTML template to use (app-down/cats/connection/ghost/hacker-terminal/l7/lost-in-space/noise/orient/shuffle/win98; ignored if a custom HTML template is set) (default: app-down) [$TEMPLATE_NAME, $HTML_TEMPLATE_NAME]
2828
--rotation-mode="…" Mode for rotating built-in HTML templates (disabled/random-on-startup/random-on-each-request/random-hourly/random-daily; ignored if a custom HTML template is set) (default: disabled) [$ROTATION_MODE]
2929
--homepage-url="…" Homepage URL to show as a link in error pages (e.g. https://app.example.com/home) (default: /) [$HOMEPAGE_URL]
30+
--add-link="…" Add extra links to error pages (format: 'LABEL=URL[||LABEL=URL...]'; separate multiple entries with '||', a newline, or a tab) [$ADD_LINK]
3031
--html-template="…" Custom HTML template for error page responses (template text/URL/file path) [$HTML_TEMPLATE, $TEMPLATE]
3132
--json-template="…" Custom JSON template for error page responses (template text/URL/file path) [$JSON_TEMPLATE]
3233
--xml-template="…" Custom XML template for error page responses (template text/URL/file path) [$XML_TEMPLATE]
@@ -109,6 +110,17 @@ ADD_CODE="418=I'm a teapot|Short and stout
109110
499=Client Closed Request|The client closed the connection"
110111
```
111112

113+
### Adding extra links
114+
115+
Add custom, labeled links (e.g. status page, contact, policy) to be displayed on every error page. Format: `LABEL=URL`.
116+
117+
```bash
118+
# multiple links separated by || or newlines
119+
error-pages --add-link "Status Page=https://status.example.com||Contact=https://example.com/contact"
120+
```
121+
122+
URLs may contain `=` signs - only the first `=` in each entry is used as the separator.
123+
112124
## Templates builder
113125

114126
<!--GENERATED:BUILDER_CLI-->
@@ -130,6 +142,7 @@ Options:
130142
--template="…" Custom template for error pages [$TEMPLATE]
131143
--disable-l10n Disable localization of error pages (if the template supports localization) [$DISABLE_L10N]
132144
--homepage-url="…" Homepage URL to show as a link in error pages (e.g. https://app.example.com/home) [$HOMEPAGE_URL]
145+
--add-link="…" Add extra links to error pages (format: 'LABEL=URL[||LABEL=URL...]'; separate multiple entries with '||', a newline, or a tab) [$ADD_LINK]
133146
--help, -h Show help
134147
--version, -v Print the version
135148
```
@@ -181,3 +194,11 @@ builder --out ./error-pages --index
181194
├── 404.html
182195
└── ...
183196
```
197+
198+
### Adding extra links
199+
200+
The `--add-link` flag works the same way as in the HTTP server - see [Adding extra links](#adding-extra-links) above.
201+
202+
```bash
203+
builder --add-link "Status Page=https://status.example.com||Contact=https://example.com/contact" --out ./error-pages
204+
```

docs/templating.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,30 @@ All templates receive a data object with the following fields:
4848
| `.RequestID` | `string` | Unique request ID * |
4949
| `.ForwardedFor` | `string` | Original client IP(s) from `X-Forwarded-For` * |
5050
| `.Host` | `string` | Request `Host` header * |
51-
| `.HomepageURL` | `string` | Homepage URL set via `--homepage-url` (empty if not configured) |
52-
| `.Config.ShowRequestDetails` | `bool` | Whether `--show-details` is enabled |
53-
| `.Config.L10nDisabled` | `bool` | Whether `--disable-l10n` is set |
51+
| `.HomepageURL` | `string` | Homepage URL set via `--homepage-url` (empty if not configured) |
52+
| `.Links` | `[]Link` | Extra links set via `--add-link` (empty slice if not configured) |
53+
| `.Config.ShowRequestDetails` | `bool` | Whether `--show-details` is enabled |
54+
| `.Config.L10nDisabled` | `bool` | Whether `--disable-l10n` is set |
5455

5556
> `*` - Requires `--show-details`
5657
58+
Each element of `.Links` has the following sub-fields:
59+
60+
| Sub-field | Type | Description |
61+
|---------------|----------|-----------------------|
62+
| `.Label` | `string` | Link text |
63+
| `.URL` | `string` | Target URL |
64+
65+
Example usage in a custom template:
66+
67+
```html
68+
{{ if .Links }}
69+
<nav>
70+
{{ range .Links }}<a href="{{ .URL }}">{{ .Label }}</a>{{ end }}
71+
</nav>
72+
{{ end }}
73+
```
74+
5775
In addition to the fields above, templates also have access to a set of built-in functions (see below), which are
5876
pipeline-friendly (needle before haystack): `{{ .Message | default "Unknown" | upper }}`.
5977

0 commit comments

Comments
 (0)