- saludMonday: cache modo_local_forzado con _modoLocalDb + setModoLocalDb - index.ts: eagerly carga modo_local_forzado al startup - convertirLeadEndpoint: crea LeadCaptura OPORTUNIDADES para ids loc_* - escaneoOportunidades: crea LeadCaptura APARTADO con usuarioId y oportunidadId - Mi día: esMio para apartados incluye oportunidadId Flujo verificado en modo local: 1. Lead loc_* creado y asignado al capturista 2. Cambio de estado funciona (CONTACTADO → CALIFICADO) 3. Agendo crea oportunidad local (loc_*) 4. Aparto crea apartado local vinculado 5. Cola sync: 9 operaciones pendientes para cuando Monday vuelva 6. Mi día muestra apartados correctamente
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
/**
|
|
* Salud de Monday (circuit breaker ligero).
|
|
*
|
|
* Estado en memoria: `mondayOk` arranca en true (optimista) y pasa a false tras
|
|
* FALLOS_PARA_CAER fallos GraphQL consecutivos reportados por monday.service.
|
|
* Mientras está caído, los callers deben saltarse Monday de inmediato (modo
|
|
* local) en lugar de pagar los reintentos por request; un cron sondea cada 60 s
|
|
* y al recuperarse dispara el sync worker.
|
|
*
|
|
* MODO_LOCAL_FORZADO=true (env var o DB) fuerza modo local aunque Monday
|
|
* responda (pruebas / toggle admin). La DB se cachea 30 s en configService.
|
|
*
|
|
* El ping se inyecta con `registrarPing()` (wiring en index.ts) para no
|
|
* importar monday.service aquí y evitar ciclos de imports.
|
|
*/
|
|
|
|
/** Fallos GraphQL consecutivos que tumban el flag */
|
|
export const FALLOS_PARA_CAER = 3
|
|
|
|
export type PingMonday = () => Promise<boolean>
|
|
|
|
export class SaludMondayService {
|
|
private _modoLocalDb = false
|
|
|
|
setModoLocalDb(v: boolean) { this._modoLocalDb = v }
|
|
private mondayOk = true
|
|
private fallosConsecutivos = 0
|
|
private pingFn: PingMonday | null = null
|
|
|
|
/** Registra el callback de ping (en index.ts: () => mondayService.ping()) */
|
|
registrarPing(fn: PingMonday): void {
|
|
this.pingFn = fn
|
|
}
|
|
|
|
/** Llamar tras cada llamada GraphQL exitosa */
|
|
registrarExito(): void {
|
|
this.fallosConsecutivos = 0
|
|
this.mondayOk = true
|
|
}
|
|
|
|
/** Llamar tras cada fallo definitivo de Monday (AppError 502 de graphql()) */
|
|
registrarFallo(): void {
|
|
this.fallosConsecutivos += 1
|
|
if (this.fallosConsecutivos >= FALLOS_PARA_CAER) this.mondayOk = false
|
|
}
|
|
|
|
/** Flag crudo (sin el override MODO_LOCAL_FORZADO); útil para el cron de sondeo */
|
|
ok(): boolean {
|
|
return this.mondayOk
|
|
}
|
|
|
|
/**
|
|
* true si los callers pueden hablar con Monday ahora mismo.
|
|
* Lee modo_local_forzado de la DB (via cache 30 s en configService) y de
|
|
* process.env como fallback. Síncrono: la DB se precarga al iniciar y se
|
|
* invalida al cambiar configs.
|
|
*/
|
|
estaDisponible(): boolean {
|
|
// Lazy import para no crear ciclos en tests (configService importa prisma)
|
|
let forzadoDb = false
|
|
try {
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const cs = require('./config.service.js') as typeof import('./config.service.js')
|
|
const syncVal = cs.obtenerConfigSync('modo_local_forzado')
|
|
if (syncVal !== undefined) {
|
|
forzadoDb = syncVal === 'true'
|
|
} else {
|
|
// Cache no cargado aún: intentar async (fire-and-forget para la próxima vez)
|
|
cs.obtenerConfig('modo_local_forzado').then((v) => {
|
|
if (v === 'true') this._modoLocalDb = true
|
|
}).catch(() => {})
|
|
}
|
|
} catch {
|
|
// configService no disponible (tests aislados, DB no inicializada)
|
|
}
|
|
const forzadoEnv = process.env.MODO_LOCAL_FORZADO === 'true'
|
|
return this.mondayOk && !forzadoDb && !this._modoLocalDb && !forzadoEnv
|
|
}
|
|
|
|
/**
|
|
* Ping a Monday. Si responde, recupera el flag y devuelve true (el caller
|
|
* decide si dispara el sync). Sin ping registrado devuelve el flag actual.
|
|
*/
|
|
async sondear(): Promise<boolean> {
|
|
if (this.pingFn === null) return this.mondayOk
|
|
const vivo = await this.pingFn()
|
|
if (vivo) this.registrarExito()
|
|
return vivo
|
|
}
|
|
|
|
/** Reset completo (tests) */
|
|
restablecer(): void {
|
|
this.mondayOk = true
|
|
this.fallosConsecutivos = 0
|
|
this.pingFn = null
|
|
}
|
|
}
|
|
|
|
/** Singleton compartido por monday.service, controllers y crons */
|
|
export const saludMonday = new SaludMondayService()
|