wip: 🔕 temporary commit

This commit is contained in:
Paramtamtam 2024-06-29 19:11:21 +04:00
parent cea34f0475
commit 4bb567b1d6
No known key found for this signature in database
GPG Key ID: 366371698FAD0A2B
5 changed files with 265 additions and 33 deletions

View File

@ -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`)

View File

@ -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
}

View 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>

View File

@ -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{

View File

@ -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)",