unoffical wafrn mirror wafrn.net
atproto social-network activitypub
at cache-folder-container 99 lines 3.4 kB view raw
1import { Op } from 'sequelize' 2import { 3 getNotificationBody, 4 getNotificationTitle, 5 NotificationBody, 6 NotificationContext 7} from './pushNotifications.js' 8import { UnifiedPushData } from '../models/unifiedPushData.js' 9import { getMutedPostsMultiple } from './cacheGetters/getMutedPosts.js' 10import WebPush from 'web-push' 11import { logger } from './logger.js' 12import { completeEnvironment } from './backendOptions.js' 13 14WebPush.setVapidDetails( 15 completeEnvironment.webpushEmail, 16 completeEnvironment.webpushPublicKey, 17 completeEnvironment.webpushPrivateKey 18) 19 20export async function sendWebPushNotifications(notifications: NotificationBody[], context?: NotificationContext) { 21 const userIds = notifications.map((elem) => elem.notifiedUserId) 22 const pushDataRows = await UnifiedPushData.findAll({ 23 where: { 24 userId: { 25 [Op.in]: userIds 26 } 27 } 28 }) 29 const mutedPosts = await getMutedPostsMultiple(userIds, true) 30 31 for (const notification of notifications) { 32 const userId = notification.notifiedUserId 33 const mutes = mutedPosts.get(userId) 34 const isMuted = mutes && notification.postId ? mutes.includes(notification.postId) : false 35 if (!isMuted) { 36 const userDevices = pushDataRows.filter((p) => p.userId === userId) 37 for (const device of userDevices) { 38 await sendWebPushNotification(notification, device, context) 39 } 40 } 41 } 42} 43 44async function sendWebPushNotification( 45 notification: NotificationBody, 46 device: UnifiedPushData, 47 context?: NotificationContext 48) { 49 try { 50 const payload = await getNotificationPayload(notification, context) 51 await WebPush.sendNotification( 52 { 53 endpoint: device.endpoint, 54 keys: { 55 auth: device.deviceAuth, 56 p256dh: device.devicePublicKey 57 } 58 }, 59 JSON.stringify(payload) 60 ) 61 } catch (error) { 62 logger.error({ message: 'Error sending web push notification: ', error: error }) 63 // TODO: if sending webpush fails, we can remove this record from the db 64 // if the error is some weird error and we should not have removed the record 65 // that is not a problem, because next time the app opens, the device will be registered again 66 await UnifiedPushData.destroy({ 67 where: { 68 endpoint: device.endpoint 69 } 70 }) 71 } 72} 73 74// TODO: need a simpler way to generate a unique number id from a string 75// maybe create the notification record in the db first and then send the notification to the devices 76function getNotificationId(notification: NotificationBody) { 77 const key = `${notification.notifiedUserId}-${notification.userId}-${notification.notificationType}-${notification.postId}` 78 return crypto.subtle.digest('SHA-256', new TextEncoder().encode(key)).then((hash) => { 79 return new Uint32Array(hash).at(0)! 80 }) 81} 82 83function getNotificationUrl(notification: NotificationBody, context?: NotificationContext) { 84 if (notification.notificationType === 'FOLLOW') { 85 return `wafrn://user/${context?.userUrl}` 86 } else { 87 return `wafrn://post/${notification.postId}` 88 } 89} 90 91async function getNotificationPayload(notification: NotificationBody, context?: NotificationContext) { 92 return { 93 id: await getNotificationId(notification), 94 url: getNotificationUrl(notification, context), 95 title: getNotificationTitle(notification, context), 96 body: getNotificationBody(notification, context), 97 type: notification.notificationType 98 } 99}