fix(csp): external bootstrap script to survive strict reverse-proxy CSP (#564)
demo.picpeak.app sits behind Caddy + Cloudflare; Caddy replaces the nginx CSP entirely with one that omits 'unsafe-inline' / hash / nonce, so the #358 inline theme-bootstrap was being blocked there — admin loaded a black page, the SPA bundle 404'd, link buttons did nothing. Move the bootstrap to /public/bootstrap.js served as 'self' so the script runs under every reasonable CSP without further coordination. Vite copies /public/* to the dist root at build time (same pipeline as /favicon-32x32.png), and it remains in <head> without defer/async so it still runs before <body> paints. The OS-preference @media CSS above still handles the first-frame dark/light baseline.
This commit is contained in:
+8
-28
@@ -39,9 +39,9 @@
|
|||||||
<meta name="twitter:description" content="${BRAND_DESCRIPTION}" />
|
<meta name="twitter:description" content="${BRAND_DESCRIPTION}" />
|
||||||
|
|
||||||
<!-- Pre-React theme bootstrap (#358).
|
<!-- Pre-React theme bootstrap (#358).
|
||||||
The browser may paint the very first frame before our inline
|
The browser may paint the very first frame before the bootstrap
|
||||||
<script> below runs, so we set OS-preference defaults via CSS
|
<script> below fetches and runs, so we set OS-preference defaults
|
||||||
here in <head> — that gets applied before any paint. The
|
via CSS here in <head> — that gets applied before any paint. The
|
||||||
script then layers a per-gallery cache hit on top when one is
|
script then layers a per-gallery cache hit on top when one is
|
||||||
available. Without this CSS, the very first frame on first-
|
available. Without this CSS, the very first frame on first-
|
||||||
visit dark-OS devices flashed white briefly (see Rekoo-PS's
|
visit dark-OS devices flashed white briefly (see Rekoo-PS's
|
||||||
@@ -56,31 +56,11 @@
|
|||||||
html { transition: background-color 200ms ease; }
|
html { transition: background-color 200ms ease; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<!-- Pre-React theme bootstrap (#358). External rather than inline so a
|
||||||
/*
|
strict CSP without 'unsafe-inline' / hash / nonce — like the one
|
||||||
* Pre-React theme bootstrap (#358).
|
Caddy puts in front of demo.picpeak.app — doesn't block it (#564).
|
||||||
*
|
No defer/async: must run before <body> paints. -->
|
||||||
* The CSS @media block above handles the OS-preference default
|
<script src="/bootstrap.js"></script>
|
||||||
* before paint. This script then applies a per-gallery cached
|
|
||||||
* background (written by ThemeContext on the previous visit) so
|
|
||||||
* revisits land on the exact theme background from frame one.
|
|
||||||
*/
|
|
||||||
(function () {
|
|
||||||
try {
|
|
||||||
var m = location.pathname.match(/\/gallery\/([^\/?#]+)/);
|
|
||||||
var bg = null;
|
|
||||||
if (m && m[1]) {
|
|
||||||
bg = localStorage.getItem('gallery-theme-bg-' + decodeURIComponent(m[1]));
|
|
||||||
}
|
|
||||||
if (bg) {
|
|
||||||
var root = document.documentElement;
|
|
||||||
root.style.backgroundColor = bg;
|
|
||||||
document.body && (document.body.style.backgroundColor = bg);
|
|
||||||
root.style.setProperty('--color-background', bg);
|
|
||||||
}
|
|
||||||
} catch (e) { /* never block render on a cache miss */ }
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Pre-React theme bootstrap (#358).
|
||||||
|
*
|
||||||
|
* Loaded as an external script (rather than inline) so a strict CSP
|
||||||
|
* with no 'unsafe-inline' / hash / nonce — like the one Caddy puts in
|
||||||
|
* front of demo.picpeak.app — does not block it (#564).
|
||||||
|
*
|
||||||
|
* Reads the per-gallery cached background written by ThemeContext on
|
||||||
|
* the previous visit and applies it before React mounts, so revisits
|
||||||
|
* land on the right colour from the first frame. The OS-preference
|
||||||
|
* default is already handled by the @media CSS in index.html for
|
||||||
|
* first-visit / cache-miss callers.
|
||||||
|
*
|
||||||
|
* Placed in /public so vite copies it to /bootstrap.js at build time
|
||||||
|
* (same pipeline as /favicon-32x32.png). Kept in <head> without
|
||||||
|
* defer/async so it runs before <body> paints.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
try {
|
||||||
|
var m = location.pathname.match(/\/gallery\/([^\/?#]+)/);
|
||||||
|
var bg = null;
|
||||||
|
if (m && m[1]) {
|
||||||
|
bg = localStorage.getItem('gallery-theme-bg-' + decodeURIComponent(m[1]));
|
||||||
|
}
|
||||||
|
if (bg) {
|
||||||
|
var root = document.documentElement;
|
||||||
|
root.style.backgroundColor = bg;
|
||||||
|
document.body && (document.body.style.backgroundColor = bg);
|
||||||
|
root.style.setProperty('--color-background', bg);
|
||||||
|
}
|
||||||
|
} catch (e) { /* never block render on a cache miss */ }
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user