feat: improve service worker and proxy configurations by adding support for Server-Sent Events, refining Content Security Policy for public routes, and enhancing guest display name handling in the dashboard

This commit is contained in:
2026-01-21 09:13:00 +02:00
parent a451ab0205
commit 428ccffd28
4 changed files with 30 additions and 16 deletions

View File

@@ -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) ||

View File

@@ -191,11 +191,9 @@ export default function GuestDashboard() {
const [isRefreshingGuest, setIsRefreshingGuest] = React.useState(false);
const refreshTimeoutRef = React.useRef<NodeJS.Timeout | null>(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 (
<div
@@ -665,7 +663,7 @@ export default function GuestDashboard() {
</p>
</div>
<h1 className="mt-4 font-heading text-3xl sm:text-4xl md:text-5xl text-wedding-evergreen tracking-tight">
Hello, <span className="font-heading italic">{finalFirstName}</span>
Hello, <span className="font-heading italic">{finalGuestDisplayName}</span>
</h1>
</motion.div>

View File

@@ -61,6 +61,8 @@ export function ServiceWorkerRegister() {
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
let updateInterval: ReturnType<typeof setInterval> | 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);
};
}, []);

View File

@@ -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",