feat(api): webhooks Monday en tiempo real + alertas de inventario cableadas

- Webhook Oportunidades: cambio de etapa dispara escaneo (Aparto→Apartado,
  Ganado→Venta) además del round-robin de leads por create_item
- Acepta los nombres reales de los eventos de Monday: create_pulse/pulseId
  y update_column_value (verificados en vivo)
- Token interno (X-Internal-Token) para que los syncs locales llamen
  POST /cotizaciones/alertas-inventario sin JWT de usuario
- sync-estatus-maestro.py notifica los cambios aplicados al endpoint de
  alertas (actividad al vendedor con cotizaciones recientes)
- Fix P1008 SQLite: escaneos del poll cron secuenciales +
  connection_limit=1 en DATABASE_URL
- Webhooks registrados en Monday sobre https://frasa.consultoria-as.com
  (Leads create_item, Oportunidades change_column_value); crons quedan
  como red de seguridad
- Docs: sección 0 en AUTOMATIZACIONES-MONDAY.md con la matriz de eventos
  y los quirks de Monday
This commit is contained in:
2026-08-28 16:46:08 +00:00
parent 30c682e4a0
commit 04941e9f80
10 changed files with 313 additions and 29 deletions

View File

@@ -48,6 +48,20 @@ export function authMiddleware(req: Request, res: Response, next: NextFunction):
return
}
// Token interno script→API: solo abre el endpoint de alertas de inventario.
// Los syncs corren en este mismo host y no tienen JWT de usuario. La guarda
// exige token no vacío Y header idéntico (un header ausente nunca matchea).
if (
req.method === 'POST' &&
req.path === '/cotizaciones/alertas-inventario' &&
typeof config.internalSyncToken === 'string' &&
config.internalSyncToken.length > 0 &&
req.headers['x-internal-token'] === config.internalSyncToken
) {
next()
return
}
const header = req.headers.authorization
const token = header?.startsWith('Bearer ') ? header.slice('Bearer '.length).trim() : null
if (!token) {