From 879b600c8b73184624043af88be1dc616d09ea5c Mon Sep 17 00:00:00 2001 From: Danilo Hoffmann Date: Thu, 21 Sep 2023 10:09:34 +0200 Subject: [PATCH] feat: fallback to loading page when SSR is taking too long (#1511) --- nginx/Dockerfile | 3 ++ nginx/docker-entrypoint.d/40-gomplate.sh | 3 ++ nginx/docker-entrypoint.d/entrypoint.sh | 5 ++ nginx/loading-fallback.sh.tmpl | 27 +++++++++++ nginx/nginx.conf | 1 + nginx/templates/loading-fallback.conf.tmpl | 53 ++++++++++++++++++++++ nginx/templates/multi-channel.conf.tmpl | 6 +++ 7 files changed, 98 insertions(+) create mode 100644 nginx/loading-fallback.sh.tmpl create mode 100644 nginx/templates/loading-fallback.conf.tmpl diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 9de5896b621..f98febc1c7d 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -2,6 +2,7 @@ FROM nginx:1.22 RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y apache2-utils libnss3-tools COPY *.conf /etc/nginx/ +COPY loading-fallback.sh.tmpl /loading-fallback.sh.tmpl COPY features /etc/nginx/features/ COPY templates /etc/nginx/templates/ COPY docker-entrypoint.d/*.sh /docker-entrypoint.d/ @@ -21,5 +22,7 @@ ENV CACHE_DURATION_NGINX_OK=10m ENV CACHE_DURATION_NGINX_NF=60m ENV LOGFORMAT=main ENV LOG_ALL=on +ENV LOADING_FALLBACK=off +ENV LOADING_FALLBACK_TIMEOUT=10s EXPOSE 80 443 9113 diff --git a/nginx/docker-entrypoint.d/40-gomplate.sh b/nginx/docker-entrypoint.d/40-gomplate.sh index fa3c71461dd..010966c5384 100755 --- a/nginx/docker-entrypoint.d/40-gomplate.sh +++ b/nginx/docker-entrypoint.d/40-gomplate.sh @@ -30,3 +30,6 @@ then fi /gomplate -d "domains=$MULTI_CHANNEL_SOURCE" -d "overrideIdentityProviders=$OVERRIDE_IDENTITY_PROVIDERS_SOURCE" -d "cachingIgnoreParams=$CACHING_IGNORE_PARAMS_SOURCE" -d 'ipwhitelist=env:///BASIC_AUTH_IP_WHITELIST?type=application/yaml' --input-dir="/etc/nginx/templates" --output-map='/etc/nginx/conf.d/{{ .in | strings.ReplaceAll ".conf.tmpl" ".conf" }}' + +/gomplate -d "domains=$MULTI_CHANNEL_SOURCE" -f /loading-fallback.sh.tmpl -o /loading-fallback.sh +chmod +x /loading-fallback.sh diff --git a/nginx/docker-entrypoint.d/entrypoint.sh b/nginx/docker-entrypoint.d/entrypoint.sh index 3d8272f29ab..dfa80192ce6 100755 --- a/nginx/docker-entrypoint.d/entrypoint.sh +++ b/nginx/docker-entrypoint.d/entrypoint.sh @@ -18,3 +18,8 @@ if env | grep -iqE "^PROMETHEUS=(on|1|true|yes)$" then (sleep 5 && /nginx-prometheus-exporter)& fi + +if env | grep -iqE "^LOADING_FALLBACK=(on|1|true|yes)$" +then + (sleep 5 && /loading-fallback.sh)& +fi diff --git a/nginx/loading-fallback.sh.tmpl b/nginx/loading-fallback.sh.tmpl new file mode 100644 index 00000000000..2ea6696c189 --- /dev/null +++ b/nginx/loading-fallback.sh.tmpl @@ -0,0 +1,27 @@ +{{- define "clean-domain" -}} +{{ . | strings.ReplaceAll ".+" "_" | strings.ReplaceAll ".*" "_" }} +{{- end -}} +#!/bin/sh + +mkdir -p /tmp/loading + +fetch() { + target="/tmp/loading/$1/$2.html" + mkdir -p "$(dirname "$target")" + curl -s -o "$target" -H "Host: $1" "http://localhost:8080$2" +} + +while true +do +{{- range $domain, $mapping := (ds "domains") }} + {{- $cdomain := tmpl.Exec "clean-domain" $domain -}} + {{- if $mapping | test.IsKind "map" }} + fetch "{{ $cdomain }}" "/loading" + {{- else }} + {{- range $mapping }} + fetch "{{ $cdomain }}" "{{ .baseHref }}/loading" + {{- end }} + {{- end }} +{{- end }} + sleep 300 +done diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 7b18b0c0932..2d0680de8b7 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -72,4 +72,5 @@ http { include /etc/nginx/conf.d/features.conf; include /etc/nginx/conf.d/multi-channel.conf; + include /etc/nginx/conf.d/loading-fallback.conf; } diff --git a/nginx/templates/loading-fallback.conf.tmpl b/nginx/templates/loading-fallback.conf.tmpl new file mode 100644 index 00000000000..fb424c684fb --- /dev/null +++ b/nginx/templates/loading-fallback.conf.tmpl @@ -0,0 +1,53 @@ +{{- define "clean-domain" -}} +{{ . | strings.ReplaceAll ".+" "_" | strings.ReplaceAll ".*" "_" }} +{{- end -}} + +{{ if getenv "LOADING_FALLBACK" | strings.ToLower | regexp.Match "on|1|true|yes" }} +upstream real_server { + server localhost:8080 fail_timeout=0; + keepalive 15; +} + + {{- range $domain, $mapping := (ds "domains") }} +server { + listen 80; + server_name ~^{{ $domain }}$; + proxy_set_header Host $http_host; + proxy_cache_bypass true; + + # https://mailman.nginx.org/pipermail/nginx/2007-May/001031.html + if ($is_bot) { + rewrite ^(.*)$ /bot-bypass$1; + } + location /bot-bypass { + rewrite ^/bot-bypass(.*)$ $1 break; + proxy_pass http://real_server; + } + + {{ if (has $mapping "channel") }} + location / { + proxy_pass http://real_server; + proxy_read_timeout {{ getenv "LOADING_FALLBACK_TIMEOUT" }}; + error_page 504 =200 /loading.html; + } + location = /loading.html { + root /tmp/loading/{{ tmpl.Exec "clean-domain" $domain }}; + } + {{- else -}} + {{ range $mapping }} + location {{ .baseHref }} { + proxy_pass http://real_server; + proxy_read_timeout {{ getenv "LOADING_FALLBACK_TIMEOUT" }}; + error_page 504 =200 {{ .baseHref }}/loading.html; + } + location = {{ .baseHref }}/loading.html { + root /tmp/loading/{{ tmpl.Exec "clean-domain" $domain }}; + } + {{ end }} + location / { + proxy_pass http://real_server; + } + {{ end }} + {{ end }} +} +{{ end }} diff --git a/nginx/templates/multi-channel.conf.tmpl b/nginx/templates/multi-channel.conf.tmpl index 8ad50d04a8a..7d71b104fc0 100644 --- a/nginx/templates/multi-channel.conf.tmpl +++ b/nginx/templates/multi-channel.conf.tmpl @@ -55,6 +55,10 @@ rewrite '^(.*)$' '$1$default_rewrite_params' break; + {{ if getenv "LOADING_FALLBACK" | strings.ToLower | regexp.Match "on|1|true|yes" }} + proxy_ignore_client_abort on; + {{ end }} + proxy_pass {{ getenv "UPSTREAM_PWA" }}; proxy_buffer_size 128k; proxy_buffers 4 256k; @@ -91,6 +95,8 @@ server { # https://ma.ttias.be/force-redirect-http-https-custom-port-nginx/ error_page 497 https://$http_host$request_uri; + {{ else if getenv "LOADING_FALLBACK" | strings.ToLower | regexp.Match "on|1|true|yes" }} + listen 8080; {{ else }} listen 80; {{- end }}