FASE 4-5-6: Infraestructura, CRM, Service Orders, Notificaciones, Ahorro, Logistica, API Publica
FASE 4: - Redis cache de stock con fallback graceful - Multi-moneda (MXN/USD) con contabilidad en MXN - Proveedores y ordenes de compra completo - Meilisearch 1.5M+ partes indexadas - Metabase KPIs con dashboard auto-generado FASE 5: - CRM mejorado: activities, tags, loyalty program, analytics - Imagenes de partes: upload, resize, thumbnails WebP - Ordenes de servicio Kanban: received->diagnosis->repair->ready->delivered - Garantias/RMA, alertas de reorden, multi-sucursal - Stubs BNPL (APLAZO) y ERP Sync (Aspel/Contpaqi) FASE 6: - Notificaciones automaticas: push/WhatsApp/email/in-app - Reportes de ahorro vs retail_price - Logistica + tracking: DHL, FedEx, Estafeta, 99min, Uber - API Publica: API keys, rate limiting, catalog search Migraciones: v1.9-v3.0 Tests: 93/93 pasando Backup: nexus_backup_20260427_045859.tar.gz
This commit is contained in:
222
scripts/health_check.py
Executable file
222
scripts/health_check.py
Executable file
@@ -0,0 +1,222 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user