Files
InfiniteAPI/src/Utils/health-status.ts
T
Renato Alcara a32be8d594 Feat/replace circuit breaker with bounded retry (#393)
Feat/replace circuit breaker with bounded retry (#393)
2026-04-27 01:06:25 -03:00

177 lines
4.2 KiB
TypeScript

/**
* Health Status Utilities
*
* Provides health check and status information for monitoring and k8s probes.
*
* @module Utils/health-status
*/
import { getVersionCacheStatus } from './version-cache.js'
/**
* Circuit breaker health information (kept for k8s probe schema stability —
* always reports an empty list now that circuit breakers were removed in
* favour of bounded-retry).
*/
export interface CircuitBreakerHealth {
name: string
state: 'closed' | 'open' | 'half-open'
failures: number
successes: number
totalCalls: number
}
/**
* Cache health information
*/
export interface CacheHealth {
versionCache: {
hasCache: boolean
isExpired: boolean
ageMs: number | null
source: string | null
}
}
/**
* Overall health status
*/
export interface HealthStatus {
status: 'healthy' | 'degraded' | 'unhealthy'
timestamp: number
uptime: number
version: string
circuitBreakers: CircuitBreakerHealth[]
cache: CacheHealth
checks: {
name: string
status: 'pass' | 'warn' | 'fail'
message?: string
}[]
}
/**
* Get the current health status of the Baileys instance.
*
* Useful for:
* - Kubernetes liveness/readiness probes
* - Load balancer health checks
* - Monitoring dashboards
*
* @returns HealthStatus object with detailed status information
*
* @example
* ```typescript
* import { getHealthStatus } from '@whiskeysockets/baileys'
*
* // Simple health check endpoint
* app.get('/health', (req, res) => {
* const health = getHealthStatus()
* const statusCode = health.status === 'healthy' ? 200 :
* health.status === 'degraded' ? 200 : 503
* res.status(statusCode).json(health)
* })
*
* // Kubernetes probe
* app.get('/healthz', (req, res) => {
* const health = getHealthStatus()
* res.status(health.status !== 'unhealthy' ? 200 : 503).send(health.status)
* })
* ```
*/
export function getHealthStatus(): HealthStatus {
const checks: HealthStatus['checks'] = []
let overallStatus: HealthStatus['status'] = 'healthy'
// 1. Check version cache
const versionCacheStatus = getVersionCacheStatus()
if (!versionCacheStatus.hasCache) {
checks.push({
name: 'version_cache',
status: 'warn',
message: 'No version cache available'
})
if (overallStatus === 'healthy') overallStatus = 'degraded'
} else if (versionCacheStatus.isExpired) {
checks.push({
name: 'version_cache',
status: 'warn',
message: 'Version cache is expired'
})
if (overallStatus === 'healthy') overallStatus = 'degraded'
} else {
checks.push({
name: 'version_cache',
status: 'pass'
})
}
// 2. Circuit breakers were removed in favour of bounded-retry.
// Keep the field for backward-compat with k8s probe schema, always empty.
const circuitBreakers: CircuitBreakerHealth[] = []
checks.push({ name: 'circuit_breakers', status: 'pass' })
// 3. Check memory usage (warn if > 90%)
const memUsage = process.memoryUsage()
const heapUsedPercent = (memUsage.heapUsed / memUsage.heapTotal) * 100
if (heapUsedPercent > 90) {
checks.push({
name: 'memory',
status: 'warn',
message: `Heap usage is ${heapUsedPercent.toFixed(1)}%`
})
if (overallStatus === 'healthy') overallStatus = 'degraded'
} else {
checks.push({
name: 'memory',
status: 'pass'
})
}
return {
status: overallStatus,
timestamp: Date.now(),
uptime: process.uptime(),
version: process.env.npm_package_version || 'unknown',
circuitBreakers,
cache: {
versionCache: {
hasCache: versionCacheStatus.hasCache,
isExpired: versionCacheStatus.isExpired,
ageMs: versionCacheStatus.age,
source: versionCacheStatus.source
}
},
checks
}
}
/**
* Simple health check - returns true if system is healthy or degraded.
* Use this for basic liveness probes.
*
* @returns true if healthy or degraded, false if unhealthy
*/
export function isHealthy(): boolean {
const status = getHealthStatus()
return status.status !== 'unhealthy'
}
/**
* Get a simple status string for minimal health endpoints.
*
* @returns 'ok', 'degraded', or 'error'
*/
export function getSimpleHealthStatus(): 'ok' | 'degraded' | 'error' {
const status = getHealthStatus()
switch (status.status) {
case 'healthy':
return 'ok'
case 'degraded':
return 'degraded'
case 'unhealthy':
return 'error'
}
}