mirror of
https://github.com/tarampampam/error-pages.git
synced 2024-08-30 18:22:40 +00:00
wip: 🔕 temporary commit
This commit is contained in:
parent
cea34f0475
commit
4bb567b1d6
48
README.md
48
README.md
@ -27,23 +27,23 @@ $ error-pages [GLOBAL FLAGS] serve [COMMAND FLAGS] [ARGUMENTS...]
|
||||
|
||||
The following flags are supported:
|
||||
|
||||
| Name | Description | Default value | Environment variables |
|
||||
|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:---------------------------:|
|
||||
| `--listen="…"` (`-l`) | IP (v4 or v6) address to listen on | `0.0.0.0` | `LISTEN_ADDR` |
|
||||
| `--port="…"` (`-p`) | TCP port number | `8080` | `LISTEN_PORT` |
|
||||
| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* |
|
||||
| `--disable-template="…"` | disable the specified template by its name | `[]` | *none* |
|
||||
| `--add-http-code="…"` | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes, unless a more specific code was described previously) | `map[]` | *none* |
|
||||
| `--json-format="…"` | override the default error page response in JSON format (Go templates are supported) | | `RESPONSE_JSON_FORMAT` |
|
||||
| `--xml-format="…"` | override the default error page response in XML format (Go templates are supported) | | `RESPONSE_XML_FORMAT` |
|
||||
| `--plaintext-format="…"` | override the default error page response in plain text format (Go templates are supported) | | `RESPONSE_PLAINTEXT_FORMAT` |
|
||||
| `--template-name="…"` (`-t`) | name of the template to use for rendering error pages | `app-down` | `TEMPLATE_NAME` |
|
||||
| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` |
|
||||
| `--default-error-page="…"` | the code of the default (index page, when a code is not specified) error page to render | `404` | `DEFAULT_ERROR_PAGE` |
|
||||
| `--send-same-http-code` | the HTTP response should have the same status code as the requested error page (by default, every response with an error page will have a status code of 200) | `false` | `SEND_SAME_HTTP_CODE` |
|
||||
| `--show-details` | show request details in the error page response (if supported by the template) | `false` | `SHOW_DETAILS` |
|
||||
| `--proxy-headers="…"` | listed here HTTP headers will be proxied from the original request to the error page response (comma-separated list) | `X-Request-Id,X-Trace-Id,X-Amzn-Trace-Id` | `PROXY_HTTP_HEADERS` |
|
||||
| `--rotation-mode="…"` | templates automatic rotation mode (disabled/random-on-startup/random-on-each-request/random-hourly/random-daily) | `disabled` | `TEMPLATES_ROTATION_MODE` |
|
||||
| Name | Description | Default value | Environment variables |
|
||||
|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:---------------------------:|
|
||||
| `--listen="…"` (`-l`) | the HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP) | `0.0.0.0` | `LISTEN_ADDR` |
|
||||
| `--port="…"` (`-p`) | the TPC port number for the HTTP server to listen on (0-65535) | `8080` | `LISTEN_PORT` |
|
||||
| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* |
|
||||
| `--disable-template="…"` | disable the specified template by its name (can be found useful to disable the builtit templates and use only custom ones) | `[]` | *none* |
|
||||
| `--add-http-code="…"` (`--add-code`) | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes, unless a more specific code was described previously) | `map[]` | *none* |
|
||||
| `--json-format="…"` | override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type) | | `RESPONSE_JSON_FORMAT` |
|
||||
| `--xml-format="…"` | override the default error page response in XML format (Go templates are supported; the error page will use this template if the client requests XML content type) | | `RESPONSE_XML_FORMAT` |
|
||||
| `--plaintext-format="…"` | override the default error page response in plain text format (Go templates are supported; the error page will use this template if the client requests PlainText content type or does not specify any) | | `RESPONSE_PLAINTEXT_FORMAT` |
|
||||
| `--template-name="…"` (`-t`) | name of the template to use for rendering error pages (builtin templates: app-down, cats, connection, ghost, hacker-terminal, l7, lost-in-space, noise, orient, shuffle) | `app-down` | `TEMPLATE_NAME` |
|
||||
| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` |
|
||||
| `--default-error-page="…"` | the code of the default (index page, when a code is not specified) error page to render | `404` | `DEFAULT_ERROR_PAGE` |
|
||||
| `--send-same-http-code` | the HTTP response should have the same status code as the requested error page (by default, every response with an error page will have a status code of 200) | `false` | `SEND_SAME_HTTP_CODE` |
|
||||
| `--show-details` | show request details in the error page response (if supported by the template) | `false` | `SHOW_DETAILS` |
|
||||
| `--proxy-headers="…"` | listed here HTTP headers will be proxied from the original request to the error page response (comma-separated list) | `X-Request-Id,X-Trace-Id,X-Amzn-Trace-Id` | `PROXY_HTTP_HEADERS` |
|
||||
| `--rotation-mode="…"` | templates automatic rotation mode (disabled/random-on-startup/random-on-each-request/random-hourly/random-daily) | `disabled` | `TEMPLATES_ROTATION_MODE` |
|
||||
|
||||
### `build` command (aliases: `b`)
|
||||
|
||||
@ -57,12 +57,14 @@ $ error-pages [GLOBAL FLAGS] build [COMMAND FLAGS] [ARGUMENTS...]
|
||||
|
||||
The following flags are supported:
|
||||
|
||||
| Name | Description | Default value | Environment variables |
|
||||
|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------:|:---------------------:|
|
||||
| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* |
|
||||
| `--disable-template="…"` | disable the specified template by its name | `[]` | *none* |
|
||||
| `--add-http-code="…"` | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes, unless a more specific code was described previously) | `map[]` | *none* |
|
||||
| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` |
|
||||
| Name | Description | Default value | Environment variables |
|
||||
|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------:|:---------------------:|
|
||||
| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* |
|
||||
| `--disable-template="…"` | disable the specified template by its name (can be found useful to disable the builtit templates and use only custom ones) | `[]` | *none* |
|
||||
| `--add-http-code="…"` (`--add-code`) | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes, unless a more specific code was described previously) | `map[]` | *none* |
|
||||
| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` |
|
||||
| `--index` (`-i`) | generate index.html file with links to all error pages | `false` | *none* |
|
||||
| `--target-dir="…"` (`--out`, `--dir`, `-o`) | directory to put the built error pages into | `.` | *none* |
|
||||
|
||||
### `healthcheck` command (aliases: `chk`, `health`, `check`)
|
||||
|
||||
|
@ -2,24 +2,34 @@ package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gh.tarampamp.am/error-pages/internal/cli/shared"
|
||||
"gh.tarampamp.am/error-pages/internal/config"
|
||||
"gh.tarampamp.am/error-pages/internal/logger"
|
||||
appTemplate "gh.tarampamp.am/error-pages/internal/template"
|
||||
)
|
||||
|
||||
//go:embed index.html
|
||||
var indexHtml string
|
||||
|
||||
type command struct {
|
||||
c *cli.Command
|
||||
|
||||
opt struct {
|
||||
createIndex bool
|
||||
targetDir string
|
||||
createIndex bool
|
||||
targetDirAbsPath string
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +80,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
Action: func(ctx context.Context, c *cli.Command) error {
|
||||
cfg.L10n.Disable = c.Bool(disableL10nFlag.Name)
|
||||
cmd.opt.createIndex = c.Bool(createIndexFlag.Name)
|
||||
cmd.opt.targetDir, _ = filepath.Abs(c.String(targetDirFlag.Name)) // an error checked by [os.Stat] validator
|
||||
cmd.opt.targetDirAbsPath, _ = filepath.Abs(c.String(targetDirFlag.Name)) // an error checked by [os.Stat] validator
|
||||
|
||||
// add templates from files to the configuration
|
||||
if add := c.StringSlice(addTplFlag.Name); len(add) > 0 {
|
||||
@ -112,6 +122,13 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
return errors.New("no templates specified")
|
||||
}
|
||||
|
||||
log.Info("Building error pages",
|
||||
logger.String("targetDir", cmd.opt.targetDirAbsPath),
|
||||
logger.Strings("templates", cfg.Templates.Names()...),
|
||||
logger.Bool("index", cmd.opt.createIndex),
|
||||
logger.Bool("l10n", !cfg.L10n.Disable),
|
||||
)
|
||||
|
||||
return cmd.Run(ctx, log, &cfg)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
@ -127,10 +144,97 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
return cmd.c
|
||||
}
|
||||
|
||||
func (cmd *command) Run(
|
||||
func (cmd *command) Run( //nolint:funlen
|
||||
ctx context.Context,
|
||||
log *logger.Logger,
|
||||
cfg *config.Config,
|
||||
) error {
|
||||
type historyItem struct{ Code, Message, RelativePath string }
|
||||
|
||||
var history = make(map[string][]historyItem, len(cfg.Codes)*len(cfg.Templates)) // map[template_name]codes
|
||||
|
||||
for templateName, templateContent := range cfg.Templates {
|
||||
log.Debug("Processing template", logger.String("name", templateName))
|
||||
|
||||
for code, codeDescription := range cfg.Codes {
|
||||
if err := createDirectory(filepath.Join(cmd.opt.targetDirAbsPath, templateName)); err != nil {
|
||||
return fmt.Errorf("cannot create directory for template '%s': %w", templateName, err)
|
||||
}
|
||||
|
||||
var codeAsUint, codeParsingErr = strconv.ParseUint(code, 10, 32)
|
||||
if codeParsingErr != nil {
|
||||
log.Warn("Cannot parse code", logger.String("code", code))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
var outFilePath = path.Join(cmd.opt.targetDirAbsPath, templateName, code+".html")
|
||||
|
||||
if content, renderErr := appTemplate.Render(templateContent, appTemplate.Props{
|
||||
Code: uint16(codeAsUint),
|
||||
Message: codeDescription.Message,
|
||||
Description: codeDescription.Description,
|
||||
L10nDisabled: cfg.L10n.Disable,
|
||||
ShowRequestDetails: false,
|
||||
}); renderErr == nil {
|
||||
if err := os.WriteFile(outFilePath, []byte(content), os.FileMode(0664)); err != nil { //nolint:mnd
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("cannot render template '%s': %w", templateName, renderErr)
|
||||
}
|
||||
|
||||
log.Debug("Page built", logger.String("template", templateName), logger.String("code", code))
|
||||
|
||||
history[templateName] = append(history[templateName], historyItem{
|
||||
Code: code,
|
||||
Message: codeDescription.Message,
|
||||
RelativePath: "." + strings.TrimPrefix(outFilePath, cmd.opt.targetDirAbsPath), // to make it relative
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.opt.createIndex {
|
||||
log.Debug("Creating the index file")
|
||||
|
||||
for name := range history {
|
||||
slices.SortFunc(history[name], func(a, b historyItem) int { return strings.Compare(a.Code, b.Code) })
|
||||
}
|
||||
|
||||
indexTpl, tplErr := template.New("index").Parse(indexHtml)
|
||||
if tplErr != nil {
|
||||
return tplErr
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
|
||||
if err := indexTpl.Execute(&buf, history); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(
|
||||
filepath.Join(cmd.opt.targetDirAbsPath, "index.html"),
|
||||
[]byte(buf.String()),
|
||||
os.FileMode(0664), //nolint:mnd
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDirectory(path string) error {
|
||||
var stat, err = os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return os.MkdirAll(path, os.FileMode(0775)) //nolint:mnd
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return errors.New("is not a directory")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
122
internal/cli/build/index.html
Normal file
122
internal/cli/build/index.html
Normal file
@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="follow,index">
|
||||
<title>Error pages list</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: #fff;
|
||||
--color-inverted: #202020;
|
||||
--color-link: #395364;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-primary: #1a1a1a;
|
||||
--color-inverted: #fff;
|
||||
--color-link: #5cb0d3;
|
||||
}
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-inverted);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
html, body {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 1200px;
|
||||
height: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 3em 0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 3em;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
article h2 {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
article h2 code {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
article ul {
|
||||
list-style: none;
|
||||
margin: 1em 0;
|
||||
padding: 0 0 0 1em;
|
||||
}
|
||||
|
||||
article ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 3em 0;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<h1>Error pages index</h1>
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<!-- {{- range $templateName, $details := . -}} -->
|
||||
<h2>Template name: <Code>{{ $templateName }}</Code></h2>
|
||||
<ul class="mb-5">
|
||||
<!-- {{ range $details -}}-->
|
||||
<li><a href="{{ .RelativePath }}"><strong>{{ .Code }}</strong>: {{ .Message }}</a></li>
|
||||
<!-- {{ end -}} -->
|
||||
</ul>
|
||||
<!-- {{ end }} -->
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
For online documentation and support please refer to the
|
||||
<a href="https://gh.tarampamp.am/error-pages">project repository</a>.
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -46,21 +46,21 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
jsonFormatFlag = cli.StringFlag{
|
||||
Name: "json-format",
|
||||
Usage: "override the default error page response in JSON format (Go templates are supported)",
|
||||
Usage: "override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type)",
|
||||
Sources: env("RESPONSE_JSON_FORMAT"),
|
||||
OnlyOnce: true,
|
||||
Config: trim,
|
||||
}
|
||||
xmlFormatFlag = cli.StringFlag{
|
||||
Name: "xml-format",
|
||||
Usage: "override the default error page response in XML format (Go templates are supported)",
|
||||
Usage: "override the default error page response in XML format (Go templates are supported; the error page will use this template if the client requests XML content type)",
|
||||
Sources: env("RESPONSE_XML_FORMAT"),
|
||||
OnlyOnce: true,
|
||||
Config: trim,
|
||||
}
|
||||
plainTextFormatFlag = cli.StringFlag{
|
||||
Name: "plaintext-format",
|
||||
Usage: "override the default error page response in plain text format (Go templates are supported)",
|
||||
Usage: "override the default error page response in plain text format (Go templates are supported; the error page will use this template if the client requests PlainText content type or does not specify any)",
|
||||
Sources: env("RESPONSE_PLAINTEXT_FORMAT"),
|
||||
OnlyOnce: true,
|
||||
Config: trim,
|
||||
@ -69,7 +69,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
Name: "template-name",
|
||||
Aliases: []string{"t"},
|
||||
Value: cfg.TemplateName,
|
||||
Usage: "name of the template to use for rendering error pages",
|
||||
Usage: "name of the template to use for rendering error pages (builtin templates: " + strings.Join(cfg.Templates.Names(), ", ") + ")",
|
||||
Sources: env("TEMPLATE_NAME"),
|
||||
OnlyOnce: true,
|
||||
Config: trim,
|
||||
@ -138,6 +138,9 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
}
|
||||
)
|
||||
|
||||
addrFlag.Usage = "the HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP)"
|
||||
portFlag.Usage = "the TPC port number for the HTTP server to listen on (0-65535)"
|
||||
|
||||
disableL10nFlag.Value = cfg.L10n.Disable // set the default value depending on the configuration
|
||||
|
||||
cmd.c = &cli.Command{
|
||||
|
@ -73,12 +73,13 @@ var AddTemplatesFlag = cli.StringSliceFlag{
|
||||
|
||||
var DisableTemplateNamesFlag = cli.StringSliceFlag{
|
||||
Name: "disable-template",
|
||||
Usage: "disable the specified template by its name",
|
||||
Usage: "disable the specified template by its name (can be found useful to disable the builtit templates and use only custom ones)",
|
||||
Config: cli.StringConfig{TrimSpace: true},
|
||||
}
|
||||
|
||||
var AddHTTPCodesFlag = cli.StringMapFlag{
|
||||
Name: "add-http-code",
|
||||
Name: "add-http-code",
|
||||
Aliases: []string{"add-code"},
|
||||
Usage: "to add a new HTTP status code, provide the code and its message/description using this flag (the format " +
|
||||
"should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, " +
|
||||
"for example, '4**' will cover all 4xx codes, unless a more specific code was described previously)",
|
||||
|
Loading…
Reference in New Issue
Block a user