PageSourceSearch

https://lunarapp-urania-production.up.railway.app/static/js/push-notifications.js

js lunarapp-urania-production.up.railway.app collected 2026-10-03 12:13:00 UTC 6,872 bytes, 227 lines download raw bytes

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.