Compare commits

..
Author SHA1 Message Date
Lotus CIandClaude Opus 5.5 bf770fed83 fix(cinny-web): no-cache for the app shell on SPA routes; keep old hashed assets 14 days (#23)
Lint / Shell (shellcheck) (push) Successful in 24s
Lint / JS (eslint) (push) Successful in 15s
Lint / No secrets in webhook configs (push) Successful in 7s
Lint / Landing page is rendered (matrix (push) Successful in 42s
Lint / Python (ruff) (push) Successful in 11s
Lint / Python deps (pip-audit) (push) Successful in 40s
Lint / Secret scan (gitleaks) (push) Successful in 6s
Lint / Shell (shellcheck) (pull_request) Successful in 9s
Lint / JS (eslint) (pull_request) Successful in 7s
Lint / No secrets in webhook configs (pull_request) Successful in 5s
Lint / Landing page is rendered (matrix (pull_request) Successful in 4s
Lint / Python (ruff) (pull_request) Successful in 6s
Lint / Python deps (pip-audit) (pull_request) Successful in 1m59s
Lint / Secret scan (gitleaks) (pull_request) Successful in 15s
SPA URLs (/, /home/…, /login/…) are rewritten to /index.html inside
location /, which set no cache headers, so browsers heuristically reused
a stale shell after a deploy, and rsync --delete had just removed the
assets it referenced: a blank app until the shell expired. Send
no-cache for the rewritten shell (map on $uri; location / now includes
the security headers, since add_header drops the inherited ones), and
stop deleting previous builds' hashed assets: prune after 14 days.

Fixes #23

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-18 01:26:02 -04:00
4 changed files with 23 additions and 12 deletions
+3 -10
View File
@@ -8,16 +8,9 @@
"allowCustomHomeservers": false, "allowCustomHomeservers": false,
"featuredCommunities": { "featuredCommunities": {
"openAsDefault": false, "openAsDefault": false,
"spaces": [ "spaces": [],
"!-1ZBnAH-JiCOV8MGSKN77zDGTuI3pgSdy8Unu_DrDyc", "rooms": [],
"#homelab:codestorm.net" "servers": []
],
"rooms": [
"#jellyfin:matrix.org"
],
"servers": [
"matrixrooms.info"
]
}, },
"hashRouter": { "hashRouter": {
"enabled": false, "enabled": false,
+7 -1
View File
@@ -160,7 +160,13 @@ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deploying to $WEBROOT..."
# /var/www/html/config.json by lxc106-cinny.sh. The build ships a DEV default # /var/www/html/config.json by lxc106-cinny.sh. The build ships a DEV default
# (allowCustomHomeservers:true); rsyncing it would clobber the production config # (allowCustomHomeservers:true); rsyncing it would clobber the production config
# on every deploy. Keep the app bundle and the runtime config separate. # on every deploy. Keep the app bundle and the runtime config separate.
rsync -a --delete --exclude config.json dist/ "$WEBROOT/" # [matrix #23] Keep the previous builds' hashed assets for a while: a browser
# still holding an older index.html (or service-worker precache) asks for its
# assets/index-<old hash>.js, and deleting them on every deploy broke the app
# for those users until their cached shell expired. Prune after 14 days.
rsync -a --delete --exclude config.json --exclude assets/ dist/ "$WEBROOT/"
rsync -a dist/assets/ "$WEBROOT/assets/"
find "$WEBROOT/assets" -type f -mtime +14 -delete
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ===== Deploy complete ($VITE_APP_VERSION) =====" echo "[$(date '+%Y-%m-%d %H:%M:%S')] ===== Deploy complete ($VITE_APP_VERSION) ====="
+1 -1
View File
@@ -12,4 +12,4 @@ add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy strict-origin-when-cross-origin always; add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Permissions-Policy 'accelerometer=(), autoplay=(self "https://call.chat.lotusguild.org"), camera=(self "https://call.chat.lotusguild.org"), display-capture=(self "https://call.chat.lotusguild.org"), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self "https://call.chat.lotusguild.org"), midi=(), payment=(), usb=()' always; add_header Permissions-Policy 'accelerometer=(), autoplay=(self "https://call.chat.lotusguild.org"), camera=(self "https://call.chat.lotusguild.org"), display-capture=(self "https://call.chat.lotusguild.org"), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self "https://call.chat.lotusguild.org"), midi=(), payment=(), usb=()' always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://matrix.lotusguild.org https://matrix.org https://*.matrix.org https://mozilla.org https://mozilla.modular.im https://drive.lotusguild.org https://media.giphy.com https://media0.giphy.com https://media1.giphy.com https://media2.giphy.com https://media3.giphy.com https://media4.giphy.com https://www.openstreetmap.org https://tile.openstreetmap.org; font-src 'self' data:; connect-src 'self' https://matrix.lotusguild.org wss://matrix.lotusguild.org https://isitup.lotusguild.org https://matrix.org https://*.matrix.org https://mozilla.org https://mozilla.modular.im https://chat.mozilla.org https://www.mozilla.org https://vector.im https://api.giphy.com https://*.giphy.com https://www.tiktok.com wss:; media-src 'self' https: blob:; frame-src 'self' https:; worker-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://matrix.lotusguild.org https://matrix.org https://*.matrix.org https://mozilla.org https://mozilla.modular.im https://drive.lotusguild.org https://media.giphy.com https://media0.giphy.com https://media1.giphy.com https://media2.giphy.com https://media3.giphy.com https://media4.giphy.com https://www.openstreetmap.org https://tile.openstreetmap.org; font-src 'self' data:; connect-src 'self' https://matrix.lotusguild.org wss://matrix.lotusguild.org https://isitup.lotusguild.org https://matrix.org https://*.matrix.org https://mozilla.org https://mozilla.modular.im https://chat.mozilla.org https://vector.im https://api.giphy.com https://*.giphy.com wss:; media-src 'self' https: blob:; frame-src 'self' https:; worker-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
+12
View File
@@ -1,3 +1,12 @@
# [matrix #23] SPA routes (/, /home/…, /login/…) are rewritten to /index.html
# inside `location /`, where no cache headers were set: browsers then reused a
# heuristically fresh shell after a deploy and requested deleted assets. Only
# the rewritten shell gets no-cache; other files there keep their defaults.
map $uri $lotus_shell_cache_control {
/index.html "no-cache, no-store, must-revalidate";
default "";
}
server { server {
listen 80; listen 80;
listen [::]:80; listen [::]:80;
@@ -99,6 +108,9 @@ server {
} }
location / { location / {
# add_header here drops the server-level headers, so include them again.
include snippets/cinny-security-headers.conf;
add_header Cache-Control $lotus_shell_cache_control always;
rewrite ^/config\.json$ /config.json break; rewrite ^/config\.json$ /config.json break;
rewrite ^/manifest\.json$ /manifest.json break; rewrite ^/manifest\.json$ /manifest.json break;
rewrite ^/sw\.js$ /sw.js break; rewrite ^/sw\.js$ /sw.js break;