/* * GitData Service Worker — Web Push 接收端。 * * 只处理推送与通知点击,不做离线缓存:这个站点是实时协作工具,缓存旧版本 * 的界面比直接报网络错误更糟。 */ self.addEventListener('install', () => { // 新版本立刻接管,推送处理逻辑没有需要等待的迁移步骤。 self.skipWaiting() }) self.addEventListener('activate', (event) => { event.waitUntil(self.clients.claim()) }) self.addEventListener('push', (event) => { let payload = {} try { payload = event.data ? event.data.json() : {} } catch { // 推送服务只保证把字节送到;解析失败时退化成一条通用提醒,而不是静默丢弃。 payload = {} } const title = typeof payload.title === 'string' && payload.title ? payload.title : 'GitDataAI' const url = typeof payload.url === 'string' && payload.url ? payload.url : '/app/@me/notify' event.waitUntil( self.registration.showNotification(title, { body: typeof payload.body === 'string' ? payload.body : undefined, icon: typeof payload.icon === 'string' ? payload.icon : '/favicon.png', tag: typeof payload.tag === 'string' ? payload.tag : undefined, // 同一 tag 的新通知要能再次打断用户:聊天场景里"上一条已经被顶掉"比 // 静默替换更容易错过。 renotify: true, data: { url }, }), ) }) self.addEventListener('notificationclick', (event) => { event.notification.close() const target = new URL(event.notification.data?.url || '/app/@me/notify', self.location.origin) event.waitUntil( (async () => { const clientList = await self.clients.matchAll({ type: 'window', includeUncontrolled: true, }) // 优先复用已经打开的窗口:直接 openWindow 会让同一个应用出现两个标签页, // 而用户期望的是"通知把我带回刚才那个界面"。 for (const client of clientList) { if (new URL(client.url).origin !== target.origin) continue await client.focus() if ('navigate' in client && client.url !== target.href) { await client.navigate(target.href).catch(() => {}) } return } await self.clients.openWindow(target.href) })(), ) }) self.addEventListener('pushsubscriptionchange', (event) => { // 浏览器会不定期轮换订阅(尤其是长期未使用的设备)。不重新注册的话,服务端 // 存着的 endpoint 会失效,推送从此静默丢失。 event.waitUntil( (async () => { const applicationServerKey = event.oldSubscription?.options?.applicationServerKey ?? (await self.registration.pushManager.getSubscription())?.options?.applicationServerKey if (!applicationServerKey) return const subscription = await self.registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey, }) const json = subscription.toJSON() await fetch('/api/v1/me/push-subscriptions', { method: 'POST', credentials: 'include', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint, p256dh_key: json.keys?.p256dh ?? '', auth_key: json.keys?.auth ?? '', platform: 'web_push', user_agent: self.navigator.userAgent, }), }) })(), ) })