mirror of
https://github.com/tarampampam/error-pages.git
synced 2024-08-30 18:22:40 +00:00
Compare commits
19 Commits
v3.0.0-bet
...
master
Author | SHA1 | Date | |
---|---|---|---|
9cafa90b4f | |||
4892822343 | |||
9fe6b32572 | |||
ff2c6a6e11 | |||
3e90b7c71e | |||
c15991458c | |||
7a2d3c1337 | |||
141c18cf29 | |||
b677064733 | |||
3782a875e2 | |||
1241579222 | |||
ac865804dd | |||
cf475cb98b | |||
086aa29fda | |||
6d40c7797a | |||
052409f945 | |||
5462a1f664 | |||
b4e9ea5ea6 | |||
a19cc5cb76 |
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- if: matrix.os == 'linux' && matrix.arch == 'amd64'
|
||||
run: mkdir ./out && ./${{ steps.values.outputs.binary-name }} build --index --target-dir ./out
|
||||
run: mkdir ./out && ./${{ steps.values.outputs.binary-name }} build --index --disable-minification --target-dir ./out
|
||||
- if: matrix.os == 'linux' && matrix.arch == 'amd64'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -93,13 +93,12 @@ jobs:
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||
build-args: "APP_VERSION=${{ steps.slug.outputs.version }}"
|
||||
tags: ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}:${{ steps.slug.outputs.version }}
|
||||
# tags: | # TODO: uncomment after the stable release
|
||||
# tarampampam/error-pages:latest
|
||||
# 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 }}
|
||||
# ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}: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 }}
|
||||
tags: |
|
||||
tarampampam/error-pages:latest
|
||||
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 }}
|
||||
ghcr.io/${{ github.actor }}/${{ github.event.repository.name }}: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 }}
|
||||
|
@ -61,7 +61,7 @@ linters: # All available linters list: <https://golangci-lint.run/usage/linters/
|
||||
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||
- errorlint # find code that will cause problems with the error wrapping scheme introduced in Go 1.13
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- exportloopref # checks for pointers to enclosing loop variables
|
||||
- copyloopvar # detects places where loop variables are copied
|
||||
- funlen # Tool for detection of long functions
|
||||
- gci # Gci control golang package import order and make it always deterministic
|
||||
- godot # Check if comments end in a period
|
||||
|
27
Dockerfile
27
Dockerfile
@ -3,12 +3,12 @@
|
||||
# -✂- this stage is used to develop and build the application locally -------------------------------------------------
|
||||
FROM docker.io/library/golang:1.22-bookworm AS develop
|
||||
|
||||
# use the /var/tmp as the GOPATH to reuse the modules cache
|
||||
# use the /var/tmp/go as the GOPATH to reuse the modules cache
|
||||
ENV GOPATH="/var/tmp/go"
|
||||
|
||||
RUN set -x \
|
||||
# renovate: source=github-releases name=golangci/golangci-lint
|
||||
&& GOLANGCI_LINT_VERSION="1.59.1" \
|
||||
&& GOLANGCI_LINT_VERSION="1.60.3" \
|
||||
&& wget -O- -nv "https://cdn.jsdelivr.net/gh/golangci/golangci-lint@v${GOLANGCI_LINT_VERSION}/install.sh" \
|
||||
| sh -s -- -b /bin "v${GOLANGCI_LINT_VERSION}"
|
||||
|
||||
@ -32,7 +32,10 @@ FROM develop AS compile
|
||||
# can be passed with any prefix (like `v1.2.3@GITHASH`), e.g.: `docker build --build-arg "APP_VERSION=v1.2.3" .`
|
||||
ARG APP_VERSION="undefined@docker"
|
||||
|
||||
RUN --mount=type=bind,source=.,target=/src set -x \
|
||||
# copy the source code
|
||||
COPY . /src
|
||||
|
||||
RUN set -x \
|
||||
&& go generate ./... \
|
||||
&& CGO_ENABLED=0 LDFLAGS="-s -w -X gh.tarampamp.am/error-pages/internal/appmeta.version=${APP_VERSION}" \
|
||||
go build -trimpath -ldflags "${LDFLAGS}" -o /tmp/error-pages ./cmd/error-pages/ \
|
||||
@ -45,10 +48,11 @@ FROM docker.io/library/alpine:3.20 AS rootfs
|
||||
WORKDIR /tmp/rootfs
|
||||
|
||||
# prepare rootfs for runtime
|
||||
RUN --mount=type=bind,source=.,target=/src set -x \
|
||||
&& mkdir -p ./etc ./bin \
|
||||
RUN set -x \
|
||||
&& mkdir -p ./etc/ssl/certs ./bin \
|
||||
&& echo 'appuser:x:10001:10001::/nonexistent:/sbin/nologin' > ./etc/passwd \
|
||||
&& echo 'appuser:x:10001:' > ./etc/group
|
||||
&& echo 'appuser:x:10001:' > ./etc/group \
|
||||
&& cp /etc/ssl/certs/ca-certificates.crt ./etc/ssl/certs/
|
||||
|
||||
# take the binary from the compile stage
|
||||
COPY --from=compile /tmp/error-pages ./bin/error-pages
|
||||
@ -69,7 +73,7 @@ ARG APP_VERSION="undefined@docker"
|
||||
LABEL \
|
||||
# docs: https://github.com/opencontainers/image-spec/blob/master/annotations.md
|
||||
org.opencontainers.image.title="error-pages" \
|
||||
org.opencontainers.image.description="Static server error pages in the docker image" \
|
||||
org.opencontainers.image.description="Pretty server's error pages" \
|
||||
org.opencontainers.image.url="https://github.com/tarampampam/error-pages" \
|
||||
org.opencontainers.image.source="https://github.com/tarampampam/error-pages" \
|
||||
org.opencontainers.image.vendor="tarampampam" \
|
||||
@ -87,13 +91,12 @@ WORKDIR /opt
|
||||
# to find out which environment variables and CLI arguments are supported by the application, run the app
|
||||
# with the `--help` flag or refer to the documentation at https://github.com/tarampampam/error-pages#readme
|
||||
|
||||
ENV LOG_LEVEL="warn"
|
||||
ENV LOG_LEVEL="warn" \
|
||||
LOG_FORMAT="json"
|
||||
|
||||
# docs: https://docs.docker.com/reference/dockerfile/#healthcheck
|
||||
HEALTHCHECK --interval=10s --start-interval=1s --start-period=5s --timeout=2s CMD [\
|
||||
"/bin/error-pages", "--log-format", "json", "healthcheck" \
|
||||
]
|
||||
HEALTHCHECK --interval=10s --start-interval=1s --start-period=5s --timeout=2s CMD ["/bin/error-pages", "healthcheck"]
|
||||
|
||||
ENTRYPOINT ["/bin/error-pages"]
|
||||
|
||||
CMD ["--log-format", "json", "serve"]
|
||||
CMD ["serve"]
|
||||
|
457
README.md
457
README.md
@ -1,5 +1,10 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/tarampampam/error-pages#readme"><img src="https://socialify.git.ci/tarampampam/error-pages/image?description=1&font=Raleway&forks=1&issues=1&logo=https%3A%2F%2Fhsto.org%2Fwebt%2Frm%2F9y%2Fww%2Frm9ywwx3gjv9agwkcmllhsuyo7k.png&owner=1&pulls=1&pattern=Solid&stargazers=1&theme=Dark" alt="banner" width="100%" /></a>
|
||||
<a href="https://github.com/tarampampam/error-pages#readme">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://socialify.git.ci/tarampampam/error-pages/image?description=1&font=Raleway&forks=1&issues=1&logo=https%3A%2F%2Fhsto.org%2Fwebt%2Frm%2F9y%2Fww%2Frm9ywwx3gjv9agwkcmllhsuyo7k.png&owner=1&pulls=1&pattern=Solid&stargazers=1&theme=Dark">
|
||||
<img align="center" src="https://socialify.git.ci/tarampampam/error-pages/image?description=1&font=Raleway&forks=1&issues=1&logo=https%3A%2F%2Fhsto.org%2Fwebt%2Frm%2F9y%2Fww%2Frm9ywwx3gjv9agwkcmllhsuyo7k.png&owner=1&pulls=1&pattern=Solid&stargazers=1&theme=Light">
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@ -15,21 +20,25 @@ One day, you might want to replace the standard error pages of your HTTP server
|
||||
original and attractive. That's why this repository was created :) It contains:
|
||||
|
||||
- A simple error page generator written in Go
|
||||
- Single-page error templates (themes) with various designs (located in the [templates](templates) directory) that
|
||||
- Single-page error templates (themes) with various designs (located in the [templates][templates-dir] directory) that
|
||||
you can customize as you wish
|
||||
- A fast and lightweight HTTP server is available as a single binary file and Docker image. It includes built-in error
|
||||
page templates from this repository. You don't need anything except the compiled binary file or Docker image
|
||||
- Pre-generated error pages (sources can be [found here][preview-sources], and the **demo** is always
|
||||
accessible [here][preview-demo])
|
||||
- Pre-generated error pages (sources can be [found here][preview-sources], and the [**demo** is always
|
||||
accessible here][preview-demo])
|
||||
|
||||
[preview-sources]:https://github.com/tarampampam/error-pages/tree/gh-pages
|
||||
[preview-demo]:https://tarampampam.github.io/error-pages/
|
||||
[templates-dir]:https://github.com/tarampampam/error-pages/tree/master/templates
|
||||
|
||||
## 🔥 Features List
|
||||
|
||||
- HTTP server written in Go, utilizing the extremely fast [FastHTTP][fasthttp] and in-memory caching
|
||||
- Respects the `Content-Type` HTTP header (and `X-Format`) value, responding with the corresponding format
|
||||
(supported formats: `json`, `xml`, and `plaintext`)
|
||||
- Error pages are configured to be excluded from search engine indexing (using meta tags and HTTP headers) to
|
||||
prevent SEO issues on your website
|
||||
- HTML content (including CSS, SVG, and JS) is minified on the fly
|
||||
- Logs written in `json` format
|
||||
- Contains a health check endpoint (`/healthz`)
|
||||
- Consumes very few resources and is suitable for use in resource-constrained environments
|
||||
@ -39,11 +48,12 @@ original and attractive. That's why this repository was created :) It contains:
|
||||
- Error pages can be embedded into your own Docker image with `nginx` in a few simple steps
|
||||
- Fully configurable
|
||||
- Distributed as a Docker image and compiled binary files
|
||||
- Localized HTML error pages (🇺🇸, 🇫🇷, 🇺🇦, 🇷🇺, 🇵🇹, 🇳🇱, 🇩🇪, 🇪🇸, 🇨🇳, 🇮🇩, 🇵🇱) - translation process
|
||||
[described here](l10n) - other translations are welcome!
|
||||
- Localized HTML error pages (🇺🇸, 🇫🇷, 🇺🇦, 🇷🇺, 🇵🇹, 🇳🇱, 🇩🇪, 🇪🇸, 🇨🇳, 🇮🇩, 🇵🇱, 🇰🇷) - translation process
|
||||
[described here][l10n-dir] - other translations are welcome!
|
||||
|
||||
[fasthttp]:https://github.com/valyala/fasthttp
|
||||
[traefik]:https://github.com/traefik/traefik
|
||||
[l10n-dir]:https://github.com/tarampampam/error-pages/tree/master/l10n
|
||||
|
||||
## 🧩 Install
|
||||
|
||||
@ -67,6 +77,189 @@ Download the latest binary file for your OS/architecture from the [releases page
|
||||
[pages-pack-zip]:https://github.com/tarampampam/error-pages/zipball/gh-pages/
|
||||
[pages-pack-tar-gz]:https://github.com/tarampampam/error-pages/tarball/gh-pages/
|
||||
|
||||
## 🪂 Templates (themes)
|
||||
|
||||
The following templates are built-in and available for use without any additional setup:
|
||||
|
||||
> [!NOTE]
|
||||
> The `cats` template is the only one of those that fetches resources (the actual cat pictures) from external
|
||||
> servers - all other templates are self-contained.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Template</th>
|
||||
<th>Preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>app-down</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fapp-down.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/app-down/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/4e668a56-a4c4-47cd-ac4d-b6b45db54ab8">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/ad4b4fd7-7c7b-4bdc-a6b6-44f9ba7f77ca">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>cats</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fcats.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/cats/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/5689880b-f770-406c-81dd-2d28629e6f2e">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/056cd00e-bc9a-4120-8325-310d7b0ebd1b">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>connection</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fconnection.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/connection/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/3f03dc1b-c1ee-4a91-b3d7-e3b93c79020e">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/099ecc2d-e724-4d9c-b5ed-66ddabd71139">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>ghost</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fghost.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/ghost/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/714482ab-f8c1-4455-8ae8-b2ae78f7a2c6">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/f253dfe7-96a0-4e96-915b-d4c544d4a237">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>hacker-terminal</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fhacker-terminal.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/hacker-terminal/404.html">
|
||||
<picture>
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/c197fc35-0844-43d0-9830-82440cee4559">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>l7</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fl7.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/l7/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/18e43ea3-6389-4459-be41-0fc6566a073f">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/05f26669-94ec-40ce-8d67-a199cde54202">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>lost-in-space</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Flost-in-space.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/lost-in-space/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/debf87c0-6f27-41a8-b141-ee3464cbd6cc">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/c347e63d-13a7-46d4-81b9-b25266819a1d">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>noise</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fnoise.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/noise/404.html">
|
||||
<picture>
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/4cc5c3bd-6ebb-4e96-bee8-02d4ad4e7266">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>orient</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Forient.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/orient/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/bc2b0dad-c32c-4628-98f6-e3eab61dd1f2">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/8fc0a7ea-694d-49ce-bb50-3ea032d52d1e">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>shuffle</code><br/><br/>
|
||||
<picture>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fshuffle.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</picture>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://tarampampam.github.io/error-pages/shuffle/404.html">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/7504b7c3-b0cb-4991-9ac2-759cd6c50fc0">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/d2a73fc8-cf5f-4f42-bff8-cce33d8ae47e">
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
> [!NOTE]
|
||||
> The "used times" counter increments when someone start the server with the specified template. Stats service does
|
||||
> not collect any information about location, IP addresses, and so on. Moreover, the stats are open and available for
|
||||
> everyone at [error-pages.goatcounter.com](https://error-pages.goatcounter.com/). This is simply a counter to display
|
||||
> how often a particular template is used, nothing more.
|
||||
|
||||
## 🛠 Usage scenarios
|
||||
|
||||
### HTTP server starting, utilizing either a binary file or Docker image
|
||||
@ -75,9 +268,9 @@ First, ensure you have a precompiled binary file on your machine or have Docker/
|
||||
server with the following command:
|
||||
|
||||
```bash
|
||||
./error-pages serve
|
||||
# or
|
||||
docker run --rm -p '8080:8080/tcp' tarampampam/error-pages serve
|
||||
$ ./error-pages serve
|
||||
# --- or ---
|
||||
$ docker run --rm -p '8080:8080/tcp' tarampampam/error-pages serve
|
||||
```
|
||||
|
||||
That's it! The server will begin running and listen on address `0.0.0.0` and port `8080`. Access error pages using
|
||||
@ -86,7 +279,7 @@ URLs like `http://127.0.0.1:8080/{page_code}.html`.
|
||||
To retrieve different error page codes using a static URL, use the `X-Code` HTTP header:
|
||||
|
||||
```bash
|
||||
curl -H 'X-Code: 500' http://127.0.0.1:8080/
|
||||
$ curl -H 'X-Code: 500' http://127.0.0.1:8080/
|
||||
```
|
||||
|
||||
The server respects the `Content-Type` HTTP header (and `X-Format`), delivering responses in requested formats
|
||||
@ -108,6 +301,55 @@ are detailed in the readme file below.
|
||||
To proxy HTTP headers from requests to responses, utilize the `--proxy-headers` flag or environment variable
|
||||
(comma-separated list of headers).
|
||||
|
||||
<details>
|
||||
<summary><strong>🚀 Start the HTTP server with my custom template (theme)</strong></summary>
|
||||
|
||||
First, create your own template file, for example `my-super-theme.html`:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ code }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>YEAH! {{ message }}: {{ description }}</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
And simply start the server with the following command:
|
||||
|
||||
```bash
|
||||
$ docker run --rm \
|
||||
-v "$(pwd)/my-super-theme.html:/opt/my-template.html:ro" \
|
||||
-p '8080:8080/tcp' ghcr.io/tarampampam/error-pages:3 serve \
|
||||
--add-template /opt/my-template.html \
|
||||
--template-name my-template
|
||||
# --- or ---
|
||||
$ ./error-pages serve \
|
||||
--add-template /opt/my-template.html \
|
||||
--template-name my-template
|
||||
```
|
||||
|
||||
And test it:
|
||||
|
||||
```bash
|
||||
$ curl -H "Accept: text/html" http://127.0.0.1:8080/503
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>503</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>YEAH! Service Unavailable: The server is temporarily overloading or down</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>🚀 Generate a set of error pages using built-in or my own template</strong></summary>
|
||||
|
||||
@ -129,8 +371,8 @@ Create a file like this:
|
||||
Save it as `my-template.html` and use it as your custom template. Then, generate your error pages using the command:
|
||||
|
||||
```bash
|
||||
mkdir -p /path/to/output
|
||||
./error-pages build --add-template /path/to/your/my-template.html --target-dir /path/to/output
|
||||
$ mkdir -p /path/to/output
|
||||
$ ./error-pages build --add-template /path/to/your/my-template.html --target-dir /path/to/output
|
||||
```
|
||||
|
||||
This will create error pages based on your template in the specified output directory:
|
||||
@ -228,14 +470,15 @@ COPY --chown=nginx \
|
||||
Now, we can build the image:
|
||||
|
||||
```bash
|
||||
docker build --tag your-nginx:local -f ./Dockerfile .
|
||||
$ docker build --tag your-nginx:local -f ./Dockerfile .
|
||||
```
|
||||
|
||||
And voilà! Let's start the image and test if everything is working as expected:
|
||||
|
||||
```bash
|
||||
docker run --rm -p '8081:80/tcp' your-nginx:local
|
||||
curl http://127.0.0.1:8081/foobar | head -n 15 # in another terminal
|
||||
$ docker run --rm -p '8081:80/tcp' your-nginx:local
|
||||
|
||||
$ curl http://127.0.0.1:8081/foobar | head -n 15 # in another terminal
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -325,6 +568,7 @@ controller:
|
||||
config:
|
||||
custom-http-errors: >-
|
||||
401,403,404,500,501,502,503
|
||||
|
||||
defaultBackend:
|
||||
enabled: true
|
||||
image:
|
||||
@ -367,11 +611,11 @@ Test completed successfully. Here is the output:
|
||||
Running 15s test @ http://127.0.0.1:8080/
|
||||
12 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 3.54ms 4.90ms 74.57ms 86.55%
|
||||
Req/Sec 16.47k 2.89k 38.11k 69.46%
|
||||
2967567 requests in 15.09s, 44.70GB read
|
||||
Requests/sec: 196596.49
|
||||
Transfer/sec: 2.96GB
|
||||
Latency 4.52ms 6.43ms 94.34ms 85.44%
|
||||
Req/Sec 15.76k 2.83k 29.64k 69.20%
|
||||
2839632 requests in 15.09s, 32.90GB read
|
||||
Requests/sec: 188185.61
|
||||
Transfer/sec: 2.18GB
|
||||
|
||||
Starting the test to bomb DIFFERENT PAGES (codes). Please, be patient...
|
||||
Test completed successfully. Here is the output:
|
||||
@ -379,11 +623,11 @@ Test completed successfully. Here is the output:
|
||||
Running 15s test @ http://127.0.0.1:8080/
|
||||
12 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 4.25ms 6.03ms 74.23ms 86.97%
|
||||
Req/Sec 14.29k 2.75k 32.16k 69.63%
|
||||
2563245 requests in 15.07s, 38.47GB read
|
||||
Requests/sec: 170062.69
|
||||
Transfer/sec: 2.55GB
|
||||
Latency 6.75ms 13.71ms 252.66ms 91.94%
|
||||
Req/Sec 14.06k 3.25k 26.39k 71.98%
|
||||
2534473 requests in 15.10s, 29.22GB read
|
||||
Requests/sec: 167899.78
|
||||
Transfer/sec: 1.94GB
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -421,7 +665,7 @@ The following flags are supported:
|
||||
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:---------------------------:|
|
||||
| `--listen="…"` (`-l`) | The HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1/::1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP) | `0.0.0.0` | `LISTEN_ADDR` |
|
||||
| `--port="…"` (`-p`) | The TCP port number for the HTTP server to listen on (0-65535) | `8080` | `LISTEN_PORT` |
|
||||
| `--add-template="…"` | To add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* |
|
||||
| `--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) | `[]` | `ADD_TEMPLATE` |
|
||||
| `--disable-template="…"` | Disable the specified template by its name (useful to disable the built-in templates and use only custom ones) | `[]` | *none* |
|
||||
| `--add-code="…"` | To add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* |
|
||||
| `--json-format="…"` | Override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type) | | `RESPONSE_JSON_FORMAT` |
|
||||
@ -435,6 +679,7 @@ The following flags are supported:
|
||||
| `--proxy-headers="…"` | HTTP headers listed here 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` |
|
||||
| `--read-buffer-size="…"` | Per-connection buffer size in bytes for reading requests, this also limits the maximum header size (increase this buffer if your clients send multi-KB Request URIs and/or multi-KB headers (e.g., large cookies), note that increasing this value will increase memory consumption) | `5120` | `READ_BUFFER_SIZE` |
|
||||
| `--disable-minification` | Disable the minification of HTML pages, including CSS, SVG, and JS (may be useful for debugging) | `false` | `DISABLE_MINIFICATION` |
|
||||
|
||||
### `build` command (aliases: `b`)
|
||||
|
||||
@ -448,14 +693,15 @@ $ 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 (useful to disable the built-in templates and use only custom ones) | `[]` | *none* |
|
||||
| `--add-code="…"` | To add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* |
|
||||
| `--disable-l10n` | Disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` |
|
||||
| `--index` (`-i`) | Generate index.html file with links to all error pages | `false` | *none* |
|
||||
| `--target-dir="…"` (`--out`, `--dir`, `-o`) | Directory to put the built error pages into | `.` | *none* |
|
||||
| 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) | `[]` | `ADD_TEMPLATE` |
|
||||
| `--disable-template="…"` | Disable the specified template by its name (useful to disable the built-in templates and use only custom ones) | `[]` | *none* |
|
||||
| `--add-code="…"` | To add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* |
|
||||
| `--disable-l10n` | Disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` |
|
||||
| `--index` (`-i`) | Generate index.html file with links to all error pages | `false` | *none* |
|
||||
| `--target-dir="…"` (`--out`, `--dir`, `-o`) | Directory to put the built error pages into | `.` | *none* |
|
||||
| `--disable-minification` | Disable the minification of HTML pages, including CSS, SVG, and JS (may be useful for debugging) | `false` | `DISABLE_MINIFICATION` |
|
||||
|
||||
### `healthcheck` command (aliases: `chk`, `health`, `check`)
|
||||
|
||||
@ -475,149 +721,6 @@ The following flags are supported:
|
||||
|
||||
<!--/GENERATED:CLI_DOCS-->
|
||||
|
||||
## 🪂 Templates (themes)
|
||||
|
||||
The following templates are built-in and available for use without any additional setup:
|
||||
|
||||
> [!NOTE]
|
||||
> The `cats` template is the only one of those that fetches resources (the actual cat pictures) from external
|
||||
> servers - all other templates are self-contained.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Template</th>
|
||||
<th>Preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>app-down</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fapp-down.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/4e668a56-a4c4-47cd-ac4d-b6b45db54ab8">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/ad4b4fd7-7c7b-4bdc-a6b6-44f9ba7f77ca">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>cats</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fcats.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/5689880b-f770-406c-81dd-2d28629e6f2e">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/056cd00e-bc9a-4120-8325-310d7b0ebd1b">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>connection</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fconnection.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/3f03dc1b-c1ee-4a91-b3d7-e3b93c79020e">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/099ecc2d-e724-4d9c-b5ed-66ddabd71139">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>ghost</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fghost.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/714482ab-f8c1-4455-8ae8-b2ae78f7a2c6">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/f253dfe7-96a0-4e96-915b-d4c544d4a237">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>hacker-terminal</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fhacker-terminal.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/c197fc35-0844-43d0-9830-82440cee4559">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>l7</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fl7.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/18e43ea3-6389-4459-be41-0fc6566a073f">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/05f26669-94ec-40ce-8d67-a199cde54202">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>lost-in-space</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Flost-in-space.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/debf87c0-6f27-41a8-b141-ee3464cbd6cc">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/c347e63d-13a7-46d4-81b9-b25266819a1d">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>noise</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fnoise.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/4cc5c3bd-6ebb-4e96-bee8-02d4ad4e7266">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>orient</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Forient.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/bc2b0dad-c32c-4628-98f6-e3eab61dd1f2">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/8fc0a7ea-694d-49ce-bb50-3ea032d52d1e">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<code>shuffle</code><br/><br/>
|
||||
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Ferror-pages.goatcounter.com%2Fcounter%2F%2Fuse-template%2Fshuffle.json&query=%24.count&label=used%20times" alt="used times">
|
||||
</td>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/tarampampam/error-pages/assets/7326800/7504b7c3-b0cb-4991-9ac2-759cd6c50fc0">
|
||||
<img align="center" src="https://github.com/tarampampam/error-pages/assets/7326800/d2a73fc8-cf5f-4f42-bff8-cce33d8ae47e">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
> [!NOTE]
|
||||
> The "used times" counter increments when someone start the server with the specified template. Stats service does
|
||||
> not collect any information about location, IP addresses, and so on. Moreover, the stats are open and available for
|
||||
> everyone at [error-pages.goatcounter.com](https://error-pages.goatcounter.com/). This is simply a counter to display
|
||||
> how often a particular template is used, nothing more.
|
||||
|
||||
## 🦾 Contributors
|
||||
|
||||
I want to say a big thank you to everyone who contributed to this project:
|
||||
|
2
go.mod
2
go.mod
@ -4,6 +4,7 @@ go 1.22
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tdewolff/minify/v2 v2.20.35
|
||||
github.com/urfave/cli-docs/v3 v3.0.0-alpha5
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9
|
||||
github.com/valyala/fasthttp v1.55.0
|
||||
@ -19,6 +20,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.15 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
7
go.sum
7
go.sum
@ -26,6 +26,13 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
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/tdewolff/minify/v2 v2.20.35 h1:/Vq/oivpkFyi2PViD25XHZZbJz+eO4OmPSgePex1kBU=
|
||||
github.com/tdewolff/minify/v2 v2.20.35/go.mod h1:L1VYef/jwKw6Wwyk5A+T0mBjjn3mMPgmjjA688RNsxU=
|
||||
github.com/tdewolff/parse/v2 v2.7.15 h1:hysDXtdGZIRF5UZXwpfn3ZWRbm+ru4l53/ajBRGpCTw=
|
||||
github.com/tdewolff/parse/v2 v2.7.15/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
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/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo=
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
_ "github.com/urfave/cli-docs/v3" // required for `go generate` to work
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"gh.tarampamp.am/error-pages/internal/appmeta"
|
||||
@ -17,7 +16,7 @@ import (
|
||||
"gh.tarampamp.am/error-pages/internal/logger"
|
||||
)
|
||||
|
||||
//go:generate go run update_readme.go
|
||||
//go:generate go run app_generate.go
|
||||
|
||||
// NewApp creates a new console application.
|
||||
func NewApp(appName string) *cli.Command { //nolint:funlen
|
||||
|
@ -1,5 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
//go:build generate
|
||||
|
||||
package main
|
||||
|
||||
@ -17,8 +16,10 @@ func main() {
|
||||
if stat, err := os.Stat(readmePath); err == nil && stat.Mode().IsRegular() {
|
||||
if err = cliDocs.ToTabularToFileBetweenTags(cli.NewApp(""), "error-pages", readmePath); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
println("✔ cli docs updated successfully")
|
||||
}
|
||||
} else if err != nil {
|
||||
println("readme file not found, cli docs not updated:", err.Error())
|
||||
println("⚠ readme file not found, cli docs not updated:", err.Error())
|
||||
}
|
||||
}
|
@ -39,11 +39,12 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
cmd command
|
||||
cfg = config.New()
|
||||
|
||||
addTplFlag = shared.AddTemplatesFlag
|
||||
disableTplFlag = shared.DisableTemplateNamesFlag
|
||||
addCodeFlag = shared.AddHTTPCodesFlag
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
createIndexFlag = cli.BoolFlag{
|
||||
addTplFlag = shared.AddTemplatesFlag
|
||||
disableTplFlag = shared.DisableTemplateNamesFlag
|
||||
addCodeFlag = shared.AddHTTPCodesFlag
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
disableMinificationFlag = shared.DisableMinificationFlag
|
||||
createIndexFlag = cli.BoolFlag{
|
||||
Name: "index",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "Generate index.html file with links to all error pages",
|
||||
@ -81,6 +82,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
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)
|
||||
cfg.DisableMinification = c.Bool(disableMinificationFlag.Name)
|
||||
cmd.opt.createIndex = c.Bool(createIndexFlag.Name)
|
||||
cmd.opt.targetDirAbsPath, _ = filepath.Abs(c.String(targetDirFlag.Name)) // an error checked by [os.Stat] validator
|
||||
|
||||
@ -140,14 +142,15 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit
|
||||
&disableL10nFlag,
|
||||
&createIndexFlag,
|
||||
&targetDirFlag,
|
||||
&disableMinificationFlag,
|
||||
},
|
||||
}
|
||||
|
||||
return cmd.c
|
||||
}
|
||||
|
||||
func (cmd *command) Run( //nolint:funlen
|
||||
ctx context.Context,
|
||||
func (cmd *command) Run( //nolint:funlen,gocognit
|
||||
_ context.Context,
|
||||
log *logger.Logger,
|
||||
cfg *config.Config,
|
||||
) error {
|
||||
@ -172,13 +175,21 @@ func (cmd *command) Run( //nolint:funlen
|
||||
|
||||
var outFilePath = path.Join(cmd.opt.targetDirAbsPath, templateName, code+".html")
|
||||
|
||||
if content, renderErr := appTemplate.Render(templateContent, appTemplate.Props{
|
||||
Code: uint16(codeAsUint),
|
||||
if content, renderErr := appTemplate.Render(templateContent, appTemplate.Props{ //nolint:nestif
|
||||
Code: uint16(codeAsUint), //nolint:gosec
|
||||
Message: codeDescription.Message,
|
||||
Description: codeDescription.Description,
|
||||
L10nDisabled: cfg.L10n.Disable,
|
||||
ShowRequestDetails: false,
|
||||
}); renderErr == nil {
|
||||
if !cfg.DisableMinification {
|
||||
if mini, minErr := appTemplate.MiniHTML(content); minErr != nil {
|
||||
log.Warn("Cannot minify the content", logger.Error(minErr))
|
||||
} else {
|
||||
content = mini
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outFilePath, []byte(content), os.FileMode(0664)); err != nil { //nolint:mnd
|
||||
return err
|
||||
}
|
||||
|
@ -108,10 +108,10 @@ func NewCommand() *cli.Command { //nolint:funlen
|
||||
var runTest = func(scriptContent string) error {
|
||||
if stdOut, stdErr, err := wrkRunTest(ctx,
|
||||
wrkBinPath,
|
||||
uint16(c.Uint(threadsFlag.Name)),
|
||||
uint16(c.Uint(connectionsFlag.Name)),
|
||||
uint16(c.Uint(threadsFlag.Name)), //nolint:gosec
|
||||
uint16(c.Uint(connectionsFlag.Name)), //nolint:gosec
|
||||
c.Duration(durationFlag.Name),
|
||||
uint16(c.Uint(portFlag.Name)),
|
||||
uint16(c.Uint(portFlag.Name)), //nolint:gosec
|
||||
scriptContent,
|
||||
); err != nil {
|
||||
var errData, _ = io.ReadAll(stdErr)
|
||||
|
@ -38,13 +38,14 @@ 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
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
jsonFormatFlag = cli.StringFlag{
|
||||
addrFlag = shared.ListenAddrFlag
|
||||
portFlag = shared.ListenPortFlag
|
||||
addTplFlag = shared.AddTemplatesFlag
|
||||
disableTplFlag = shared.DisableTemplateNamesFlag
|
||||
addCodeFlag = shared.AddHTTPCodesFlag
|
||||
disableL10nFlag = shared.DisableL10nFlag
|
||||
disableMinificationFlag = shared.DisableMinificationFlag
|
||||
jsonFormatFlag = cli.StringFlag{
|
||||
Name: "json-format",
|
||||
Usage: "Override the default error page response in JSON format (Go templates are supported; the error " +
|
||||
"page will use this template if the client requests JSON content type)",
|
||||
@ -175,13 +176,14 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
Suggest: true,
|
||||
Action: func(ctx context.Context, c *cli.Command) error {
|
||||
cmd.opt.http.addr = c.String(addrFlag.Name)
|
||||
cmd.opt.http.port = uint16(c.Uint(portFlag.Name))
|
||||
cmd.opt.http.port = uint16(c.Uint(portFlag.Name)) //nolint:gosec
|
||||
cmd.opt.http.readBufferSize = uint(c.Uint(readBufferSizeFlag.Name))
|
||||
cfg.L10n.Disable = c.Bool(disableL10nFlag.Name)
|
||||
cfg.DefaultCodeToRender = uint16(c.Uint(defaultCodeToRenderFlag.Name))
|
||||
cfg.DefaultCodeToRender = uint16(c.Uint(defaultCodeToRenderFlag.Name)) //nolint:gosec
|
||||
cfg.RespondWithSameHTTPCode = c.Bool(sendSameHTTPCodeFlag.Name)
|
||||
cfg.RotationMode, _ = config.ParseRotationMode(c.String(rotationModeFlag.Name))
|
||||
cfg.ShowDetails = c.Bool(showDetailsFlag.Name)
|
||||
cfg.DisableMinification = c.Bool(disableMinificationFlag.Name)
|
||||
|
||||
{ // override default JSON, XML, and PlainText formats
|
||||
if c.IsSet(jsonFormatFlag.Name) {
|
||||
@ -303,6 +305,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy
|
||||
&proxyHeadersListFlag,
|
||||
&rotationModeFlag,
|
||||
&readBufferSizeFlag,
|
||||
&disableMinificationFlag,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -97,5 +97,5 @@ func getFreeTcpPort(t *testing.T) uint16 {
|
||||
<-time.After(5 * time.Millisecond)
|
||||
}
|
||||
|
||||
return uint16(port)
|
||||
return uint16(port) //nolint:gosec
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ var AddTemplatesFlag = cli.StringSliceFlag{
|
||||
Usage: "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)",
|
||||
Config: cli.StringConfig{TrimSpace: true},
|
||||
Sources: cli.EnvVars("ADD_TEMPLATE"),
|
||||
Category: CategoryTemplates,
|
||||
Validator: func(paths []string) error {
|
||||
for _, path := range paths {
|
||||
@ -146,3 +147,11 @@ var DisableL10nFlag = cli.BoolFlag{
|
||||
Category: CategoryOther,
|
||||
OnlyOnce: true,
|
||||
}
|
||||
|
||||
var DisableMinificationFlag = cli.BoolFlag{
|
||||
Name: "disable-minification",
|
||||
Usage: "Disable the minification of HTML pages, including CSS, SVG, and JS (may be useful for debugging)",
|
||||
Sources: cli.EnvVars("DISABLE_MINIFICATION"),
|
||||
Category: CategoryOther,
|
||||
OnlyOnce: true,
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ func TestAddTemplatesFlag(t *testing.T) {
|
||||
var flag = shared.AddTemplatesFlag
|
||||
|
||||
assert.Equal(t, "add-template", flag.Name)
|
||||
assert.Contains(t, flag.Sources.String(), "ADD_TEMPLATE")
|
||||
|
||||
for wantErrMsg, giveValue := range map[string][]string{
|
||||
"missing template path": {""},
|
||||
@ -216,3 +217,12 @@ func TestDisableL10nFlag(t *testing.T) {
|
||||
assert.Equal(t, "disable-l10n", flag.Name)
|
||||
assert.Contains(t, flag.Sources.String(), "DISABLE_L10N")
|
||||
}
|
||||
|
||||
func TestDisableMinificationFlag(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var flag = shared.DisableMinificationFlag
|
||||
|
||||
assert.Equal(t, "disable-minification", flag.Name)
|
||||
assert.Contains(t, flag.Sources.String(), "DISABLE_MINIFICATION")
|
||||
}
|
||||
|
@ -56,6 +56,9 @@ type Config struct {
|
||||
// ShowDetails determines whether to show additional details in the error response, extracted from the
|
||||
// incoming request (if supported by the template).
|
||||
ShowDetails bool
|
||||
|
||||
// DisableMinification determines whether to disable minification of the rendered content (e.g., HTML, CSS) or not.
|
||||
DisableMinification bool
|
||||
}
|
||||
|
||||
const defaultJSONFormat string = `{
|
||||
|
@ -24,6 +24,7 @@ func TestNew(t *testing.T) {
|
||||
assert.NotEmpty(t, cfg.TemplateName)
|
||||
assert.True(t, cfg.Templates.Has(cfg.TemplateName))
|
||||
assert.Equal(t, uint16(http.StatusNotFound), cfg.DefaultCodeToRender)
|
||||
assert.False(t, cfg.DisableMinification)
|
||||
})
|
||||
|
||||
t.Run("changing cfg1 should not affect cfg2", func(t *testing.T) {
|
||||
|
@ -28,7 +28,7 @@ func extractCodeFromURL(url string) (uint16, bool) {
|
||||
}
|
||||
|
||||
if code, err := strconv.ParseUint(fileName, 10, 16); err == nil && code > 0 && code < 999 {
|
||||
return uint16(code), true
|
||||
return uint16(code), true //nolint:gosec
|
||||
}
|
||||
|
||||
return 0, false
|
||||
@ -47,7 +47,7 @@ func extractCodeFromHeaders(headers *fasthttp.RequestHeader) (uint16, bool) {
|
||||
// HTTP status code returned by the request
|
||||
if value := headers.Peek("X-Code"); len(value) > 0 && len(value) <= 3 {
|
||||
if code, err := strconv.ParseUint(string(value), 10, 16); err == nil && code > 0 && code < 999 {
|
||||
return uint16(code), true
|
||||
return uint16(code), true //nolint:gosec
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +175,14 @@ func New(cfg *config.Config, log *logger.Logger) (_ fasthttp.RequestHandler, clo
|
||||
err.Error(),
|
||||
))
|
||||
} else {
|
||||
if !cfg.DisableMinification {
|
||||
if mini, minErr := template.MiniHTML(content); minErr != nil {
|
||||
log.Warn("HTML minification failed", logger.Error(minErr))
|
||||
} else {
|
||||
content = mini
|
||||
}
|
||||
}
|
||||
|
||||
cache.Put(tpl, tplProps, []byte(content))
|
||||
|
||||
write(ctx, log, content)
|
||||
|
@ -48,7 +48,7 @@ func TestHandler(t *testing.T) {
|
||||
wantStatusCode: http.StatusOK,
|
||||
wantHeaders: map[string]string{"Content-Type": "text/html; charset=utf-8"},
|
||||
wantBodyIncludes: []string{
|
||||
"<!DOCTYPE html>",
|
||||
"<!doctype html>",
|
||||
"<title>407: Proxy Authentication Required",
|
||||
"Proxy Authentication Required",
|
||||
},
|
||||
|
@ -40,7 +40,7 @@ func NewServer(log *logger.Logger, readBufferSize uint) Server {
|
||||
server: &fasthttp.Server{
|
||||
ReadTimeout: readTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
ReadBufferSize: int(readBufferSize),
|
||||
ReadBufferSize: int(readBufferSize), //nolint:gosec
|
||||
DisablePreParseMultipartForm: true,
|
||||
NoDefaultServerHeader: true,
|
||||
CloseOnShutdown: true,
|
||||
|
@ -392,5 +392,5 @@ func getFreeTcpPort(t *testing.T) uint16 {
|
||||
<-time.After(5 * time.Millisecond)
|
||||
}
|
||||
|
||||
return uint16(port)
|
||||
return uint16(port) //nolint:gosec
|
||||
}
|
||||
|
23
internal/template/minify.go
Normal file
23
internal/template/minify.go
Normal file
@ -0,0 +1,23 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/tdewolff/minify/v2"
|
||||
"github.com/tdewolff/minify/v2/css"
|
||||
"github.com/tdewolff/minify/v2/html"
|
||||
"github.com/tdewolff/minify/v2/js"
|
||||
"github.com/tdewolff/minify/v2/svg"
|
||||
)
|
||||
|
||||
var htmlMinify = func() *minify.M { //nolint:gochecknoglobals
|
||||
var m = minify.New()
|
||||
|
||||
m.AddFunc("text/css", css.Minify)
|
||||
m.Add("text/html", &html.Minifier{KeepDocumentTags: true, KeepEndTags: true, KeepQuotes: true})
|
||||
m.AddFunc("image/svg+xml", svg.Minify)
|
||||
m.AddFunc("application/javascript", js.Minify)
|
||||
|
||||
return m
|
||||
}()
|
||||
|
||||
// MiniHTML minifies HTML data, including inline CSS, SVG and JS.
|
||||
func MiniHTML(data string) (string, error) { return htmlMinify.String("text/html", data) }
|
94
internal/template/minify_test.go
Normal file
94
internal/template/minify_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
package template_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"gh.tarampamp.am/error-pages/internal/template"
|
||||
)
|
||||
|
||||
func TestMiniHTML(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for range 100 { // race condition provocation
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
for give, want := range map[string]string{
|
||||
"": "",
|
||||
`<!-- Simple HTML page -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">Test</h1>
|
||||
</body>
|
||||
</html>`: `<!doctype html><html><head><title>Test</title></head><body><h1 align="center">Test</h1></body></html>`,
|
||||
`<!-- css styles -->
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.foo:hover {
|
||||
color: #f0a; /* comment */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p style="color: red" class="bar">Text</p>
|
||||
</body>
|
||||
</html>`: `<html><head><style>.foo:hover{color:#f0a}</style></head><body><p style="color:red" class="bar">Text</p></body></html>`,
|
||||
`<!-- svg -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
|
||||
</g>
|
||||
</svg>`: `<svg><g><circle cx="50" cy="50" r="40" stroke="#000" stroke-width="3" fill="red"/></g></svg>`,
|
||||
`<!-- js -->
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
// comment
|
||||
console.log('Hello, World!');
|
||||
|
||||
let foo = 1;
|
||||
foo++;
|
||||
</script>
|
||||
</body>
|
||||
</html>`: `<html><body><script>console.log("Hello, World!");let foo=1;foo++</script></body></html>`,
|
||||
`<!-- js module not changed -->
|
||||
<html>
|
||||
<body>
|
||||
<script type="module">
|
||||
// comment
|
||||
console.log('Hello, World!');
|
||||
|
||||
let foo = 1;
|
||||
foo++;
|
||||
</script>
|
||||
</body>
|
||||
</html>`: `<html><body><script type="module">
|
||||
// comment
|
||||
console.log('Hello, World!');
|
||||
|
||||
let foo = 1;
|
||||
foo++;
|
||||
</script></body></html>`,
|
||||
} {
|
||||
var got, err = template.MiniHTML(give)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
74
l10n/l10n.js
74
l10n/l10n.js
@ -16,7 +16,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes language codes list (column `Set 1` or `ISO 639-1:2002`)
|
||||
*
|
||||
* @type {Map<string, Map<'fr'|'ru'|'uk'|'pt'|'nl'|'de'|'es'|'zh'|'id'|'pl', string>>}
|
||||
* @type {Map<string, Map<'fr'|'ru'|'uk'|'pt'|'nl'|'de'|'es'|'zh'|'id'|'pl'|'ko', string>>}
|
||||
*/
|
||||
const data = Object.freeze(new Map([
|
||||
[tkn('Error'), new Map([
|
||||
@ -30,6 +30,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '错误'],
|
||||
['id', 'Kesalahan'],
|
||||
['pl', 'Błąd'],
|
||||
['ko', '오류'],
|
||||
])],
|
||||
[tkn('Good luck'), new Map([
|
||||
['fr', 'Bonne chance'],
|
||||
@ -42,6 +43,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '祝好运'],
|
||||
['id', 'Semoga berhasil!'],
|
||||
['pl', 'Powodzenia'],
|
||||
['ko', '행운을 빌어요'],
|
||||
])],
|
||||
[tkn('UH OH'), new Map([
|
||||
['fr', 'Oups'],
|
||||
@ -54,6 +56,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '哎呀'],
|
||||
['id', 'Ups'],
|
||||
['pl', 'Ojej'],
|
||||
['ko', '헉'],
|
||||
])],
|
||||
[tkn('Request details'), new Map([
|
||||
['fr', 'Détails de la requête'],
|
||||
@ -66,6 +69,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求详情'],
|
||||
['id', 'Rincian permintaan'],
|
||||
['pl', 'Poproś o szczegóły'],
|
||||
['ko', '요청 세부사항'],
|
||||
])],
|
||||
[tkn('Double-check the URL'), new Map([
|
||||
['fr', 'Vérifiez l’URL'],
|
||||
@ -78,6 +82,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请再次检查地址'],
|
||||
['id', 'Periksa URL'],
|
||||
['pl', 'Sprawdź adres URL'],
|
||||
['ko', 'URL을 다시 확인하세요'],
|
||||
])],
|
||||
[tkn('Alternatively, go back'), new Map([
|
||||
['fr', 'Essayer de revenir en arrière'],
|
||||
@ -90,6 +95,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '或返回上一页'],
|
||||
['id', 'Atau, kembali'],
|
||||
['pl', 'Alternatywnie wróć'],
|
||||
['ko', '혹은, 돌아가기'],
|
||||
])],
|
||||
[tkn("Here's what might have happened"), new Map([
|
||||
['fr', 'Voici ce qui aurait pu se passer'],
|
||||
@ -102,6 +108,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '可能原因有'],
|
||||
['id', 'Inilah yang bisa saja terjadi'],
|
||||
['pl', 'Oto, co mogło się wydarzyć'],
|
||||
['ko', '다음이 발생했을 수 있어요'],
|
||||
])],
|
||||
[tkn('You may have mistyped the URL'), new Map([
|
||||
['fr', 'Vous avez peut-être mal tapé l’URL'],
|
||||
@ -114,6 +121,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '您可能输入了错误的地址'],
|
||||
['id', 'Anda mungkin tersalah memasukkan URL'],
|
||||
['pl', 'Być może błędnie wpisałeś adres URL'],
|
||||
['ko', 'URL을 잘못 입력하셨을 수 있어요'],
|
||||
])],
|
||||
[tkn('The site was moved'), new Map([
|
||||
['fr', 'Le site a été déplacé'],
|
||||
@ -126,6 +134,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '站点已被转移'],
|
||||
['id', 'Halaman dipindahkan'],
|
||||
['pl', 'Witryna została przeniesiona'],
|
||||
['ko', '사이트가 이동했어요'],
|
||||
])],
|
||||
[tkn('It was never here'), new Map([
|
||||
['fr', 'Il n’a jamais été ici'],
|
||||
@ -138,6 +147,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '站点从未存在'],
|
||||
['id', 'Itu Tidak pernah di sini'],
|
||||
['pl', 'Nigdy jej nie było'],
|
||||
['ko', '여기에 있던 적이 없어요'],
|
||||
])],
|
||||
[tkn('Bad Request'), new Map([
|
||||
['fr', 'Mauvaise requête'],
|
||||
@ -150,6 +160,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '错误请求'],
|
||||
['id', 'Permintaan yang salah'],
|
||||
['pl', 'Nieprawidłowe żądanie'],
|
||||
['ko', '잘못된 요청'],
|
||||
])],
|
||||
[tkn('The server did not understand the request'), new Map([
|
||||
['fr', 'Le serveur ne comprend pas la requête'],
|
||||
@ -162,6 +173,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器不理解该请求'],
|
||||
['id', 'Server tidak memahami permintaan'],
|
||||
['pl', 'Serwer nie zrozumiał żądania'],
|
||||
['ko', '서버가 요청을 이해하지 못했어요'],
|
||||
])],
|
||||
[tkn('Unauthorized'), new Map([
|
||||
['fr', 'Non autorisé'],
|
||||
@ -174,6 +186,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '未经授权'],
|
||||
['id', 'Tidak diotorisasi'],
|
||||
['pl', 'Nieautoryzowany'],
|
||||
['ko', '권한 없음'],
|
||||
])],
|
||||
[tkn('The requested page needs a username and a password'), new Map([
|
||||
['fr', 'La page demandée nécessite un nom d’utilisateur et un mot de passe'],
|
||||
@ -186,6 +199,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求的页面需要用户名和密码'],
|
||||
['id', 'Halaman yang diminta membutuhkan nama pengguna dan kata sandi'],
|
||||
['pl', 'Żądana strona wymaga podania nazwy użytkownika i hasła'],
|
||||
['ko', '요청하신 페이지에는 사용자 이름과 비밀번호가 필요해요'],
|
||||
])],
|
||||
[tkn('Forbidden'), new Map([
|
||||
['fr', 'Interdit'],
|
||||
@ -198,6 +212,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '禁止访问'],
|
||||
['id', 'Dilarang'],
|
||||
['pl', 'Zabroniony'],
|
||||
['ko', '금지됨'],
|
||||
])],
|
||||
[tkn('Access is forbidden to the requested page'), new Map([
|
||||
['fr', 'Accès interdit à la page demandée'],
|
||||
@ -210,6 +225,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '禁止访问请求的页面'],
|
||||
['id', 'Akses dilarang ke halaman yang diminta'],
|
||||
['pl', 'Dostęp do żądanej strony jest zabroniony'],
|
||||
['ko', '요청하신 페이지에 대한 접근이 금지되어 있어요'],
|
||||
])],
|
||||
[tkn('Not Found'), new Map([
|
||||
['fr', 'Introuvable'],
|
||||
@ -222,6 +238,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '未找到'],
|
||||
['id', 'Tidak ditemukan'],
|
||||
['pl', 'Nie znaleziono'],
|
||||
['ko', '찾을 수 없음'],
|
||||
])],
|
||||
[tkn('The server can not find the requested page'), new Map([
|
||||
['fr', 'Le serveur ne peut trouver la page demandée'],
|
||||
@ -234,6 +251,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器找不到请求的页面'],
|
||||
['id', 'Server tidak dapat menemukan halaman yang diminta'],
|
||||
['pl', 'Serwer nie może znaleźć żądanej strony'],
|
||||
['ko', '서버가 요청한 페이지를 찾을 수 없어요'],
|
||||
])],
|
||||
[tkn('Method Not Allowed'), new Map([
|
||||
['fr', 'Méthode Non Autorisée'],
|
||||
@ -246,6 +264,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '方法不被允许'],
|
||||
['id', 'Metode tidak diizinkan'],
|
||||
['pl', 'Niedozwolona metoda'],
|
||||
['ko', '허용되지 않은 메소드'],
|
||||
])],
|
||||
[tkn('The method specified in the request is not allowed'), new Map([
|
||||
['fr', 'La méthode spécifiée dans la requête n’est pas autorisée'],
|
||||
@ -258,6 +277,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求指定的方法不被允许'],
|
||||
['id', 'Metode dalam permintaan tidak diizinkan'],
|
||||
['pl', 'Metoda określona w żądaniu jest niedozwolona'],
|
||||
['ko', '요청에 사용한 메소드는 허용되지 않아요'],
|
||||
])],
|
||||
[tkn('Proxy Authentication Required'), new Map([
|
||||
['fr', 'Authentification proxy requise'],
|
||||
@ -270,6 +290,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '需要代理服务器身份验证'],
|
||||
['id', 'Diperlukan otentikasi proxy'],
|
||||
['pl', 'Wymagane uwierzytelnianie proxy'],
|
||||
['ko', '프록시 인증 필요'],
|
||||
])],
|
||||
[tkn('You must authenticate with a proxy server before this request can be served'), new Map([
|
||||
['fr', 'Vous devez vous authentifier avec un serveur proxy avant que cette requête puisse être servie'],
|
||||
@ -282,6 +303,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '您必须对代理服务器进行身份验证,然后才能让请求得到处理'],
|
||||
['id', 'Anda harus mengautentikasi dengan server proxy sebelum permintaan ini dapat dilayani'],
|
||||
['pl', 'Musisz uwierzytelnić się na serwerze proxy, zanim to żądanie będzie mogło zostać obsłużone'],
|
||||
['ko', '이 요청을 처리하려면 프록시 서버로 인증해야 해요'],
|
||||
])],
|
||||
[tkn('Request Timeout'), new Map([
|
||||
['fr', 'Requête expiré'],
|
||||
@ -294,6 +316,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求超时'],
|
||||
['id', 'Meminta batas waktu'],
|
||||
['pl', 'Przekroczenie limitu czasu żądania'],
|
||||
['ko', '요청 시간초과'],
|
||||
])],
|
||||
[tkn('The request took longer than the server was prepared to wait'), new Map([
|
||||
['fr', 'La requête prend plus de temps que prévu'],
|
||||
@ -306,6 +329,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求用时超过了服务器设置的最长等待时间'],
|
||||
['id', 'Permintaan memakan waktu lebih lama dari yang bisa ditunggu oleh server'],
|
||||
['pl', 'Żądanie trwało dłużej niż serwer był gotowy czekać'],
|
||||
['ko', '요청이 서버가 기다릴 수 있는 시간보다 오래 걸렸어요'],
|
||||
])],
|
||||
[tkn('Conflict'), new Map([
|
||||
['fr', 'Conflit'],
|
||||
@ -318,6 +342,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '冲突'],
|
||||
['id', 'Konflik'],
|
||||
['pl', 'Konflikt'],
|
||||
['ko', '상충'],
|
||||
])],
|
||||
[tkn('The request could not be completed because of a conflict'), new Map([
|
||||
['fr', 'La requête n’a pas pu être complétée à cause d’un conflit'],
|
||||
@ -330,6 +355,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '由于冲突,请求无法完成'],
|
||||
['id', 'Permintaan tidak dapat diselesaikan karena adanya konflik'],
|
||||
['pl', 'Żądanie nie mogło zostać wykonane z powodu konfliktu'],
|
||||
['ko', '상충으로 인해 요청을 완료할 수 없었어요'],
|
||||
])],
|
||||
[tkn('Gone'), new Map([
|
||||
['fr', 'Supprimé'],
|
||||
@ -342,6 +368,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '已移除'],
|
||||
['id', 'Menghilang'],
|
||||
['pl', 'Usunięto'],
|
||||
['ko', '사라짐'],
|
||||
])],
|
||||
[tkn('The requested page is no longer available'), new Map([
|
||||
['fr', 'La page demandée n’est plus disponible'],
|
||||
@ -354,6 +381,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求的页面不再可用'],
|
||||
['id', 'Halaman yang diminta tidak lagi tersedia'],
|
||||
['pl', 'Żądana strona nie jest już dostępna'],
|
||||
['ko', '요청하신 페이지는 더 이상 사용할 수 없어요'],
|
||||
])],
|
||||
[tkn('Length Required'), new Map([
|
||||
['fr', 'Longueur requise'],
|
||||
@ -366,6 +394,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '需要长度'],
|
||||
['id', 'Panjang yang diperlukan'],
|
||||
['pl', 'Wymagana długość'],
|
||||
['ko', '길이 필요'],
|
||||
])],
|
||||
[tkn('The "Content-Length" is not defined. The server will not accept the request without it'), new Map([
|
||||
['fr', 'Le "Content-Length" n’est pas défini. Le serveur ne prendra pas en compte la requête'],
|
||||
@ -378,6 +407,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '未指定Content-Length(内容长度)。服务器将不接受不包含此头信息的请求'],
|
||||
['id', '"Content-Length" tidak ditentukan. Server tidak akan menerima permintaan tanpa itu'],
|
||||
['pl', 'Wartość "Content-Length" nie jest zdefiniowana. Serwer nie zaakceptuje żądania bez tego parametru'],
|
||||
['ko', '"Content-Length"가 정의되지 않았습니다. 이 값이 없으면 서버는 요청을 수락하지 않아요'],
|
||||
])],
|
||||
[tkn('Precondition Failed'), new Map([
|
||||
['fr', 'Échec de la condition préalable'],
|
||||
@ -390,6 +420,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '前置条件判定失败'],
|
||||
['id', 'Prasyarat gagal'],
|
||||
['pl', 'Niespełnienie warunku wstępnego'],
|
||||
['ko', '선결 조건 실패'],
|
||||
])],
|
||||
[tkn('The pre condition given in the request evaluated to false by the server'), new Map([
|
||||
['fr', 'La précondition donnée dans la requête a été évaluée comme étant fausse par le serveur'],
|
||||
@ -402,6 +433,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器评估请求中给出的前置条件的结果为false(假)'],
|
||||
['id', 'Prakondisi gagal'],
|
||||
['pl', 'Warunek wstępny podany w żądaniu został oceniony przez serwer jako nieprawidłowy'],
|
||||
['ko', '요청에 제공된 선결 조건을 서버는 거짓으로 평가했어요'],
|
||||
])],
|
||||
[tkn('Payload Too Large'), new Map([
|
||||
['fr', 'Charge trop volumineuse'],
|
||||
@ -414,6 +446,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求体过大'],
|
||||
['id', 'Muatan terlalu besar'],
|
||||
['pl', 'Żądanie jest zbyt duże'],
|
||||
['ko', '콘텐츠가 너무 큼'],
|
||||
])],
|
||||
[tkn('The server will not accept the request, because the request entity is too large'), new Map([
|
||||
['fr', 'Le serveur ne prendra pas en compte la requête, car l’entité de la requête est trop volumineuse'],
|
||||
@ -426,6 +459,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求体过大,服务器将不接受该请求'],
|
||||
['id', 'Server tidak akan menerima permintaan, karena entitas permintaan terlalu besar'],
|
||||
['pl', 'Serwer nie zaakceptuje żądania, ponieważ żądanie jest zbyt duże'],
|
||||
['ko', '요청 엔터티가 너무 크기 때문에 서버가 요청을 수락하지 않았어요'],
|
||||
])],
|
||||
[tkn('Requested Range Not Satisfiable'), new Map([
|
||||
['fr', 'Requête non satisfaisante'],
|
||||
@ -438,6 +472,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '不满足请求范围'],
|
||||
['id', 'Rentang yang diminta tidak dapat dipenuhi'],
|
||||
['pl', 'Żądany zakres nie jest satysfakcjonujący'],
|
||||
['ko', '처리할 수 없는 요청 범위'],
|
||||
])],
|
||||
[tkn('The requested byte range is not available and is out of bounds'), new Map([
|
||||
['fr', 'Le byte range demandé n’est pas disponible et est hors des limites'],
|
||||
@ -450,6 +485,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求的字节范围不可用,超出边界'],
|
||||
['id', 'Rentang byte yang diminta tidak tersedia dan di luar batas'],
|
||||
['pl', 'Żądany zakres bajtów nie jest dostępny i znajduje się poza zakresem'],
|
||||
['ko', '요청한 범위를 사용할 수 없고, 범위를 벗어났어요'],
|
||||
])],
|
||||
[tkn("I'm a teapot"), new Map([
|
||||
['fr', 'Je suis une théière'],
|
||||
@ -462,6 +498,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '我是一只茶壶'],
|
||||
['id', 'Saya adalah teko'],
|
||||
['pl', 'Jestem czajniczkiem'],
|
||||
['ko', '저는 찻주전자에요'],
|
||||
])],
|
||||
[tkn('Attempt to brew coffee with a teapot is not supported'), new Map([
|
||||
['fr', 'Tenter de préparer du café avec une théière n’est pas pris en charge'],
|
||||
@ -474,6 +511,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '用茶壶泡咖啡不受支持'],
|
||||
['id', 'Upaya menyeduh kopi dengan teko tidak didukung'],
|
||||
['pl', 'Próba zaparzenia kawy za pomocą czajniczka nie jest obsługiwana'],
|
||||
['ko', '찻주전자로 커피를 내리는 시도는 지원되지 않아요'],
|
||||
])],
|
||||
[tkn('Too Many Requests'), new Map([
|
||||
['fr', 'Trop de requêtes'],
|
||||
@ -486,6 +524,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求过多'],
|
||||
['id', 'Terlalu banyak permintaan'],
|
||||
['pl', 'Zbyt wiele żądań'],
|
||||
['ko', '요청이 너무 많음'],
|
||||
])],
|
||||
[tkn('Too many requests in a given amount of time'), new Map([
|
||||
['fr', 'Trop de requêtes dans un délai donné'],
|
||||
@ -498,6 +537,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '在给定的时间内发送了过多请求'],
|
||||
['id', 'Terlalu banyak permintaan dalam waktu tertentu'],
|
||||
['pl', 'Zbyt wiele żądań w określonym czasie'],
|
||||
['ko', '지정된 시간 내에 요청이 너무 많아요'],
|
||||
])],
|
||||
[tkn('Internal Server Error'), new Map([
|
||||
['fr', 'Erreur interne du serveur'],
|
||||
@ -510,6 +550,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '内部服务器错误'],
|
||||
['id', 'Kesalahan server internal'],
|
||||
['pl', 'Wewnętrzny błąd serwera'],
|
||||
['ko', '내부 서버 오류'],
|
||||
])],
|
||||
[tkn('The server met an unexpected condition'), new Map([
|
||||
['fr', 'Le serveur a rencontré une condition inattendue'],
|
||||
@ -522,6 +563,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器遇到了意外情况'],
|
||||
['id', 'Server mengalami kondisi yang tidak terduga'],
|
||||
['pl', 'Serwer napotkał nieoczekiwany stan'],
|
||||
['ko', '서버가 예상치 못한 조건이에요'],
|
||||
])],
|
||||
[tkn('Bad Gateway'), new Map([
|
||||
['fr', 'Mauvaise passerelle'],
|
||||
@ -534,6 +576,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '无效网关'],
|
||||
['id', 'Gateway yang buruk'],
|
||||
['pl', 'Błąd bramki'],
|
||||
['ko', '게이트웨이 불량'],
|
||||
])],
|
||||
[tkn('The server received an invalid response from the upstream server'), new Map([
|
||||
['fr', 'Le serveur a reçu une réponse invalide du serveur distant'],
|
||||
@ -546,6 +589,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器从上游服务器收到了无效的响应'],
|
||||
['id', 'Server menerima respons yang tidak valid dari server induk'],
|
||||
['pl', 'Serwer otrzymał nieprawidłową odpowiedź od serwera nadrzędnego'],
|
||||
['ko', '게이트웨이가 업스트림 서버로부터 잘못된 응답을 받았어요'],
|
||||
])],
|
||||
[tkn('Service Unavailable'), new Map([
|
||||
['fr', 'Service indisponible'],
|
||||
@ -558,6 +602,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务不可用'],
|
||||
['id', 'Layanan tidak tersedia'],
|
||||
['pl', 'Serwis niedostępny'],
|
||||
['ko', '서비스 불가능'],
|
||||
])],
|
||||
[tkn('The server is temporarily overloading or down'), new Map([
|
||||
['fr', 'Le serveur est temporairement en surcharge ou indisponible'],
|
||||
@ -570,6 +615,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器暂时过载或不可用'],
|
||||
['id', 'Server untuk sementara kelebihan beban atau tidak tersedia'],
|
||||
['pl', 'Serwer jest tymczasowo przeciążony lub wyłączony'],
|
||||
['ko', '서버가 일시적으로 과부하 상태이거나 다운되었어요'],
|
||||
])],
|
||||
[tkn('Gateway Timeout'), new Map([
|
||||
['fr', 'Expiration Passerelle'],
|
||||
@ -582,6 +628,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '网关超时'],
|
||||
['id', 'Batas waktu gateway'],
|
||||
['pl', 'Przekroczenie limitu czasu bramki'],
|
||||
['ko', '게이트웨이 시간초과'],
|
||||
])],
|
||||
[tkn('The gateway has timed out'), new Map([
|
||||
['fr', 'Le temps d’attente de la passerelle est dépassé'],
|
||||
@ -594,6 +641,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '网关响应已经超时'],
|
||||
['id', 'Sambungan ke server induk telah kedaluwarsa'],
|
||||
['pl', 'Bramka przekroczyła limit czasu'],
|
||||
['ko', '게이트웨이 시간이 초과되었어요'],
|
||||
])],
|
||||
[tkn('HTTP Version Not Supported'), new Map([
|
||||
['fr', 'Version HTTP non prise en charge'],
|
||||
@ -606,6 +654,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', 'HTTP版本不受支持'],
|
||||
['id', 'Versi HTTP tidak didukung'],
|
||||
['pl', 'Wersja HTTP nie jest obsługiwana'],
|
||||
['ko', '지원하지 않는 HTTP 버전'],
|
||||
])],
|
||||
[tkn('The server does not support the "http protocol" version'), new Map([
|
||||
['fr', 'Le serveur ne supporte pas la version du protocole HTTP'],
|
||||
@ -618,6 +667,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务器不支持该HTTP协议版本'],
|
||||
['id', 'Server tidak mendukung versi HTTP ini'],
|
||||
['pl', 'Serwer nie obsługuje wersji "protokołu http"'],
|
||||
['ko', '서버가 해당 "HTTP 프로토콜"을 지원하지 않아요'],
|
||||
])],
|
||||
[tkn('Host'), new Map([
|
||||
['fr', 'Hôte'],
|
||||
@ -630,6 +680,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '主机'],
|
||||
['id', 'Host'],
|
||||
['pl', 'Host'],
|
||||
['ko', '호스트'],
|
||||
])],
|
||||
[tkn('Original URI'), new Map([
|
||||
['fr', 'URI d’origine'],
|
||||
@ -642,6 +693,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '原始URI'],
|
||||
['id', 'URL asli'],
|
||||
['pl', 'Oryginalny URI'],
|
||||
['ko', '원시 URI'],
|
||||
])],
|
||||
[tkn('Forwarded for'), new Map([
|
||||
['fr', 'Transmis pour'],
|
||||
@ -654,6 +706,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '转发自'],
|
||||
['id', 'Diteruskan untuk'],
|
||||
['pl', 'Przekazane do'],
|
||||
['ko', '전달받은 대상'],
|
||||
])],
|
||||
[tkn('Namespace'), new Map([
|
||||
['fr', 'Espace de noms'],
|
||||
@ -666,6 +719,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '命名空间'],
|
||||
['id', 'Ruang nama'],
|
||||
['pl', 'Przestrzeń nazw'],
|
||||
['ko', '네임스페이스'],
|
||||
])],
|
||||
[tkn('Ingress name'), new Map([
|
||||
['fr', 'Nom ingress'],
|
||||
@ -678,6 +732,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '入口名'],
|
||||
['id', 'Nama ingress'],
|
||||
['pl', 'Nazwa wejścia'],
|
||||
['ko', '인그레스 이름'],
|
||||
])],
|
||||
[tkn('Service name'), new Map([
|
||||
['fr', 'Nom du service'],
|
||||
@ -690,6 +745,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务名'],
|
||||
['id', 'Nama layanan'],
|
||||
['pl', 'Nazwa usługi'],
|
||||
['ko', '서비스 이름'],
|
||||
])],
|
||||
[tkn('Service port'), new Map([
|
||||
['fr', 'Port du service'],
|
||||
@ -702,6 +758,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务端口'],
|
||||
['id', 'Port layanan'],
|
||||
['pl', 'Port usługi'],
|
||||
['ko', '서비스 포트'],
|
||||
])],
|
||||
[tkn('Request ID'), new Map([
|
||||
['fr', 'Identifiant de la requête'],
|
||||
@ -714,6 +771,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请求ID'],
|
||||
['id', 'ID permintaan'],
|
||||
['pl', 'Identyfikator żądania'],
|
||||
['ko', '요청 ID'],
|
||||
])],
|
||||
[tkn('Timestamp'), new Map([
|
||||
['fr', 'Horodatage'],
|
||||
@ -726,6 +784,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '时间戳'],
|
||||
['id', 'Cap waktu'],
|
||||
['pl', 'Sygnatura czasowa'],
|
||||
['ko', '시간 기록'],
|
||||
])],
|
||||
[tkn('client-side error'), new Map([
|
||||
['fr', 'Erreur Client'],
|
||||
@ -738,6 +797,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '客户端错误'],
|
||||
['id', 'Kesalahan sisi klien'],
|
||||
['pl', 'błąd po stronie klienta'],
|
||||
['ko', '클라이언트 측 오류'],
|
||||
])],
|
||||
[tkn('server-side error'), new Map([
|
||||
['fr', 'Erreur Serveur'],
|
||||
@ -750,6 +810,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '服务端错误'],
|
||||
['id', 'Kesalahan sisi server'],
|
||||
['pl', 'błąd po stronie serwera'],
|
||||
['ko', '서버 측 오류'],
|
||||
])],
|
||||
[tkn('Your Client'), new Map([
|
||||
['fr', 'Votre Client'],
|
||||
@ -762,6 +823,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '您的客户端'],
|
||||
['id', 'Klien Anda'],
|
||||
['pl', 'Klient'],
|
||||
['ko', '내 클라이언트'],
|
||||
])],
|
||||
[tkn('Network'), new Map([
|
||||
['fr', 'Réseau'],
|
||||
@ -774,6 +836,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '网络'],
|
||||
['id', 'Jaringan'],
|
||||
['pl', 'Sieć'],
|
||||
['ko', '네트워크'],
|
||||
])],
|
||||
[tkn('Web Server'), new Map([
|
||||
['fr', 'Serveur Web'],
|
||||
@ -786,6 +849,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', 'Web服务器'],
|
||||
['id', 'Server web'],
|
||||
['pl', 'Serwer WWW'],
|
||||
['ko', '웹 서버'],
|
||||
])],
|
||||
[tkn('What happened?'), new Map([
|
||||
['fr', 'Que s’est-il passé ?'],
|
||||
@ -798,6 +862,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '发生了什么?'],
|
||||
['id', 'Apa yang terjadi?'],
|
||||
['pl', 'Co się stało?'],
|
||||
['ko', '어떤 일이 일어났나요?'],
|
||||
])],
|
||||
[tkn('What can I do?'), new Map([
|
||||
['fr', 'Que puis-je faire ?'],
|
||||
@ -810,6 +875,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '我能做什么?'],
|
||||
['id', 'Apa yang bisa saya lakukan?'],
|
||||
['pl', 'Co mogę zrobić?'],
|
||||
['ko', '어떤 것을 할 수 있나요?'],
|
||||
])],
|
||||
[tkn('Please try again in a few minutes'), new Map([
|
||||
['fr', 'Veuillez réessayer dans quelques minutes'],
|
||||
@ -822,6 +888,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请在几分钟后重试'],
|
||||
['id', 'Silakan coba lagi dalam beberapa menit'],
|
||||
['pl', 'Spróbuj ponownie za kilka minut'],
|
||||
['ko', '몇 분 후에 다시 시도해 주세요'],
|
||||
])],
|
||||
[tkn('Working'), new Map([
|
||||
['fr', 'Opérationnel'],
|
||||
@ -834,6 +901,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '正常运行'],
|
||||
['id', 'Fungsi'],
|
||||
['pl', 'Działa'],
|
||||
['ko', '작동 중'],
|
||||
])],
|
||||
[tkn('Unknown'), new Map([
|
||||
['fr', 'Inconnu'],
|
||||
@ -846,6 +914,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '未知'],
|
||||
['id', 'Tidak diketahui'],
|
||||
['pl', 'Nieznany'],
|
||||
['ko', '알 수 없음'],
|
||||
])],
|
||||
[tkn('Please try to change the request method, headers, payload, or URL'), new Map([
|
||||
['fr', 'Veuillez essayer de changer la méthode de requête, les en-têtes, le contenu ou l’URL'],
|
||||
@ -858,6 +927,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请尝试更改请求方法、标头、有效负载或URL'],
|
||||
['id', 'Coba lagi dengan metode, header, muatan, atau URL yang berbeda'],
|
||||
['pl', 'Spróbuj zmienić metodę żądania, nagłówki, żądanie lub adres URL'],
|
||||
['ko', '요청 방법, 헤더, 콘텐츠 또는 URL을 변경해 보세요'],
|
||||
])],
|
||||
[tkn('Please check your authorization data'), new Map([
|
||||
['fr', 'Veuillez vérifier vos données d’autorisation'],
|
||||
@ -870,6 +940,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请检查您的授权数据'],
|
||||
['id', 'Memeriksa detail autentikasi'],
|
||||
['pl', 'Sprawdź swoje dane autoryzacyjne'],
|
||||
['ko', '인증 데이터를 확인해주세요'],
|
||||
])],
|
||||
[tkn('Please double-check the URL and try again'), new Map([
|
||||
['fr', 'Veuillez vérifier l’URL et réessayer'],
|
||||
@ -882,6 +953,7 @@ Object.defineProperty(window, 'l10n', {
|
||||
['zh', '请再次检查URL并重试'],
|
||||
['id', 'Periksa URL dan coba lagi'],
|
||||
['pl', 'Sprawdź adres URL i spróbuj ponownie'],
|
||||
['ko', 'URL을 다시 한번 확인해 주세요'],
|
||||
])],
|
||||
]));
|
||||
|
||||
|
@ -118,8 +118,8 @@
|
||||
|
||||
const $langSwitch = document.getElementById('lang-switch');
|
||||
|
||||
['fr', 'ru', 'uk', 'pt', 'nl', 'de', 'es', 'zh', 'id', 'pl'].forEach((lang) => {
|
||||
// ^^^ add your newly added locale here
|
||||
['fr', 'ru', 'uk', 'pt', 'nl', 'de', 'es', 'zh', 'id', 'pl', 'ko' ].forEach((lang) => {
|
||||
// ^^^ add your newly added locale here
|
||||
const $li = document.createElement('li');
|
||||
const $btn = document.createElement('button');
|
||||
|
||||
|
@ -28,3 +28,4 @@ different locales, please follow these steps:
|
||||
- 🇨🇳 Chinese by [@CDN18](https://github.com/CDN18)
|
||||
- 🇮🇩 Indonesian by [@getwisp](https://github.com/getwisp)
|
||||
- 🇵🇱 Polish by [@wielorzeczownik](https://github.com/wielorzeczownik)
|
||||
- 🇰🇷 Korean by [@NavyStack](https://github.com/NavyStack)
|
||||
|
@ -50,6 +50,7 @@
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
color: var(--color-inverted);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -50,6 +50,7 @@
|
||||
background-color: var(--color-bg-primary);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -39,6 +39,7 @@
|
||||
color: var(--color-inverted);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
overflow: hidden;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -37,6 +37,7 @@
|
||||
color: var(--color-inverted);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -43,6 +43,7 @@
|
||||
color: var(--color-text-primary);
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -39,6 +39,7 @@
|
||||
overflow: hidden;
|
||||
font-family: sans-serif;
|
||||
font-size: 20px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
canvas {
|
||||
|
@ -41,6 +41,7 @@
|
||||
padding: 0;
|
||||
background-color: var(--color-bg-primary);
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
color: var(--color-inverted);
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2000px) {
|
||||
|
Reference in New Issue
Block a user