← Назад
const express = require('express'); const router = express.Router(); const webpush = require('web-push'); const prisma = require('../services/db'); const logger = require('../utils/logger'); // Setup Web Push const publicVapidKey = process.env.VAPID_PUBLIC_KEY; const privateVapidKey = process.env.VAPID_PRIVATE_KEY; if (publicVapidKey && privateVapidKey) { webpush.setVapidDetails( 'mailto:test@szhub.space', publicVapidKey, privateVapidKey ); logger.info('VAPID Web Push initialized properly.'); } else { logger.error('VAPID keys are missing! Web push will not work.'); } // Return the public VAPID key to the frontend so it can subscribe router.get('/vapidPublicKey', (req, res) => { res.json({ publicKey: publicVapidKey }); }); // Save the push subscription Object from the browser router.post('/subscribe', async (req, res) => { const subscription = req.body; if (!subscription || !subscription.endpoint) { return res.status(400).json({ error: 'Invalid subscription object' }); } try { // Save or update the subscription in the database await prisma.pushSubscription.upsert({ where: { endpoint: subscription.endpoint }, update: { p256dh: subscription.keys.p256dh, auth: subscription.keys.auth, }, create: { endpoint: subscription.endpoint, p256dh: subscription.keys.p256dh, auth: subscription.keys.auth, }, }); // Send a welcome push to confirm it worked! const payload = JSON.stringify({ title: 'Уведомления Включены! 🚀', body: 'Теперь вы будете первыми узнавать о новых сильных сигналах на рынке опционов.', icon: '/pwa-192x192.png' }); await webpush.sendNotification(subscription, payload).catch(err => logger.error(`Error sending welcome push: ${err.message}`)); logger.info(`New Web Push subscription saved successfully.`); res.status(201).json({}); } catch (error) { logger.error(`Error saving push subscription: ${error.message}`); res.status(500).json({ error: 'Failed to subscribe' }); } }); // Remove the push subscription router.post('/unsubscribe', async (req, res) => { const subscription = req.body; if (!subscription || !subscription.endpoint) { return res.status(400).json({ error: 'Invalid subscription object' }); } try { await prisma.pushSubscription.delete({ where: { endpoint: subscription.endpoint }, }); logger.info('User unsubscribed from push notifications.'); res.status(200).json({}); } catch (error) { res.status(200).json({}); } }); // Diagnostic Test Endpoint router.post('/test', async (req, res) => { logger.info('Executing diagnostic push notification broadcast...'); try { await broadcastPushNotification({ title: 'Инструмент отладки ⚙️', body: 'Связь с сервером установлена. Push-уведомления работают исправно!', icon: '/pwa-192x192.png' }); res.status(200).json({ success: true, message: 'Test broadcast fired.' }); } catch (error) { logger.error(`Failed diagnostic test: ${error.message}`); res.status(500).json({ error: 'Failed' }); } }); async function broadcastPushNotification(payloadData) { if (!publicVapidKey) return; const payload = JSON.stringify(payloadData); try { const subscriptions = await prisma.pushSubscription.findMany(); const promises = subscriptions.map(sub => { const pushSub = { endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } }; return webpush.sendNotification(pushSub, payload).catch(err => { if (err.statusCode === 404 || err.statusCode === 410) { // The subscription has expired or been unsubscribed, remove from DB logger.info(`Subscription expired. Removing endpoint: ${sub.id}`); return prisma.pushSubscription.delete({ where: { id: sub.id } }).catch(e => logger.error(`Failed DB cleanup: ${e.message}`)); } else { logger.error(`Push notification error for device ${sub.id}: ${err.message}`); } }); }); await Promise.allSettled(promises); logger.info(`Successfully broadcasted push chunk to ${subscriptions.length} devices.`); } catch (err) { logger.error(`Error broadcasting push chunk: ${err.message}`); } } module.exports = { router, broadcastPushNotification };