async function currentEndpoint() { const subscription = await self.registration.pushManager.getSubscription(); return subscription ? subscription.endpoint : ''; } async function windowClientCount() { const allClients = await clients.matchAll({ type: 'window', includeUncontrolled: true, }); return allClients.length; } function logPushEvent(eventName, payload = {}, extra = {}) { return Promise.all([ currentEndpoint().catch(() => ''), windowClientCount().catch(() => 0), ]).then(([endpoint, clientCount]) => fetch('/api/push_event.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', cache: 'no-store', body: JSON.stringify({ event: eventName, endpoint, push_id: payload.push_id || '', tag: payload.tag || '', client_count: clientCount, meta: extra, }), })).catch(() => {}); } self.addEventListener('push', event => { let payload = {}; try { payload = event.data ? event.data.json() : {}; } catch (e) { payload = { body: event.data ? event.data.text() : '', }; } const title = payload.title || 'Seoul Control Center'; const vibrate = Array.isArray(payload.vibrate) ? payload.vibrate.map(value => Number(value)).filter(value => Number.isFinite(value) && value >= 0) : undefined; const options = { body: payload.body || 'System notice detected', icon: '/assets/icon-192.png', badge: '/assets/icon-192.png', tag: payload.tag || 'control-push', renotify: payload.renotify === true, requireInteraction: payload.require_interaction === true || payload.requireInteraction === true, silent: payload.silent === true, vibrate, data: { url: payload.url || '/', push_id: payload.push_id || '', tag: payload.tag || 'control-push', }, }; event.waitUntil((async () => { await logPushEvent('push_received', payload); try { await self.registration.showNotification(title, options); await logPushEvent('notification_shown', payload); } catch (error) { await logPushEvent('notification_show_failed', payload, { reason: error && error.message ? error.message : 'show_failed', }); throw error; } })()); }); self.addEventListener('notificationclick', event => { event.notification.close(); const url = event.notification.data?.url || '/'; const payload = { push_id: event.notification.data?.push_id || '', tag: event.notification.data?.tag || event.notification.tag || '', }; event.waitUntil((async () => { await logPushEvent('notification_click', payload, { action: event.action || '', }); const allClients = await clients.matchAll({ type: 'window', includeUncontrolled: true, }); for (const client of allClients) { if ('focus' in client) { await client.focus(); if ('navigate' in client) { return client.navigate(url); } return; } } if (clients.openWindow) { return clients.openWindow(url); } })()); }); self.addEventListener('notificationclose', event => { const payload = { push_id: event.notification.data?.push_id || '', tag: event.notification.data?.tag || event.notification.tag || '', }; event.waitUntil(logPushEvent('notification_close', payload)); });