PageSourceSearch

https://timespro.com/firebase-messaging-sw.js

js timespro.com collected 2026-09-24 09:29:03 UTC 5,945 bytes, 200 lines download raw bytes

1/* eslint-disable no-undef */
2// Service Worker globals: importScripts, self, clients, firebase are provided by the SW runtime
3importScripts(
4  'https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js'
5);
6importScripts(
7  'https://www.gstatic.com/firebasejs/9.0.0/firebase-messaging-compat.js'
8);
9// Initialize Firebase in the Service Worker
10firebase.initializeApp({
11  apiKey: 'AIzaSyBg5V0PW1vXKj8f4OW9YF-B1ApZzXCeXJA',
12  authDomain: 'time-mobile-prod.firebaseapp.com',
13  projectId: 'time-mobile-prod',
14  storageBucket: 'time-mobile-prod.firebasestorage.app',
15  messagingSenderId: '295945882373',
16  appId: '1:295945882373:web:f08628500b0086069a056c',
17  measurementId: 'G-1XXDT38ZQ3',
18});
19
20// ============================================
21// Notification Webhook Configuration
22// ============================================
23let webhookConfig = {
24  baseUrl: '',
25  clientId: '',
26  deviceToken: '',
27  GUID: '',
28  jwt: '',
29};
30
31// Listen for config messages from the main app
32self.addEventListener('message', (event) => {
33  if (event.data && event.data.type === 'SET_WEBHOOK_CONFIG') {
34    webhookConfig = {
35      baseUrl: event.data.baseUrl || webhookConfig.baseUrl,
36      clientId: event.data.clientId || webhookConfig.clientId,
37      deviceToken: event.data.deviceToken || webhookConfig.deviceToken,
38      GUID: event.data.GUID || webhookConfig.GUID,
39      jwt: event.data.jwt || webhookConfig.jwt,
40    };
41    console.log('[SW] Webhook config updated');
42  }
43});
44
45/**
46 * Reads a cookie value from within the service worker using the Cookie Store
47 * API (Chromium). Returns '' if unavailable or not found.
48 */
49async function readCookieValue(name) {
50  try {
51    if (typeof cookieStore !== 'undefined' && cookieStore.get) {
52      const cookie = await cookieStore.get(name);
53      if (cookie && cookie.value) return cookie.value;
54    }
55  } catch (e) {
56    // ignore and fall back to config
57  }
58  return '';
59}
60
61/**
62 * Sends notification event to backend webhook.
63 * @param {'DELIVERED' | 'CLICKED'} eventType
64 * @param {Object} data - { GUID, messageId }
65 */
66async function sendNotificationWebhook(eventType, data) {
67  if (!webhookConfig.baseUrl || !webhookConfig.deviceToken) {
68    console.warn(
69      '[SW] Webhook config not available, skipping event:',
70      eventType
71    );
72    return;
73  }
74
75  // Prefer live cookie values, then config, then anything on the notification.
76  const cookieGuid = await readCookieValue('notificationGUID');
77  const guid = cookieGuid || webhookConfig.GUID || data.GUID || '';
78
79  const cookieJwt = await readCookieValue('notificationJwt');
80  const jwt = cookieJwt || webhookConfig.jwt || '';
81
82  const payload = {
83    deviceToken: webhookConfig.deviceToken,
84    eventType: eventType,
85    GUID: guid,
86    messageId: data.messageId || '',
87  };
88
89  const headers = {
90    'Content-Type': 'application/json',
91    clientid: webhookConfig.clientId,
92  };
93  if (jwt) {
94    headers.Authorization = jwt;
95  }
96
97  return fetch(`${webhookConfig.baseUrl}/comm/webhook/notification/events`, {
98    method: 'POST',
99    headers: headers,
100    body: JSON.stringify(payload),
101  })
102    .then((res) => {
103      if (res.ok) {
104        console.log(
105          `[SW] ${eventType} webhook sent for messageId: ${data.messageId}`
106        );
107      } else {
108        console.error(
109          `[SW] ${eventType} webhook failed with status: ${res.status}`
110        );
111      }
112    })
113    .catch((err) => {
114      console.error(`[SW] ${eventType} webhook error:`, err);
115    });
116}
117
118// Extracts GUID from the push payload data, tolerating different key casings.
119function extractGuid(data) {
120  if (!data) return '';
121  return data.GUID || data.guid || data.Guid || data.guid_id || '';
122}
123
124// Extracts messageId from the push payload, tolerating different key casings.
125function extractMessageId(payload) {
126  const data = payload?.data || {};
127  return (
128    data.messageId ||
129    data.messageID ||
130    data.message_id ||
131    payload?.messageId ||
132    payload?.fcmMessageId ||
133    ''
134  );
135}
136
137// Initialize Firebase Messaging
138const isSupported = firebase.messaging.isSupported();
139if (isSupported) {
140  const messaging = firebase.messaging();
141  messaging.onBackgroundMessage((payload) => {
142    // GUID is sourced from webhookConfig (updateFCMToken response). messageId
143    // still comes from the push payload since it is per-message.
144    const guid = webhookConfig.GUID || extractGuid(payload.data);
145    const messageId = extractMessageId(payload);
146
147    const notificationTitle =
148      payload.data?.notification_title || 'New Notification';
149    const notificationOptions = {
150      body: payload.data?.notification_body || 'You have a new message.',
151      icon: payload.data?.icon || '/default-icon.png',
152      data: {
153        click_action: payload.data?.click_action,
154        GUID: guid,
155        messageId: messageId,
156      },
157    };
158
159    self.registration.showNotification(notificationTitle, notificationOptions);
160
161    // Send DELIVERED webhook (GUID resolved live from cookie inside the helper)
162    sendNotificationWebhook('DELIVERED', {
163      GUID: guid,
164      messageId: messageId,
165    });
166  });
167}
168
169self.addEventListener('push', (event) => {
170  console.log('Push event received:', event);
171  if (event.data) {
172    console.log('Push data:', event.data.json());
173  }
174});
175
176self.addEventListener('notificationclick', (event) => {
177  event.notification.close();
178
179  const notificationData = event.notification.data || {};
180
181  // Keep the SW alive until the CLICKED webhook completes, then open the link.
182  event.waitUntil(
183    sendNotificationWebhook('CLICKED', {
184      GUID: notificationData.GUID || '',
185      messageId: notificationData.messageId || '',
186    }).then(() => {
187      if (notificationData.click_action) {
188        return clients.openWindow(notificationData.click_action);
189      }
190    })
191  );
192});
193
194self.addEventListener('install', (event) => {
195  self.skipWaiting();
196});
197
198self.addEventListener('activate', (event) => {
199  event.waitUntil(self.clients.claim());
200});

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.