Add prod nginx image with clearnet, onion, and SPA build.
Multi-stage frontend embed, envsubst entrypoint for TLS vhosts and upload body limits, shared locations with minimal response headers and zero access logs.
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
FROM node:22.23.4-alpine AS frontend-builder
|
||||||
|
|
||||||
|
WORKDIR /frontend
|
||||||
|
|
||||||
|
COPY frontend/package*.json .
|
||||||
|
COPY frontend/patches ./patches
|
||||||
|
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY frontend/ .
|
||||||
|
|
||||||
|
ARG VITE_API_BASE_URL
|
||||||
|
ARG VITE_CLEARNET_SCHEME
|
||||||
|
ARG VITE_CLEARNET_DOMAIN
|
||||||
|
ARG VITE_ONION_HOSTNAME
|
||||||
|
ARG VITE_ALTCHA_ENABLED
|
||||||
|
ARG VITE_ALTCHA_MAX_NUMBER
|
||||||
|
ARG VITE_UPLOAD_MAX_QUEUE_SIZE
|
||||||
|
ARG VITE_VALIDATION_UPLOAD_MAX_VIEWS_MAX
|
||||||
|
ARG VITE_VALIDATION_UPLOAD_MAX_IMAGE_BYTES
|
||||||
|
|
||||||
|
ENV VITE_API_BASE_URL=$VITE_API_BASE_URL \
|
||||||
|
VITE_CLEARNET_SCHEME=$VITE_CLEARNET_SCHEME \
|
||||||
|
VITE_CLEARNET_DOMAIN=$VITE_CLEARNET_DOMAIN \
|
||||||
|
VITE_ONION_HOSTNAME=$VITE_ONION_HOSTNAME \
|
||||||
|
VITE_ALTCHA_ENABLED=$VITE_ALTCHA_ENABLED \
|
||||||
|
VITE_ALTCHA_MAX_NUMBER=$VITE_ALTCHA_MAX_NUMBER \
|
||||||
|
VITE_UPLOAD_MAX_QUEUE_SIZE=$VITE_UPLOAD_MAX_QUEUE_SIZE \
|
||||||
|
VITE_VALIDATION_UPLOAD_MAX_VIEWS_MAX=$VITE_VALIDATION_UPLOAD_MAX_VIEWS_MAX \
|
||||||
|
VITE_VALIDATION_UPLOAD_MAX_IMAGE_BYTES=$VITE_VALIDATION_UPLOAD_MAX_IMAGE_BYTES
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:1.29.4-alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache gettext
|
||||||
|
|
||||||
|
COPY --from=frontend-builder /frontend/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
COPY nginx/conf.d/ /etc/nginx/templates/conf.d/
|
||||||
|
COPY nginx/snippets/private-image-locations.conf.template /etc/nginx/templates/snippets/
|
||||||
|
COPY nginx/docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name ${CLEARNET_DOMAIN} www.${CLEARNET_DOMAIN};
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name ${CLEARNET_DOMAIN} www.${CLEARNET_DOMAIN};
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/certs/live/${CLEARNET_DOMAIN}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/certs/live/${CLEARNET_DOMAIN}/privkey.pem;
|
||||||
|
|
||||||
|
${ONION_LOCATION_HEADER}
|
||||||
|
|
||||||
|
include /etc/nginx/snippets/private-image-locations.conf;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
include /etc/nginx/snippets/private-image-locations.conf;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
max_image_bytes="${VALIDATION_UPLOAD_MAX_IMAGE_BYTES:?}"
|
||||||
|
json_padding="${VALIDATION_UPLOAD_JSON_BODY_PADDING_BYTES:?}"
|
||||||
|
|
||||||
|
UPLOAD_MAX_REQUEST_BODY_BYTES=$(( (max_image_bytes * 4 + 2) / 3 + json_padding ))
|
||||||
|
|
||||||
|
export UPLOAD_MAX_REQUEST_BODY_BYTES BACKEND_PORT
|
||||||
|
|
||||||
|
render_locations() {
|
||||||
|
envsubst '${BACKEND_PORT} ${UPLOAD_MAX_REQUEST_BODY_BYTES}' \
|
||||||
|
< /etc/nginx/templates/snippets/private-image-locations.conf.template \
|
||||||
|
> /etc/nginx/snippets/private-image-locations.conf
|
||||||
|
}
|
||||||
|
|
||||||
|
render_server() {
|
||||||
|
template_path="$1"
|
||||||
|
output_path="$2"
|
||||||
|
surface="$3"
|
||||||
|
|
||||||
|
if [ "$surface" = "clearnet" ]; then
|
||||||
|
if [ -n "${ONION_HOSTNAME:-}" ]; then
|
||||||
|
export ONION_LOCATION_HEADER="add_header Onion-Location \"http://${ONION_HOSTNAME}\$request_uri\";"
|
||||||
|
else
|
||||||
|
export ONION_LOCATION_HEADER=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
export CLEARNET_DOMAIN
|
||||||
|
|
||||||
|
envsubst '${CLEARNET_DOMAIN} ${ONION_LOCATION_HEADER}' \
|
||||||
|
< "$template_path" \
|
||||||
|
> "$output_path"
|
||||||
|
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$template_path" "$output_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p /etc/nginx/snippets
|
||||||
|
|
||||||
|
render_locations
|
||||||
|
render_server /etc/nginx/templates/conf.d/clearnet.conf.template /etc/nginx/conf.d/clearnet.conf clearnet
|
||||||
|
render_server /etc/nginx/templates/conf.d/onion.conf.template /etc/nginx/conf.d/onion.conf onion
|
||||||
|
|
||||||
|
exec nginx -g 'daemon off;'
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
location ^~ /i/ {
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||||||
|
add_header Cache-Control "private, no-store" always;
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_pass http://backend:${BACKEND_PORT};
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api {
|
||||||
|
client_max_body_size ${UPLOAD_MAX_REQUEST_BODY_BYTES};
|
||||||
|
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
proxy_send_timeout 600s;
|
||||||
|
|
||||||
|
proxy_pass http://backend:${BACKEND_PORT};
|
||||||
|
}
|
||||||
|
|
||||||
|
location /pgadmin {
|
||||||
|
proxy_set_header X-Script-Name /pgadmin;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Scheme $scheme;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
proxy_pass_header Set-Cookie;
|
||||||
|
|
||||||
|
proxy_pass http://pgadmin;
|
||||||
|
|
||||||
|
proxy_redirect off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
add_header Referrer-Policy "no-referrer" always;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user