Преглед изворни кода

fix(frontend): emit relative asset paths so SPA loads under any subpath (#1195)

  Vite's default base of '/' baked absolute asset URLs into the built
  index.html (/assets/..., /manifest.json, /img/..., /sw-register.js),
  so any path-prefixed reverse proxy (Traefik, nginx subpath, Cloudflare
  Tunnel with path routing) served the SPA as a blank white page — the
  browser requested assets from the host root and got HTML or text/plain
  back, triggering MIME-mismatch errors on every stylesheet/script.

  Set base: '' in vite.config.ts so the HTML transform emits relative
  URLs everywhere. Update public/sw-register.js to register('sw.js')
  (relative) so SW scope auto-pins to whatever subpath the document
  loaded from.

  Out of scope: API_BASE in client.ts is still absolute. The supported
  HA embedding path remains Webpage panel + TRUSTED_FRAME_ORIGINS, not
  HA Ingress (subpath-aware SPA bootstrapping has too many failure modes
  around PWA scope, push subscriptions, and deep-link reloads to take on
  in core). Documented explicitly in docker.md.

  Reported by @Spegeli, follow-up to #1167.
maziggy пре 3 недеља
родитељ
комит
d6a3139397

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
CHANGELOG.md


+ 1 - 1
frontend/public/sw-register.js

@@ -10,7 +10,7 @@ if ('serviceWorker' in navigator) {
     });
   } else {
     window.addEventListener('load', () => {
-      navigator.serviceWorker.register('/sw.js')
+      navigator.serviceWorker.register('sw.js')
         .then((registration) => {
           console.log('SW registered:', registration.scope);
         })

+ 4 - 0
frontend/vite.config.ts

@@ -74,6 +74,10 @@ function serveGcodeViewer() {
 }
 
 export default defineConfig({
+  // Empty base emits relative asset URLs (./assets/... instead of /assets/...)
+  // so the built SPA loads correctly when served at any subpath — HA Ingress,
+  // nginx/Traefik path prefix, Cloudflare Tunnel path routing, etc. (#1195).
+  base: '',
   plugins: [react(), serveGcodeViewer()],
   build: {
     outDir: '../static',

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
static/assets/index-Ba_hWP_H.js


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
static/assets/index-iyRP9PYM.css


+ 8 - 8
static/index.html

@@ -17,17 +17,17 @@
     <meta name="apple-mobile-web-app-title" content="Bambuddy" />
 
     <!-- Manifest -->
-    <link rel="manifest" href="/manifest.json" />
+    <link rel="manifest" href="./manifest.json" />
 
     <!-- Favicons -->
-    <link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png" />
-    <link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png" />
-    <link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png" />
+    <link rel="icon" type="image/png" sizes="32x32" href="./img/favicon-32x32.png" />
+    <link rel="icon" type="image/png" sizes="16x16" href="./img/favicon-16x16.png" />
+    <link rel="apple-touch-icon" sizes="180x180" href="./img/apple-touch-icon.png" />
 
     <!-- Splash screens for iOS -->
-    <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-CwcBz1oz.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-Cw7zekS6.css">
+    <link rel="apple-touch-startup-image" href="./img/android-chrome-512x512.png" />
+    <script type="module" crossorigin src="./assets/index-CwcBz1oz.js"></script>
+    <link rel="stylesheet" crossorigin href="./assets/index-Cw7zekS6.css">
   </head>
   <body>
     <div id="root"></div>
@@ -35,6 +35,6 @@
     <!-- Service Worker Registration (skip on SpoolBuddy kiosk).
          Kept as an external file so the CSP `script-src 'self'` covers it
          without needing 'unsafe-inline' or per-build hashes. -->
-    <script src="/sw-register.js"></script>
+    <script src="./sw-register.js"></script>
   </body>
 </html>

+ 1 - 1
static/sw-register.js

@@ -10,7 +10,7 @@ if ('serviceWorker' in navigator) {
     });
   } else {
     window.addEventListener('load', () => {
-      navigator.serviceWorker.register('/sw.js')
+      navigator.serviceWorker.register('sw.js')
         .then((registration) => {
           console.log('SW registered:', registration.scope);
         })

Неке датотеке нису приказане због велике количине промена