Fixes images 404'ing under /arrangement/ — Vite's publicDir copies assets to the build root, but the base prefix is only applied to bundled assets (JS/CSS), not to runtime URL strings. assetUrl() helper resolves paths against import.meta.env.BASE_URL so '/images/foo.png' becomes '/arrangement/images/foo.png' in production while staying '/images/foo.png' in dev. - src/demo/shared/assets.ts — assetUrl() helper - providers.ts + DemoNav.tsx — wrap all public asset paths - nginx/parsons-demos.conf — swag site-conf for parsons.tensordesign.com.au (asset cache regex above SPA fallback regex per nginx first-match rule) - docs/reference/client-demo-deploy.md — server runbook (DNS, swag SUBDOMAINS, mount, htpasswd, deploy loop) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
82 lines
2.7 KiB
Plaintext
82 lines
2.7 KiB
Plaintext
# Parsons demo host — drop into swag's /config/nginx/site-confs/ directory.
|
|
#
|
|
# Serves static demo slices at parsons.tensordesign.com.au/<slice>/ behind
|
|
# basic auth. One server block, one cert (Let's Encrypt via swag), one
|
|
# htpasswd covering all slices.
|
|
#
|
|
# Document root layout (host filesystem):
|
|
# <host_path>/parsons-demos/
|
|
# index.html ← optional landing page listing slices
|
|
# arrangement/
|
|
# index.html
|
|
# assets/...
|
|
# <other-slices>/
|
|
#
|
|
# Bind-mount that directory into swag at /config/www/parsons-demos/ — the
|
|
# `root` directive below assumes that path. Adjust if you mount elsewhere.
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
http2 on;
|
|
|
|
server_name parsons.*;
|
|
|
|
# swag manages the cert chain via SUBDOMAINS — make sure `parsons` is in
|
|
# the SUBDOMAINS env var of the swag container so this resolves.
|
|
include /config/nginx/ssl.conf;
|
|
|
|
root /config/www/parsons-demos;
|
|
index index.html;
|
|
|
|
# One credential file covering every slice. Create with:
|
|
# docker exec -it swag htpasswd -c /config/nginx/.htpasswd-parsons client
|
|
auth_basic "Parsons demos";
|
|
auth_basic_user_file /config/nginx/.htpasswd-parsons;
|
|
|
|
# Optional: don't auth the root listing if you want it publicly visible.
|
|
# (Currently auth covers it too — change to `auth_basic off;` to expose.)
|
|
|
|
# Root path serves the optional landing index.html if present, else 404.
|
|
location = / {
|
|
try_files /index.html =404;
|
|
}
|
|
|
|
# Long cache for fingerprinted assets — Vite produces hashed filenames so
|
|
# this is safe. HTML is short-cache so updates land on next refresh.
|
|
# NOTE: asset + html regex locations must come BEFORE the slice fallback
|
|
# below, because nginx uses the first matching regex location.
|
|
location ~* \.(?:js|css|woff2?|ttf|otf|eot|png|jpg|jpeg|gif|svg|webp|ico)$ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
access_log off;
|
|
}
|
|
|
|
location ~* \.html$ {
|
|
expires -1;
|
|
add_header Cache-Control "no-cache, must-revalidate";
|
|
}
|
|
|
|
# SPA fallback per slice. /<slice>/<react-route> resolves to that
|
|
# slice's index.html so React Router handles the rest. Static assets
|
|
# (.js/.css/.png/etc.) are handled by the regex blocks above.
|
|
location ~ ^/(?<slice>[^/]+)/ {
|
|
try_files $uri $uri/ /$slice/index.html;
|
|
}
|
|
|
|
# Hide hidden files (e.g. .htpasswd if it ever ends up in webroot)
|
|
location ~ /\. {
|
|
deny all;
|
|
}
|
|
}
|
|
|
|
# HTTP → HTTPS redirect — swag's default server already covers this for
|
|
# wildcard subdomains, but include explicitly here in case the default is
|
|
# customised.
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name parsons.*;
|
|
return 301 https://$host$request_uri;
|
|
}
|