Files
Ninjaserver/lib/push-service.js
2025-09-23 14:13:24 +02:00

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();