#!/bin/bash # GoChat Health Check Script # Reference: Chatwoot health_check endpoint pattern # Checks: HTTP server, database, Redis, worker queues, WebSocket hub # # Usage: # ./scripts/health_check.sh [--full] [--json] [--timeout SECONDS] # # Exit codes: # 0 = healthy # 1 = degraded (some checks failed but core is OK) # 2 = unhealthy (critical checks failed) set -euo pipefail # Configuration GOCHAT_HOST="${GOCHAT_HOST:-localhost}" GOCHAT_PORT="${GOCHAT_PORT:-3000}" GOCHAT_METRICS_PORT="${GOCHAT_METRICS_PORT:-9090}" TIMEOUT="${TIMEOUT:-5}" FULL_CHECK=false JSON_OUTPUT=false # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in --full) FULL_CHECK=true; shift ;; --json) JSON_OUTPUT=true; shift ;; --timeout) TIMEOUT="$2"; shift 2 ;; --help) echo "Usage: $0 [--full] [--json] [--timeout SECONDS]"; exit 0 ;; *) echo "Unknown option: $1"; exit 1 ;; esac done BASE_URL="http://${GOCHAT_HOST}:${GOCHAT_PORT}" METRICS_URL="http://${GOCHAT_HOST}:${GOCHAT_METRICS_PORT}" # Result tracking RESULTS=() CRITICAL_FAIL=0 DEGRADED_FAIL=0 check_result() { local name="$1" status="$2" detail="$3" RESULTS+=("${name}|${status}|${detail}") if [[ "$status" == "CRITICAL_FAIL" ]]; then CRITICAL_FAIL=$((CRITICAL_FAIL + 1)) elif [[ "$status" == "DEGRADED" ]]; then DEGRADED_FAIL=$((DEGRADED_FAIL + 1)) fi } # ---- Check 1: HTTP liveness endpoint ---- check_liveness() { local response response=$(curl -sf --max-time "${TIMEOUT}" "${BASE_URL}/live" 2>&1) && { check_result "liveness" "OK" "${response}" } || { check_result "liveness" "CRITICAL_FAIL" "HTTP /live endpoint unreachable" } } # ---- Check 2: HTTP readiness endpoint ---- check_readiness() { local response response=$(curl -sf --max-time "${TIMEOUT}" "${BASE_URL}/ready" 2>&1) && { check_result "readiness" "OK" "${response}" } || { check_result "readiness" "CRITICAL_FAIL" "HTTP /ready endpoint unreachable" } } # ---- Check 3: Database connectivity ---- check_database() { if [[ "${FULL_CHECK}" == "true" ]]; then local response response=$(curl -sf --max-time "${TIMEOUT}" "${BASE_URL}/health?check=database" 2>&1) && { check_result "database" "OK" "${response}" } || { check_result "database" "CRITICAL_FAIL" "Database connectivity failed" } else check_result "database" "SKIPPED" "Not in full mode" fi } # ---- Check 4: Redis connectivity ---- check_redis() { if [[ "${FULL_CHECK}" == "true" ]]; then local response response=$(curl -sf --max-time "${TIMEOUT}" "${BASE_URL}/health?check=redis" 2>&1) && { check_result "redis" "OK" "${response}" } || { check_result "redis" "DEGRADED" "Redis connectivity failed (non-critical)" } else check_result "redis" "SKIPPED" "Not in full mode" fi } # ---- Check 5: Metrics endpoint ---- check_metrics() { local response response=$(curl -sf --max-time "${TIMEOUT}" "${METRICS_URL}/metrics" 2>&1) && { check_result "metrics" "OK" "Prometheus metrics endpoint responding" } || { check_result "metrics" "DEGRADED" "Metrics endpoint unreachable (non-critical)" } } # ---- Check 6: WebSocket hub (full mode) ---- check_websocket() { if [[ "${FULL_CHECK}" == "true" ]]; then # WebSocket health check via HTTP status endpoint local response response=$(curl -sf --max-time "${TIMEOUT}" "${BASE_URL}/health?check=websocket" 2>&1) && { check_result "websocket" "OK" "${response}" } || { check_result "websocket" "DEGRADED" "WebSocket hub unreachable" } else check_result "websocket" "SKIPPED" "Not in full mode" fi } # ---- Run all checks ---- check_liveness check_readiness check_database check_redis check_metrics check_websocket # ---- Output results ---- if [[ "${JSON_OUTPUT}" == "true" ]]; then # JSON output for programmatic consumption echo '{' echo ' "checks": [' for i in "${!RESULTS[@]}"; do IFS='|' read -r name status detail <<< "${RESULTS[$i]}" comma="" [[ $i -gt 0 ]] && comma="," echo " ${comma}{\"name\": \"${name}\", \"status\": \"${status}\", \"detail\": \"${detail}\"}" done echo ' ],' echo ' "summary": {' echo ' "critical_failures": ${CRITICAL_FAIL},' echo ' "degraded_failures": ${DEGRADED_FAIL}' echo ' }' echo '}' else # Human-readable output echo "=== GoChat Health Check Report ===" for result in "${RESULTS[@]}"; do IFS='|' read -r name status detail <<< "${result}" case "$status" in OK) icon="✅" ;; CRITICAL_FAIL) icon="❌" ;; DEGRADED) icon="⚠️" ;; SKIPPED) icon="⏭️" ;; esac echo " ${icon} ${name}: ${status} — ${detail}" done echo "" echo "Critical failures: ${CRITICAL_FAIL} | Degraded: ${DEGRADED_FAIL}" fi # ---- Determine exit code ---- if [[ ${CRITICAL_FAIL} -gt 0 ]]; then exit 2 elif [[ ${DEGRADED_FAIL} -gt 0 ]]; then exit 1 else exit 0 fi