Code Monkey home page Code Monkey logo

error-pages's Issues

Error pages middleware break other services

Adding middleware to a service or middleware chain beaks whenever the error pages services is down.

To reproduce add the error pages middleware to a service and than stop the error pages service and try to access the other service.

There should be another way to catch this error pages but without breaking other services if error pages service goes down.

IP Restricted additional information

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

I want to only enable SHOW_DETAILS when the request comes from a specific ip range.
Ex. Customers don't really need to know where the request is going inside the k8s cluster. But for debugging, it would be beneficial.

Suggest a solution

Add an environment variable to allow source-ip ranges for SHOW_DETAILS.

Additional context

No response

Support IPv6

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

We are trying to use the image on a K8S only IPv6.
I was not successful when using env LISTEN_ADDR, with "[::]" and "::".

Log:
listen tcp4: address :::8080: too many colons in address

Suggest a solution

No response

Additional context

No response

random template

It would be a nice option to have and let generator pick random template from available ones? Or maybe specifying more than one in TEMPLATE_NAME and let it randomly pick from preselected templates.

I'm libra and can't decide between ghost and shuffle ;)

Fork github actions issue

Heya,

I thought I'd update my fork to the new version that you've released (doing a test on a different repo before committing to my actual fork rather than creating a branch for now) to keep it up to date and to add a couple more templates, and it seems that the github actions fail on build with the error:

stat /home/runner/work/error-page-test/error-page-test/cmd/error-pages: directory not found

https://github.com/modem7/error-page-test/runs/4361391755?check_suite_focus=true

Is this something you'd be willing to assist with or would this be something outside your remit?

Many thanks!

Redirect missing requests to default status page

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Use case:

I require 404 status codes to be handled by the application instead of error-pages. To do so I now have the following middleware setup in Traefik config.

http:
  middlewares:
    errorPages:
      errors:
        # 404 excluded
        status: 400-403,405-499,501,502,504-599
        service: error-pages@docker
        query: /{status}.html

Error-pages is setup with docker compose:

version: '3.9'

services:
  error-pages:
    image: tarampampam/error-pages:${ERROR_PAGES_IMAGE_VERSION} # 2.21
    security_opt:
      - no-new-privileges:true
    restart: always
    environment:
      TEMPLATE_NAME: ${ERROR_PAGES_TEMPLATE_NAME} # cats
      SHOW_DETAILS: ${ERROR_PAGES_SHOW_DETAILS} # true
    labels:
      traefik.enable: true
      traefik.docker.network: proxy
      traefik.http.routers.error-pages.tls: true
      traefik.http.routers.error-pages.rule: HostRegexp(`{host:.+}`)
      traefik.http.routers.error-pages.priority: 10
      traefik.http.routers.error-pages.entrypoints: websecure
      traefik.http.routers.error-pages.service: error-pages
      traefik.http.services.error-pages.loadbalancer.server.port: 8080
    networks:
      - error-pages
      - proxy

Now the following currently happens:

  • If example.com is up and a user requests a non-existing page, e.g. example.com/test, the user will see the application's specific error page.
  • If example.com is down it shows the 404 (cats) error-pages when requesting example.com. But requesting a non-existing page, e.g. example.com/test, the user will now see the following message:
Wrong request URL. Error pages are available at the following URLs: /{code}.html

What I would like to see is that it defaults to the default error page (cats 404) instead of this message.

Suggest a solution

Possible options:

  • Add a new option/variable to specify to fallback on the 404.html page when at the notfound handler.
  • Use the existing DEFAULT_ERROR_PAGE / DefaultErrorPage variable and use it at the notfound handler.

Note: I'm not familiar with Go.

Additional context

  • This was also mentioned in #140

  • Docker Compose v2.17
  • Traefik v2.9
  • Error-pages 2.21

Forced language setting via variable

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Hi!

Now the service can automatically determine the locale settings and install the language, I have a question, is it possible to set the desired language through a variable to turn off this check?

For example, I need only English, how can I do it now? Thank u in advance!

Suggest a solution

Add environment variable for set up language of pages.

Additional context

No response

http status code 200 instead 404

In the old version the http status code from a not found site was 404 and now it's 200 (of course with the text being '404 not found').

I'm not sure if this is the correct behaviour in web, because even if we serve a custom status page, I think the status code should still be "the error" (e.g. 404).

Can someone tell me what I did wrong or if this is just the new behaviour?

If this is not the expected outcome, feel free to ask me any system details that are needed, as I'm not sure what is relevant. some listed here:

  • reverse proxy: traefik:v2.5
  • tarampampam/error-pages:2.2.0

Β 

PS: Thanks for rewriting this cool project in golang. ✨

Gifs in readme

Heya,

Out of curiosity, how are you generating the gifs in the readme file?

Thanks!

Healthcheck fails after update to 2.20.0

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

The container stays unhealthy because the healthcheck fails. This seems to be caused by the change

  • Flags --verbose, --debug and --log-json are deprecated, use --log-level and --log-format instead #163

since the healthcheck is still

HEALTHCHECK &{["CMD" "/bin/error-pages" "--log-json" "healthcheck"] "7s" "2s" "0s" '\x00'}

Steps to reproduce

No response

Configuration files

No response

Relevant log output

No response

Anything else?

No response

New error template

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>{{ code }} - {{ message }}</title>

    <style>
        body {
            background-color: #222;
            color: #aaa;
            font-family: 'Hack', monospace;
        }

        #error_text {
            position: absolute;
            top: 50%;
            left: 0;
            right: 0;
            text-align: center;
            margin-top: -35px;
            height: 30px;
            font-size: 2em;
        }
    </style>
</head>
<body>

<div id="error_text">{{ code }}: {{ message }}</div>

<script>
    'use strict';

    const setCharAt = function (str, index, chr) {
        return (index > str.length - 1)
                ? str
                : str.substr(0, index) + chr + str.substr(index + 1);
    };

    const $errorText = document.getElementById('error_text');
    const text = $errorText.innerText;

    let progress = 0;

    const scrambleInterval = window.setInterval(() => {
        const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-=+<>,./?[{()}]!@#$%^&*~`\|'.split('');
        let message = text;

        for (let i = 0; i < text.length; i++) {
            if (i >= progress) {
                message = setCharAt(message, i, characters[Math.round(Math.random() * (characters.length - 1))]);
            }
        }

        $errorText.innerText = message;
    }, 800 / 60);

    window.setTimeout(() => {
        let revealInterval = window.setInterval(() => {
            if (progress < text.length) {
                progress++;
            } else {
                clearInterval(revealInterval);
                clearInterval(scrambleInterval);
            }
        }, 70);
    }, 500);
</script>
</body>
</html>

Looks like:

image

Publish image to GHCR

Dockerhub rate limits are a pita, it would be cool if you could push this image to the github container registry as well. It's not really hard to do.

Thanks!

Can we add the possibility of inline translation using Accept-Language header?

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

The current translation (l10n) works fine but it bothers me that the l10n.js is added at the bottom of the page, async and deferred. My connection is not slow and I can see the page blinking

I would love to implement this, but I'm a newbie with go

Suggest a solution

I think a very simple solution would be:

On startup we read the translations, which are already in l10n.js, and store it in a hash

On errorpage.go we initialize the props as usual (props.Message = page.Message()), after this we check the Accept-Header for the language codes, sort by weight and get the first that matches one of the available languages, if the language is not detected or is en we just skip the translation, if different than en we call a function that just returns the value for the key message, just as the l10n already does. Then just for caching we add the lang-code to props

Additional context

My pseudo code would be something like this:

LiveTranslation.parse('translations.json')

if page, exists := cfg.Pages[pageCode]; exists {
  props.Message = page.Message()
  props.Description = page.Description()
} else if c, err := strconv.Atoi(pageCode); err == nil {
  if s := fasthttp.StatusMessage(c); s != "Unknown Status Code" { // as a fallback
    props.Message = s
  }
}

selected_lang = LiveTranslation.parse_and_match( string(ctx.Request.Header.Peek("Accept-Language")) )
if selected_lang != "en" && selected_lang != null {
  for name, value in props {
    props .name = LiveTranslation.translate(value, selected_lang)
  }
  props.Lang = selected_lang
}

CPU spikes

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

I noticed cpu spikes on one of my VM machines, and figured with: docker stats --format "table {{.Name}}t{{.CPUPerc}}t{{.MemUsage}}" that like every 5-10 seconds error-pages the cpu usage spikes to 8-16% while my other containers chill at ~1%.

traefikt0.00%t24.01MiB / 1.929GiB
error-pagest17.05%t2.922MiB / 1.929GiB
adguardhomet0.02%t45.77MiB / 1.929GiB
portainert0.00%t8.27MiB / 1.929GiB
dozzlet0.00%t13.95MiB / 1.929GiB
sp_watchtowert0.02%t2.418MiB / 1.929GiB
apachet0.01%t5.945MiB / 1.929GiB
sp_traefikt0.01%t9.016MiB / 1.929GiB
autheliat0.00%t21.08MiB / 1.929GiB
sp_portainert0.01%t2.32MiB / 1.929GiB
whoamit0.00%t1MiB / 1.929GiB
traefik-bouncert0.00%t18.5MiB / 1.929GiB

honestly no idea why, I closed all http/s connections to the server and still had this issue.

Steps to reproduce

No response

Configuration files

error-pages:
    <<: *common-keys-alwaysup # See EXTENSION FIELDS at the top
    image: tarampampam/error-pages:2.20.0 # Using the latest tag is highly discouraged. Please, use tags in X.Y.Z format
    container_name: error-pages
    command:
      - "--log-level=error"      
      - "serve"
    networks:
      - traefik
    volumes:
      - ${APPDATA_DIR:?err}/error-pages/mytemplates:/opt/templates:ro # my templates
    environment:
      # TEMPLATE_PATH: /mytemplates/matrix.html
      TEMPLATE_NAME: matrix # set the error pages template
      SHOW_DETAILS: true
    labels:
      traefik.enable: true
      traefik.http.services.error-pages.loadbalancer.server.port: 8080
      com.centurylinklabs.watchtower.monitor-only: true

Relevant log output

No response

Anything else?

maybe you can tell me, what could cause this

thanks a lot and I really like this project

Custom env variables to be used in templates

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

I want to dynamically set the document.domain in my custom template based on the mode I am in. I use different domains for development and production mode. Currently this is the solution I have:

<script>
      {{ if show_details }}
      document.domain = 'local.mydomain.com';
      {{ else }}
      document.domain = 'mydomain.com';
      {{ end }}
</script>

This is not perfect since I actually have 3 states: development, staging and production, each with their own domain. The current solution just handles development and production with the help of the show_details flag since this is turned off in production I can use it to set the domain accordingly.

Suggest a solution

I searched existing issues and also had a peak inside the source code but did not find anything the would allow me to set a custom environment variables that are populated inside the template like so:

...
extraEnv:
  DOMAIN: mydomain.com

that would be available inside the html template:

<script>
      document.domain = '{{domain}}';
</script>

Additional context

If there is a similar existing solution I have missed please let me know.

Upgrade to 2.0.0 broke error-pages

I am trying to upgrade to 2.0.0 of this docker image, great job re-writing it in Go!

I would have thought it would have been a seamless upgrade however on 2.0.0 I am getting the message. Hi there! Error pages are available at the following URLs: /{code}.html. If I rollback to 1.8.0 it works fine.

1.8.0 logs

error-pages-644fdd7c94-g5pzr error-pages 10.69.152.237 - - [05/Oct/2021:13:43:13 +0000] "GET / HTTP/1.1" 404 3526 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36" "x.x.x.x, 172.70.110.112"

2.0.0 logs

error-pages-6b4cc76777-4mt4l error-pages {"level":"info","ts":1633441272.6022592,"msg":"HTTP request processed","useragent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36","method":"GET","url":"/","status_code":404,"connection_close":false,"duration":0.000023755}

You can see my use of error-pages with the middleware and ingressroute here:
https://github.com/onedr0p/home-cluster/tree/main/cluster/apps/networking/error-pages

Is a requirement of the new version supposed to force us to annotate each ingress with using this middleware? If so, can that be changed to be global again?

Thanks!

The DockerHub has the wrong project description

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

As you can see here
Screenshot_20221111_140705

The issue is that the project tinifier is publishing the page to the wrong docker repository.

See tarampampam/tinifier#109

You should force am update from this repo after the fix

Steps to reproduce

No response

Configuration files

No response

Relevant log output

No response

Anything else?

No response

Ingress Nginx backend support

Thanks for making these! I am wondering if it's all possible to support a default backend when using ingress-nginx.

When using ingress-nginx in kubernetes you can set a defaultBackend

https://github.com/kubernetes/ingress-nginx/blob/f5cfd5730c4b296c87fbc531c83a6e4f33483b75/charts/ingress-nginx/values.yaml#L605

an example of a default backend is below, using Go is not a hard requirement. It just acts on HTTP headers as described in the table on this project.

https://github.com/181192/custom-error-pages

I totally get not wanting to support this, but I'd rather have it built in here instead of forking your repo.

Thanks!

"Too big request header"

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

When the auth cookies for my website are present and I try to a non existing page I get "Too big request header" instead of the error page. When I clear said cookies I see my error page as expected.

Steps to reproduce

  1. Setup error pages
  2. Set long token in cookies
  3. Get "Too big request header" instead of error page

Configuration files

No response

Relevant log output

No response

Anything else?

I am not sure if this is a traefik issue, It does only happen with the error page container, accessing the normal webpage is fine with and without the auth cookies set.

Authorization prompts

Some services (Syncthing in my case), have a popup authorization prompt rather than something on the page itself. I'm not familiar with what this authorization method is, but it's the same thing my router uses for its admin UI. When my error-pages range includes status code 401, error-pages intercepts and presents the unauthorized page, never giving the popup the chance to... pop up. Right now I've just set my range to 402-599 to avoid this, but I thought you should know in case there's something that can be done to fix it.

cross origin request to fonts.bunny.net fails

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

I deployed error pages by building it in my docker image of openresty. I manage to get the error page ghost working. The only thing that fails for me is the loading of the font.

I get these error messages in console:
image

Do you know if there is anything I can do to make the font load properly?

Steps to reproduce

No response

Configuration files

No response

Relevant log output

No response

Anything else?

No response

add extra template

Hi,

Thank you for this simple docker container. I use it with Traefik and it works flawless.
I used another before (very simular to this thought) which has antoher template called "noise"
How can I add this to this repo? I tried to add template, updated tests.yml to add folder and did a docker build.

Step 9/16 : RUN ./generator/generator.js -c ./config.json -o ./out ---> Running in 08d7c027a654 SyntaxError: Unexpected token { in JSON at position 422 at JSON.parse (<anonymous>) at Object.<anonymous> (/src/generator/generator.js:32:32) at Module._compile (node:internal/modules/cjs/loader:1092:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1121:10) at Module.load (node:internal/modules/cjs/loader:972:32) at Function.Module._load (node:internal/modules/cjs/loader:813:14) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12) at node:internal/main/run_main_module:17:47 The command '/bin/sh -c ./generator/generator.js -c ./config.json -o ./out' returned a non-zero code: 1

I need to say, I'm new to building docker images (but running docker containers for quit some time now) and I'm not a programmer
Can you help me out building it or how to add this template to this repo.

Thanks you for your reply,

Ralph

HTTP status code not set per code template

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

Fetching a specific error with /403.html (and anything not 404) returns as HTTP/1.1 200 OK but with the correct template content. Instead fetching / with a X-Code: 403 works as expected.

Steps to reproduce

  1. docker run --rm -it -p 1234:8080 ghcr.io/tarampampam/error-pages:2.24
  2. curl -v -H "X-Code: 403" localhost:1234 > /dev/null -> < HTTP/1.1 403 Forbidden
  3. curl -v localhost:1234/403.html > /dev/null -> < HTTP/1.1 200 OK

Configuration files

N/A, defaults are used.

Relevant log output

{"level":"info","ts":1682500829.970848,"msg":"We will use the requested template","name":"ghost"}
{"level":"info","ts":1682500829.971214,"msg":"Server starting","addr":"0.0.0.0","port":8080,"default error page":"404","default HTTP response code":404,"proxy headers":[],"show request details":false,"localization disabled":false}
{"level":"info","ts":1682500852.084728,"msg":"HTTP request processed","useragent":"''","method":"GET","url":"/403.html","referer":"","status_code":200,"content_type":"text/html; charset=utf-8","connection_close":false,"duration":0.000796617,"request_headers":["Host: localhost:1234","User-Agent: ''","Accept: */*"],"response_headers":["Content-Type: text/html; charset=utf-8","X-Robots-Tag: noindex"]}
{"level":"info","ts":1682500968.6593013,"msg":"HTTP request processed","useragent":"''","method":"GET","url":"/","referer":"","status_code":403,"content_type":"text/html; charset=utf-8","connection_close":false,"duration":0.000545044,"request_headers":["Host: localhost:1234","User-Agent: ''","Accept: */*","X-Code: 403"],"response_headers":["Content-Type: text/html; charset=utf-8","X-Robots-Tag: noindex"]}

Anything else?

No response

Everything either returns traefik's 404 or EP's 404

Hello there!
Firstly let me thank you for creating and keeping alive this project, it's such a simple yet useful thing

I have been having issues with getting error-pages to work with my traefik configuration.

I will use pastebin for configs as they take some place. I will use example of Traefik + Authelia + Nginx + Error Pages

If I use provided labes from the main page (adjusting entrypoints etc), I just get Traefik's 404 everywhere even traefik.domainname.com that worked before now returns traefik's 404.

With my base setup if I go to traefik.domainname.com it redirects to authelia login page and then back to traefik once logged in, it's visible in middlewares section on pastebin.

With the config I attached in pastebin, which is basically the same, domainname.com loads just fine, traefik.domainname.com returns errorpages 404 also domainname.com/anything returns errorpage's 404 even if the file exists, for example index.html. If used just domainname.com, it correctly returns default index.html from nginx configuration but does not put it in the website bar.

https://pastebin.com/mQFXjQCV (configs)
http://prntscr.com/26klxo7 (not working)
http://prntscr.com/26klxy9 (working correctly)
http://prntscr.com/26klzg5 (traefik log)
http://prntscr.com/26km0mm (error pages log)

Once I remove errorspage from middleswares (dynamic file) it all returs to normal. I have tried setting priority to 2 or 11 in errorspages.

I would love to get a direction where to look for, logs shows just that client is hitting 404. If something is missing I will gladly upload more, though it might take me about a day.

Thank you for your time.

Edit 1:
I have found my traefik labels were somehow missing - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" but it does not help the issue I am having right now.

Custom static-pages ( no error pages, but default-backend related )

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Hello,
first of all, thank you very much for this great project. I like to use this project in different situations. I would like to ask if it would be possible to extend the tool with the following feature:

I am interested in more usecases for simple static-pages which from my ops point of view could run best directly integrated in the "defaultbackend" so this tool.

Currently I am establishing the /.well-known/security.txt on the loadbalancers. See background. This is a static TXT which I would like to read via configmap or similar.

Why here? Because I like the logging, the lightweight implementation and the deployment without filesystem-access.

Suggest a solution

The way of request-handling could be triggered via 'static-path' or better a request header Static-Page: foobar (e.g. set by the loadbalancer).
maybe also an ENV variable with a comma separated list of static pages as path or header-value (value=filename) to include.

Additional context

I'm not sure if this is beyond the scope of this tool from your point of view, I think it would be a good extension.
Would there be interest in such a feature?
kind regards

For anything other than 404 in traefik

More an FYI for others:

Just been doing some research and it seems that if you want to apply these to any container that does exist, you'll need to create new middleware in traefik.

Easy enough by following:
https://dille.name/blog/2021/03/14/using-traefik-error-pages-to-handle-unavailable-services/

And applying the middleware to all your services.

A good way to test the guide above:

  httpbin: # A simple HTTP Request & Response Service.
    container_name: httpbin
    image: kennethreitz/httpbin
    restart: always
    networks:
      pihole:
        ipv4_address: 'static.ip.address.here'
    security_opt:
      - no-new-privileges:true
    labels: 
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.httpbin-rtr.entrypoints=https"
      - "traefik.http.routers.httpbin-rtr.rule=Host(`httpbin.$DOMAINNAME`)"
      - "traefik.http.routers.httpbin-rtr.tls=true"
      ## Middlewares
      - "traefik.http.routers.httpbin-rtr.middlewares=chain-no-auth@file"
      - "traefik.http.routers.httpbin-rtr.middlewares=traefik-error-pages"
      ## HTTP Services
      - "traefik.http.routers.httpbin-rtr.service=httpbin-svc"
      - "traefik.http.services.httpbin-svc.loadbalancer.server.port=80"

https://www.modem7.com/books/docker/page/current-docker-compose β€” you can see how I've implemented multiple middlewares in each container.

docker-enrypoint.sh error creating existent directory

Hi

I have the following error when restarting the container after initial start.

error-pages           | mkdir: can't create directory '/opt/html/nginx-error-pages': File exists
error-pages           | /docker-entrypoint.sh: set pages for template 'ghost' as default (make accessible in root directory)

Solved by chaging docker-entrypoint.sh line 16, from
mkdir /opt/html/nginx-error-pages;

to

mkdir -p /opt/html/nginx-error-pages;

Custom 404 page

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

First of all thank you for your wonderful package!
I would like to use your pages (for instance, the l7-dark template) but with a custom 404 page.

Suggest a solution

Would it be possible to implement an environmental variable, such as CUSTOM_404 that, if not set, has a default to False?
Then, in the docker container, is just a matter to set CUSTOM_404 to true and implement the correct mapping via a volume entry..
Something like

  error-pages:
    image: tarampampam/error-pages:latest
    ports:
      - 8080:8080
    volumes:
      - /path/to/local/404.html:/custom/404.html:ro
    environment:
      TEMPLATE_NAME: l7-dark
      CUSTOM_404:  true
    labels:
      traefik.enable: true

Thank you!

Additional context

No response

Documented HostRegexp rule is broken for Traefik v3

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

Hi,

The wiki needs to be updated based on the findings in this discussion: #275

Traefik v3 introduces some change to regex handling and the current wiki version of tarampampam/error-pages suggests an invalid configuration option.

Steps to reproduce

Try using the official documentation with Traefik v3

Configuration files

# Wrong
traefik.http.routers.error-pages-router.rule: HostRegexp(`{host:.+}`)

# Good
traefik.http.routers.error-pages-router.rule: HostRegexp(`.+`)

Relevant log output

N/A

Anything else?

N/A

Support customization through env variables

It would be awesome to be able to define error pages content by env variables.

It'd be sooo nice to be able to do something like:

# docker-compose.yml
version: '3.4'

services:
  error-pages:
    image: tarampampam/error-pages:latest
    environment:
      CUSTOM_503: |
        <body>
          <div>My custom error page</div>
        </body>

Middleware "error-pages-middleware@docker" does not exist

Problem
I'm currently setting up this container with the example provided in the README, but when looking at my Traefik logs, I see the following error log popping up when I up my containers.

{"entryPointName":"websecure","level":"error","msg":"middleware \"error-pages-middleware@docker\" does not exist","routerName":"maintenance-secure@docker","time":"2021-12-19T19:10:24+01:00"}

Maybe I'm missing something obvious, but I just can't seem to find it.

Config
docker-compose.yml

# Custom error pages for downed containers
error-pages:
  image: tarampampam/error-pages:${ERROR_PAGES_VERSION}
  container_name: error-pages
  depends_on:
    - traefik
  environment:
    TEMPLATE_NAME: custom
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.error-pages-router.rule=HostRegexp(`{host:.+}`)"
    - "traefik.http.routers.error-pages-router.priority=10"
    - "traefik.http.routers.error-pages-router.tls=true"
    - "traefik.http.routers.error-pages-router.entrypoints=websecure"
    - "traefik.http.routers.error-pages-router.middlewares=error-pages-middleware@docker"
    - "traefik.http.services.error-pages-service.loadbalancer.server.port=8080"
    - "traefik.http.middlewares.error-pages-middleware.errors.status=400-599"
    - "traefik.http.middlewares.error-pages-middleware.errors.service=error-pages-service@docker"
    - "traefik.http.middlewares.error-pages-middleware.errors.query=/{status}.html"
    - "traefik.docker.network=proxy"
  networks:
    - proxy
  volumes:
    - /var/www/html/error-pages/error-pages.yml:/opt/error-pages.yml
    - /var/www/html/error-pages/custom.html:/opt/custom.html
    - /var/www/html/error-pages/custom:/opt/templates/custom

# Application maintenance page
maintenance:
  build:
    context: docker/apache
    args:
      APACHE_VERSION: ${APACHE_VERSION}
  container_name: maintenance_proxy
  depends_on:
    - error-pages
  security_opt:
    - no-new-privileges:true
  restart: 'no'
  volumes:
    - /var/www/html/error-pages/custom/503.2.html:/usr/local/apache2/htdocs/index.html:ro
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.maintenance-secure.entrypoints=websecure"
    - "traefik.http.routers.maintenance-secure.rule=Host(`${APP_DOMAIN}`, `www.${APP_DOMAIN}`)"
    - "traefik.http.routers.maintenance-secure.service=maintenance-service"
    - "traefik.http.routers.maintenance-secure.tls=true"
    - 'traefik.http.routers.maintenance-secure.middlewares=error-pages-middleware@docker'
    - "traefik.http.services.maintenance-service.loadbalancer.server.port=80"
    - "traefik.docker.network=proxy"
  networks:
    - proxy

traefik.yml

api:
  dashboard: true

log:
  format: json
  level: ERROR

entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure

  websecure:
    address: :443

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    watch: true
    network: proxy
  file:
    directory: /dynamic
    watch: true

Versions

  • error-pages: 2.0.0
  • Traefik: 2.5

Support Docker Volumes for Configurable Error Pages

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Currently, configuring error pages in Docker containers requires manual adjustments to the entrypoint script and ensuring proper permissions for mounted directories. Users need a streamlined way to mount volumes for error page templates, HTML, and configuration files, allowing easy customization and configuration without modifying the container's filesystem or rebuilding the image. Additionally, users should have shell access with docker exec to manage files if needed

Suggest a solution

Implement support for Docker volumes in the docker-entrypoint.sh script and Dockerfile to allow users to mount local directories to the container. This can be achieved by modifying the entrypoint script to handle symbolic links and permissions, and updating the Dockerfile to ensure proper setup. The following changes are suggested:

  1. Dockerfile Adjustments:
  • Ensure directories like /config/templates, /config/html, and /config/error-pages.yml are writable by the container user.
  • Install necessary dependencies and set up the environment for running the error pages application.
  1. Entrypoint Script:
  • Create symbolic links from /config to /opt if the directories or files do not already exist.
  • Set appropriate permissions for the /config directories and files to ensure they are writable by the container's user.
  • Log actions and potential issues with detailed and colored messages for easier debugging and monitoring.
  1. Shell Access:
  • Ensure users have shell access using the alpine image in dockerfile with docker exec to manage files if needed.
  • Maintain appropriate permissions so users can read and write to mounted volumes without requiring elevated privileges.

Additional context

The following is a proposed Dockerfile and docker-entrypoint.sh script to support volume mounting and to support shell access:

dockerfile changes

# use alpine instead of scratch
FROM alpine:latest AS runtime

# Copy the entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/

# Make the script executable
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Install recommended packages
RUN apk add --no-cache bash rsync nano

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

dockerfile full output

# syntax=docker/dockerfile:1

# this stage is used to build the application
FROM docker.io/library/golang:1.22-bookworm AS builder

COPY ./go.* /src/

WORKDIR /src

# burn the modules cache
RUN go mod download

# this stage is used to compile the application
FROM builder AS compiler

# can be passed with any prefix (like `v1.2.3@GITHASH`), e.g.: `docker build --build-arg "APP_VERSION=v1.2.3@GITHASH" .`
ARG APP_VERSION="undefined@docker"

WORKDIR /src

COPY . .

# arguments to pass on each go tool link invocation
ENV LDFLAGS="-s -w -X gh.tarampamp.am/error-pages/internal/version.version=$APP_VERSION"

# build the application
RUN set -x \
    && CGO_ENABLED=0 go build -trimpath -ldflags "$LDFLAGS" -o ./error-pages ./cmd/error-pages/ \
    && ./error-pages --version \
    && ./error-pages -h

WORKDIR /tmp/rootfs

# prepare rootfs for runtime
RUN set -x \
    && mkdir -p \
        ./etc \
        ./bin \
        ./opt/html \
    && echo 'appuser:x:10001:10001::/nonexistent:/sbin/nologin' > ./etc/passwd \
    && echo 'appuser:x:10001:' > ./etc/group \
    && mv /src/error-pages ./bin/error-pages \
    && mv /src/templates ./opt/templates \
    && rm ./opt/templates/*.md \
    && mv /src/error-pages.yml ./opt/error-pages.yml

WORKDIR /tmp/rootfs/opt

# generate static error pages (for usage inside another docker images, for example)
RUN set -x \
    && ./../bin/error-pages --verbose build --config-file ./error-pages.yml --index ./html \
    && ls -l ./html

# use alpine instead of scratch
FROM alpine:latest AS runtime

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.url="https://github.com/tarampampam/error-pages" \
    org.opencontainers.image.source="https://github.com/tarampampam/error-pages" \
    org.opencontainers.image.vendor="tarampampam" \
    org.opencontainers.version="$APP_VERSION" \
    org.opencontainers.image.licenses="MIT"

# Install recommended packages
RUN apk add --no-cache bash rsync nano

# Import from compiler build stage
COPY --from=compiler /tmp/rootfs /

# Copy the entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/

# Make the script executable
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Use an unprivileged user
USER 10001:10001

WORKDIR /opt

# Set environment variables
ENV LISTEN_PORT="8080" \
    TEMPLATE_NAME="ghost" \
    DEFAULT_ERROR_PAGE="404" \
    DEFAULT_HTTP_CODE="404" \
    SHOW_DETAILS="false" \
    DISABLE_L10N="false" \
    READ_BUFFER_SIZE="2048"

# Healthcheck
HEALTHCHECK --interval=7s --timeout=2s CMD ["/bin/error-pages", "--log-json", "healthcheck"]

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["--log-json", "serve"]

docker-entrypoint.sh

#!/bin/sh

# ANSI Escape Code for Colors
reset="\033[0m"
white_fg_strong="\033[90m"
red_fg_strong="\033[91m"
green_fg_strong="\033[92m"
yellow_fg_strong="\033[93m"
blue_fg_strong="\033[94m"
magenta_fg_strong="\033[95m"
cyan_fg_strong="\033[96m"

# Normal Background Colors
red_bg="\033[41m"
blue_bg="\033[44m"
yellow_bg="\033[43m"

# Function to log messages with timestamps and colors
log_message() {
    # This is only time
    current_time=$(date +'%H:%M:%S')
    # This is with date and time
    # current_time=$(date +'%Y-%m-%d %H:%M:%S')
    case "$1" in
        "INFO")
            echo -e "${blue_bg}[$current_time]${reset} ${blue_fg_strong}[INFO]${reset} $2"
            ;;
        "WARN")
            echo -e "${yellow_bg}[$current_time]${reset} ${yellow_fg_strong}[WARN]${reset} $2"
            ;;
        "ERROR")
            echo -e "${red_bg}[$current_time]${reset} ${red_fg_strong}[ERROR]${reset} $2"
            ;;
        *)
            echo -e "${blue_bg}[$current_time]${reset} ${blue_fg_strong}[DEBUG]${reset} $2"
            ;;
    esac
}

set -e


# Default to appuser (UID 10001), so old installations won't break
export PUID=${PUID:-10001}
export PGID=${PGID:-10001}

# Create symbolic links if necessary
if [ ! -d "/opt/templates" ]; then
    ln -s /config/templates /opt/templates
    log_message "INFO" "Created symbolic link: /config/templates -> /opt/templates"
fi

if [ ! -d "/opt/html" ]; then
    ln -s /config/html /opt/html
    log_message "INFO" "Created symbolic link: /config/html -> /opt/html"
fi

if [ ! -f "/opt/error-pages.yml" ]; then
    ln -s /config/error-pages.yml /opt/error-pages.yml
    log_message "INFO" "Created symbolic link: /config/error-pages.yml -> /opt/error-pages.yml"
fi

export ERRORPAGES_BUILDTIME=$(date +%s)

# Set privileges for /opt and /config but only if pid 1 user is root and we are dropping privileges.
# If container is run as an unprivileged user, it means owner already handled ownership setup on their own.
# Running chown in that case (as non-root) will cause error
if [ "$(id -u)" = "0" ] && [ "${PUID}" != "0" ]; then
    chown -R ${PUID}:${PGID} /opt
    chown -R ${PUID}:${PGID} /config
    chown -R 10001:10001 /config/templates
    chown -R 10001:10001 /config/html
    log_message "INFO" "Changed ownership of /opt and /config to ${PUID}:${PGID}"
    log_message "INFO" "Changed ownership of /config/templates and /config/html to 10001:10001"
fi

# Drop privileges (when asked to) if root, otherwise run as current user
if [ "$(id -u)" = "0" ] && [ "${PUID}" != "0" ]; then
    log_message "INFO" "Starting error-pages as user ${PUID}:${PGID}"
    su-exec ${PUID}:${PGID} /bin/error-pages "$@"
else
    log_message "WARN" "No privileges to drop. Running error-pages as current user."
    exec /bin/error-pages "$@"
fi

Make sure to add docker-entrypoint.sh to the .dockerignore file

.dockerignore

## Ignore everything
*

## Except the following files and directories
!/docker-entrypoint.sh
!/cmd
!/internal
!/templates
!/error-pages.yml
!/go.*

By following this approach, users can mount volumes with the docker compose file:

docker-compose.yml

services:
  error-pages:
    image: error-pages:latest
    container_name: error-pages
    environment:
      PGID: 1000
      PUID: 1000
      TEMPLATE_NAME: connection # set the error pages template
    volumes:
      - ./error-pages/data/templates:/config/templates
      - ./error-pages/data/html:/config/html
      - ./error-pages/data/config/error-pages.yml:/config/error-pages.yml:rw
    ports:
      - "8176:8080"
    restart: unless-stopped

Turn off logs

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Trying to disable logs

Suggest a solution

Have an env var to disable logs

Additional context

No response

GDPR - Replace Google Fonts by Bunny Fonts

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Issue derived from discussion #129

After finding out that that serving fonts through Google Fonts is not GDPR compliant (bunny.net; official ruling), is it maybe an idea to update this in the templates by switching to another font service, like for example Bunny Fonts? It would be as easy as just replacing fonts.googleapis.com by fonts.bunny.net.

Suggest a solution

Replace fonts.googleapis.com by fonts.bunny.net in link tags.

Additional context

No response

ADD_TEMPLATE env var

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

Trying out v3 in docker with tarampampam/error-pages:3.1.0

I have always been running a custom template and with the new version I need to run the cli flag --add-template and it makes the Docker-composefile a bit messy which was not needed prior.

Suggest a solution

Adding a companion env var ADD_TEMPLATE as for the other config flags, would allow me to not override the default command in docker.

Additional context

No response

X-Transmission-Session-id Header Stripped

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Great work on this project, it's certainly beautified my end users experience when they get an error however...

As transmission uses rpc to serve its pages to protect against CSRF it expects a valid session-id header in the request, otherwise it returns a 409 page.

Without error-pages defined on the entrypoint in Traefik, the request hits the rpc endpoint and on the initial try it obtains a session-id and then retries resulting in a successful response and the page loads.

With error-pages defined, all consequent hits to the rpc endpoint result in a 409 as this error page strips the X-Transmission-Session-id header and therefore gets rejected.

Is there anyway of setting "Pass-through request/response headers" or an abilitiy to set a list of headers that are allowed to be kept?

Thanks in advance.

Steps to reproduce

Logs:

Without error-pages:

172.16.200.15 - - [20/Feb/2022:23:21:13 +0000] "POST /transmission/rpc HTTP/1.1" 409 593 "https://transmission.mydomain.com/transmission/web/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" "10.32.100.189"
172.16.200.3 - - [20/Feb/2022:23:21:15 +0000] "POST /transmission/rpc HTTP/1.1" 200 424 "-" "Python-urllib/3.9" "-"
172.16.200.3 - - [20/Feb/2022:23:21:15 +0000] "POST /transmission/rpc HTTP/1.1" 200 23925 "-" "Python-urllib/3.9" "-"
172.16.200.3 - - [20/Feb/2022:23:21:15 +0000] "POST /transmission/rpc HTTP/1.1" 200 1563 "-" "Python-urllib/3.9" "-"

With error-pages:

172.16.200.15 - - [20/Feb/2022:23:21:18 +0000] "POST /transmission/rpc HTTP/1.1" 409 593 "https://transmission.mydomain.com/transmission/web/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" "10.32.100.189"
172.16.200.15 - - [20/Feb/2022:23:21:23 +0000] "POST /transmission/rpc HTTP/1.1" 409 593 "https://transmission.mydomain.com/transmission/web/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" "10.32.100.189"
172.16.200.15 - - [20/Feb/2022:23:21:28 +0000] "POST /transmission/rpc HTTP/1.1" 409 593 "https://transmission.mydomain.com/transmission/web/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" "10.32.100.189"
172.16.200.15 - - [20/Feb/2022:23:21:33 +0000] "POST /transmission/rpc HTTP/1.1" 409 593 "https://transmission.mydomain.com/transmission/web/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" "10.32.100.189"
172.16.200.15 - - [20/Feb/2022:23:21:38 +0000] "POST /transmission/rpc HTTP/1.1" 409 593 "https://transmission.mydomain.com/transmission/web/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" "10.32.100.189"

Configuration files

No response

Relevant log output

No response

Anything else?

No response

ingress-nginx support

Hi πŸ‘‹πŸΌ It would be pretty neat to have your project support ingress-nginx, currently I've been using 181192/custom-error-pages which has some of your themes (there's credit to you in the README). However it would be awesome if your project was a solution for both traefik and ingress-nginx. Thanks!

Edit: This is a very basic implentation of the default-backend as provided by the ingress-nginx project.
https://github.com/kubernetes/ingress-nginx/tree/main/images/custom-error-pages/rootfs

πŸŽ‰ Need help with `v3` alpha|beta testing

Hello everyone!

I've been working hard over the past few weeks on a major version of this project, and my work is nearly complete. The most important parts are done and ready to be tested. Please note that the following changes are not backward-compatible, which is why this is a major update:

  • The configuration file (error-pages.yml) is no longer needed. Everything is now configured using CLI flags and environment variables
  • The app is built with templates from this repository (they are built-in to the app binary file), and you can still use custom templates using the flag --add-template /path/to/your/tpl.htm
  • While most CLI flags and environment variables remain the same as in v2, some have changed. Please update your charts accordingly. For the latest information, check here. The README file is still in progress, but the CLI documentation is available
  • The error pages can now be accessed using the following URLs - /{code}, /{code}.html, and /{code}.htm. Additionally, you can use the flag --send-same-http-code to retain the same HTTP status code as the page, if needed
  • To override or append a custom code, you can do this using the flag --add-http-code="…" in the format %code%=%message%/%description%. Moreover, you can use "placeholder" codes like 4**, which means if the requested code is not described but is between 400 and 499, the provided message with its description will be returned. This approach is similar to the one used in the OpenAPI specification, where instead of describing each code individually, you can describe a pattern
  • The default content type for error pages is now PlainText. This means that when you curl an error page, you will receive more readable content without tons of HTML tags. This change should not cause any issues, but it may
  • I am still deciding on two things - whether to keep the fasthttp HTTP server or switch to the stdlib HTTP server, and whether to keep the templates caching mechanism used in v2. Currently, I have switched to the stdlib HTTP server (due to better support and built-in HTTP/2 support, though with lower performance - 40k RPS has dropped to 11k). I've also removed the caching mechanism, which should reduce memory consumption but may increase CPU load slightly. I haven't finalized these decisions yet, so please share your thoughts
  • The templates have been redesigned to be more responsive. A dark theme is now automatically applied if the user prefers it. Themes l7-dark and l7-light have been merged into a single l7 theme, and the matrix theme has been removed (because I created it and it wasn't very appealing)
  • The templates no longer download anything from the far-far galaxies - everything is now embedded, including the localization script, which is now inlined into the page. Additionally, custom fonts have been removed
  • The metrics endpoint /metrics has been not implemented yet, If someone found it useful and has used it - just tell me, and I'll re-implement it
  • I'll add any other important updates here later

I'm reaching out to see if anyone would like to assist with early testing of the app. I can provide early builds (Docker image tags) via messages in this thread. It would be fantastic if you could run them locally or on your staging cluster and provide feedback on whether everything is working smoothly or if you encounter any issues.

Please leave a comment to let me know if you're willing to help. Your assistance would be greatly appreciated!

/cc @deffcolony @moschlar @r2DoesInc @fuog @kfirfer @CodeAnthem @NicosKaralis @GoliathLabs @onedr0p @modem7 @ToshY

Pass through error message and/or error description from response body

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem to be solved

I have this setup as a default error handler on my k8s cluster, and it works great overall. My apis deployed on the cluster have a bit of an issue though.

When returning a 404 or something that this project is setup to respond for, my error content is ignored.

This turns my

{
    "error": "location_invalid",
    "message": "Given location either does not exist or is invalid."
}

Into

{
    "error": true,
    "code": "404",
    "message": "Not Found",
    "description": "The server can not find the requested page",
}

Suggest a solution

I propose modifying the error page to pull from the response body if it is provided

Something like

props.Message = page.Message()

props.Message =  string(ctx.Request.Body.Peek(Message))

I dont know go, so not sure if thats how it would be implemented.

This could be configured behind a bool for ALLOW_ERROR_MESSAGE_PASSTHROUGH which would default to false to maintain consistency with prior behavior.

Additional context

No response

Not found returns 405 if methods are POST/DELETE etc..

Is there an existing issue for this?

  • I have searched the existing issues
  • And it has nothing to do with Traefik

Describe the bug

When sending regular not found URL with GET method, im getting 404 as usual
when sending the same thing with methods other than GET, its returns getting 405

Using ingress-nginx controller
And the helm chart configuration & log below:

Steps to reproduce

No response

Configuration files

controller:
  config:
    custom-http-errors: "400,401,403,404,405,407,408,409,410,411,412,413,416,418,429,500,502,503,504,505"
    
defaultBackend:
  enabled: true
  image:
    repository: tarampampam/error-pages
    tag: "2.20.0"
  extraEnvs:
    - name: TEMPLATE_NAME # Optional: change the default theme
      value: "app-down"
    - name: SHOW_DETAILS # Optional: enables the output of additional information on error pages
      value: "true"
    - name: LOG_FORMAT
      value: "json"   


### Relevant log output

```shell
This is the log from default backend pod:

[ingress-nginx-defaultbackend-869c98f496-d644d ingress-nginx-default-backend] 2023-02-19T10:26:44.092636417Z {"level":"info","ts":1676802404.092549,"msg":"HTTP request processed","useragent":"PostmanRuntime/7.28.4","method":"POST","url":"/","referer":"","status_code":405,"content_type":"text/plain; charset=utf-8","connection_close":false,"duration":0.000008231,"request_headers":["Host: paa-theruntime.local.platform.test","Content-Length: 243","Content-Type: application/json","User-Agent: PostmanRuntime/7.28.4","Cookie: INGRESSCOOKIE=2ec12f7f26a5b8a4fd603d8b8e5fbd0c|d8628a0b5df4f2b5b6b4858b98a105ce","X-Code: 404","X-Format: */*","X-Original-Uri: /api/runtime/token/v3","X-Request-Id: f4d77e12250191364f9b947e3182d74f","X-Forwarded-For: 192.168.65.4","Accept: */*","Postman-Token: 9d83ccbe-2284-4699-b9ae-42f9eabda61d","Accept-Encoding: gzip, deflate, br","X-Forwarded-Proto: http","X-Envoy-Internal: true","X-B3-Traceid: f5ded2c870cdfe6564cb192bbd092bb9","X-B3-Spanid: 64cb192bbd092bb9","X-B3-Sampled: 1"],"response_headers":["Content-Type: text/plain; charset=utf-8","Allow: GET, OPTIONS"]}


### Anything else?

_No response_

Compatibility with nextcloud

IΒ΄m running nextcloud with a nextcloud push server container. Nextcloud itself is running with priority 1 and the sidecar container with priority 2. However, when adding error-pages with priority 10, nextcloud is down. When removing the priorities from nextcloud, they came back. I donΒ΄ t know how to fix this problem.

Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.