diff --git a/public/sw.js b/public/sw.js index 76257d5..bb5a630 100644 --- a/public/sw.js +++ b/public/sw.js @@ -54,6 +54,13 @@ self.addEventListener("fetch", (event) => { return; } + // Never intercept Server-Sent Events / EventSource streams. + // Browsers (notably Firefox) can surface SW-handled SSE as aborted/failed connections. + const accept = request.headers.get("accept") || ""; + if (url.pathname === "/api/realtime" || accept.includes("text/event-stream")) { + return; + } + // NEVER cache audio files - stream only (Apple T&C compliance) if ( url.pathname.match(/\.(mp3|m4a|aac|ogg|wav|flac|webm|opus)$/i) || diff --git a/src/components/features/dashboard/dashboard-view.tsx b/src/components/features/dashboard/dashboard-view.tsx index 2c2a4bc..8d332f3 100644 --- a/src/components/features/dashboard/dashboard-view.tsx +++ b/src/components/features/dashboard/dashboard-view.tsx @@ -191,11 +191,9 @@ export default function GuestDashboard() { const [isRefreshingGuest, setIsRefreshingGuest] = React.useState(false); const refreshTimeoutRef = React.useRef(null); - // Compute firstName early (BEFORE any conditional returns) to follow Rules of Hooks - const firstName = React.useMemo( - () => guest?.name.split(" ")[0] || "Guest", - [guest] - ); + // Compute guest display name early (BEFORE any conditional returns) to follow Rules of Hooks + // We want the *full* guest name in the portal header (not just a first token like "Mr"). + const guestDisplayName = React.useMemo(() => guest?.name?.trim() || "Guest", [guest]); // Graceful refresh function with loading state const refreshGuestData = React.useCallback(async (silent = false) => { @@ -622,7 +620,7 @@ export default function GuestDashboard() { } const displayGuest = guest; - const finalFirstName = firstName; + const finalGuestDisplayName = guestDisplayName; return (

- Hello, {finalFirstName} + Hello, {finalGuestDisplayName}

diff --git a/src/components/layout/service-worker-register.tsx b/src/components/layout/service-worker-register.tsx index 04730c1..372a7ba 100644 --- a/src/components/layout/service-worker-register.tsx +++ b/src/components/layout/service-worker-register.tsx @@ -61,6 +61,8 @@ export function ServiceWorkerRegister() { window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); + let updateInterval: ReturnType | null = null; + // Register service worker navigator.serviceWorker .register("/sw.js", { @@ -72,6 +74,9 @@ export function ServiceWorkerRegister() { setRegistration(reg); + // Kick an update check immediately (best-effort) + reg.update().catch(() => {}); + // Check for updates reg.addEventListener('updatefound', () => { const newWorker = reg.installing; @@ -95,22 +100,20 @@ export function ServiceWorkerRegister() { if (reg.waiting) { setUpdateAvailable(true); } + + // Periodic update checks (use the live `reg`, not stale state) + updateInterval = setInterval(() => { + reg.update().catch(() => {}); + }, 60 * 60 * 1000); // Check every hour }) .catch((error) => { console.error("[SW] Registration failed:", error); }); - // Periodic update checks - const updateInterval = setInterval(() => { - if (registration) { - registration.update().catch(console.error); - } - }, 60 * 60 * 1000); // Check every hour - return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); - clearInterval(updateInterval); + if (updateInterval) clearInterval(updateInterval); }; }, []); diff --git a/src/proxy.ts b/src/proxy.ts index 5a0ea28..6d99e9b 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -192,10 +192,16 @@ export async function proxy(request: NextRequest) { return null; })(); + // Some browser/runtime bundles (and a few third-party libs) can rely on `eval`/`new Function` + // even in production builds. We keep admin/security routes stricter, but allow `unsafe-eval` + // on public/guest routes to avoid breaking functionality. + const isSensitiveRoute = pathname.startsWith("/admin") || pathname.startsWith("/security"); + const allowUnsafeEval = !isProd || !isSensitiveRoute; + const scriptSrc = [ "'self'", "'unsafe-inline'", - ...(isProd ? [] : ["'unsafe-eval'"]), + ...(allowUnsafeEval ? ["'unsafe-eval'"] : []), "https://js-cdn.music.apple.com", // Cloudflare Web Analytics / Insights "https://static.cloudflareinsights.com",