diff --git a/src/PowderCoating.Web/wwwroot/sw.js b/src/PowderCoating.Web/wwwroot/sw.js index 17140e7..2ce4a66 100644 --- a/src/PowderCoating.Web/wwwroot/sw.js +++ b/src/PowderCoating.Web/wwwroot/sw.js @@ -1,11 +1,26 @@ // Minimal service worker — required for PWA installability. -// No caching: all requests pass through to the network normally. -// This exists solely so browsers recognize the site as installable, -// which causes iOS/Android to persist camera permissions after "Add to Home Screen." +// No caching: all requests pass through to the network. +// Exists solely so browsers recognize the site as installable +// (iOS/Android persist camera permissions after "Add to Home Screen"). +// +// IMPORTANT: /hubs/ (SignalR) requests are excluded from interception entirely. +// Service worker fetch() wraps SSE/WebSocket responses in a buffered Response, +// which prevents real-time streaming — SignalR handshakes time out as a result. + +const SKIP_PREFIXES = ['/hubs/', '/Kiosk/PollSession']; self.addEventListener('install', () => self.skipWaiting()); self.addEventListener('activate', e => e.waitUntil(self.clients.claim())); + self.addEventListener('fetch', e => { - if (new URL(e.request.url).origin !== self.location.origin) return; + const url = new URL(e.request.url); + + // Always skip cross-origin requests + if (url.origin !== self.location.origin) return; + + // Skip SignalR hubs and kiosk polling — let the browser handle these directly + if (SKIP_PREFIXES.some(p => url.pathname.startsWith(p))) return; + + // Passthrough: no caching, no modification e.respondWith(fetch(e.request)); });