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:
@@ -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) ||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user