From 14815b4c735323cd0ac3e9e30808951f53471997 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Sat, 29 Jun 2024 14:54:47 +0400 Subject: [PATCH] =?UTF-8?q?wip:=20=F0=9F=94=95=20temporary=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yml | 3 +- .github/release.yml | 13 + .github/workflows/dependabot.yml | 10 +- .github/workflows/documentation.yml | 7 +- .github/workflows/release.yml | 217 +- .github/workflows/tests.yml | 366 +- .golangci.yml | 4 - Dockerfile | 2 - README.md | 40 +- error-pages.yml | 140 - go.mod | 29 +- go.sum | 63 +- internal/appmeta/version_test.go | 2 + internal/cli/app_test.go | 9 +- internal/cli/perftest/command.go | 10 +- internal/cli/serve/command.go | 2 +- internal/cli/serve/command_test.go | 101 + internal/cli/serve/testdata/foo-template.html | 10 + .../http/handlers/error_page/handler_test.go | 226 ++ .../http/middleware/logreq/middleware_test.go | 44 + profile001.svg | 3116 ----------------- schemas/config/1.0.schema.json | 108 - schemas/config/readme.md | 13 - schemas/readme.md | 15 - 24 files changed, 723 insertions(+), 3827 deletions(-) create mode 100644 .github/release.yml delete mode 100644 error-pages.yml create mode 100644 internal/cli/serve/command_test.go create mode 100644 internal/cli/serve/testdata/foo-template.html create mode 100644 internal/http/handlers/error_page/handler_test.go create mode 100644 internal/http/middleware/logreq/middleware_test.go delete mode 100644 profile001.svg delete mode 100644 schemas/config/1.0.schema.json delete mode 100644 schemas/config/readme.md delete mode 100644 schemas/readme.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 148e1df..dc23e1a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,4 +1,5 @@ -# Docs: +# yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json +# docs: https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/customizing-dependency-updates version: 2 diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..2a0b405 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-release-config.json +# docs: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes + +changelog: + categories: + - title: 🛠 Fixes + labels: [type:fix, type:bug] + - title: 🚀 Features + labels: [type:feature, type:feature_request] + - title: 📦 Dependency updates + labels: [dependencies] + - title: Other Changes + labels: ['*'] diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 1a9f9cc..53955f6 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -1,4 +1,7 @@ -name: dependabot +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +# docs: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +name: 🤖 Dependabot on: pull_request: {} @@ -9,6 +12,7 @@ permissions: jobs: dependabot: # https://tinyurl.com/e69djmen + name: Enable auto-merge for Dependabot PRs runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: @@ -16,10 +20,8 @@ jobs: id: metadata with: {github-token: "${{ secrets.GITHUB_TOKEN }}"} - - name: Enable auto-merge for Dependabot PRs - if: ${{ contains(fromJSON('["version-update:semver-minor", "version-update:semver-patch"]'), steps.metadata.outputs.update-type) }} + - if: ${{ contains(fromJSON('["version-update:semver-minor", "version-update:semver-patch"]'), steps.metadata.outputs.update-type) }} run: gh pr merge --auto --merge "$PR_URL" - continue-on-error: true env: PR_URL: ${{ github.event.pull_request.html_url }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 53e6421..b988f75 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,4 +1,7 @@ -name: documentation +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +# docs: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +name: 📚 Documentation on: push: @@ -12,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: peter-evans/dockerhub-description@v4 # Action page: + - uses: peter-evans/dockerhub-description@v4 with: username: ${{ secrets.DOCKER_LOGIN }} password: ${{ secrets.DOCKER_USER_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3345e0b..d3351d5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,113 +1,122 @@ -name: release +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +# docs: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +name: 🚀 Release on: release: # Docs: types: [published] jobs: - purge-cdn-cache: - name: Purge jsDelivr CDN cache + wip: runs-on: ubuntu-latest steps: - - uses: gacts/purge-jsdelivr-cache@v1 # Action page: - with: - url: | - https://cdn.jsdelivr.net/gh/tarampampam/error-pages@2/l10n/l10n.js - https://cdn.jsdelivr.net/gh/tarampampam/error-pages@2/l10n/l10n.min.js - https://cdn.jsdelivr.net/gh/tarampampam/error-pages@latest/l10n/l10n.js - https://cdn.jsdelivr.net/gh/tarampampam/error-pages@latest/l10n/l10n.min.js + - run: echo WIP - build: - name: Build for ${{ matrix.os }} (${{ matrix.arch }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - os: [linux, darwin] # linux, freebsd, darwin, windows - arch: [amd64] # amd64, 386 - steps: - - uses: actions/checkout@v4 - - - uses: gacts/setup-go-with-cache@v1 - with: {go-version-file: go.mod} - - - {uses: gacts/github-slug@v1, id: slug} - - - name: Generate builder values - id: values - run: echo "binary-name=error-pages-${{ matrix.os }}-${{ matrix.arch }}" >> $GITHUB_OUTPUT - - - name: Build application - env: - GOOS: ${{ matrix.os }} - GOARCH: ${{ matrix.arch }} - CGO_ENABLED: 0 - LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/version.version=${{ steps.slug.outputs.version }} - run: go build -trimpath -ldflags "$LDFLAGS" -o "./${{ steps.values.outputs.binary-name }}" ./cmd/error-pages/ - - - name: Upload binary file to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.values.outputs.binary-name }} - asset_name: ${{ steps.values.outputs.binary-name }} - tag: ${{ github.ref }} - - docker-image: - name: Build docker image - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - {uses: gacts/github-slug@v1, id: slug} - - - uses: docker/setup-qemu-action@v3 # Action page: - - - uses: docker/setup-buildx-action@v3 # Action page: - - - uses: docker/login-action@v3 # Action page: - with: - username: ${{ secrets.DOCKER_LOGIN }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - uses: docker/login-action@v3 # Action page: - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - uses: docker/build-push-action@v5 # Action page: - with: - context: . - file: Dockerfile - push: true - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm64/v8 - build-args: "APP_VERSION=${{ steps.slug.outputs.version }}" - tags: | - tarampampam/error-pages:${{ steps.slug.outputs.version }} - tarampampam/error-pages:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }} - tarampampam/error-pages:${{ steps.slug.outputs.version-major }} - tarampampam/error-pages:latest - ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version }} - ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }} - ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version-major }} - ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:latest - - demo: - name: Update the demonstration - runs-on: ubuntu-latest - needs: [docker-image] - steps: - - {uses: gacts/github-slug@v1, id: slug} - - - name: Take rendered templates from the built docker image - run: | - docker create --name img ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version }} - docker cp img:/opt/html ./out - docker rm -f img - - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./out +#jobs: +# purge-cdn-cache: +# name: Purge jsDelivr CDN cache +# runs-on: ubuntu-latest +# steps: +# - uses: gacts/purge-jsdelivr-cache@v1 # Action page: +# with: +# url: | +# https://cdn.jsdelivr.net/gh/tarampampam/error-pages@2/l10n/l10n.js +# https://cdn.jsdelivr.net/gh/tarampampam/error-pages@2/l10n/l10n.min.js +# https://cdn.jsdelivr.net/gh/tarampampam/error-pages@latest/l10n/l10n.js +# https://cdn.jsdelivr.net/gh/tarampampam/error-pages@latest/l10n/l10n.min.js +# +# build: +# name: Build for ${{ matrix.os }} (${{ matrix.arch }}) +# runs-on: ubuntu-latest +# strategy: +# fail-fast: false +# matrix: +# os: [linux, darwin] # linux, freebsd, darwin, windows +# arch: [amd64] # amd64, 386 +# steps: +# - uses: actions/checkout@v4 +# +# - uses: gacts/setup-go-with-cache@v1 +# with: {go-version-file: go.mod} +# +# - {uses: gacts/github-slug@v1, id: slug} +# +# - name: Generate builder values +# id: values +# run: echo "binary-name=error-pages-${{ matrix.os }}-${{ matrix.arch }}" >> $GITHUB_OUTPUT +# +# - name: Build application +# env: +# GOOS: ${{ matrix.os }} +# GOARCH: ${{ matrix.arch }} +# CGO_ENABLED: 0 +# LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/version.version=${{ steps.slug.outputs.version }} +# run: go build -trimpath -ldflags "$LDFLAGS" -o "./${{ steps.values.outputs.binary-name }}" ./cmd/error-pages/ +# +# - name: Upload binary file to release +# uses: svenstaro/upload-release-action@v2 +# with: +# repo_token: ${{ secrets.GITHUB_TOKEN }} +# file: ${{ steps.values.outputs.binary-name }} +# asset_name: ${{ steps.values.outputs.binary-name }} +# tag: ${{ github.ref }} +# +# docker-image: +# name: Build docker image +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# +# - {uses: gacts/github-slug@v1, id: slug} +# +# - uses: docker/setup-qemu-action@v3 # Action page: +# +# - uses: docker/setup-buildx-action@v3 # Action page: +# +# - uses: docker/login-action@v3 # Action page: +# with: +# username: ${{ secrets.DOCKER_LOGIN }} +# password: ${{ secrets.DOCKER_PASSWORD }} +# +# - uses: docker/login-action@v3 # Action page: +# with: +# registry: ghcr.io +# username: ${{ github.actor }} +# password: ${{ secrets.GITHUB_TOKEN }} +# +# - uses: docker/build-push-action@v5 # Action page: +# with: +# context: . +# file: Dockerfile +# push: true +# platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm64/v8 +# build-args: "APP_VERSION=${{ steps.slug.outputs.version }}" +# tags: | +# tarampampam/error-pages:${{ steps.slug.outputs.version }} +# tarampampam/error-pages:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }} +# tarampampam/error-pages:${{ steps.slug.outputs.version-major }} +# tarampampam/error-pages:latest +# ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version }} +# ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }} +# ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version-major }} +# ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:latest +# +# demo: +# name: Update the demonstration +# runs-on: ubuntu-latest +# needs: [docker-image] +# steps: +# - {uses: gacts/github-slug@v1, id: slug} +# +# - name: Take rendered templates from the built docker image +# run: | +# docker create --name img ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version }} +# docker cp img:/opt/html ./out +# docker rm -f img +# +# - name: Deploy to GitHub Pages +# uses: peaceiris/actions-gh-pages@v4 +# with: +# github_token: ${{ secrets.GITHUB_TOKEN }} +# publish_dir: ./out diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ff4441..bb420e2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,7 @@ -name: tests +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +# docs: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +name: 🧪 Tests on: push: @@ -12,224 +15,177 @@ concurrency: group: ${{ github.ref }} cancel-in-progress: true -jobs: # Docs: +jobs: gitleaks: - name: Gitleaks + name: Check for GitLeaks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - with: {fetch-depth: 0} - - - name: Check for GitLeaks - uses: gacts/gitleaks@v1 # Action page: + - {uses: actions/checkout@v4, with: {fetch-depth: 0}} + - uses: gacts/gitleaks@v1 golangci-lint: - name: Golang-CI (lint) + name: Run golangci-lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - uses: gacts/setup-go-with-cache@v1 - with: {go-version-file: go.mod} - + - {uses: actions/setup-go@v5, with: {go-version-file: go.mod}} - uses: golangci/golangci-lint-action@v6 - with: {skip-pkg-cache: true, skip-build-cache: true} - - validate-config-file: - name: Validate config file - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - {uses: actions/setup-node@v4, with: {node-version: 16}} - - - name: Install linter - run: npm install -g ajv-cli # Package page: - - - name: Run linter - run: ajv validate --all-errors --verbose -s ./schemas/config/1.0.schema.json -d ./error-pages.y*ml - - lint-l10n: - name: Lint l10n file(s) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - {uses: actions/setup-node@v4, with: {node-version: 16}} - - - name: Install eslint - run: npm install -g eslint@v8 # Package page: - - - name: Run linter - working-directory: l10n - run: eslint ./*.js go-test: name: Unit tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: {fetch-depth: 2} # Fixes codecov error 'Issue detecting commit SHA' + - {uses: actions/setup-go@v5, with: {go-version-file: go.mod}} + - run: go test -race ./... - - uses: gacts/setup-go-with-cache@v1 - with: {go-version-file: go.mod} +# build: +# name: Build for ${{ matrix.os }} (${{ matrix.arch }}) +# runs-on: ubuntu-latest +# strategy: +# fail-fast: false +# matrix: +# os: [linux, darwin] # linux, freebsd, darwin, windows +# arch: [amd64] # amd64, 386 +# needs: [golangci-lint, go-test, validate-config-file] +# steps: +# - uses: actions/checkout@v4 +# +# - uses: gacts/setup-go-with-cache@v1 +# with: {go-version-file: go.mod} +# +# - {uses: gacts/github-slug@v1, id: slug} +# +# - name: Build application +# env: +# GOOS: ${{ matrix.os }} +# GOARCH: ${{ matrix.arch }} +# CGO_ENABLED: 0 +# LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/version.version=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }} +# 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 +# +# - uses: actions/upload-artifact@v4 +# with: +# name: error-pages-${{ matrix.os }}-${{ matrix.arch }} +# path: error-pages +# if-no-files-found: error +# retention-days: 1 - - name: Run Unit tests - run: go test -race -covermode=atomic -coverprofile /tmp/coverage.txt ./... - - - uses: codecov/codecov-action@v4 # https://github.com/codecov/codecov-action - continue-on-error: true - with: - file: /tmp/coverage.txt - token: ${{ secrets.CODECOV_TOKEN }} - - build: - name: Build for ${{ matrix.os }} (${{ matrix.arch }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - os: [linux, darwin] # linux, freebsd, darwin, windows - arch: [amd64] # amd64, 386 - needs: [golangci-lint, go-test, validate-config-file] - steps: - - uses: actions/checkout@v4 - - - uses: gacts/setup-go-with-cache@v1 - with: {go-version-file: go.mod} - - - {uses: gacts/github-slug@v1, id: slug} - - - name: Build application - env: - GOOS: ${{ matrix.os }} - GOARCH: ${{ matrix.arch }} - CGO_ENABLED: 0 - LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/version.version=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }} - 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 - - - uses: actions/upload-artifact@v4 - with: - name: error-pages-${{ matrix.os }}-${{ matrix.arch }} - path: error-pages - if-no-files-found: error - retention-days: 1 - - generate: - name: Run templates generator - runs-on: ubuntu-latest - needs: [build] - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - with: - name: error-pages-linux-amd64 - path: .artifact - - - name: Prepare binary file to run - working-directory: .artifact - run: mv ./error-pages ./../error-pages && chmod +x ./../error-pages - - - name: Run generator - run: ./error-pages --verbose build --index ./out - - - name: Test files creation - run: | - test -f ./out/index.html - test -f ./out/ghost/404.html - test -f ./out/l7-dark/404.html - test -f ./out/l7-light/404.html - test -f ./out/shuffle/404.html - test -f ./out/noise/404.html - test -f ./out/hacker-terminal/404.html - test -f ./out/cats/404.html - test -f ./out/lost-in-space/404.html - test -f ./out/app-down/404.html - test -f ./out/connection/404.html - test -f ./out/matrix/404.html - test -f ./out/orient/404.html - - docker-image: - name: Build docker image - runs-on: ubuntu-latest - needs: [golangci-lint, go-test, validate-config-file] - steps: - - uses: actions/checkout@v4 - - - {uses: gacts/github-slug@v1, id: slug} - - - uses: docker/build-push-action@v5 # Action page: - with: - context: . - file: Dockerfile - push: false - build-args: "APP_VERSION=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }}" - tags: app:ci - - - run: docker save app:ci > ./docker-image.tar - - - uses: actions/upload-artifact@v4 - with: - name: docker-image - path: ./docker-image.tar - retention-days: 1 - - scan-docker-image: - name: Scan the docker image - runs-on: ubuntu-latest - needs: [docker-image] - steps: - - uses: actions/checkout@v4 # is needed for `upload-sarif` action - - - uses: actions/download-artifact@v4 - with: - name: docker-image - path: .artifact - - - uses: aquasecurity/trivy-action@0.21.0 # action page: - with: - input: .artifact/docker-image.tar - format: sarif - severity: MEDIUM,HIGH,CRITICAL - exit-code: 1 - output: trivy-results.sarif - - - uses: github/codeql-action/upload-sarif@v3 - if: always() - continue-on-error: true - with: {sarif_file: trivy-results.sarif} - - poke-docker-image: - name: Run the docker image - runs-on: ubuntu-latest - needs: [docker-image] - timeout-minutes: 2 - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - with: - name: docker-image - path: .artifact - - - working-directory: .artifact - run: docker load < docker-image.tar - - - uses: gacts/install-hurl@v1 - - - name: Run container with the app - run: docker run --rm -d -p "8080:8080/tcp" -e "SHOW_DETAILS=true" -e "PROXY_HTTP_HEADERS=X-Foo,Bar,Baz_blah" --name app app:ci - - - name: Wait for container "healthy" state - run: until [[ "`docker inspect -f {{.State.Health.Status}} app`" == "healthy" ]]; do echo "wait 1 sec.."; sleep 1; done - - - run: hurl --color --test --fail-at-end --variable host=127.0.0.1 --variable port=8080 ./test/hurl/*.hurl - - - name: Stop the container - if: always() - run: docker kill app +# generate: +# name: Run templates generator +# runs-on: ubuntu-latest +# needs: [build] +# steps: +# - uses: actions/checkout@v4 +# +# - uses: actions/download-artifact@v4 +# with: +# name: error-pages-linux-amd64 +# path: .artifact +# +# - name: Prepare binary file to run +# working-directory: .artifact +# run: mv ./error-pages ./../error-pages && chmod +x ./../error-pages +# +# - name: Run generator +# run: ./error-pages --verbose build --index ./out +# +# - name: Test files creation +# run: | +# test -f ./out/index.html +# test -f ./out/ghost/404.html +# test -f ./out/l7-dark/404.html +# test -f ./out/l7-light/404.html +# test -f ./out/shuffle/404.html +# test -f ./out/noise/404.html +# test -f ./out/hacker-terminal/404.html +# test -f ./out/cats/404.html +# test -f ./out/lost-in-space/404.html +# test -f ./out/app-down/404.html +# test -f ./out/connection/404.html +# test -f ./out/matrix/404.html +# test -f ./out/orient/404.html +# +# docker-image: +# name: Build docker image +# runs-on: ubuntu-latest +# needs: [golangci-lint, go-test, validate-config-file] +# steps: +# - uses: actions/checkout@v4 +# +# - {uses: gacts/github-slug@v1, id: slug} +# +# - uses: docker/build-push-action@v5 # Action page: +# with: +# context: . +# file: Dockerfile +# push: false +# build-args: "APP_VERSION=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }}" +# tags: app:ci +# +# - run: docker save app:ci > ./docker-image.tar +# +# - uses: actions/upload-artifact@v4 +# with: +# name: docker-image +# path: ./docker-image.tar +# retention-days: 1 +# +# scan-docker-image: +# name: Scan the docker image +# runs-on: ubuntu-latest +# needs: [docker-image] +# steps: +# - uses: actions/checkout@v4 # is needed for `upload-sarif` action +# +# - uses: actions/download-artifact@v4 +# with: +# name: docker-image +# path: .artifact +# +# - uses: aquasecurity/trivy-action@0.21.0 # action page: +# with: +# input: .artifact/docker-image.tar +# format: sarif +# severity: MEDIUM,HIGH,CRITICAL +# exit-code: 1 +# output: trivy-results.sarif +# +# - uses: github/codeql-action/upload-sarif@v3 +# if: always() +# continue-on-error: true +# with: {sarif_file: trivy-results.sarif} +# +# poke-docker-image: +# name: Run the docker image +# runs-on: ubuntu-latest +# needs: [docker-image] +# timeout-minutes: 2 +# steps: +# - uses: actions/checkout@v4 +# +# - uses: actions/download-artifact@v4 +# with: +# name: docker-image +# path: .artifact +# +# - working-directory: .artifact +# run: docker load < docker-image.tar +# +# - uses: gacts/install-hurl@v1 +# +# - name: Run container with the app +# run: docker run --rm -d -p "8080:8080/tcp" -e "SHOW_DETAILS=true" -e "PROXY_HTTP_HEADERS=X-Foo,Bar,Baz_blah" --name app app:ci +# +# - name: Wait for container "healthy" state +# run: until [[ "`docker inspect -f {{.State.Health.Status}} app`" == "healthy" ]]; do echo "wait 1 sec.."; sleep 1; done +# +# - run: hurl --color --test --fail-at-end --variable host=127.0.0.1 --variable port=8080 ./test/hurl/*.hurl +# +# - name: Stop the container +# if: always() +# run: docker kill app diff --git a/.golangci.yml b/.golangci.yml index 50d0d53..80ed761 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -110,10 +110,6 @@ issues: - tmp - temp - testdata - - cmd-old # TODO: remove - - internal-old # TODO: remove - - templates-old # TODO: remove - - test-old # TODO: remove exclude-rules: - {path: flags\.go, linters: [gochecknoglobals, lll, mnd, dupl]} - {path: env\.go, linters: [lll, gosec]} diff --git a/Dockerfile b/Dockerfile index 108585c..4a72cfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,6 @@ FROM docker.io/library/golang:1.22-bookworm AS develop ENV GOPATH="/var/tmp/go" RUN set -x \ - && GOBIN=/bin go install gotest.tools/gotestsum@latest \ - && go clean -cache -modcache \ # renovate: source=github-releases name=golangci/golangci-lint && GOLANGCI_LINT_VERSION="1.59.1" \ && wget -O- -nv "https://cdn.jsdelivr.net/gh/golangci/golangci-lint@v${GOLANGCI_LINT_VERSION}/install.sh" \ diff --git a/README.md b/README.md index c9cde26..da7c724 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ $ error-pages [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] Global flags: -| Name | Description | Default value | Environment variables | -|--------------------|---------------------------------------------|:-------------:|:---------------------:| -| `--log-level="…"` | logging level (debug/info/warn/error/fatal) | `info` | `LOG_LEVEL` | -| `--log-format="…"` | logging format (console/json) | `console` | `LOG_FORMAT` | +| Name | Description | Default value | Environment variables | +|--------------------|---------------------------------------|:-------------:|:---------------------:| +| `--log-level="…"` | logging level (debug/info/warn/error) | `info` | `LOG_LEVEL` | +| `--log-format="…"` | logging format (console/json) | `console` | `LOG_FORMAT` | ### `serve` command (aliases: `s`, `server`, `http`) @@ -27,21 +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* | -| `--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` | -| `--template-name="…"` (`-t`) | name of the template to use for rendering error pages | `template-1` | `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-daily/random-hourly) | `disabled` | `TEMPLATES_ROTATION_MODE` | +| 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` | ### `healthcheck` command (aliases: `chk`, `health`, `check`) diff --git a/error-pages.yml b/error-pages.yml deleted file mode 100644 index c23b6f6..0000000 --- a/error-pages.yml +++ /dev/null @@ -1,140 +0,0 @@ -templates: - # - name: {string} Template name (optional, if path is defined) - # path: {string} Path to the template file - # content: {string} Template content, if path is not defined - - path: ./templates/ghost.html - name: ghost # name is optional, if path is defined - content: ${GHOST_TEMPLATE_CONTENT} - - path: ./templates/l7-light.html - - path: ./templates/l7-dark.html - - path: ./templates/shuffle.html - - path: ./templates/noise.html - - path: ./templates/hacker-terminal.html - - path: ./templates/cats.html - - path: ./templates/lost-in-space.html - - path: ./templates/app-down.html - - path: ./templates/connection.html - - path: ./templates/matrix.html - - path: ./templates/orient.html - -formats: - json: - content: | - { - "error": true, - "code": {{ code | json }}, - "message": {{ message | json }}, - "description": {{ description | json }}{{ if show_details }}, - "details": { - "host": {{ host | json }}, - "original_uri": {{ original_uri | json }}, - "forwarded_for": {{ forwarded_for | json }}, - "namespace": {{ namespace | json }}, - "ingress_name": {{ ingress_name | json }}, - "service_name": {{ service_name | json }}, - "service_port": {{ service_port | json }}, - "request_id": {{ request_id | json }}, - "timestamp": {{ now.Unix }} - }{{ end }} - } - - xml: - content: | - - - {{ code }} - {{ message }} - {{ description }}{{ if show_details }} -
- {{ host }} - {{ original_uri }} - {{ forwarded_for }} - {{ namespace }} - {{ ingress_name }} - {{ service_name }} - {{ service_port }} - {{ request_id }} - {{ now.Unix }} -
{{ end }} -
- -pages: - 400: - message: Bad Request - description: The server did not understand the request - - 401: - message: Unauthorized - description: The requested page needs a username and a password - - 403: - message: Forbidden - description: Access is forbidden to the requested page - - 404: - message: Not Found - description: The server can not find the requested page - - 405: - message: Method Not Allowed - description: The method specified in the request is not allowed - - 407: - message: Proxy Authentication Required - description: You must authenticate with a proxy server before this request can be served - - 408: - message: Request Timeout - description: The request took longer than the server was prepared to wait - - 409: - message: Conflict - description: The request could not be completed because of a conflict - - 410: - message: Gone - description: The requested page is no longer available - - 411: - message: Length Required - description: The "Content-Length" is not defined. The server will not accept the request without it - - 412: - message: Precondition Failed - description: The pre condition given in the request evaluated to false by the server - - 413: - message: Payload Too Large - description: The server will not accept the request, because the request entity is too large - - 416: - message: Requested Range Not Satisfiable - description: The requested byte range is not available and is out of bounds - - 418: - message: I'm a teapot - description: Attempt to brew coffee with a teapot is not supported - - 429: - message: Too Many Requests - description: Too many requests in a given amount of time - - 500: - message: Internal Server Error - description: The server met an unexpected condition - - 502: - message: Bad Gateway - description: The server received an invalid response from the upstream server - - 503: - message: Service Unavailable - description: The server is temporarily overloading or down - - 504: - message: Gateway Timeout - description: The gateway has timed out - - 505: - message: HTTP Version Not Supported - description: The server does not support the "http protocol" version diff --git a/go.mod b/go.mod index 208e3d4..adfefaa 100644 --- a/go.mod +++ b/go.mod @@ -3,41 +3,20 @@ module gh.tarampamp.am/error-pages go 1.22 require ( - github.com/a8m/envsubst v1.4.2 - github.com/fasthttp/router v1.5.1 - github.com/fatih/color v1.17.0 - github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d - github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.19.1 - github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.9.0 github.com/urfave/cli-docs/v3 v3.0.0-alpha5 - github.com/urfave/cli/v2 v2.27.2 github.com/urfave/cli/v3 v3.0.0-alpha9 - github.com/valyala/fasthttp v1.54.0 go.uber.org/automaxprocs v1.5.3 - go.uber.org/goleak v1.3.0 - go.uber.org/zap v1.27.0 - gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/klauspost/compress v1.17.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/common v0.50.0 // indirect - github.com/prometheus/procfs v0.13.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.18.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c8bf601..0f972aa 100644 --- a/go.sum +++ b/go.sum @@ -1,82 +1,35 @@ -github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg= -github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fasthttp/router v1.5.1 h1:uViy8UYYhm5npJSKEZ4b/ozM//NGzVCfJbh6VJ0VKr8= -github.com/fasthttp/router v1.5.1/go.mod h1:WrmsLo3mrerZP2VEXRV1E8nL8ymJFYCDTr4HmnB8+Zs= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d h1:cVtBfNW5XTHiKQe7jDaDBSh/EVM4XLPutLAGboIXuM0= -github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ= -github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ= -github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= -github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8= -github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli-docs/v3 v3.0.0-alpha5 h1:H1oWnR2/GN0dNm2PVylws+GxSOD6YOwW/jI5l78YfPk= github.com/urfave/cli-docs/v3 v3.0.0-alpha5/go.mod h1:AIqom6Q60U4tiqHp41i7+/AB2XHgi1WvQ7jOFlccmZ4= -github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= -github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo= github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0= -github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/appmeta/version_test.go b/internal/appmeta/version_test.go index e382c58..d78bcc0 100644 --- a/internal/appmeta/version_test.go +++ b/internal/appmeta/version_test.go @@ -3,6 +3,8 @@ package appmeta import "testing" func TestVersion(t *testing.T) { + t.Parallel() + for give, want := range map[string]string{ // without changes "vvv": "vvv", diff --git a/internal/cli/app_test.go b/internal/cli/app_test.go index 31ea661..152bae9 100644 --- a/internal/cli/app_test.go +++ b/internal/cli/app_test.go @@ -4,7 +4,6 @@ import ( "context" "testing" - "github.com/kami-zh/go-capturer" "github.com/stretchr/testify/assert" "gh.tarampamp.am/error-pages/internal/cli" @@ -15,11 +14,5 @@ func TestNewApp(t *testing.T) { app := cli.NewApp("appName") - assert.NotEmpty(t, app.Flags) - - output := capturer.CaptureStdout(func() { - assert.NoError(t, app.Run(context.Background(), []string{""})) - }) - - assert.NotEmpty(t, output) + assert.NoError(t, app.Run(context.Background(), []string{""})) } diff --git a/internal/cli/perftest/command.go b/internal/cli/perftest/command.go index 733ec88..4371d63 100644 --- a/internal/cli/perftest/command.go +++ b/internal/cli/perftest/command.go @@ -73,7 +73,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit logger.Uint64("failed", failed.Load()), logger.Duration("duration", time.Since(startedAt)), logger.Float64("RPS", float64(success.Load()+failed.Load())/time.Since(startedAt).Seconds()), - logger.Float64("errors rate", float64(failed.Load())/float64(success.Load()+failed.Load())*100), + logger.Float64("errors rate", float64(failed.Load())/float64(success.Load()+failed.Load())*100), //nolint:mnd ) }() @@ -99,7 +99,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit var req, rErr = makeRequest(perfCtx, uint16(c.Uint(portFlag.Name))) if rErr != nil { - log.Error("failed to create a new request", logger.Error(rErr)) + log.Error("Failed to create a new request", logger.Error(rErr)) return } @@ -110,11 +110,11 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit var resp, respErr = httpClient.Do(req) if resp != nil { if _, err := io.Copy(io.Discard, resp.Body); err != nil && !errIsDone(err) { - log.Error("failed to read response body", logger.Error(err)) + log.Error("Failed to read response body", logger.Error(err)) } if err := resp.Body.Close(); err != nil && !errIsDone(err) { - log.Error("failed to close response body", logger.Error(err)) + log.Error("Failed to close response body", logger.Error(err)) } } @@ -123,7 +123,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit return } - log.Error("request failed", logger.Error(respErr)) + log.Error("Request failed", logger.Error(respErr)) failed.Add(1) continue diff --git a/internal/cli/serve/command.go b/internal/cli/serve/command.go index ddcd170..002064e 100644 --- a/internal/cli/serve/command.go +++ b/internal/cli/serve/command.go @@ -110,7 +110,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy OnlyOnce: true, } proxyHeadersListFlag = cli.StringFlag{ - Name: "proxy-headers", // TODO: add support for the "*" wildcard + Name: "proxy-headers", Usage: "listed here HTTP headers will be proxied from the original request to the error page response " + "(comma-separated list)", Value: strings.Join(cfg.ProxyHeaders, ","), diff --git a/internal/cli/serve/command_test.go b/internal/cli/serve/command_test.go new file mode 100644 index 0000000..ef04557 --- /dev/null +++ b/internal/cli/serve/command_test.go @@ -0,0 +1,101 @@ +package serve_test + +import ( + "context" + "fmt" + "net" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "gh.tarampamp.am/error-pages/internal/cli/serve" + "gh.tarampamp.am/error-pages/internal/logger" +) + +func TestCommand_Run(t *testing.T) { + t.Parallel() + + var ( + port = getFreeTcpPort(t) + cmd = serve.NewCommand(logger.NewNop()) + ) + + var ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + var ch = make(chan error, 1) + + go func() { + defer close(ch) + + ch <- cmd.Run(ctx, []string{ + "serve", + "--port", strconv.Itoa(int(port)), + "--add-template", "./testdata/foo-template.html", + "--disable-template", "ghost", + "--disable-template", "", + "--add-http-code", "200=Code/Description", + "--json-format", "json format", + "--xml-format", "xml format", + "--plaintext-format", "plaintext format", + "--template-name", "foo-template", + "--disable-l10n", + "--default-error-page", "503", + "--send-same-http-code", + "--show-details", + "--proxy-headers", "X-Forwarded-For,X-Forwarded-Proto", + "--rotation-mode", "random-on-each-request", + }) + }() + + var connected bool + + for { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), time.Second) + if err == nil { + connected = true + + require.NoError(t, conn.Close()) + + break + } else { + t.Log(err) + } + + select { + case <-ctx.Done(): + t.Fatal("timeout") + case chErr := <-ch: + require.NoError(t, chErr) + case <-time.After(10 * time.Millisecond): + } + } + + require.True(t, connected, "server is not running") +} + +// getFreeTcpPort is a helper function to get a free TCP port number. +func getFreeTcpPort(t *testing.T) uint16 { + t.Helper() + + l, lErr := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, lErr) + + port := l.Addr().(*net.TCPAddr).Port + require.NoError(t, l.Close()) + + // make sure port is closed + for { + conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + break + } + + require.NoError(t, conn.Close()) + <-time.After(5 * time.Millisecond) + } + + return uint16(port) +} diff --git a/internal/cli/serve/testdata/foo-template.html b/internal/cli/serve/testdata/foo-template.html new file mode 100644 index 0000000..04e75bc --- /dev/null +++ b/internal/cli/serve/testdata/foo-template.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + diff --git a/internal/http/handlers/error_page/handler_test.go b/internal/http/handlers/error_page/handler_test.go new file mode 100644 index 0000000..e6e608f --- /dev/null +++ b/internal/http/handlers/error_page/handler_test.go @@ -0,0 +1,226 @@ +package error_page_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + + "gh.tarampamp.am/error-pages/internal/config" + "gh.tarampamp.am/error-pages/internal/http/handlers/error_page" + "gh.tarampamp.am/error-pages/internal/logger" +) + +func TestHandler(t *testing.T) { + t.Parallel() + + for name, tt := range map[string]struct { + giveConfig func() *config.Config + giveUrl string + giveHeaders map[string]string + + wantStatusCode int + wantHeaders map[string]string + wantBodyIncludes []string + }{ + "common, plain text": { + giveConfig: func() *config.Config { cfg := config.New(); return &cfg }, + giveUrl: "/", + giveHeaders: map[string]string{"Content-Type": "text/plain"}, + + wantStatusCode: http.StatusOK, + wantHeaders: map[string]string{"Content-Type": "text/plain; charset=utf-8"}, + wantBodyIncludes: []string{"Error 404", "Not Found"}, + }, + "common, html": { + giveConfig: func() *config.Config { + cfg := config.New() + + cfg.TemplateName = "ghost" + + return &cfg + }, + giveUrl: "/", + giveHeaders: map[string]string{"X-Format": "text/html", "X-Code": "407"}, + + wantStatusCode: http.StatusOK, + wantHeaders: map[string]string{"Content-Type": "text/html; charset=utf-8"}, + wantBodyIncludes: []string{ + "", + "407: Proxy Authentication Required", + "Proxy Authentication Required", + }, + }, + "common, json": { + giveConfig: func() *config.Config { + cfg := config.New() + + cfg.RespondWithSameHTTPCode = true + + return &cfg + }, + giveUrl: "/503.html", + giveHeaders: map[string]string{"Accept": "application/json", "X-FooBar": "baz"}, + + wantStatusCode: http.StatusServiceUnavailable, + wantHeaders: map[string]string{ + "Content-Type": "application/json; charset=utf-8", + "X-FooBar": "", // is not in the list of proxy headers + }, + wantBodyIncludes: []string{"503", "Service Unavailable"}, + }, + "common, xml": { + giveConfig: func() *config.Config { + cfg := config.New() + + cfg.ProxyHeaders = append(cfg.ProxyHeaders, "X-FooBar") + + return &cfg + }, + giveUrl: "/500", + giveHeaders: map[string]string{"Accept": "application/xml", "X-FooBar": "baz"}, + + wantStatusCode: http.StatusOK, + wantHeaders: map[string]string{ + "Content-Type": "application/xml; charset=utf-8", + "X-FooBar": "baz", + }, + wantBodyIncludes: []string{"500", "Internal Server Error"}, + }, + "show details": { + giveConfig: func() *config.Config { + cfg := config.New() + + cfg.ShowDetails = true + + return &cfg + }, + giveUrl: "/503", + giveHeaders: map[string]string{ + "Accept": "application/json", + "X-Original-URI": "/foo/bar", + "X-Namespace": "some-Namespace", + "X-Ingress-Name": "ingress-name", + "X-Service-Name": "service-name", + "X-Service-Port": "666", + "X-Request-ID": "req-id-777", + "X-Forwarded-For": "123.123.123.123:12312", + "Host": "example.com", + }, + + wantStatusCode: http.StatusOK, + wantHeaders: map[string]string{"Content-Type": "application/json; charset=utf-8"}, + wantBodyIncludes: []string{ + "503", + "Service Unavailable", + "details", + "/foo/bar", + "some-Namespace", + "ingress-name", + "service-name", + "666", + "req-id-777", + "123.123.123.123:12312", + "example.com", + }, + }, + "fallback to StatusText if code is not found": { + giveConfig: func() *config.Config { + cfg := config.New() + + cfg.Codes = config.Codes{} + + return &cfg + }, + giveUrl: "/100", + giveHeaders: map[string]string{"Accept": "application/json"}, + + wantStatusCode: http.StatusOK, + wantHeaders: map[string]string{"Content-Type": "application/json; charset=utf-8"}, + wantBodyIncludes: []string{"100", "Continue"}, + }, + "unknown code": { + giveConfig: func() *config.Config { + cfg := config.New() + + cfg.Codes = config.Codes{} + + return &cfg + }, + giveUrl: "/1", + giveHeaders: map[string]string{"Accept": "application/json"}, + + wantStatusCode: http.StatusOK, + wantHeaders: map[string]string{"Content-Type": "application/json; charset=utf-8"}, + wantBodyIncludes: []string{"1", "Unknown Status Code"}, + }, + } { + t.Run(name, func(t *testing.T) { + t.Parallel() + + var ( + req = httptest.NewRequest(http.MethodGet, tt.giveUrl, http.NoBody) + handler = error_page.New(tt.giveConfig(), logger.NewNop()) + rr = httptest.NewRecorder() + ) + + for k, v := range tt.giveHeaders { + req.Header.Set(k, v) + } + + handler.ServeHTTP(rr, req) + + assert.Equal(t, rr.Code, tt.wantStatusCode) + + for hName, hWant := range tt.wantHeaders { + for hGot := range rr.Header() { + if hGot == hName { + assert.Contains(t, hWant, rr.Header().Get(hGot)) + } + } + } + + for _, wantBodyInclude := range tt.wantBodyIncludes { + assert.Contains(t, rr.Body.String(), wantBodyInclude) + } + }) + } +} + +func TestRotationModeOnEachRequest(t *testing.T) { + t.Parallel() + + var cfg = config.New() + + cfg.RotationMode = config.RotationModeRandomOnEachRequest + cfg.Templates = map[string]string{ + "foo": "foo", + "bar": "bar", + } + + var ( + lastResponseBody string + changedTimes int + + handler = error_page.New(&cfg, logger.NewNop()) + ) + + for range 300 { + var ( + req = httptest.NewRequest(http.MethodGet, "/", http.NoBody) + rr = httptest.NewRecorder() + ) + + req.Header.Set("Accept", "text/html") + + handler.ServeHTTP(rr, req) + + if lastResponseBody != rr.Body.String() { + changedTimes++ + lastResponseBody = rr.Body.String() + } + } + + assert.True(t, changedTimes > 30, "the template should be changed at least 30 times") +} diff --git a/internal/http/middleware/logreq/middleware_test.go b/internal/http/middleware/logreq/middleware_test.go new file mode 100644 index 0000000..0a93d31 --- /dev/null +++ b/internal/http/middleware/logreq/middleware_test.go @@ -0,0 +1,44 @@ +package logreq_test + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + + "gh.tarampamp.am/error-pages/internal/http/middleware/logreq" + "gh.tarampamp.am/error-pages/internal/logger" +) + +func TestNew(t *testing.T) { + t.Parallel() + + var ( + buf bytes.Buffer + log, _ = logger.New(logger.DebugLevel, logger.JSONFormat, &buf) + + mw = logreq.New(log, nil) + rr = httptest.NewRecorder() + req = httptest.NewRequest(http.MethodPut, "/foo/bar", http.NoBody) + ) + + req.Header.Set("User-Agent", "test") + req.Header.Set("Referer", "https://example.com") + req.Header.Set("Content-Type", "application/json") + + mw(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })).ServeHTTP(rr, req) + + var logRecord = buf.String() + + assert.Contains(t, logRecord, `"level":"info"`) + assert.Contains(t, logRecord, `"msg":"HTTP request processed"`) + assert.Contains(t, logRecord, `"useragent":"test"`) + assert.Contains(t, logRecord, `"method":"PUT"`) + assert.Contains(t, logRecord, `"url":"/foo/bar"`) + assert.Contains(t, logRecord, `"referer":"https://example.com"`) + assert.Contains(t, logRecord, `application/json`) +} diff --git a/profile001.svg b/profile001.svg deleted file mode 100644 index 73459fd..0000000 --- a/profile001.svg +++ /dev/null @@ -1,3116 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<!-- Generated by graphviz version 2.43.0 (0) - --> -<!-- Title: error-pages Pages: 1 --> -<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> -<script type="text/ecmascript"><![CDATA[ -/** - * SVGPan library 1.2.2 - * ====================== - * - * Given an unique existing element with id "viewport" (or when missing, the - * first g-element), including the library into any SVG adds the following - * capabilities: - * - * - Mouse panning - * - Mouse zooming (using the wheel) - * - Object dragging - * - * You can configure the behaviour of the pan/zoom/drag with the variables - * listed in the CONFIGURATION section of this file. - * - * Known issues: - * - * - Zooming (while panning) on Safari has still some issues - * - * Releases: - * - * 1.2.2, Tue Aug 30 17:21:56 CEST 2011, Andrea Leofreddi - * - Fixed viewBox on root tag (#7) - * - Improved zoom speed (#2) - * - * 1.2.1, Mon Jul 4 00:33:18 CEST 2011, Andrea Leofreddi - * - Fixed a regression with mouse wheel (now working on Firefox 5) - * - Working with viewBox attribute (#4) - * - Added "use strict;" and fixed resulting warnings (#5) - * - Added configuration variables, dragging is disabled by default (#3) - * - * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui - * Fixed a bug with browser mouse handler interaction - * - * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui - * Updated the zoom code to support the mouse wheel on Safari/Chrome - * - * 1.0, Andrea Leofreddi - * First release - * - * This code is licensed under the following BSD license: - * - * Copyright 2009-2017 Andrea Leofreddi <a.leofreddi@vleo.net>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of Andrea Leofreddi. - */ - -"use strict"; - -/// CONFIGURATION -/// ====> - -var enablePan = 1; // 1 or 0: enable or disable panning (default enabled) -var enableZoom = 1; // 1 or 0: enable or disable zooming (default enabled) -var enableDrag = 0; // 1 or 0: enable or disable dragging (default disabled) -var zoomScale = 0.2; // Zoom sensitivity - -/// <==== -/// END OF CONFIGURATION - -var root = document.documentElement; - -var state = 'none', svgRoot = null, stateTarget, stateOrigin, stateTf; - -setupHandlers(root); - -/** - * Register handlers - */ -function setupHandlers(root){ - setAttributes(root, { - "onmouseup" : "handleMouseUp(evt)", - "onmousedown" : "handleMouseDown(evt)", - "onmousemove" : "handleMouseMove(evt)", - //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element - }); - - if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0) - window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari - else - window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others -} - -/** - * Retrieves the root element for SVG manipulation. The element is then cached into the svgRoot global variable. - */ -function getRoot(root) { - if(svgRoot == null) { - var r = root.getElementById("viewport") ? root.getElementById("viewport") : root.documentElement, t = r; - - while(t != root) { - if(t.getAttribute("viewBox")) { - setCTM(r, t.getCTM()); - - t.removeAttribute("viewBox"); - } - - t = t.parentNode; - } - - svgRoot = r; - } - - return svgRoot; -} - -/** - * Instance an SVGPoint object with given event coordinates. - */ -function getEventPoint(evt) { - var p = root.createSVGPoint(); - - p.x = evt.clientX; - p.y = evt.clientY; - - return p; -} - -/** - * Sets the current transform matrix of an element. - */ -function setCTM(element, matrix) { - var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; - - element.setAttribute("transform", s); -} - -/** - * Dumps a matrix to a string (useful for debug). - */ -function dumpMatrix(matrix) { - var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]"; - - return s; -} - -/** - * Sets attributes of an element. - */ -function setAttributes(element, attributes){ - for (var i in attributes) - element.setAttributeNS(null, i, attributes[i]); -} - -/** - * Handle mouse wheel event. - */ -function handleMouseWheel(evt) { - if(!enableZoom) - return; - - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - var delta; - - if(evt.wheelDelta) - delta = evt.wheelDelta / 360; // Chrome/Safari - else - delta = evt.detail / -9; // Mozilla - - var z = Math.pow(1 + zoomScale, delta); - - var g = getRoot(svgDoc); - - var p = getEventPoint(evt); - - p = p.matrixTransform(g.getCTM().inverse()); - - // Compute new scale matrix in current mouse position - var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y); - - setCTM(g, g.getCTM().multiply(k)); - - if(typeof(stateTf) == "undefined") - stateTf = g.getCTM().inverse(); - - stateTf = stateTf.multiply(k.inverse()); -} - -/** - * Handle mouse move event. - */ -function handleMouseMove(evt) { - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - var g = getRoot(svgDoc); - - if(state == 'pan' && enablePan) { - // Pan mode - var p = getEventPoint(evt).matrixTransform(stateTf); - - setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y)); - } else if(state == 'drag' && enableDrag) { - // Drag mode - var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse()); - - setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM())); - - stateOrigin = p; - } -} - -/** - * Handle click event. - */ -function handleMouseDown(evt) { - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - var g = getRoot(svgDoc); - - if( - evt.target.tagName == "svg" - || !enableDrag // Pan anyway when drag is disabled and the user clicked on an element - ) { - // Pan mode - state = 'pan'; - - stateTf = g.getCTM().inverse(); - - stateOrigin = getEventPoint(evt).matrixTransform(stateTf); - } else { - // Drag mode - state = 'drag'; - - stateTarget = evt.target; - - stateTf = g.getCTM().inverse(); - - stateOrigin = getEventPoint(evt).matrixTransform(stateTf); - } -} - -/** - * Handle mouse button release event. - */ -function handleMouseUp(evt) { - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - if(state == 'pan' || state == 'drag') { - // Quit pan mode - state = ''; - } -} -]]></script><g id="viewport" transform="scale(0.5,0.5) translate(0,0)"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 3228)"> -<title>error-pages - - -cluster_L - - - - -File: error-pages - - -File: error-pages -Type: cpu -Time: Jun 28, 2024 at 8:51pm (+04) -Duration: 35.93s, Total samples = 116.95s (325.54%) -Showing nodes accounting for 63.69s, 54.46% of 116.95s total -Dropped 934 nodes (cum <= 0.58s) -Dropped 112 edges (freq <= 0.12s) -Showing top 80 nodes out of 254 -See https://git.io/JfYMW for how to read the graph - - - - - -N1 - - -http -(*conn) -serve -0.29s (0.25%) -of 72.80s (62.25%) - - - - - -N2 - - -http -HandlerFunc -ServeHTTP -0.06s (0.051%) -of 59.60s (50.96%) - - - - - -N1->N2 - - - - - - - 59.60s - - - - - -N34 - - -bufio -(*Writer) -Flush -0.06s (0.051%) -of 7.58s (6.48%) - - - - - -N1->N34 - - - - - - - 5.95s - - - - - -N44 - - -http -(*conn) -readRequest -0.09s (0.077%) -of 3.84s (3.28%) - - - - - -N1->N44 - - - - - - - 3.84s - - - - - -N8 - - -http -(*Server) -Register -New -func4 -0.11s (0.094%) -of 52.49s (44.88%) - - - - - -N2->N8 - - - - - - - 52.49s - - - - - -N10 - - -http -(*Server) -Register -(*Server) -Register -New -func6 -func7 -0.06s (0.051%) -of 59.56s (50.93%) - - - - - -N2->N10 - - - - - - - 59.56s - - - - - -N3 - - -template -Render -0.07s (0.06%) -of 45.06s (38.53%) - - - - - -N7 - - -template -(*state) -walk -0.17s (0.15%) -of 13.77s (11.77%) - - - - - -N3->N7 - - - - - - - 13.77s - - - - - -N42 - - -template -(*Template) -Parse -0.05s (0.043%) -of 20.68s (17.68%) - - - - - -N3->N42 - - - - - - - 20.68s - - - - - -N68 - - -template -(*Template) -Funcs -0.04s (0.034%) -of 6.15s (5.26%) - - - - - -N3->N68 - - - - - - - 6.15s - - - - - -N4 - - -runtime -systemstack -0.03s (0.026%) -of 40.57s (34.69%) - - - - - -N46 - - -runtime -wbBufFlush1 -0.80s (0.68%) -of 3.66s (3.13%) - - - - - -N4->N46 - - - - - - - 3.28s - - - - - -N57 - - -runtime -futex -1.90s (1.62%) - - - - - -N4->N57 - - - - - - - 0.18s - - - - - -N66 - - -runtime -(*sweepLocked) -sweep -0.55s (0.47%) -of 1.84s (1.57%) - - - - - -N4->N66 - - - - - - - 0.60s - - - - - -N80 - - -runtime -gcBgMarkWorker -func2 -0 of 31.60s (27.02%) - - - - - -N4->N80 - - - - - - - 31.60s - - - - - -N5 - - -runtime -mallocgc -2.43s (2.08%) -of 14.73s (12.60%) - - - - - -N5->N4 - - - - - - - 3.13s - - - - - -N24 - - -runtime -(*mspan) -base -4.83s (4.13%) - - - - - -N5->N24 - - - - - - - 0.98s - (inline) - - - - - -N63 - - -runtime -memclrNoHeapPointers -1.56s (1.33%) - - - - - -N5->N63 - - - - - - - 1.10s - - - - - -N6 - - -runtime -gcDrain -1.28s (1.09%) -of 31.59s (27.01%) - - - - - -N11 - - -runtime -scanobject -2.72s (2.33%) -of 14.96s (12.79%) - - - - - -N6->N11 - - - - - - - 13.35s - - - - - -N48 - - -runtime -markroot -0.17s (0.15%) -of 14.97s (12.80%) - - - - - -N6->N48 - - - - - - - 14.78s - - - - - -N56 - - -runtime -wbBufFlush -0.01s (0.0086%) -of 3.58s (3.06%) - - - - - -N6->N56 - - - - - - - 0.25s - - - - - -N22 - - -runtime -memmove -3.50s (2.99%) - - - - - -N7->N22 - - - - - - - 0.49s - - - - - -N29 - - -template -(*state) -evalPipeline -0.16s (0.14%) -of 9.16s (7.83%) - - - - - -N7->N29 - - - - - - - 7.04s - - - - - -N51 - - -template -(*state) -walkIfOrWith -0.06s (0.051%) -of 9.31s (7.96%) - - - - - -N7->N51 - - - - - - - 9.31s - - - - - -N77 - - -strings -(*Builder) -Write -0.10s (0.086%) -of 2.50s (2.14%) - - - - - -N7->N77 - - - - - - - 2.50s - - - - - -N8->N3 - - - - - - - 45.06s - - - - - -N67 - - -error_page -write[go -shape -string] -0.01s (0.0086%) -of 5.78s (4.94%) - - - - - -N8->N67 - - - - - - - 5.78s - - - - - -N9 - - -runtime -gcBgMarkWorker -0.05s (0.043%) -of 32.91s (28.14%) - - - - - -N9->N4 - - - - - - - 32.71s - - - - - -N10->N2 - - - - - - - 52.73s - - - - - -N50 - - -slog -(*commonHandler) -handle -0.11s (0.094%) -of 5.38s (4.60%) - - - - - -N10->N50 - - - - - - - 5.38s - - - - - -N21 - - -runtime -greyobject -0.74s (0.63%) -of 8.17s (6.99%) - - - - - -N11->N21 - - - - - - - 7.01s - - - - - -N26 - - -runtime -findObject -4.50s (3.85%) -of 6.05s (5.17%) - - - - - -N11->N26 - - - - - - - 3.21s - - - - - -N12 - - -parse -(*Tree) -pipeline -0.25s (0.21%) -of 9.50s (8.12%) - - - - - -N19 - - -parse -(*Tree) -nextNonSpace -1.07s (0.91%) -of 7.65s (6.54%) - - - - - -N12->N19 - - - - - - - 0.63s - - - - - -N30 - - -parse -(*Tree) -command -0.19s (0.16%) -of 6.69s (5.72%) - - - - - -N12->N30 - - - - - - - 6.69s - - - - - -N52 - - -parse -(*Tree) -peekNonSpace -0.29s (0.25%) -of 3.45s (2.95%) - - - - - -N12->N52 - - - - - - - 0.93s - (inline) - - - - - -N13 - - -parse -(*Tree) -textOrAction -0.20s (0.17%) -of 16.52s (14.13%) - - - - - -N16 - - -parse -(*Tree) -action -0.14s (0.12%) -of 15.67s (13.40%) - - - - - -N13->N16 - - - - - - - 15.67s - - - - - -N59 - - -runtime -stringtoslicebyte -0.05s (0.043%) -of 2.44s (2.09%) - - - - - -N13->N59 - - - - - - - 0.83s - - - - - -N14 - - -syscall -Syscall6 -10.51s (8.99%) - - - - - -N15 - - -runtime -newobject -0.34s (0.29%) -of 6.43s (5.50%) - - - - - -N15->N5 - - - - - - - 6.08s - - - - - -N16->N12 - - - - - - - 6.40s - - - - - -N16->N19 - - - - - - - 2.42s - - - - - -N39 - - -parse -(*Tree) -parseControl -0.02s (0.017%) -of 12.92s (11.05%) - - - - - -N16->N39 - - - - - - - 12.92s - - - - - -N17 - - -runtime -mapassign_faststr -1.30s (1.11%) -of 6.46s (5.52%) - - - - - -N17->N15 - - - - - - - 0.61s - (inline) - - - - - -N61 - - -runtime -evacuate_faststr -0.63s (0.54%) -of 2.40s (2.05%) - - - - - -N17->N61 - - - - - - - 2.40s - - - - - -N18 - - -parse -(*lexer) -nextItem -1.16s (0.99%) -of 7.70s (6.58%) - - - - - -N53 - - -runtime -bulkBarrierPreWrite -0.45s (0.38%) -of 1.93s (1.65%) - - - - - -N18->N53 - - - - - - - 0.70s - - - - - -N74 - - -parse -lexIdentifier -0.25s (0.21%) -of 2.31s (1.98%) - - - - - -N18->N74 - - - - - - - 2.31s - - - - - -N65 - - -parse -(*Tree) -next -0.47s (0.4%) -of 6.64s (5.68%) - - - - - -N19->N65 - - - - - - - 6.57s - (inline) - - - - - -N20 - - -runtime -scanstack -0.10s (0.086%) -of 13.45s (11.50%) - - - - - -N27 - - -runtime -scanblock -0.86s (0.74%) -of 4.74s (4.05%) - - - - - -N20->N27 - - - - - - - 0.53s - - - - - -N28 - - -runtime -scanframeworker -0.21s (0.18%) -of 7.75s (6.63%) - - - - - -N20->N28 - - - - - - - 7.75s - - - - - -N60 - - -runtime -(*lfstack) -push -1.83s (1.56%) - - - - - -N20->N60 - - - - - - - 1.45s - - - - - -N75 - - -runtime -(*unwinder) -resolveInternal -0.90s (0.77%) -of 2.68s (2.29%) - - - - - -N20->N75 - - - - - - - 2.58s - - - - - -N21->N24 - - - - - - - 3.49s - (inline) - - - - - -N43 - - -runtime -(*gcBits) -bitp -3.16s (2.70%) -of 3.22s (2.75%) - - - - - -N21->N43 - - - - - - - 2.77s - (inline) - - - - - -N23 - - -parse -(*Tree) -parse -0.23s (0.2%) -of 19.28s (16.49%) - - - - - -N23->N13 - - - - - - - 16.52s - - - - - -N23->N19 - - - - - - - 1.18s - - - - - -N73 - - -parse -(*Tree) -peek -0.30s (0.26%) -of 2.25s (1.92%) - - - - - -N23->N73 - - - - - - - 0.89s - - - - - -N25 - - -runtime -growslice -0.29s (0.25%) -of 3.94s (3.37%) - - - - - -N25->N5 - - - - - - - 3.03s - - - - - -N25->N22 - - - - - - - 0.21s - - - - - -N25->N63 - - - - - - - 0.24s - - - - - -N26->N24 - - - - - - - 0.23s - (inline) - - - - - -N27->N21 - - - - - - - 1.15s - - - - - -N27->N26 - - - - - - - 0.62s - - - - - -N36 - - -runtime -getempty -0.49s (0.42%) -of 4.60s (3.93%) - - - - - -N27->N36 - - - - - - - 2.08s - - - - - -N28->N27 - - - - - - - 3.66s - - - - - -N41 - - -runtime -pcvalue -1.50s (1.28%) -of 3.27s (2.80%) - - - - - -N28->N41 - - - - - - - 1.41s - - - - - -N64 - - -template -(*state) -evalCommand -0.14s (0.12%) -of 8.86s (7.58%) - - - - - -N29->N64 - - - - - - - 8.86s - - - - - -N30->N25 - - - - - - - 0.75s - - - - - -N32 - - -gcWriteBarrier -0.33s (0.28%) -of 2.48s (2.12%) - - - - - -N30->N32 - - - - - - - 0.13s - - - - - -N30->N52 - - - - - - - 1.12s - (inline) - - - - - -N69 - - -parse -(*Tree) -operand -0.11s (0.094%) -of 4.27s (3.65%) - - - - - -N30->N69 - - - - - - - 4.27s - - - - - -N31 - - -runtime -findRunnable -0.28s (0.24%) -of 6.08s (5.20%) - - - - - -N31->N14 - - - - - - - 0.55s - - - - - -N31->N66 - - - - - - - 0.23s - - - - - -N32->N56 - - - - - - - 2.15s - - - - - -N33 - - -bufio -(*Writer) -Write -0.07s (0.06%) -of 4.22s (3.61%) - - - - - -N33->N22 - - - - - - - 0.14s - - - - - -N33->N34 - - - - - - - 1.63s - - - - - -N45 - - -http -(*chunkWriter) -Write -0.02s (0.017%) -of 5.26s (4.50%) - - - - - -N33->N45 - - - - - - - 4.06s - - - - - -N54 - - -http -checkConnErrorWriter -Write -0.06s (0.051%) -of 8.12s (6.94%) - - - - - -N33->N54 - - - - - - - 1.80s - - - - - -N34->N45 - - - - - - - 1.20s - - - - - -N34->N54 - - - - - - - 6.32s - - - - - -N35 - - -Value -call -1.08s (0.92%) -of 5.62s (4.81%) - - - - - -N35->N5 - - - - - - - 1.32s - - - - - -N35->N22 - - - - - - - 0.12s - - - - - -N35->N32 - - - - - - - 0.20s - - - - - -N40 - - -runtime -(*lfstack) -pop -4.24s (3.63%) - - - - - -N36->N40 - - - - - - - 4.03s - (inline) - - - - - -N37 - - -template -(*state) -evalCall -0.19s (0.16%) -of 6.98s (5.97%) - - - - - -N37->N29 - - - - - - - 1.18s - - - - - -N37->N35 - - - - - - - 5.62s - - - - - -N38 - - -parse -(*Tree) -itemList -0.06s (0.051%) -of 10.48s (8.96%) - - - - - -N38->N13 - - - - - - - 8.88s - - - - - -N38->N52 - - - - - - - 1.40s - (inline) - - - - - -N39->N12 - - - - - - - 3.10s - - - - - -N39->N38 - - - - - - - 10.48s - - - - - -N42->N17 - - - - - - - 0.48s - - - - - -N42->N23 - - - - - - - 19.28s - - - - - -N44->N17 - - - - - - - 0.20s - - - - - -N45->N33 - - - - - - - 3.62s - - - - - -N72 - - -runtime -mapiternext -0.75s (0.64%) -of 1.35s (1.15%) - - - - - -N45->N72 - - - - - - - 0.24s - - - - - -N46->N26 - - - - - - - 2.20s - - - - - -N46->N43 - - - - - - - 0.43s - (inline) - - - - - -N47 - - -runtime -mcall -0.03s (0.026%) -of 6.72s (5.75%) - - - - - -N78 - - -runtime -schedule -0.16s (0.14%) -of 6.95s (5.94%) - - - - - -N47->N78 - - - - - - - 6.36s - - - - - -N48->N11 - - - - - - - 0.18s - - - - - -N48->N20 - - - - - - - 13.45s - - - - - -N48->N27 - - - - - - - 0.55s - - - - - -N49 - - -runtime -mapaccess1_faststr -1.14s (0.97%) -of 2s (1.71%) - - - - - -N58 - - -slog -(*handleState) -appendAttr -0.16s (0.14%) -of 2.95s (2.52%) - - - - - -N50->N58 - - - - - - - 2.95s - - - - - -N62 - - -poll -(*FD) -Write -0.04s (0.034%) -of 9.03s (7.72%) - - - - - -N50->N62 - - - - - - - 1.08s - - - - - -N51->N7 - - - - - - - 7.49s - - - - - -N51->N29 - - - - - - - 2.12s - - - - - -N52->N19 - - - - - - - 3.14s - - - - - -N53->N56 - - - - - - - 1.09s - - - - - -N54->N62 - - - - - - - 7.95s - - - - - -N55 - - -parse -(*Tree) -term -0.31s (0.27%) -of 2.96s (2.53%) - - - - - -N55->N12 - - - - - - - 1.33s - - - - - -N55->N15 - - - - - - - 0.71s - - - - - -N55->N19 - - - - - - - 0.17s - - - - - -N55->N49 - - - - - - - 0.61s - - - - - -N56->N4 - - - - - - - 3.28s - - - - - -N58->N5 - - - - - - - 0.34s - - - - - -N58->N32 - - - - - - - 0.16s - - - - - -N59->N5 - - - - - - - 1.12s - - - - - -N59->N22 - - - - - - - 1.14s - - - - - -N61->N32 - - - - - - - 0.26s - - - - - -N61->N53 - - - - - - - 0.55s - - - - - -N71 - - -poll -ignoringEINTRIO -0 of 10.50s (8.98%) - - - - - -N62->N71 - - - - - - - 8.88s - (inline) - - - - - -N70 - - -template -(*state) -evalFunction -0.06s (0.051%) -of 7.43s (6.35%) - - - - - -N64->N70 - - - - - - - 7.43s - - - - - -N65->N18 - - - - - - - 5.85s - - - - - -N66->N4 - - - - - - - 0.23s - - - - - -N67->N33 - - - - - - - 4.10s - - - - - -N67->N59 - - - - - - - 1.60s - - - - - -N68->N17 - - - - - - - 4.38s - - - - - -N68->N72 - - - - - - - 0.75s - - - - - -N69->N55 - - - - - - - 2.96s - - - - - -N69->N73 - - - - - - - 1.33s - - - - - -N70->N37 - - - - - - - 6.63s - - - - - -N70->N49 - - - - - - - 0.48s - - - - - -N79 - - -syscall -Syscall -0.05s (0.043%) -of 10.43s (8.92%) - - - - - -N71->N79 - - - - - - - 10.43s - - - - - -N72->N32 - - - - - - - 0.38s - - - - - -N73->N18 - - - - - - - 1.85s - - - - - -N74->N49 - - - - - - - 0.54s - - - - - -N76 - - -parse -(*lexer) -next -0.71s (0.61%) -of 1.63s (1.39%) - - - - - -N74->N76 - - - - - - - 0.76s - - - - - -N75->N41 - - - - - - - 1.77s - - - - - -N77->N22 - - - - - - - 0.97s - - - - - -N77->N25 - - - - - - - 1.33s - - - - - -N78->N31 - - - - - - - 6.08s - - - - - -N79->N14 - - - - - - - 9.86s - - - - - -N80->N6 - - - - - - - 31.59s - - - - - diff --git a/schemas/config/1.0.schema.json b/schemas/config/1.0.schema.json deleted file mode 100644 index 4f0ee3a..0000000 --- a/schemas/config/1.0.schema.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Error-Pages config file schema", - "description": "Error-Pages config file schema.", - "type": "object", - "properties": { - "templates": { - "type": "array", - "description": "Templates list", - "items": { - "type": "object", - "description": "Template properties", - "properties": { - "path": { - "type": "string", - "description": "Path to the template file", - "examples": [ - "./templates/ghost.html", - "/opt/tpl/ghost.htm" - ] - }, - "name": { - "type": "string", - "description": "Template name (optional, if path is defined)", - "examples": [ - "ghost" - ] - }, - "content": { - "type": "string", - "description": "Template content, if path is not defined", - "examples": [ - "{{ code }}: {{ message }}" - ] - } - }, - "additionalProperties": false - } - }, - "formats": { - "type": "object", - "description": "Responses, based on requested content-type format", - "properties": { - "json": { - "type": "object", - "description": "JSON format", - "properties": { - "content": { - "type": "string", - "description": "JSON response body (template tags are allowed here)", - "examples": [ - "{\"error\": true, \"code\": {{ code | json }}, \"message\": {{ message | json }}}" - ] - } - }, - "additionalProperties": false - }, - "xml": { - "type": "object", - "description": "XML format", - "properties": { - "content": { - "type": "string", - "description": "XML response body (template tags are allowed here)", - "examples": [ - "{{ code }}{{ message }}" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - "pages": { - "type": "object", - "description": "Error pages (codes)", - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "type": "object", - "description": "Error page (code)", - "properties": { - "message": { - "type": "string", - "description": "Error page message (title)", - "examples": [ - "Bad Request" - ] - }, - "description": { - "type": "string", - "description": "Error page description", - "examples": [ - "The server did not understand the request" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false, - "required": [ - "templates" - ] -} diff --git a/schemas/config/readme.md b/schemas/config/readme.md deleted file mode 100644 index d97bb81..0000000 --- a/schemas/config/readme.md +++ /dev/null @@ -1,13 +0,0 @@ -# Config file schemas - -These schemas describe Error Pages configuration file and used by: - -- - -Schemas naming agreement: `..schema.json`. - -## Contributing guide - -If you want to modify the existing schema - your changes **MUST** be backward compatible. If your changes break backward compatibility - you **MUST** create a new schema file with a fresh version and "register" it in a [schemas catalog][schemas_catalog]. - -[schemas_catalog]:https://github.com/SchemaStore/schemastore/blob/master/src/api/json/catalog.json diff --git a/schemas/readme.md b/schemas/readme.md deleted file mode 100644 index 870bc14..0000000 --- a/schemas/readme.md +++ /dev/null @@ -1,15 +0,0 @@ -# Schemas - -This directory contains public schemas for the most important parts of application. - -**Do not rename or remove this directory or any file or directory inside.** - -- You can validate existing config file using the following command: - - ```bash - $ docker run --rm -v "$(pwd):/src" -w "/src" node:16-alpine sh -c \ - "npm install -g ajv-cli && \ - ajv validate --all-errors --verbose \ - -s ./schemas/config/1.0.schema.json \ - -d ./error-pages.y*ml" - ```