diff --git a/ui/src/main.mjs b/ui/src/main.mjs index 5c0778b70..f19e1dd10 100644 --- a/ui/src/main.mjs +++ b/ui/src/main.mjs @@ -89,7 +89,8 @@ const host = new URL(window.location.href) function getDashboardReloadUrl () { const setupBasePath = store.state.setup.setup?.basePath const currentDashboardPath = window.location.pathname.match(/^(.+?\/dashboard)(?:\/|$)/)?.[1] - const basePath = setupBasePath || currentDashboardPath || '/dashboard' + const rawBasePath = setupBasePath || currentDashboardPath || '/dashboard' + const basePath = rawBasePath.endsWith('/') ? rawBasePath : rawBasePath + '/' return new URL(basePath, window.location.origin) } diff --git a/ui/src/sw.js b/ui/src/sw.js index 2096874cd..e7414876f 100644 --- a/ui/src/sw.js +++ b/ui/src/sw.js @@ -1,7 +1,21 @@ /// import { clientsClaim } from 'workbox-core' -import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching' +import { cleanupOutdatedCaches, matchPrecache, precacheAndRoute } from 'workbox-precaching' import { NavigationRoute, registerRoute } from 'workbox-routing' +import { NetworkOnly } from 'workbox-strategies' + +// Network-first (keeps 'navigate' mode so an auth proxy's login redirect is followed), cached shell +// on failure. Registered before the precache route so it owns every navigation. +registerRoute(new NavigationRoute(new NetworkOnly({ + networkTimeoutSeconds: 5, + plugins: [{ + // 5xx -> serve the shell so it can self-heal. Match >= 500 not !response.ok, so the + // status-0 opaqueredirect (the auth redirect) passes through. + fetchDidSucceed: async ({ response }) => + response.status >= 500 ? (await matchPrecache('index.html')) || response : response, + handlerDidError: async () => (await matchPrecache('index.html')) || Response.error() + }] +}))) // self.__WB_MANIFEST is the default injection point precacheAndRoute(self.__WB_MANIFEST) @@ -9,27 +23,6 @@ precacheAndRoute(self.__WB_MANIFEST) // clean old assets cleanupOutdatedCaches() -/** @type {RegExp[] | undefined} */ -const denylist = [] - -// in dev mode, do not precache anything -if (import.meta.env.DEV) { - // don't precache anything - console.log('Development mode, not pre-caching anything') - denylist.push(/.*/) -} else { - // don't precache anything where the urls pathname ends with a slash (including times when the url has a query string) - // this permits the request to be handled by the server which will do a redirect as required - const configPath = self.location.pathname.split('/')[1] - denylist.push(new RegExp(`/${configPath}/[^?]*/(\\?.*)*$`)) -} - -// to allow work offline for allowed routes only -registerRoute(new NavigationRoute( - createHandlerBoundToURL('index.html'), - { denylist } -)) - self.skipWaiting() // https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim clientsClaim() diff --git a/vite.config.js b/vite.config.js index e0ce35fc0..23d1dcd21 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,7 +7,7 @@ import { VitePWA } from 'vite-plugin-pwa' */ // https://vitejs.dev/config/ -export default defineConfig({ +export default defineConfig(({ mode }) => ({ resolve: { alias: { vue: 'vue/dist/vue.esm-bundler.js' @@ -31,8 +31,8 @@ export default defineConfig({ manifest: false, injectManifest: { - maximumFileSizeToCacheInBytes: process.env.NODE_ENV === 'development' ? 6000000 : 3350000, - globPatterns: ['**/*.{js,css,html,svg,png,ico,ttf,eot,woff,woff2}'] + maximumFileSizeToCacheInBytes: mode === 'development' ? 6000000 : 3350000, + globPatterns: mode === 'development' ? [] : ['**/*.{js,css,html,svg,png,ico,ttf,eot,woff,woff2}'] }, devOptions: { @@ -45,7 +45,7 @@ export default defineConfig({ ], root: 'ui', build: { - minify: process.env.NODE_ENV === 'development' ? false : undefined, + minify: mode === 'development' ? false : undefined, outDir: '../dist', emptyOutDir: true, rollupOptions: { @@ -60,4 +60,4 @@ export default defineConfig({ } }, base: './' -}) +}))