Todos contra el fuego — microservicio de notificaciones (FCM, email, Telegram)
Find a file
vjrj d20e168c90 Fase 1a: microservicio tcef-notifications (workers FCM v1 + email)
Sustituye el envío de push por la API legacy de GCM (muerta desde jun-2024) y
saca el envío del observer de Meteor a una cola BullMQ controlada.

- poller: consume 'notifications' pendientes (campos correctos; el cron viejo
  tenía un typo nofitied/emailNofitied que nunca casaba)
- fcm-worker: firebase-admin (FCM HTTP v1), payload compatible con la app
  Flutter (FLUTTER_NOTIFICATION_CLICK, collapseKey=_id, data); purga tokens
  muertos sin reintentar
- email-worker: nodemailer + plantillas new-fire portadas verbatim
- idempotencia persistente: notification_sends, indice unico (notificationId,
  channel) -> sobrevive reinicios y replays
- salvaguardas anti-spam: modos dry-run/shadow/canary/full, exclusion mutua por
  canal (OWNED_CHANNELS + notifDisabledChannels en Meteor), kill switch,
  limites por usuario/dia y circuit breaker por batch
- docs/legacy-behavior.md (caracterizacion) + docs/rollout.md
- 47 tests (vitest + mongodb-memory-server), typecheck y build OK
- deploy: systemd + pm2 (Node 22 standalone)

Falta el service account de Firebase para envio real de push (pedir al usuario).
2026-07-13 00:10:29 +02:00
deploy Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
docs Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
scripts Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
src Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
test Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
.env.example Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
.gitignore Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
config.example.json Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
package-lock.json Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
package.json Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
README.md Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
tsconfig.json Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00
vitest.config.ts Fase 1a: microservicio tcef-notifications (workers FCM v1 + email) 2026-07-13 00:10:29 +02:00

tcef-notifications

Microservicio de envío de notificaciones para Todos contra el Fuego (alertas tempranas de incendios NASA FIRMS). Sustituye el envío de push que hacía la web Meteor con la API legacy de GCM (apagada por Google en jun-2024) y saca el envío del observer de Meteor a una cola controlada.

Fase 1a del plan de modernización: solo workers de envío. Consume la colección notifications que hoy genera node-red; el matching geoespacial (fase 1b) y Telegram (fase 1c) vienen después.

Qué hace

  • poller: cada N s busca docs pendientes en notifications (con los nombres de campo correctos — el cron viejo tenía un typo, ver docs/legacy-behavior.md §2) y encola un job por canal.
  • fcm-worker: envía push con firebase-admin (FCM HTTP v1), preservando el payload que espera la app Flutter (FLUTTER_NOTIFICATION_CLICK, collapseKey = _id, data). Purga tokens muertos sin reintentar.
  • email-worker: nodemailer + las plantillas new-fire portadas verbatim.
  • idempotencia persistente: colección notification_sends, índice único (notificationId, channel) — sobrevive reinicios y replays de cola.

Salvaguardas anti-spam

El riesgo nº1 es spamear a los usuarios. Por eso:

  • Modos graduales MODE: dry-runshadowcanaryfull. Nunca se salta un modo; full solo con confirmación explícita del usuario.
  • Exclusión mutua por canal: OWNED_CHANNELS aquí + notifDisabledChannels en Meteor. El viejo y el nuevo jamás envían por el mismo canal a la vez.
  • Kill switch: KILL_SWITCH=1 detiene todo envío al instante.
  • Límites: máx envíos/usuario/día y circuit breaker por batch (para y alerta).

Detalle del despliegue y la secuencia de cutover: docs/rollout.md.

Desarrollo

npm ci
npm test            # 47 tests (vitest + mongodb-memory-server, sin infra externa)
npm run typecheck
npm run build       # -> dist/ (+ plantillas)

Ejecución

Config por env (ver .env.example) o config.json (ver config.example.json); env gana. Secretos (Mongo, MAIL_URL, service account de Firebase) desde el repo privado tcef-private-config — nunca en este repo.

MODE=dry-run OWNED_CHANNELS=fcm MONGO_URL=... npm start

Despliegue en shiva: deploy/tcef-notifications.service (systemd) o deploy/pm2-tcef-notifications.json. Node 22 standalone (no el Node 8 del sistema) + Redis local. En fase 3 pasa a Docker Compose.

Estado

  • Workers FCM v1 + email, poller, idempotencia, salvaguardas, tests.
  • Flag de cutover en Meteor (notifDisabledChannels).
  • Falta: service account de Firebase (pedir al usuario) para poder enviar push de verdad.
  • Rollout en shiva (dry-run → shadow → canary → full), con el usuario.