From dec614047e8e2847221f9ccba5af876d6eb2ef98 Mon Sep 17 00:00:00 2001 From: Julien Oculi Date: Thu, 15 May 2025 16:05:30 +0200 Subject: [PATCH] feat(webpush): send registration notification --- routes/api/webpush/subscription.ts | 10 +++++- src/webpush/mod.ts | 56 ++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/routes/api/webpush/subscription.ts b/routes/api/webpush/subscription.ts index 4888f59..ad1d626 100644 --- a/routes/api/webpush/subscription.ts +++ b/routes/api/webpush/subscription.ts @@ -1,6 +1,6 @@ import { define } from '../../../utils.ts' import { respondApi } from ':src/utils.ts' -import { subscriptions } from ':src/webpush/mod.ts' +import { sendWebPushNotification, subscriptions } from ':src/webpush/mod.ts' export const handler = define.handlers({ async POST(ctx) { @@ -8,6 +8,14 @@ export const handler = define.handlers({ try { await saveSubscription(subscription) + + await sendWebPushNotification( + 'Notifications desormais disponible sur votre appareil', + {}, + (sub) => + sub.keys !== undefined && sub.keys.auth === subscription.keys?.auth, + ) + return respondApi('success', 'ok') } catch (error) { console.error('[webpush/subscription]:', error) diff --git a/src/webpush/mod.ts b/src/webpush/mod.ts index b18049c..fb37ce4 100644 --- a/src/webpush/mod.ts +++ b/src/webpush/mod.ts @@ -17,22 +17,48 @@ webpush.setVapidDetails( vapidKeys.privateKey, ) -// const itemKey = 'webpush-subscription' -// setInterval(async () => { -// const subscriptions = JSON.parse(localStorage.getItem(itemKey) ?? '[]') as PushSubscriptionJSON[] +export async function sendWebPushNotification( + title: string, + options: Partial = {}, + filter: (subscription: PushSubscriptionJSON) => boolean = () => true, +) { + for await ( + const { value: subscription, key } of subscriptions.list< + PushSubscriptionJSON + >({ prefix: ['subscription'] }) + ) { + if (!filter(subscription)) { + continue + } -// for (const subscription of subscriptions) { -// try { -// // console.log('PUSH NOTIFICATION', subscription) -// const payload: { title: string, options?: Partial } = { title: `This is a test @ ${new Date().toLocaleTimeString()}` } -// //@ts-ignore TODO check endpoint is defined -// const a = await webpush.sendNotification(subscription, JSON.stringify(payload)) -// console.log(a) -// } catch (error) { -// console.error(error) -// } -// } -// }, 10_000) + try { + const payload = { title, options } + + if (subscription.endpoint === undefined) { + throw new TypeError( + `missing endpoint in subscription : ${JSON.stringify(subscription)}`, + ) + } + + const response = await webpush.sendNotification( + //@ts-ignore endpoint is defined + subscription, + JSON.stringify(payload), + ) + + // Subscription has expired or is no longer valid + if (response.statusCode === 410 || response.statusCode === 404) { + return subscriptions.delete(key) + } + } catch (error) { + if (error instanceof WebPushError) { + if (error.statusCode === 410 || error.statusCode === 404) return + console.error(error) + } + console.error(error) + } + } +} function getVapidKeys(): webpush.VapidKeys { const itemKey = 'webpush-vapid-keys'