feat(docker): add containerized nginx deployment with runtime base path

Two-stage build: node builds the static bundle, nginx serves it. The bundle is
built against a placeholder base token that the entrypoint rewrites to $BASE_URL
at start, so one image serves from any path without a rebuild.

nginx config caches hashed assets forever, never caches index.html, and applies
security headers (CSP, no framing, no referrer) suited to an app that handles
cloud credentials client-side.

.gitattributes pins LF on the container-consumed files, since core.autocrlf
would otherwise give the entrypoint a CRLF shebang.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 15:03:19 +02:00
co-authored by Claude Opus 5
parent 48802cf13c
commit 32159a404d
7 changed files with 149 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# Ready2Blob handles cloud credentials in the browser and sends them nowhere.
# These headers keep it that way: no framing, no external origins, no referrer leakage.
#
# Included per-location as well as at server level on purpose: an `add_header` inside
# a location block REPLACES every header inherited from the parent, so any location
# that sets its own Cache-Control must re-include this file.
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), usb=()" always;
# script-src keeps 'unsafe-inline' for the theme/FOUC bootstrap in index.html.
# To tighten it: build once, read the emitted inline <script> from dist/index.html,
# and swap 'unsafe-inline' for its 'sha256-...' hash.
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'self'; object-src 'none'" always;