services/api/src/scripts/sendPurgeWarnings.js
Overview
Este archivo define un script de mantenimiento para el ciclo de cierre de cuenta. Su función es localizar cuentas ya anonimizadas que tienen la purga final programada dentro de los próximos 7 días, y preparar o emitir el aviso ACCOUNT_PURGE_WARNING para esas cuentas
El script está pensado para ejecutarse como módulo importable o como comando directo de Node. Exporta runSendPurgeWarnings para pruebas y reutilización, y solo abre conexión a MongoDB cuando el archivo se ejecuta como programa principal
[!NOTE] La idempotencia no se guarda en
User. El script la resuelve consultandoNotificationOutboxpara detectar avisos previosACCOUNT_PURGE_WARNINGdel mismo usuario
How it works
flowchart TD
A[CLI o importacion directa] --> B[runSendPurgeWarnings]
B --> C[Calcula ventana de 7 dias]
C --> D[Busca usuarios con accountStatus anonymized_pending_purge]
D --> E[Consulta NotificationOutbox]
E --> F[Detecta usuarios ya avisados]
F --> G[Procesa cada usuario]
G --> H{dryRun}
H -->|true| I[Solo cuenta el resultado]
H -->|false| J[buildAccountExportUrl]
J --> K[emitNotificationEvent]
K --> L[ACCOUNT_PURGE_WARNING]
G --> M[Acumula errores por usuario]
B --> N[Devuelve resumen JSON]
La ejecución empieza con una ventana temporal fija de 7 días. Luego busca usuarios cuyo accountStatus es anonymized_pending_purge y cuya purgeScheduledAt cae entre now y now + 7 días
Antes de emitir nada, agrupa los usuarios por lote. Para cada lote, consulta NotificationOutbox y evita reenviar avisos a usuarios que ya tienen un registro ACCOUNT_PURGE_WARNING
Cuando dryRun es false, genera una URL de exportación de cuenta con buildAccountExportUrl, y luego emite el evento ACCOUNT_PURGE_WARNING con emitNotificationEvent
Key components
{
"title": "runSendPurgeWarnings",
"content": "Funci\u00f3n p\u00fablica [badge:public] que calcula qu\u00e9 cuentas deben recibir el aviso de purga y devuelve un resumen de la corrida. Soporta modo dry-run y modo apply.",
"type": "function",
"filePath": "services/api/src/scripts/sendPurgeWarnings.js",
"badges": [
"public"
]
}
{
"title": "NotificationOutbox",
"content": "Colecci\u00f3n consultada para detectar si un usuario ya recibi\u00f3 un evento ACCOUNT_PURGE_WARNING. El script la usa como fuente de idempotencia.",
"type": "class",
"filePath": "services/api/src/models/NotificationOutbox.js",
"badges": [
"public"
]
}
{
"title": "User",
"content": "Modelo consultado para encontrar cuentas con estado anonymized_pending_purge y una fecha de purga dentro de la ventana de 7 d\u00edas.",
"type": "class",
"filePath": "services/api/src/models/User.js",
"badges": [
"public"
]
}
{
"title": "emitNotificationEvent",
"content": "Funci\u00f3n del m\u00f3dulo de notificaciones que publica el evento ACCOUNT_PURGE_WARNING cuando el script corre en modo apply.",
"type": "function",
"filePath": "services/api/src/modules/notifications/notification.events.js",
"badges": [
"public"
]
}
{
"title": "buildAccountExportUrl",
"content": "Funci\u00f3n usada para construir la URL que se incluye en el payload del aviso de purga.",
"type": "function",
"filePath": "services/api/src/modules/auth/account-export.service.js",
"badges": [
"public"
]
}
Data & fields
Opciones de ejecución de runSendPurgeWarnings
[
{
"name": "dryRun",
"type": "boolean",
"description": "Si es true, calcula resultados sin emitir eventos. Por defecto es true."
},
{
"name": "batchSize",
"type": "number",
"description": "Cantidad máxima de usuarios por lote. Por defecto es 50."
},
{
"name": "now",
"type": "Date",
"description": "Momento de referencia para calcular la ventana de 7 días. Por defecto es la fecha actual."
}
]
Campos usados en la consulta de usuarios
[
{
"name": "accountStatus",
"type": "string",
"required": true,
"description": "Debe ser anonymized_pending_purge para que el usuario entre al proceso."
},
{
"name": "purgeScheduledAt",
"type": "Date",
"required": true,
"description": "Debe ser mayor que now y menor o igual que now + 7 días."
},
{
"name": "_id",
"type": "ObjectId",
"required": true,
"description": "Identificador del usuario, usado para correlacionar el outbox y el evento."
}
]
Campos consultados en NotificationOutbox
[
{
"name": "eventType",
"type": "string",
"required": true,
"description": "Se filtra por ACCOUNT_PURGE_WARNING."
},
{
"name": "recipientUserId",
"type": "ObjectId",
"required": true,
"description": "Se usa para saber si el usuario ya fue avisado."
}
]
Estructura del payload emitido
[
{
"name": "recipientUserId",
"type": "ObjectId",
"required": true,
"description": "Usuario destinatario del aviso."
},
{
"name": "recipientChannel",
"type": "string",
"required": true,
"description": "Canal usado por el evento. El script fija primary_email."
},
{
"name": "payload",
"type": "object",
"required": true,
"description": "Contenido específico del aviso.",
"children": [
{
"name": "purgeScheduledAt",
"type": "Date",
"required": true,
"description": "Fecha programada para la purga definitiva."
},
{
"name": "exportUrl",
"type": "string",
"required": true,
"description": "URL construida para exportación de la cuenta."
}
]
}
]
Resumen devuelto por la función
[
{
"name": "mode",
"type": "string",
"required": true,
"description": "dry-run o apply, según el modo de ejecución."
},
{
"name": "batchSize",
"type": "number",
"required": true,
"description": "Tamaño de lote usado en la corrida."
},
{
"name": "matched",
"type": "number",
"required": true,
"description": "Total de usuarios encontrados por la consulta."
},
{
"name": "alreadyWarned",
"type": "number",
"required": true,
"description": "Usuarios omitidos porque ya tenían un outbox entry de aviso."
},
{
"name": "warned",
"type": "number",
"required": true,
"description": "Usuarios efectivamente procesados. En dry-run siempre retorna 0."
},
{
"name": "errors",
"type": "number",
"required": true,
"description": "Cantidad de usuarios con errores durante el procesamiento."
},
{
"name": "errorUserIds",
"type": "array",
"required": true,
"description": "Lista de IDs de usuarios que fallaron."
}
]
Behaviors
runSendPurgeWarnings[badge:public] — Recorre usuarios elegibles, evita duplicados conNotificationOutbox, y devuelve un resumen de la corrida.buildAccountExportUrl[badge:public] — Construye la URL de exportación usada dentro del payload del aviso.emitNotificationEvent[badge:public] — Publica el eventoACCOUNT_PURGE_WARNINGcuando el script no está en dry-run.User.find[badge:public] — Busca usuarios conaccountStatus = anonymized_pending_purgeypurgeScheduledAtdentro de la ventana.NotificationOutbox.find[badge:public] — Verifica si ya existe un aviso previo para cada usuario.console.error[badge:public] — Registra errores por usuario sin detener toda la corrida.
[!IMPORTANT] El script no modifica
accountStatusnipurgeScheduledAt. La lista elegible no se reduce sola entre corridas; la exclusión depende del outbox
Workflows
1. Cargar entorno | Si el archivo se ejecuta como programa principal, carga variables desde `../../.env` y luego desde el entorno actual.
2. Conectar a MongoDB | Usa `MONGODB_URI` o el valor por defecto `mongodb://127.0.0.1:27017/plano_backoffice`.
3. Resolver modo | `--apply` activa envío real. Sin ese flag, el script queda en `dry-run`.
4. Calcular lotes | Lee `--batch-size=<n>` y usa 50 si no recibe un valor válido.
5. Ejecutar procesamiento | Llama a `runSendPurgeWarnings` con `dryRun` y `batchSize`.
6. Imprimir resultado | Serializa el resumen final como JSON legible.
7. Desconectar | Cierra la conexión con MongoDB en un bloque `finally`.
Detailed runtime flow
El archivo detecta si fue invocado directamente comparando process.argv[1] con import.meta.url. Si coincide, prepara el entorno, conecta a MongoDB y ejecuta el script con parámetros derivados de la línea de comandos
El procesamiento interno usa un for (;;). Cada iteración carga un lote con User.find(query).select('_id purgeScheduledAt').skip(skip).limit(batchSize).lean(). Si el lote queda vacío, el ciclo termina
Luego, el script busca avisos previos en NotificationOutbox. Si un usuario ya tiene ACCOUNT_PURGE_WARNING, el script incrementa alreadyWarned y no vuelve a emitir el evento
Si no existe un aviso previo y el modo no es dry-run, el script construye una URL con buildAccountExportUrl y emite el evento con emitNotificationEvent(EVENT_TYPES.ACCOUNT_PURGE_WARNING, ...)
Gotchas & edge cases
[!WARNING] En
dry-run, el script no crea eventos ni registros de outbox. El resumen puede mostrar coincidencias, perowarnedretorna0
[!WARNING] La idempotencia depende de
NotificationOutbox. Si ya existe un registroACCOUNT_PURGE_WARNING, el usuario se cuenta enalreadyWarnedy se omite
[!CAUTION] El procesamiento es por lotes y usa
skip. El script asume que el conjunto objetivo no cambia durante la corrida, porque la consulta no modifica los campos filtrados
[!NOTE] Los errores se aíslan por usuario. Un fallo en un registro no detiene la ejecución completa; solo incrementa
errorsy agrega el ID al arregloerrorUserIds
[!IMPORTANT] El valor por defecto del modo es
dry-run. Para enviar avisos reales, el comando debe incluir--apply
Related
Este script se integra con el ciclo de lifecycle de cuenta y con el sistema de notificaciones. El mismo patrón de script autónomo aparece en otros jobs del repositorio, y la salida esperada también es un resumen JSON fileciteturn0file9turn0file15
El evento ACCOUNT_PURGE_WARNING forma parte del sistema de notificaciones de lifecycle de cuenta, junto con otros eventos del ciclo como ACCOUNT_ANONYMIZED y ACCOUNT_PURGED fileciteturn0file15turn0file16
También reutiliza la lógica de exportación de cuenta definida en services/api/src/modules/auth/account-export.service.js, que ya aparece como dependencia de la capa de autenticación y retención de cuenta fileciteturn0file9turn0file10
- Filtra usuarios
anonymized_pending_purge. - Limita la selección a una ventana de 7 días.
- Evita duplicados con
NotificationOutbox. - Emite
ACCOUNT_PURGE_WARNINGsolo en modoapply. - Devuelve un resumen JSON con contadores y errores.