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
86aa4aab93
commit
cea34f0475
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
@ -58,11 +58,7 @@ jobs:
|
||||
CGO_ENABLED: 0
|
||||
LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/appmeta.version=${{ steps.slug.outputs.branch-name-slug }}
|
||||
run: go build -trimpath -ldflags "$LDFLAGS" -o ./error-pages ./cmd/error-pages/
|
||||
|
||||
- name: Try to execute
|
||||
if: matrix.os == 'linux'
|
||||
run: ./error-pages --version && ./error-pages -h
|
||||
|
||||
- {if: matrix.os == 'linux', run: ./error-pages --version && ./error-pages -h}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: error-pages-${{ matrix.os }}-${{ matrix.arch }}
|
||||
|
19
README.md
19
README.md
@ -45,6 +45,25 @@ The following flags are supported:
|
||||
| `--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`)
|
||||
|
||||
Build the static error pages and put them into a specified directory.
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
$ 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` |
|
||||
|
||||
### `healthcheck` command (aliases: `chk`, `health`, `check`)
|
||||
|
||||
Health checker for the HTTP server. The use case - docker health check.
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gh.tarampamp.am/error-pages/internal/appmeta"
|
||||
"gh.tarampamp.am/error-pages/internal/cli/build"
|
||||
"gh.tarampamp.am/error-pages/internal/cli/healthcheck"
|
||||
"gh.tarampamp.am/error-pages/internal/cli/perftest"
|
||||
"gh.tarampamp.am/error-pages/internal/cli/serve"
|
||||
@ -77,6 +78,7 @@ func NewApp(appName string) *cli.Command { //nolint:funlen
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
serve.NewCommand(log),
|
||||
build.NewCommand(log),
|
||||
healthcheck.NewCommand(log, healthcheck.NewHTTPHealthChecker()),
|
||||
perftest.NewCommand(log),
|
||||
},
|
||||
|
136
internal/cli/build/command.go
Normal file
136
internal/cli/build/command.go
Normal file
@ -0,0 +1,136 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
c *cli.Command
|
||||
|
||||
opt struct {
|
||||
createIndex bool
|
||||
targetDir string
|
||||
}
|
||||
}
|
||||
|
||||
// NewCommand creates `build` command.
|
||||
func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
var (
|
||||
cmd command
|
||||
cfg = config.New()
|
||||
|
||||
addTplFlag = shared.AddTemplatesFlag
|
||||
disableTplFlag = shared.DisableTemplateNamesFlag
|
||||
addCodeFlag = shared.AddHTTPCodesFlag
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
createIndexFlag = cli.BoolFlag{
|
||||
Name: "index",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "generate index.html file with links to all error pages",
|
||||
}
|
||||
targetDirFlag = cli.StringFlag{
|
||||
Name: "target-dir",
|
||||
Aliases: []string{"out", "dir", "o"},
|
||||
Usage: "directory to put the built error pages into",
|
||||
Value: ".", // current directory by default
|
||||
Config: cli.StringConfig{TrimSpace: true},
|
||||
OnlyOnce: true,
|
||||
Validator: func(dir string) error {
|
||||
if dir == "" {
|
||||
return errors.New("missing target directory")
|
||||
}
|
||||
|
||||
if stat, err := os.Stat(dir); err != nil {
|
||||
return fmt.Errorf("cannot access the target directory '%s': %w", dir, err)
|
||||
} else if !stat.IsDir() {
|
||||
return fmt.Errorf("'%s' is not a directory", dir)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
disableL10nFlag.Value = cfg.L10n.Disable // set the default value depending on the configuration
|
||||
|
||||
cmd.c = &cli.Command{
|
||||
Name: "build",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "Build the static error pages and put them into a specified directory",
|
||||
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
|
||||
|
||||
// add templates from files to the configuration
|
||||
if add := c.StringSlice(addTplFlag.Name); len(add) > 0 {
|
||||
for _, templatePath := range add {
|
||||
if addedName, err := cfg.Templates.AddFromFile(templatePath); err != nil {
|
||||
return fmt.Errorf("cannot add template from file %s: %w", templatePath, err)
|
||||
} else {
|
||||
log.Info("Template added",
|
||||
logger.String("name", addedName),
|
||||
logger.String("path", templatePath),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable templates specified by the user
|
||||
if disable := c.StringSlice(disableTplFlag.Name); len(disable) > 0 {
|
||||
for _, templateName := range disable {
|
||||
if ok := cfg.Templates.Remove(templateName); ok {
|
||||
log.Info("Template disabled", logger.String("name", templateName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add custom HTTP codes to the configuration
|
||||
if add := c.StringMap(addCodeFlag.Name); len(add) > 0 {
|
||||
for code, desc := range shared.ParseHTTPCodes(add) {
|
||||
cfg.Codes[code] = desc
|
||||
|
||||
log.Info("HTTP code added",
|
||||
logger.String("code", code),
|
||||
logger.String("message", desc.Message),
|
||||
logger.String("description", desc.Description),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.Templates) == 0 {
|
||||
return errors.New("no templates specified")
|
||||
}
|
||||
|
||||
return cmd.Run(ctx, log, &cfg)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
&addTplFlag,
|
||||
&disableTplFlag,
|
||||
&addCodeFlag,
|
||||
&disableL10nFlag,
|
||||
&createIndexFlag,
|
||||
&targetDirFlag,
|
||||
},
|
||||
}
|
||||
|
||||
return cmd.c
|
||||
}
|
||||
|
||||
func (cmd *command) Run(
|
||||
ctx context.Context,
|
||||
log *logger.Logger,
|
||||
cfg *config.Config,
|
||||
) error {
|
||||
return nil
|
||||
}
|
@ -38,12 +38,13 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
)
|
||||
|
||||
var (
|
||||
addrFlag = shared.ListenAddrFlag
|
||||
portFlag = shared.ListenPortFlag
|
||||
addTplFlag = shared.AddTemplatesFlag
|
||||
disableTplFlag = shared.DisableTemplateNamesFlag
|
||||
addCodeFlag = shared.AddHTTPCodesFlag
|
||||
jsonFormatFlag = cli.StringFlag{
|
||||
addrFlag = shared.ListenAddrFlag
|
||||
portFlag = shared.ListenPortFlag
|
||||
addTplFlag = shared.AddTemplatesFlag
|
||||
disableTplFlag = shared.DisableTemplateNamesFlag
|
||||
addCodeFlag = shared.AddHTTPCodesFlag
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
jsonFormatFlag = cli.StringFlag{
|
||||
Name: "json-format",
|
||||
Usage: "override the default error page response in JSON format (Go templates are supported)",
|
||||
Sources: env("RESPONSE_JSON_FORMAT"),
|
||||
@ -73,13 +74,6 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
OnlyOnce: true,
|
||||
Config: trim,
|
||||
}
|
||||
disableL10nFlag = cli.BoolFlag{
|
||||
Name: "disable-l10n",
|
||||
Usage: "disable localization of error pages (if the template supports localization)",
|
||||
Value: cfg.L10n.Disable,
|
||||
Sources: env("DISABLE_L10N"),
|
||||
OnlyOnce: true,
|
||||
}
|
||||
defaultCodeToRenderFlag = cli.UintFlag{
|
||||
Name: "default-error-page",
|
||||
Usage: "the code of the default (index page, when a code is not specified) error page to render",
|
||||
@ -144,6 +138,8 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
}
|
||||
)
|
||||
|
||||
disableL10nFlag.Value = cfg.L10n.Disable // set the default value depending on the configuration
|
||||
|
||||
cmd.c = &cli.Command{
|
||||
Name: "serve",
|
||||
Aliases: []string{"s", "server", "http"},
|
||||
@ -203,20 +199,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
|
||||
// add custom HTTP codes to the configuration
|
||||
if add := c.StringMap(addCodeFlag.Name); len(add) > 0 {
|
||||
for code, msgAndDesc := range add {
|
||||
var (
|
||||
parts = strings.SplitN(msgAndDesc, "/", 2) //nolint:mnd
|
||||
desc config.CodeDescription
|
||||
)
|
||||
|
||||
if len(parts) > 0 {
|
||||
desc.Message = strings.TrimSpace(parts[0])
|
||||
}
|
||||
|
||||
if len(parts) > 1 {
|
||||
desc.Description = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
for code, desc := range shared.ParseHTTPCodes(add) {
|
||||
cfg.Codes[code] = desc
|
||||
|
||||
log.Info("HTTP code added",
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gh.tarampamp.am/error-pages/internal/config"
|
||||
)
|
||||
|
||||
// Note: Don't use pointers for flags, because they have own state which is not thread-safe.
|
||||
@ -99,3 +101,33 @@ var AddHTTPCodesFlag = cli.StringMapFlag{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// ParseHTTPCodes converts a map of HTTP status codes and their messages/descriptions into a map of codes and
|
||||
// descriptions. Should be used together with [AddHTTPCodesFlag].
|
||||
func ParseHTTPCodes(codes map[string]string) map[string]config.CodeDescription {
|
||||
var result = make(map[string]config.CodeDescription, len(codes))
|
||||
|
||||
for code, msgAndDesc := range codes {
|
||||
var (
|
||||
parts = strings.SplitN(msgAndDesc, "/", 2)
|
||||
desc config.CodeDescription
|
||||
)
|
||||
|
||||
desc.Message = strings.TrimSpace(parts[0])
|
||||
|
||||
if len(parts) > 1 {
|
||||
desc.Description = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
result[code] = desc
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
var DisableL10nFlag = cli.BoolFlag{
|
||||
Name: "disable-l10n",
|
||||
Usage: "disable localization of error pages (if the template supports localization)",
|
||||
Sources: cli.EnvVars("DISABLE_L10N"),
|
||||
OnlyOnce: true,
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"gh.tarampamp.am/error-pages/internal/cli/shared"
|
||||
"gh.tarampamp.am/error-pages/internal/config"
|
||||
)
|
||||
|
||||
func TestListenAddrFlag(t *testing.T) {
|
||||
@ -169,3 +170,49 @@ func TestAddHTTPCodesFlag(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHTTPCodes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, shared.ParseHTTPCodes(nil), map[string]config.CodeDescription{})
|
||||
|
||||
assert.Equal(t,
|
||||
shared.ParseHTTPCodes(map[string]string{"200": "msg"}),
|
||||
map[string]config.CodeDescription{"200": {Message: "msg", Description: ""}},
|
||||
)
|
||||
|
||||
assert.Equal(t,
|
||||
shared.ParseHTTPCodes(map[string]string{"200": "/aaa"}),
|
||||
map[string]config.CodeDescription{"200": {Message: "", Description: "aaa"}},
|
||||
)
|
||||
|
||||
assert.Equal(t, // not sure here
|
||||
shared.ParseHTTPCodes(map[string]string{"aa": "////aaa"}),
|
||||
map[string]config.CodeDescription{"aa": {Message: "", Description: "///aaa"}},
|
||||
)
|
||||
|
||||
assert.Equal(t,
|
||||
shared.ParseHTTPCodes(map[string]string{"200": "msg/desc"}),
|
||||
map[string]config.CodeDescription{"200": {Message: "msg", Description: "desc"}},
|
||||
)
|
||||
|
||||
assert.Equal(t,
|
||||
shared.ParseHTTPCodes(map[string]string{
|
||||
"200": "msg/desc",
|
||||
"foo": "Word word/Desc desc // adsadas",
|
||||
}),
|
||||
map[string]config.CodeDescription{
|
||||
"200": {Message: "msg", Description: "desc"},
|
||||
"foo": {Message: "Word word", Description: "Desc desc // adsadas"},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestDisableL10nFlag(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var flag = shared.DisableL10nFlag
|
||||
|
||||
assert.Equal(t, "disable-l10n", flag.Name)
|
||||
assert.Contains(t, flag.Sources.String(), "DISABLE_L10N")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user