Skip to content

Commit 285283d

Browse files
Notifications library functions.
1 parent 0ca207e commit 285283d

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

src/lib/notifications.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import webpush, { WebPushError } from 'web-push'
2+
import { messaging } from './firebase' // Adjust as needed
3+
import { PrismaClient, Subscription } from '@prisma/client'
4+
import { JsonValue } from '@prisma/client/runtime/library'
5+
6+
const prisma = new PrismaClient()
7+
8+
// Interfaces extending the Prisma Subscription type
9+
interface WebSubscription extends Subscription {
10+
type: 'web'
11+
keys: JsonValue // Represents { auth: string; p256dh: string } as JsonValue
12+
}
13+
14+
interface FcmSubscription extends Subscription {
15+
type: 'fcm'
16+
keys: JsonValue // Represents { token: string } as JsonValue
17+
}
18+
19+
// Union type covering both subscription types
20+
export type SubscriptionRecord = WebSubscription | FcmSubscription
21+
22+
// Notification payload interface
23+
interface NotificationPayload {
24+
title: string
25+
body: string
26+
url: string
27+
icon?: string
28+
badge?: string
29+
}
30+
31+
// Helper function to validate web keys
32+
function isWebKeys(keys: JsonValue): keys is { auth: string; p256dh: string } {
33+
return (
34+
typeof keys === 'object' &&
35+
keys !== null &&
36+
'auth' in keys &&
37+
'p256dh' in keys &&
38+
typeof (keys as any).auth === 'string' &&
39+
typeof (keys as any).p256dh === 'string'
40+
)
41+
}
42+
43+
// Helper function to validate FCM keys
44+
function isFcmKeys(keys: JsonValue): keys is { token: string } {
45+
return (
46+
typeof keys === 'object' &&
47+
keys !== null &&
48+
'token' in keys &&
49+
typeof (keys as any).token === 'string'
50+
)
51+
}
52+
53+
// Unified function to send notifications
54+
export async function sendNotification(
55+
subscription: SubscriptionRecord,
56+
notificationPayload: NotificationPayload
57+
): Promise<void> {
58+
try {
59+
console.log('Sending notification to subscription:', subscription)
60+
61+
if (subscription.type === 'web') {
62+
if (!isWebKeys(subscription.keys)) {
63+
throw new Error(`Invalid keys for web subscription: ${JSON.stringify(subscription.keys)}`)
64+
}
65+
66+
await webpush.sendNotification(
67+
{
68+
endpoint: subscription.endpoint,
69+
keys: {
70+
auth: subscription.keys.auth,
71+
p256dh: subscription.keys.p256dh,
72+
},
73+
},
74+
JSON.stringify({
75+
title: notificationPayload.title,
76+
body: notificationPayload.body,
77+
icon: notificationPayload.icon,
78+
badge: notificationPayload.badge,
79+
url: notificationPayload.url,
80+
})
81+
)
82+
} else if (subscription.type === 'fcm') {
83+
if (!isFcmKeys(subscription.keys)) {
84+
throw new Error(`Invalid keys for FCM subscription: ${JSON.stringify(subscription.keys)}`)
85+
}
86+
87+
const fcmMessage = {
88+
notification: {
89+
title: notificationPayload.title,
90+
body: notificationPayload.body,
91+
},
92+
data: {
93+
url: notificationPayload.url,
94+
},
95+
token: subscription.keys.token,
96+
}
97+
98+
const response = await messaging.send(fcmMessage)
99+
console.log('FCM response:', response)
100+
}
101+
} catch (error) {
102+
if (subscription.type === 'web' && error instanceof WebPushError) {
103+
if (error.statusCode === 410) {
104+
await prisma.subscription.delete({ where: { id: subscription.id } })
105+
console.log(`Subscription with id ${subscription.id} removed due to expiration.`)
106+
} else {
107+
console.error(
108+
`Failed to send notification to subscription id ${subscription.id}:`,
109+
error.statusCode,
110+
error.body
111+
)
112+
}
113+
} else {
114+
console.error(
115+
`An error occurred while sending notification to subscription id ${subscription.id}:`,
116+
error
117+
)
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)