176 lines
5.8 KiB
JavaScript
176 lines
5.8 KiB
JavaScript
const { Router } = require('express');
|
|
const pool = require('../config/db');
|
|
const { authenticate } = require('../middleware/auth');
|
|
const webPush = require('web-push');
|
|
|
|
const router = Router();
|
|
|
|
const publicVapidKey = process.env.VAPID_PUBLIC_KEY;
|
|
const privateVapidKey = process.env.VAPID_PRIVATE_KEY;
|
|
|
|
if (publicVapidKey && privateVapidKey) {
|
|
webPush.setVapidDetails('mailto:pierre@secourit.fr', publicVapidKey, privateVapidKey);
|
|
}
|
|
|
|
router.post('/subscribe', authenticate, async (req, res) => {
|
|
try {
|
|
const { endpoint, keys, reminderDays } = req.body;
|
|
if (!endpoint || !keys?.p256dh || !keys?.auth) {
|
|
return res.status(400).json({ error: 'endpoint, p256dh, and auth required' });
|
|
}
|
|
await pool.query(
|
|
`INSERT INTO push_subscriptions (user_id, endpoint, p256dh, auth, reminder_days)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE p256dh = VALUES(p256dh), auth = VALUES(auth), reminder_days = VALUES(reminder_days)`,
|
|
[req.user.id, endpoint, keys.p256dh, keys.auth, reminderDays || 3]
|
|
);
|
|
res.json({ message: 'Abonnement aux notifications enregistré' });
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'Erreur serveur' });
|
|
}
|
|
});
|
|
|
|
router.delete('/unsubscribe', authenticate, async (req, res) => {
|
|
try {
|
|
const { endpoint } = req.body;
|
|
if (!endpoint) return res.status(400).json({ error: 'endpoint required' });
|
|
await pool.query('DELETE FROM push_subscriptions WHERE user_id = ? AND endpoint = ?', [req.user.id, endpoint]);
|
|
res.json({ message: 'Désabonné' });
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'Erreur serveur' });
|
|
}
|
|
});
|
|
|
|
router.get('/subscriptions', authenticate, async (req, res) => {
|
|
try {
|
|
const [rows] = await pool.query(
|
|
'SELECT id, endpoint, reminder_days, last_sent_at, created_at FROM push_subscriptions WHERE user_id = ?',
|
|
[req.user.id]
|
|
);
|
|
res.json(rows);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'Erreur serveur' });
|
|
}
|
|
});
|
|
|
|
router.get('/vapid-public-key', (req, res) => {
|
|
res.json({ publicKey: publicVapidKey });
|
|
});
|
|
|
|
router.get('/members-with-push', authenticate, async (req, res) => {
|
|
if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' });
|
|
try {
|
|
const [rows] = await pool.query(
|
|
`SELECT u.id, u.prenom, u.nom, u.societe,
|
|
(SELECT COUNT(*) FROM push_subscriptions ps WHERE ps.user_id = u.id) AS has_push
|
|
FROM users u WHERE u.actif = 1 ORDER BY u.prenom`
|
|
);
|
|
res.json(rows);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'Erreur serveur' });
|
|
}
|
|
});
|
|
|
|
router.post('/broadcast', authenticate, async (req, res) => {
|
|
if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' });
|
|
try {
|
|
const { title, body, url, userIds } = req.body;
|
|
if (!title || !body) return res.status(400).json({ error: 'Titre et message requis' });
|
|
|
|
let subs;
|
|
if (userIds && Array.isArray(userIds) && userIds.length > 0) {
|
|
const placeholders = userIds.map(() => '?').join(',');
|
|
[subs] = await pool.query(
|
|
`SELECT endpoint, p256dh, auth, user_id FROM push_subscriptions WHERE user_id IN (${placeholders})`,
|
|
userIds
|
|
);
|
|
} else {
|
|
[subs] = await pool.query('SELECT endpoint, p256dh, auth, user_id FROM push_subscriptions');
|
|
}
|
|
|
|
if (subs.length === 0) return res.status(400).json({ error: 'Aucun abonné' });
|
|
|
|
const [notifResult] = await pool.query(
|
|
'INSERT INTO notifications (sender_id, title, body) VALUES (?, ?, ?)',
|
|
[req.user.id, title.slice(0, 100), body.slice(0, 255)]
|
|
);
|
|
const notifId = notifResult.insertId;
|
|
|
|
const recipientIds = [...new Set(subs.map((s) => s.user_id))];
|
|
for (const uid of recipientIds) {
|
|
await pool.query(
|
|
'INSERT IGNORE INTO notification_recipients (notification_id, user_id) VALUES (?, ?)',
|
|
[notifId, uid]
|
|
);
|
|
}
|
|
|
|
const payload = JSON.stringify({
|
|
title: title.slice(0, 100),
|
|
body: body.slice(0, 255),
|
|
icon: '/logo.svg',
|
|
badge: '/favicon.svg',
|
|
data: { url: '/notifications' },
|
|
});
|
|
|
|
let sent = 0;
|
|
for (const sub of subs) {
|
|
try {
|
|
await webPush.sendNotification(
|
|
{ endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } },
|
|
payload
|
|
);
|
|
sent++;
|
|
} catch (err) {
|
|
if (err.statusCode === 410 || err.statusCode === 404) {
|
|
await pool.query('DELETE FROM push_subscriptions WHERE endpoint = ?', [sub.endpoint]);
|
|
}
|
|
}
|
|
}
|
|
res.json({ message: `Notification envoyée à ${sent} abonné(s) sur ${recipientIds.length} destinataire(s)` });
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'Erreur serveur' });
|
|
}
|
|
});
|
|
|
|
router.post('/test', authenticate, async (req, res) => {
|
|
try {
|
|
const [subs] = await pool.query(
|
|
'SELECT endpoint, p256dh, auth FROM push_subscriptions WHERE user_id = ?',
|
|
[req.user.id]
|
|
);
|
|
if (subs.length === 0) return res.status(400).json({ error: 'Aucun abonnement trouvé' });
|
|
|
|
const payload = JSON.stringify({
|
|
title: 'Dyna Biz Coignières',
|
|
body: 'Notification de test ✓',
|
|
icon: '/logo.svg',
|
|
badge: '/favicon.svg',
|
|
data: { url: '/' },
|
|
});
|
|
|
|
for (const sub of subs) {
|
|
try {
|
|
await webPush.sendNotification(
|
|
{ endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } },
|
|
payload
|
|
);
|
|
} catch (err) {
|
|
if (err.statusCode === 410 || err.statusCode === 404) {
|
|
await pool.query('DELETE FROM push_subscriptions WHERE endpoint = ?', [sub.endpoint]);
|
|
}
|
|
}
|
|
}
|
|
res.json({ message: 'Notification de test envoyée' });
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'Erreur serveur' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|