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 86 87 88 89 90 91 92 | 2x 2x 2x 2x 14x 14x 13x 9x 9x 9x 9x 11x 11x 11x 9x 9x 6x 6x 6x 6x 6x 9x 5x 9x 5x 1x 2x | /**
* Roadmap update notification trigger.
*
* Queries all subscribers who opted into roadmapUpdate notifications,
* then creates notification queue entries for dispatch via the
* notification-dispatch cron job.
*/
const { db } = require('./firebase');
const log = require('./log');
const { now } = require('./helpers');
const BATCH_LIMIT = 400;
/**
* Notify all roadmapUpdate subscribers about a roadmap change.
*
* @param {string} message - Description of what changed (shown to users)
*/
async function notifyRoadmapSubscribers(message) {
try {
// Server-side filter on the denormalised `roadmapUpdateOptedIn` flag
// (Phase 2A finding #2). The previous full-collection scan was a
// quota grenade — at 5K subs every roadmap edit cost 5K reads
// regardless of how few opted in. The flag is maintained on every
// PUT /subscriptions/me and backfilled by the
// `backfillRoadmapOptedIn` cron for legacy subs.
const snap = await db
.collection('subscriptions')
.where('roadmapUpdateOptedIn', '==', true)
.get();
if (snap.empty) return;
let batch = db.batch();
let batchCount = 0;
let total = 0;
for (const doc of snap.docs) {
const sub = doc.data();
const prefs = sub.channelPreferences?.roadmapUpdate;
// Defensive double-check: if the denormalised flag drifted from
// the actual prefs (race between PUT and notify), trust the prefs.
// Drift recovers on the next PUT — log and skip this tick.
if (!prefs) continue;
const hasAnyChannel = prefs.email || prefs.push || prefs.inApp || prefs.systemMessage;
if (!hasAnyChannel) continue;
const notifRef = db.collection('notificationQueue').doc();
batch.set(notifRef, {
type: 'roadmapUpdate',
uid: sub.uid || doc.id,
title: 'Roadmap Update',
body: message,
channels: {
email: !!prefs.email,
push: !!prefs.push,
inApp: !!prefs.inApp,
systemMessage: !!prefs.systemMessage,
},
email: sub.email || null,
pushToken: sub.pushToken || null,
status: 'queued',
createdAt: now(),
});
batchCount++;
total++;
Iif (batchCount === BATCH_LIMIT) {
await batch.commit();
batch = db.batch();
batchCount = 0;
}
}
if (batchCount > 0) {
await batch.commit();
}
if (total > 0) {
log.info('roadmap-notify', `Queued ${total} roadmap update notifications`);
}
} catch (err) {
log.error('roadmap-notify', 'Failed to notify roadmap subscribers', {
error: err.message,
});
}
}
module.exports = { notifyRoadmapSubscribers };
|