#!/usr/bin/env python3 """Nexus Autoparts — Post-Installation Health Check Verifies that all components are running correctly after installation. Usage: python3 scripts/health_check.py """ import sys import os import requests import psycopg2 import redis # Ensure we can import from project root sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) def info(msg): print(f"[INFO] {msg}") def ok(msg): print(f"[OK] {msg}") def fail(msg): print(f"[FAIL] {msg}") return False def check_postgresql(): """Verify PostgreSQL is running and accessible.""" info("Checking PostgreSQL...") try: conn = psycopg2.connect("postgresql://nexus@localhost/nexus_autoparts") cur = conn.cursor() cur.execute("SELECT version()") version = cur.fetchone()[0] cur.close() conn.close() ok(f"PostgreSQL running: {version.split()[0]} {version.split()[1]}") return True except Exception as e: return fail(f"PostgreSQL connection failed: {e}") def check_master_db(): """Verify master DB has required tables.""" info("Checking master database schema...") try: conn = psycopg2.connect("postgresql://nexus@localhost/nexus_autoparts") cur = conn.cursor() cur.execute(""" SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name IN ('tenants', 'brands', 'models', 'years', 'part_categories') """) tables = {r[0] for r in cur.fetchall()} cur.close() conn.close() required = {'tenants', 'brands', 'models', 'years', 'part_categories'} missing = required - tables if missing: return fail(f"Missing master tables: {missing}") ok("Master database schema is complete") return True except Exception as e: return fail(f"Master DB check failed: {e}") def check_tenant_template(): """Verify tenant_template database exists.""" info("Checking tenant_template database...") try: conn = psycopg2.connect("postgresql://nexus@localhost/tenant_template") cur = conn.cursor() cur.execute("SELECT 1 FROM pg_tables WHERE tablename = 'sales'") if not cur.fetchone(): return fail("tenant_template missing 'sales' table") cur.close() conn.close() ok("tenant_template database exists and has core tables") return True except Exception as e: return fail(f"tenant_template check failed: {e}") def check_first_tenant(): """Verify at least one tenant exists.""" info("Checking first tenant...") try: conn = psycopg2.connect("postgresql://nexus@localhost/nexus_autoparts") cur = conn.cursor() cur.execute("SELECT COUNT(*) FROM tenants WHERE is_active = true") count = cur.fetchone()[0] cur.close() conn.close() if count == 0: return fail("No active tenants found") ok(f"Found {count} active tenant(s)") return True except Exception as e: return fail(f"Tenant check failed: {e}") def check_pos_health(): """Verify POS health endpoint responds.""" info("Checking POS health endpoint...") try: resp = requests.get("http://localhost:5001/pos/health", timeout=5) if resp.status_code == 200 and resp.json().get("status") == "ok": ok("POS health endpoint is responding") return True return fail(f"POS health returned: {resp.status_code} {resp.text}") except requests.exceptions.ConnectionError: return fail("POS not running on port 5001") except Exception as e: return fail(f"POS health check failed: {e}") def check_redis(): """Verify Redis is running and accessible.""" info("Checking Redis...") try: r = redis.from_url( os.environ.get('REDIS_URL', 'redis://localhost:6379/0'), decode_responses=True ) if r.ping(): info = r.info('server') ok(f"Redis {info.get('redis_version', '?')} running") return True return fail("Redis PING returned False") except Exception as e: return fail(f"Redis connection failed: {e}") def check_meilisearch(): """Verify Meilisearch is running.""" info("Checking Meilisearch...") try: sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pos')) from services.meili_search import health_check if health_check(): ok("Meilisearch running") return True return fail("Meilisearch health check failed") except Exception as e: return fail(f"Meilisearch connection failed: {e}") def check_metabase(): """Verify Metabase is running.""" info("Checking Metabase...") try: import requests url = os.environ.get('METABASE_URL', 'http://localhost:3000') r = requests.get(f"{url}/api/health", timeout=5) if r.status_code == 200 and r.json().get('status') == 'ok': ok("Metabase running") return True return fail(f"Metabase returned: {r.status_code}") except Exception as e: return fail(f"Metabase connection failed: {e}") def check_web_health(): """Verify web/dashboard responds.""" info("Checking web dashboard...") try: resp = requests.get("http://localhost:5000/", timeout=5) if resp.status_code == 200: ok("Web dashboard is responding") return True return fail(f"Web returned status: {resp.status_code}") except requests.exceptions.ConnectionError: return fail("Web server not running on port 5000") except Exception as e: return fail(f"Web check failed: {e}") def main(): print("=" * 60) print(" Nexus Autoparts — Health Check") print("=" * 60) print() results = [] results.append(("PostgreSQL", check_postgresql())) results.append(("Redis Cache", check_redis())) results.append(("Meilisearch", check_meilisearch())) results.append(("Metabase", check_metabase())) results.append(("Master DB Schema", check_master_db())) results.append(("Tenant Template", check_tenant_template())) results.append(("First Tenant", check_first_tenant())) results.append(("POS Health", check_pos_health())) results.append(("Web Dashboard", check_web_health())) print() print("=" * 60) passed = sum(1 for _, r in results if r) total = len(results) print(f" Results: {passed}/{total} checks passed") print("=" * 60) if passed < total: print() print("Failed checks:") for name, result in results: if not result: print(f" - {name}") sys.exit(1) else: print() print("All systems operational!") sys.exit(0) if __name__ == '__main__': main()