Fase 1b: cableado del matcher (ingesta polling, runner shadow/full, comparador)

- config: bloque matcher (MATCHER_MODE off/shadow/full, IRON_PASSWORD, audiencia,
  poll, batch) + validacion
- db: colecciones subscriptions/activefires/notifications_shadow/matcher_state +
  ensureMatcherIndexes (2dsphere en notifications_shadow para el dedupe 500m)
- matcher/context: MatchContext sobre Mongo real (candidatas geo $near 1000km +
  audiencia; dedupe geo $near 500m; lang del owner)
- matcher/ingest: polling de activefires por createdAt>checkpoint (Mongo 3.2 sin
  change streams), checkpoint persistido en matcher_state
- matcher/runner: loop shadow(->notifications_shadow)/full(->notifications),
  idempotente por dedupe+checkpoint
- matcher/compare: comparador de shadow (shadowOnly=spam, realOnly=faltantes,
  contentMismatches) — el gate antes del cutover
- index: arranca el matcher; fake-repo ampliado (upsert, sort, $gt/$lt)
- docs/env/README actualizados

73 tests en verde, typecheck y build OK.
This commit is contained in:
vjrj 2026-07-13 18:07:06 +02:00
parent b3aeb6108f
commit a1ed206985
13 changed files with 531 additions and 19 deletions

View file

@ -5,10 +5,10 @@ Microservicio de **envío de notificaciones** para *Todos contra el Fuego*
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.
Fases **1a** (workers de envío) y **1b** (matching geoespacial) del
[plan de modernización](../plan-modernizacion/README.md). La 1a consume la
colección `notifications`; la 1b la **genera** (fuego → subs 2dsphere),
sustituyendo el matching O(fuegos×subs) de node-red. Telegram (1c) viene después.
## Qué hace
@ -68,8 +68,12 @@ 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.
- [x] **1a**: workers FCM v1 + email, poller, idempotencia, salvaguardas, tests.
- [x] **1a**: flag de cutover en Meteor (`notifDisabledChannels`).
- [x] **1a**: canary FCM verificado (push real entregada a dispositivos propios).
- [x] **1b**: matcher geoespacial (content i18n, geolib, `sealed` vía Iron —
compatible con la web —, dedupe 500 m), ingesta por polling, modos
`shadow`/`full`, comparador de shadow. Tests en verde.
- [ ] **1a full**: envío masivo FCM (~8176 users) — con el usuario, vigilando.
- [ ] **1b**: rollout `shadow` ≥3 días → comparar → cutover node-red → `full`.
- [ ] **1c**: envío Telegram.