1// Sistema de Notificaciones Push 2// La Nación de Urania - Lunar App 3 4// VAPID public key (se obtiene automáticamente del servidor) 5let VAPID_PUBLIC_KEY = localStorage.getItem('vapid_public_key') || ''; 6 7// Convertir base64 a Uint8Array 8function urlBase64ToUint8Array(base64String) { 9 const padding = '='.repeat((4 - base64String.length % 4) % 4); 10 const base64 = (base64String + padding) 11 .replace(/\-/g, '+') 12 .replace(/_/g, '/'); 13 14 const rawData = window.atob(base64); 15 const outputArray = new Uint8Array(rawData.length); 16 17 for (let i = 0; i < rawData.length; ++i) { 18 outputArray[i] = rawData.charCodeAt(i); 19 } 20 return outputArray; 21} 22 23// Obtener clave VAPID del servidor 24async function fetchVapidKey() { 25 try { 26 const response = await fetch('/api/push/vapid-public-key'); 27 if (response.ok) { 28 const key = await response.text(); 29 if (key && key.length > 20) { 30 VAPID_PUBLIC_KEY = key.trim(); 31 localStorage.setItem('vapid_public_key', VAPID_PUBLIC_KEY); 32 console.log('â Clave VAPID obtenida del servidor'); 33 return true; 34 } 35 } 36 console.error('â No se pudo obtener clave VAPID'); 37 return false; 38 } catch (error) { 39 console.error('â Error obteniendo clave VAPID:', error); 40 return false; 41 } 42} 43 44// Registrar Service Worker 45async function registerServiceWorker() { 46 if (!('serviceWorker' in navigator)) { 47 console.log('â Service Worker no soportado'); 48 return null; 49 } 50 51 try { 52 const registration = await navigator.serviceWorker.register('/sw.js'); 53 console.log('â Service Worker registrado'); 54 return registration; 55 } catch (error) { 56 console.error('â Error registrando Service Worker:', error); 57 return null; 58 } 59} 60 61// Pedir permiso para notificaciones 62async function requestNotificationPermission() { 63 if (!('Notification' in window)) { 64 console.log('â Notificaciones no soportadas'); 65 return false; 66 } 67 68 if (Notification.permission === 'granted') { 69 console.log('â Permiso ya concedido'); 70 return true; 71 } 72 73 if (Notification.permission === 'denied') { 74 console.log('â Permiso denegado previamente'); 75 return false; 76 } 77 78 const permission = await Notification.requestPermission(); 79 80 if (permission === 'granted') { 81 console.log('â Permiso concedido'); 82 return true; 83 } else { 84 console.log('â Permiso denegado'); 85 return false; 86 } 87} 88 89// Suscribirse a notificaciones push 90async function subscribeToPush(registration) { 91 try { 92 // Verificar si ya está suscrito 93 let subscription = await registration.pushManager.getSubscription(); 94 95 if (subscription) { 96 console.log('â Ya está suscrito, reenviando al servidor...'); 97 // Siempre reenviar al servidor para asegurar que está guardada 98 await sendSubscriptionToServer(subscription); 99 return subscription; 100 } 101 102 // Obtener clave VAPID del servidor si no la tenemos 103 if (!VAPID_PUBLIC_KEY) { 104 const fetched = await fetchVapidKey(); 105 if (!fetched) { 106 console.error('â No hay clave VAPID disponible'); 107 return null; 108 } 109 } 110 111 // Crear nueva suscripción 112 const applicationServerKey = urlBase64ToUint8Array(VAPID_PUBLIC_KEY); 113 114 subscription = await registration.pushManager.subscribe({ 115 userVisibleOnly: true, 116 applicationServerKey: applicationServerKey 117 }); 118 119 console.log('â Suscripción creada'); 120 return subscription; 121 122 } catch (error) { 123 console.error('â Error al suscribirse:', error); 124 return null; 125 } 126} 127 128// Enviar suscripción al servidor 129async function sendSubscriptionToServer(subscription) { 130 try { 131 const response = await fetch('/api/push/subscribe', { 132 method: 'POST', 133 headers: { 134 'Content-Type': 'application/json' 135 }, 136 body: JSON.stringify(subscription) 137 }); 138 139 const data = await response.json(); 140 141 if (data.exito) { 142 console.log('â Suscripción enviada al servidor'); 143 return true; 144 } else { 145 console.error('â Error guardando suscripción:', data.error); 146 return false;
147 } 148 } catch (error) { 149 console.error('â Error enviando suscripción:', error); 150 return false; 151 } 152} 153 154// Inicializar sistema de notificaciones 155async function initPushNotifications() { 156 console.log('ð Inicializando notificaciones push...'); 157 158 // Obtener clave VAPID del servidor 159 await fetchVapidKey(); 160 161 // Registrar service worker 162 const registration = await registerServiceWorker(); 163 if (!registration) return false; 164 165 // Pedir permiso 166 const hasPermission = await requestNotificationPermission(); 167 if (!hasPermission) return false; 168 169 // Suscribirse 170 const subscription = await subscribeToPush(registration); 171 if (!subscription) return false; 172 173 // Enviar al servidor 174 const saved = await sendSubscriptionToServer(subscription); 175 176 return saved; 177} 178 179// Verificar si está suscrito 180async function checkSubscriptionStatus() { 181 if (!('serviceWorker' in navigator)) return false; 182 183 try { 184 const registration = await navigator.serviceWorker.getRegistration(); 185 if (!registration) return false; 186 187 const subscription = await registration.pushManager.getSubscription(); 188 return subscription !== null; 189 } catch (error) { 190 return false; 191 } 192} 193 194// Desuscribirse 195async function unsubscribeFromPush() { 196 try { 197 const registration = await navigator.serviceWorker.getRegistration(); 198 if (!registration) return false; 199 200 const subscription = await registration.pushManager.getSubscription(); 201 if (!subscription) return false; 202 203 await subscription.unsubscribe(); 204 205 // Notificar al servidor 206 await fetch('/api/push/unsubscribe', { 207 method: 'POST', 208 headers: { 209 'Content-Type': 'application/json' 210 }, 211 body: JSON.stringify(subscription) 212 }); 213 214 console.log('â Desuscrito exitosamente'); 215 return true; 216 } catch (error) { 217 console.error('â Error al desuscribirse:', error); 218 return false; 219 } 220} 221 222// Exportar funciones 223window.pushNotifications = { 224 init: initPushNotifications, 225 checkStatus: checkSubscriptionStatus, 226 unsubscribe: unsubscribeFromPush 227};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.