Caracterizacion del matching viejo de node-red en docs/legacy-matching.md (reglas: candidatas geo $near 1000km, filtro fino geolib dist/1000<=sub.distance, dedupe 500m, content i18n kmnasa/kmvecinal con 'км' cirilico verbatim, sealed via Iron). Modulos: - matcher/content.ts: i18n kmnasa/kmvecinal + km redondeado (Math.round dist/1000*10/10) - matcher/geo.ts: geolib.getDistance (identico al viejo) + isHit - matcher/seal.ts: @hapi/iron (Fe26.2) — COMPATIBLE con el iron@5 de la web (verificado: sello en servicio -> unseal en web round-trip OK) - matcher/matcher.ts: matchFire(fire, ctx) -> docs notifications identicos a node-red; dedupe 500m + 1 notif/usuario/fuego; solo audiencia web/mobile - deps: @hapi/iron, geolib 63 tests en verde (incluye interop CJS/ESM en dist). MongoDB 3.2 -> ingesta por polling (sin change streams), pendiente de cablear. |
||
|---|---|---|
| deploy | ||
| docs | ||
| scripts | ||
| src | ||
| test | ||
| .env.example | ||
| .gitignore | ||
| config.example.json | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
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, verdocs/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 plantillasnew-fireportadas 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;fullsolo con confirmación explícita del usuario. - Exclusión mutua por canal:
OWNED_CHANNELSaquí +notifDisabledChannelsen Meteor. El viejo y el nuevo jamás envían por el mismo canal a la vez. - Kill switch:
KILL_SWITCH=1detiene 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 + 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
mongodbv3.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 endocs/legacy-behavior.md§0.
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.