const VAPID_PUBLIC_KEY = import.meta.env.VITE_PUBLIC_VAPID_KEY as string; const VITE_BASE_URL = import.meta.env.VITE_BASE_URL as string; function urlBase64ToUint8Array(base64String: string) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } export async function registerServiceWorker() { if (!('serviceWorker' in navigator)) throw new Error('Service workers not supported'); const register = await navigator.serviceWorker.register('/service-worker.js'); return register; } export async function subscribeUserToPush(register: ServiceWorkerRegistration) { if (!VAPID_PUBLIC_KEY) throw new Error('Missing VAPID public key'); const subscription = await register.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) }); // Attach auth token if available const token = (window as any)?.localStorage?.getItem?.('accessToken') || (document?.cookie || '').match(/accessToken=([^;]+)/)?.[1] || ''; await fetch(`${VITE_BASE_URL}/api/v1/workflows/notifications/subscribe`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), }, body: JSON.stringify(subscription) }); return subscription; } export async function setupPushNotifications() { const permission = await Notification.requestPermission(); if (permission !== 'granted') return; const reg = await registerServiceWorker(); await subscribeUserToPush(reg); }