163 lines
4.4 KiB
TypeScript
163 lines
4.4 KiB
TypeScript
import Colle from '#models/colle'
|
|
import Subscription from '#models/subscription'
|
|
import env from '#start/env'
|
|
import { DateTime } from 'luxon'
|
|
import { UAParser } from 'ua-parser-js'
|
|
import webpush, { PushSubscription } from 'web-push'
|
|
|
|
export const EVENTS = {
|
|
SYSTEM: 1 << 0,
|
|
|
|
COLLE_ADDED: 1 << 1,
|
|
COLLE_REMOVED: 1 << 2,
|
|
COLLE_UPDATED: 1 << 3,
|
|
|
|
GRADE_ADDED: 1 << 4,
|
|
GRADE_UPDATED: 1 << 5,
|
|
|
|
ROOM_UPDATED: 1 << 6,
|
|
}
|
|
|
|
type Event = (typeof EVENTS)[keyof typeof EVENTS]
|
|
type NotificationId = keyof typeof NOTIFICATIONS
|
|
|
|
export class NotificationService {
|
|
constructor() {
|
|
webpush.setVapidDetails(
|
|
env.get('VAPID_DETAILS'),
|
|
env.get('VAPID_PUBLIC_KEY'),
|
|
env.get('VAPID_PRIVATE_KEY')
|
|
)
|
|
}
|
|
|
|
public setEvents(events: Event[]): number {
|
|
return events.reduce((acc, event) => acc | event, 0)
|
|
}
|
|
|
|
public hasEvent(events: number, event: Event): boolean {
|
|
return (events & event) !== 0
|
|
}
|
|
|
|
private pushNotification(subscription: PushSubscription, payload: Record<string, any>) {
|
|
return webpush.sendNotification(subscription, JSON.stringify(payload)).catch((err) => {
|
|
console.error('Error sending notification:', err)
|
|
return false
|
|
})
|
|
}
|
|
|
|
public async sendNotification(notificationId: NotificationId, colle: Colle) {
|
|
const payload = Object.assign(DEFAULT_NOTIFICATION, NOTIFICATIONS[notificationId](colle))
|
|
|
|
const subscriptions = await Subscription.query()
|
|
.where('enabled', true)
|
|
.where('userId', colle.studentId)
|
|
// TODO: Check if working??
|
|
.whereRaw(`(events & ${EVENTS[notificationId]}) > 0`)
|
|
|
|
for (const subscription of subscriptions) {
|
|
await this.pushNotification(subscription.data, payload)
|
|
// TODO: Count failed attempts and disable subscription if too many failures
|
|
}
|
|
}
|
|
|
|
public async sendTestNotification(subscription: PushSubscription) {
|
|
const payload = Object.assign(DEFAULT_NOTIFICATION, {
|
|
title: 'Test Notification',
|
|
body: 'Ceci est une notification de test.',
|
|
})
|
|
|
|
return this.pushNotification(subscription, payload)
|
|
}
|
|
|
|
public getUserSignature(uaString: string) {
|
|
const parser = new UAParser(uaString)
|
|
const browser = parser.getBrowser()
|
|
const os = parser.getOS()
|
|
const device = parser.getDevice()
|
|
|
|
const browserStr = browser.name
|
|
? `${browser.name} ${browser.version?.split('.')[0] || ''}`.trim()
|
|
: ''
|
|
const osStr = os.name ? `${os.name} ${os.version || ''}`.trim() : ''
|
|
const deviceStr = device.model
|
|
? `${device.vendor || ''} ${device.model}`.trim()
|
|
: device.type
|
|
? device.type
|
|
: 'Desktop'
|
|
|
|
return [deviceStr, osStr, browserStr].filter(Boolean).join(' | ')
|
|
}
|
|
}
|
|
|
|
const NOTIFICATIONS = {
|
|
COLLE_ADDED: (colle: Colle) => ({
|
|
title: 'Nouvelle colle',
|
|
body: `Colle de ${colle.subject} ajoutée le ${formatDate(colle.date)}.`,
|
|
data: {
|
|
id: colle.id,
|
|
},
|
|
actions: [OPEN_ACION, HOME_ACTION],
|
|
}),
|
|
COLLE_REMOVED: (colle: Colle) => ({
|
|
title: 'Colle supprimée',
|
|
body: `Votre colle de ${colle.subject}, le ${formatDate(colle.date)} a été supprimée.`,
|
|
actions: [HOME_ACTION],
|
|
}),
|
|
COLLE_UPDATED: (colle: Colle) => ({
|
|
title: 'Colle modifiée',
|
|
body: `Votre colle de ${colle.subject} du ${formatDate(colle.date)} a été modifiée.`,
|
|
data: {
|
|
id: colle.id,
|
|
},
|
|
actions: [OPEN_ACION, HOME_ACTION],
|
|
}),
|
|
|
|
GRADE_ADDED: (colle: Colle) => ({
|
|
title: 'Nouvelle note',
|
|
body: `Colle de ${colle.subject} : ${colle.grade}/20`,
|
|
data: {
|
|
id: colle.id,
|
|
},
|
|
actions: [OPEN_ACION, HOME_ACTION],
|
|
}),
|
|
GRADE_UPDATED: (colle: Colle) => ({
|
|
title: 'Note modifiée',
|
|
body: `Colle de ${colle.subject} : ${colle.grade}/20`,
|
|
data: {
|
|
id: colle.id,
|
|
},
|
|
actions: [OPEN_ACION, HOME_ACTION],
|
|
}),
|
|
|
|
ROOM_UPDATED: (colle: Colle) => ({
|
|
title: 'Salle modifiée',
|
|
body: `Colle de ${colle.subject} en ${colle.room}.`,
|
|
data: {
|
|
id: colle.id,
|
|
},
|
|
actions: [OPEN_ACION, HOME_ACTION],
|
|
}),
|
|
}
|
|
|
|
const OPEN_ACION = {
|
|
action: 'open',
|
|
title: 'Ouvrir',
|
|
}
|
|
|
|
const HOME_ACTION = {
|
|
action: 'home',
|
|
title: 'Mes colles',
|
|
}
|
|
|
|
const DEFAULT_NOTIFICATION = {
|
|
title: 'Notification',
|
|
body: 'Vous avez une nouvelle notification.',
|
|
requireInteraction: true,
|
|
icon: env.get('PUBLIC_URL') + '/web-app-manifest-192x192.png',
|
|
}
|
|
|
|
const formatDate = (date: DateTime) =>
|
|
date.toJSDate().toLocaleDateString('fr-FR', {
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
})
|