services/api/src/scripts/purgeHistoricalNotificationOutbox.js

Overview

Este script realiza un saneamiento histórico de NotificationOutbox para cuentas que ya estaban en accountStatus="purged". Cubre el hueco dejado por corridas anteriores de purga, cuando notification_outbox todavía no se limpiaba para esas cuentas.

Su responsabilidad es limitada y precisa: solo elimina o cuenta filas de NotificationOutbox. No modifica User, Listing ni Lead, porque esos datos ya fueron saneados durante la purga original de cada cuenta.

[!IMPORTANT] Este script existe para corregir historial acumulado. No forma parte de un flujo automático de arranque. Requiere ejecución explícita como CLI.

How it works

flowchart TD
    Start([Inicio CLI o importación]) --> Load[runPurgeHistoricalNotificationOutbox]
    Load --> QueryUsers[Buscar User con accountStatus purged]
    QueryUsers --> Batch[Procesar lotes con skip y limit]
    Batch --> Mode{dryRun}
    Mode -->|Sí| CountOutbox[Contar NotificationOutbox por recipientUserId]
    Mode -->|No| DeleteOutbox[Eliminar NotificationOutbox por recipientUserId]
    CountOutbox --> Summary[Acumular matchedUsers deletedEntries errors]
    DeleteOutbox --> Summary
    Summary --> NextBatch{Hay más usuarios}
    NextBatch -->|Sí| Batch
    NextBatch -->|No| Return[Devolver resumen JSON]
    Return --> MainCLI{Es módulo principal}
    MainCLI -->|Sí| Connect[Conectar a MongoDB]
    Connect --> Execute[Ejecutar job con flags CLI]
    Execute --> Print[Imprimir resumen JSON]
    Print --> Disconnect[Desconectar MongoDB]

El flujo principal empieza con una consulta fija: User.find({ accountStatus: 'purged' }). Después, el script avanza por lotes usando skip y limit, y procesa cada lote contra NotificationOutbox con el recipientUserId de esos usuarios.

En dry-run, el script solo cuenta documentos. En apply, ejecuta deleteMany. Si ocurre un error en un lote, registra un error seguro y continúa con el siguiente lote.

[!NOTE] La paginación con skip es segura aquí porque accountStatus="purged" es estable. Un usuario purgado no vuelve a salir del filtro entre corridas. fileciteturn0file2turn0file4

Key components

{
    "title": "runPurgeHistoricalNotificationOutbox",
    "content": "Funci\u00f3n p\u00fablica que ejecuta el saneamiento hist\u00f3rico de NotificationOutbox. Soporta dry-run, apply y tama\u00f1o de lote configurable, y devuelve un resumen de la ejecuci\u00f3n.",
    "type": "function",
    "filePath": "services/api/src/scripts/purgeHistoricalNotificationOutbox.js",
    "badges": [
        "public"
    ]
}
{
    "title": "CLI principal del script",
    "content": "Bloque de arranque que carga dotenv, resuelve MONGO_URI, interpreta --apply, --dry-run y --batch-size, conecta a MongoDB, ejecuta el job y muestra el resumen JSON.",
    "type": "function",
    "filePath": "services/api/src/scripts/purgeHistoricalNotificationOutbox.js",
    "badges": [
        "public"
    ]
}

Responsabilidades observadas

  • runPurgeHistoricalNotificationOutbox ejecuta la lógica de negocio del saneamiento.
  • El bloque isMainModule convierte el archivo en herramienta de línea de comandos.
  • dotenv.config() carga variables desde ../../.env y luego desde el entorno actual.
  • mongoose.connect() y mongoose.disconnect() enmarcan la ejecución manual.

Data & fields

El script trabaja con un objeto de opciones de entrada y con un resumen de salida. También usa campos concretos de User y NotificationOutbox durante la consulta y el borrado.

[
    {
        "name": "dryRun",
        "type": "boolean",
        "description": "Si es true, solo cuenta entradas de NotificationOutbox. Si es false, elimina las filas."
    },
    {
        "name": "batchSize",
        "type": "number",
        "description": "Cantidad máxima de usuarios procesados por lote. El valor por defecto es 50."
    },
    {
        "name": "mode",
        "type": "string",
        "required": true,
        "description": "Estado de ejecución devuelto por el resumen. Puede ser dry-run o apply."
    },
    {
        "name": "matchedUsers",
        "type": "number",
        "required": true,
        "description": "Total de usuarios purgados encontrados por la consulta."
    },
    {
        "name": "deletedEntries",
        "type": "number",
        "required": true,
        "description": "Total de filas contadas o eliminadas en NotificationOutbox."
    },
    {
        "name": "errors",
        "type": "number",
        "required": true,
        "description": "Cantidad de lotes que produjeron error."
    },
    {
        "name": "errorUserIds",
        "type": "string[]",
        "required": true,
        "description": "Lista de ids de usuario afectados por errores. Se serializan como texto."
    }
]

Campos consultados en MongoDB

  • User.accountStatus [badge:required] string — El filtro del job selecciona solo usuarios con valor purged.
  • User._id [badge:required] ObjectId — El script lo proyecta con select('_id') y lo usa para construir el filtro de outbox.
  • NotificationOutbox.recipientUserId [badge:required] ObjectId o valor comparable — Se usa con $in para localizar filas de outbox asociadas a cada lote.
[
    {
        "name": "mode",
        "type": "string",
        "required": true,
        "description": "Indica si la ejecución fue dry-run o apply."
    },
    {
        "name": "batchSize",
        "type": "number",
        "required": true,
        "description": "Tamaño efectivo del lote utilizado por el job."
    },
    {
        "name": "matchedUsers",
        "type": "number",
        "required": true,
        "description": "Usuarios purgados encontrados durante la ejecución."
    },
    {
        "name": "deletedEntries",
        "type": "number",
        "required": true,
        "description": "Entradas de outbox afectadas según el modo."
    },
    {
        "name": "errors",
        "type": "number",
        "required": true,
        "description": "Número de lotes con fallo."
    },
    {
        "name": "errorUserIds",
        "type": "string[]",
        "required": true,
        "description": "Identificadores de usuario asociados a errores."
    }
]

Behaviors

  • runPurgeHistoricalNotificationOutbox — Ejecuta la búsqueda por lotes de usuarios purgados y limpia sus filas de NotificationOutbox. Devuelve un objeto resumen con conteos y errores.
  • User.find(query).select('_id').skip(skip).limit(batchSize).lean() — Obtiene solo ids de usuarios purgados.
  • NotificationOutbox.countDocuments(...) — Cuenta entradas coincidentes cuando dryRun está activo.
  • NotificationOutbox.deleteMany(...) — Borra filas cuando dryRun está desactivado.
  • console.error('[purgeHistoricalNotificationOutbox]', ...) — Registra solo un código seguro si un lote falla.
  • dotenv.config() — Carga configuración local y de entorno antes de conectar a MongoDB.
  • mongoose.connect() / mongoose.disconnect() — Abren y cierran la conexión al ejecutar el archivo como programa.

Lógica de ejecución

  1. Define query = { accountStatus: 'purged' }.
  2. Lee usuarios con _id solamente.
  3. Agrupa los ids por lote.
  4. En dry-run, cuenta filas de outbox.
  5. En apply, elimina filas de outbox.
  6. Acumula totales y errores.
  7. Devuelve un resumen final en JSON.

Workflows

1. Cargar configuración | El script resuelve `__dirname`, carga `../../.env` y después el entorno actual.
2. Resolver modo de ejecución | `--apply` activa borrado; `--dry-run` prevalece como simulación. Sin `--apply`, el modo es dry-run.
3. Resolver batch size | `--batch-size=<n>` se interpreta como entero positivo. Si es inválido, se usa 50.
4. Conectar a MongoDB | El CLI abre conexión con `mongoose.connect(MONGO_URI)`.
5. Ejecutar el job | `runPurgeHistoricalNotificationOutbox` procesa usuarios purgados por lotes.
6. Mostrar resultado | El resumen se imprime con `JSON.stringify(summary, null, 2)`.
7. Cerrar conexión | `mongoose.disconnect()` se ejecuta en `finally`.

Uso CLI observado

El archivo puede ejecutarse directamente como script. La lógica de arranque comprueba si el módulo actual es el principal y, en ese caso, ejecuta el flujo completo.

  • MONGO_URI usa process.env.MONGODB_URI o el valor por defecto mongodb://127.0.0.1:27017/plano_backoffice.
  • DRY_RUN queda activo por defecto si no se pasa --apply.
  • BATCH_SIZE usa 50 cuando no hay valor válido.

Gotchas & edge cases

[!WARNING] El script solo limpia NotificationOutbox. No toca User, Listing ni Lead. Esa separación es intencional y está documentada en el código. fileciteturn0file2turn0file4

[!WARNING] Los errores por lote no detienen toda la ejecución. El script registra el fallo, agrega los ids afectados y continúa.

[!CAUTION] errorUserIds guarda ids convertidos a texto. El log de error evita PII y solo imprime code.

[!NOTE] La consulta es estable entre corridas. Por eso, skip funciona aquí sin el riesgo que existe en otros jobs de cierre.

Este archivo se relaciona con el ciclo de cierre de cuenta y con los jobs de lifecycle ya existentes. El código lo describe como complemento histórico de purgeExpiredAccounts.js, que ya limpia notification_outbox para purgas nuevas. fileciteturn0file2turn0file4

También encaja con la política general de retención de notification_outbox: el histórico purgado se corrige con este script, mientras que las cuentas no purgadas dependen del TTL nativo de MongoDB sobre createdAt. fileciteturn0file4turn0file7

Relación directa con otros componentes verificados

  • services/api/src/scripts/purgeExpiredAccounts.js — Referido por el propio comentario como el job que ya limpia el outbox para purgas nuevas.
  • services/api/src/models/User.js — Fuente de la consulta de usuarios purgados.
  • services/api/src/models/NotificationOutbox.js — Modelo afectado por conteo o borrado.
  • services/api/src/scripts/anonymizeExpiredAccounts.js — Mencionado solo como contraste de paginación con skip.

[!IMPORTANT] El test dedicado confirma dos modos de trabajo: dry-run reporta sin borrar, y apply elimina solo las filas de usuarios purgados.