All files / src/routes notifications.js

97.29% Statements 36/37
93.75% Branches 15/16
100% Functions 3/3
97.22% Lines 35/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85                2x 2x 2x     2x 10x 10x 7x     3x 3x       1x   2x 2x         2x 9x 9x 7x     2x 2x       1x   1x 1x         2x 7x 7x       7x               7x 7x 35x     7x 1x     6x   5x   1x 1x       2x  
/**
 * Notification routes — FCM token management and notification settings.
 *
 * POST   /api/notifications/token    -> Save FCM token
 * DELETE /api/notifications/token    -> Remove FCM token
 * PATCH  /api/notifications/settings -> Update notification settings
 */
 
const router = require('express').Router();
const { db, FieldValue } = require('../utils/firebase');
const log = require('../utils/log');
 
// -- Save FCM token --
router.post('/notifications/token', async (req, res) => {
  try {
    if (!req.body?.token || typeof req.body.token !== 'string' || req.body.token.length > 500) {
      return res.status(400).json({ error: 'token must be a non-empty string' });
    }
 
    const uniqueId = req.auth.uniqueId;
    await db.doc(`users/${uniqueId}`).update({
      fcmTokens: FieldValue.arrayUnion(req.body.token),
    });
 
    return res.json({ success: true });
  } catch (err) {
    log.error('notifications', 'Error saving FCM token', { error: err.message });
    return res.status(500).json({ error: 'Internal server error' });
  }
});
 
// -- Remove FCM token --
router.delete('/notifications/token', async (req, res) => {
  try {
    if (!req.body?.token || typeof req.body.token !== 'string' || req.body.token.length > 500) {
      return res.status(400).json({ error: 'token must be a non-empty string' });
    }
 
    const uniqueId = req.auth.uniqueId;
    await db.doc(`users/${uniqueId}`).update({
      fcmTokens: FieldValue.arrayRemove(req.body.token),
    });
 
    return res.json({ success: true });
  } catch (err) {
    log.error('notifications', 'Error removing FCM token', { error: err.message });
    return res.status(500).json({ error: 'Internal server error' });
  }
});
 
// -- Update notification settings --
router.patch('/notifications/settings', async (req, res) => {
  try {
    Iif (!req.body) {
      return res.status(400).json({ error: 'Invalid body' });
    }
 
    const allowedFields = [
      'pmNotificationsEnabled',
      'pmSoundEnabled',
      'pmShowTimestamps',
      'pmShowDateSeparators',
      'pmNotificationPreview',
    ];
 
    const updates = {};
    for (const key of allowedFields) {
      if (key in req.body) updates[key] = !!req.body[key];
    }
 
    if (Object.keys(updates).length === 0) {
      return res.status(400).json({ error: 'No valid fields' });
    }
 
    await db.doc(`users/${req.auth.uniqueId}`).update(updates);
 
    return res.json({ success: true });
  } catch (err) {
    log.error('notifications', 'Error updating notification settings', { error: err.message });
    return res.status(500).json({ error: 'Internal server error' });
  }
});
 
module.exports = router;