# 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](../plan-modernizacion/fase-1a-notif-workers.md): 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`](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-run` → `shadow` → `canary` → `full`. 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`](docs/rollout.md). ## Desarrollo ```bash npm ci npm test # 47 tests (vitest + fake-repo en memoria, sin infra externa) npm run typecheck npm run build # -> dist/ (+ plantillas) ``` > **Nota MongoDB**: producción corre **MongoDB 3.2** (EOL), así que el servicio > usa el driver `mongodb` **v3.7** (el moderno no conecta a 3.2) y no hay change > streams (fase 1b usará polling). Los tests van contra un fake en memoria > porque no hay mongod tan antiguo ejecutable en hosts modernos. Detalle en > [`docs/legacy-behavior.md`](docs/legacy-behavior.md) §0. ## Ejecución Config por env (ver [`.env.example`](.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. ```bash 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 - [x] Workers FCM v1 + email, poller, idempotencia, salvaguardas, tests. - [x] 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.