From c674aac3441e0b34f1558596ad084b675487ce1d Mon Sep 17 00:00:00 2001 From: elpatron Date: Tue, 2 Jun 2026 20:40:41 +0200 Subject: [PATCH] Add debug logging for push and Service Worker registration --- client/src/main.tsx | 4 ++ client/src/services/pushNotifications.ts | 52 +++++++++++++++++++++--- server/src/routes/push.ts | 5 +++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/client/src/main.tsx b/client/src/main.tsx index 092b49c..152012d 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -14,6 +14,7 @@ import { reconcileVersionOnStartup } from './services/pwaStartup.ts' import { redirectToPasskeyCompatibleHostIfNeeded } from './utils/passkeyHost.ts' +import { logToBackend } from './services/pushNotifications.ts' declare global { interface Window { @@ -74,13 +75,16 @@ async function bootstrap(): Promise { } if ('serviceWorker' in navigator && !import.meta.env.DEV) { + logToBackend('Attempting manual Service Worker registration...') navigator.serviceWorker .register('/sw.js', { scope: '/' }) .then((reg) => { console.log('Service Worker registered successfully with scope:', reg.scope) + logToBackend('Service Worker registered successfully with scope: ' + reg.scope) }) .catch((err) => { console.error('Service Worker registration failed:', err) + logToBackend('Service Worker registration failed', err) }) } diff --git a/client/src/services/pushNotifications.ts b/client/src/services/pushNotifications.ts index b16fa71..71e2d04 100644 --- a/client/src/services/pushNotifications.ts +++ b/client/src/services/pushNotifications.ts @@ -2,6 +2,29 @@ import { apiFetch, apiJson } from './api.js' const API_BASE = '/api/push' +export async function logToBackend(message: string, error?: any): Promise { + try { + await fetch(`${API_BASE}/debug-log`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + message, + error: error ? { + name: error.name, + message: error.message, + stack: error.stack, + ...error + } : undefined, + userAgent: navigator.userAgent, + href: window.location.href, + timestamp: new Date().toISOString() + }) + }) + } catch (err) { + console.warn('Failed to send debug log:', err) + } +} + function urlBase64ToUint8Array(base64String: string): Uint8Array { const padding = '='.repeat((4 - (base64String.length % 4)) % 4) const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/') @@ -189,23 +212,35 @@ async function saveSubscriptionToServer(subscription: PushSubscription): Promise } export async function subscribeToPush(): Promise { + logToBackend('subscribeToPush called') if (!isPushSupported()) { + logToBackend('subscribeToPush: push not supported') throw new Error('Push notifications are not supported on this device') } // Pre-resolve registration using getRegistrationCompat to prevent ready state hangs let registration = cachedRegistration if (!registration) { - registration = await getRegistrationCompat() - cachedRegistration = registration + try { + logToBackend('subscribeToPush: getting registration...') + registration = await getRegistrationCompat() + cachedRegistration = registration + logToBackend('subscribeToPush: got registration successfully') + } catch (err) { + logToBackend('subscribeToPush: failed to get registration', err) + throw err + } } const publicKey = cachedVapidKey || await fetchVapidPublicKey() if (!publicKey) { + logToBackend('subscribeToPush: no public key available') throw new Error('Push notifications are not configured on this server') } + logToBackend('subscribeToPush: requesting permission...') const permission = await requestNotificationPermission() + logToBackend(`subscribeToPush: permission result: ${permission}`) if (permission !== 'granted') { throw new Error('Notification permission denied') } @@ -214,6 +249,7 @@ export async function subscribeToPush(): Promise { const applicationServerKey = new Uint8Array(keyBytes) // Always call subscribe with timeout to prevent silent hangs on push network errors + logToBackend('subscribeToPush: subscribing via pushManager...') const subscribePromise = registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey @@ -221,9 +257,15 @@ export async function subscribeToPush(): Promise { const subscribeTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout establishing subscription with push service (FCM/APNs)')), 12000) ) - const subscription = await Promise.race([subscribePromise, subscribeTimeout]) - - await saveSubscriptionToServer(subscription) + try { + const subscription = await Promise.race([subscribePromise, subscribeTimeout]) + logToBackend('subscribeToPush: subscribed successfully, saving to server...') + await saveSubscriptionToServer(subscription) + logToBackend('subscribeToPush: saved to server successfully') + } catch (err) { + logToBackend('subscribeToPush: subscription or save failed', err) + throw err + } } export async function unsubscribeFromPush(): Promise { diff --git a/server/src/routes/push.ts b/server/src/routes/push.ts index 4c9f648..ec14a07 100644 --- a/server/src/routes/push.ts +++ b/server/src/routes/push.ts @@ -22,6 +22,11 @@ router.get('/vapid-public-key', (_req, res) => { return res.json({ publicKey }) }) +router.post('/debug-log', (req, res) => { + console.log('[CLIENT_DEBUG]', req.body) + return res.json({ success: true }) +}) + router.use(requireUser) router.get('/prefs', async (req: any, res) => {