from typing import Any, Dict, List, Optional from fastapi import APIRouter, Header, HTTPException import requests from app.core.config import ( SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_TIMEOUT_SECONDS, SUPABASE_RAW_BUCKET, SUPABASE_PROCESSED_BUCKET, SUPABASE_ENFORCE_AUTH, MODEL_INFERENCE_PROVIDER, MODEL_SERVICE_ENABLE_LOCAL_ENDPOINT, MODEL_PATHS, MODELS_DIR, PARKING_HOURLY_RATE_EGP, PARKING_DAILY_RATE_EGP, PARKING_EXTRA_AFTER_FIRST_HOUR_EGP, PARKING_BILLING_MODE, PARKING_LOCATION_HOURLY_RATES, PARKING_LOCATION_PRICING, PARKING_LOCATION_CAPACITIES, GARAGE_TOTAL_CAPACITY, APP_SERVICE_FEE_EGP, CORE_TABLES, supabase_configured, model_service_configured, resolve_model_service_infer_url, should_load_local_models, LOGGER, ) from app.services.supabase_client import supabase_list_buckets, supabase_table_exists from app.security.auth import require_authenticated_user, require_developer_user, _supabase_headers router = APIRouter(tags=["health"]) @router.get("/health") def health(): local_models_required = should_load_local_models() status_value = "ok" model_inference_payload: Dict[str, Any] = { "provider": MODEL_INFERENCE_PROVIDER, "local_models_required": local_models_required, "local_endpoint_enabled": MODEL_SERVICE_ENABLE_LOCAL_ENDPOINT, "remote_service_configured": model_service_configured(), "remote_infer_url": resolve_model_service_infer_url(), } return { "status": status_value, "model_inference": model_inference_payload, "supabase_configured": supabase_configured(), "supabase_auth_enforced": SUPABASE_ENFORCE_AUTH, "pricing": { "default_billing_mode": PARKING_BILLING_MODE, "default_hourly_rate_egp": round(max(0.0, PARKING_HOURLY_RATE_EGP), 2), "default_daily_rate_egp": round(max(0.0, PARKING_DAILY_RATE_EGP), 2), "default_extra_after_first_hour_egp": round(max(0.0, PARKING_EXTRA_AFTER_FIRST_HOUR_EGP), 2), "location_hourly_rates": PARKING_LOCATION_HOURLY_RATES, "location_pricing": PARKING_LOCATION_PRICING, "location_capacities": PARKING_LOCATION_CAPACITIES, "garage_total_capacity": GARAGE_TOTAL_CAPACITY, "app_service_fee_egp": round(max(0.0, APP_SERVICE_FEE_EGP), 2), }, } @router.get("/supabase/health") def supabase_health(): if not supabase_configured(): return {"status": "disabled", "reason": "Set SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY"} try: response = requests.get( f"{SUPABASE_URL}/auth/v1/settings", headers={"apikey": SUPABASE_ANON_KEY}, timeout=SUPABASE_TIMEOUT_SECONDS, ) if response.status_code != 200: return {"status": "error", "http_status": response.status_code, "detail": response.text[:400]} existing_buckets = set(supabase_list_buckets()) required_buckets = {SUPABASE_RAW_BUCKET, SUPABASE_PROCESSED_BUCKET} missing_buckets = sorted(required_buckets - existing_buckets) table_checks: Dict[str, Any] = {} all_tables_ready = True for table_name in sorted(CORE_TABLES): exists, detail = supabase_table_exists(table_name) table_checks[table_name] = {"exists": exists, "detail": detail} if not exists: all_tables_ready = False ready_for_persistence = all_tables_ready and not missing_buckets return { "status": "ok" if ready_for_persistence else "partial", "url": SUPABASE_URL, "readiness": { "ready_for_persistence": ready_for_persistence, "missing_buckets": missing_buckets, "tables": table_checks, }, } except requests.RequestException as exc: return {"status": "error", "detail": str(exc)} @router.get("/models") def models(): return { "inference_provider": MODEL_INFERENCE_PROVIDER, "local_models_required": should_load_local_models(), "remote_service_configured": model_service_configured(), "remote_infer_url": resolve_model_service_infer_url(), } @router.get("/developer/models") def developer_models(authorization: Optional[str] = Header(default=None)): request_user, requester_role = require_developer_user(authorization) return models()