163 lines
5.3 KiB
JavaScript
163 lines
5.3 KiB
JavaScript
const webpush = require('web-push');
|
|
|
|
// VAPID Keys (sollten in Umgebungsvariablen gespeichert werden)
|
|
const vapidKeys = {
|
|
publicKey: 'BJmNVx0C3XeVxeKGTP9c-Z4HcuZNmdk6QdiLocZgCmb-miCS0ESFO3W2TvJlRhhNAShV63pWA5p36BTVSetyTds',
|
|
privateKey: 'HBdRCtmZUAzsWpVjZ2LDaoWliIPHldAb5ExAt8bvDeg'
|
|
};
|
|
|
|
// Configure web-push
|
|
webpush.setVapidDetails(
|
|
'mailto:admin@reptilfpv.de',
|
|
vapidKeys.publicKey,
|
|
vapidKeys.privateKey
|
|
);
|
|
|
|
class PushService {
|
|
constructor() {
|
|
this.subscriptions = new Map(); // Map<playerId, Set<subscription>>
|
|
}
|
|
|
|
// Subscribe user to push notifications
|
|
subscribe(userId, subscription) {
|
|
if (!this.subscriptions.has(userId)) {
|
|
this.subscriptions.set(userId, new Set());
|
|
}
|
|
this.subscriptions.get(userId).add(subscription);
|
|
console.log(`User ${userId} subscribed to push notifications (${this.subscriptions.get(userId).size} total subscriptions)`);
|
|
}
|
|
|
|
// Unsubscribe user from push notifications
|
|
unsubscribe(userId) {
|
|
this.subscriptions.delete(userId);
|
|
console.log(`User ${userId} unsubscribed from push notifications`);
|
|
}
|
|
|
|
// Unsubscribe specific device from push notifications
|
|
unsubscribeDevice(userId, endpoint) {
|
|
const subscriptions = this.subscriptions.get(userId);
|
|
if (!subscriptions) {
|
|
console.log(`No subscriptions found for user ${userId}`);
|
|
return false;
|
|
}
|
|
|
|
// Find and remove the specific subscription by endpoint
|
|
let removed = false;
|
|
for (const subscription of subscriptions) {
|
|
if (subscription.endpoint === endpoint) {
|
|
subscriptions.delete(subscription);
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no subscriptions left, remove the user entirely
|
|
if (subscriptions.size === 0) {
|
|
this.subscriptions.delete(userId);
|
|
console.log(`User ${userId} unsubscribed from push notifications (all devices)`);
|
|
} else {
|
|
console.log(`Device ${endpoint} unsubscribed from push notifications for user ${userId} (${subscriptions.size} devices remaining)`);
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
// Send push notification to specific user
|
|
async sendToUser(userId, payload) {
|
|
const subscriptions = this.subscriptions.get(userId);
|
|
if (!subscriptions || subscriptions.size === 0) {
|
|
console.log(`No subscriptions found for user ${userId}`);
|
|
return false;
|
|
}
|
|
|
|
let successCount = 0;
|
|
let totalCount = subscriptions.size;
|
|
|
|
for (const subscription of subscriptions) {
|
|
try {
|
|
await webpush.sendNotification(subscription, JSON.stringify(payload));
|
|
successCount++;
|
|
} catch (error) {
|
|
console.error(`Error sending push notification to user ${userId}:`, error);
|
|
|
|
// If subscription is invalid, remove it
|
|
if (error.statusCode === 410) {
|
|
subscriptions.delete(subscription);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Push notification sent to ${successCount}/${totalCount} subscriptions for user ${userId}`);
|
|
return successCount > 0;
|
|
}
|
|
|
|
// Send push notification to all subscribed users
|
|
async sendToAll(payload) {
|
|
const results = [];
|
|
for (const [userId, subscription] of this.subscriptions) {
|
|
const result = await this.sendToUser(userId, payload);
|
|
results.push({ userId, success: result });
|
|
}
|
|
return results;
|
|
}
|
|
|
|
// Send achievement notification
|
|
async sendAchievementNotification(userId, achievementName) {
|
|
const payload = {
|
|
title: '🏆 Neues Achievement!',
|
|
body: `Du hast "${achievementName}" erreicht!`,
|
|
icon: '/pictures/icon-192.png',
|
|
badge: '/pictures/icon-192.png',
|
|
data: {
|
|
type: 'achievement',
|
|
achievement: achievementName,
|
|
timestamp: Date.now()
|
|
},
|
|
actions: [
|
|
{
|
|
action: 'view',
|
|
title: 'Dashboard öffnen'
|
|
}
|
|
]
|
|
};
|
|
|
|
return await this.sendToUser(userId, payload);
|
|
}
|
|
|
|
// Send best time notification
|
|
async sendBestTimeNotification(userId, timeType, locationName) {
|
|
const payload = {
|
|
title: `🏁 ${timeType} Bestzeit!`,
|
|
body: `Du hast die beste Zeit in ${locationName} erreicht!`,
|
|
icon: '/pictures/icon-192.png',
|
|
badge: '/pictures/icon-192.png',
|
|
data: {
|
|
type: 'best_time',
|
|
timeType: timeType,
|
|
location: locationName,
|
|
timestamp: Date.now()
|
|
},
|
|
actions: [
|
|
{
|
|
action: 'view',
|
|
title: 'Dashboard öffnen'
|
|
}
|
|
]
|
|
};
|
|
|
|
return await this.sendToUser(userId, payload);
|
|
}
|
|
|
|
// Get subscription count
|
|
getSubscriptionCount() {
|
|
return this.subscriptions.size;
|
|
}
|
|
|
|
// Get all user IDs with subscriptions
|
|
getSubscribedUsers() {
|
|
return Array.from(this.subscriptions.keys());
|
|
}
|
|
}
|
|
|
|
module.exports = new PushService();
|