import os from fastapi import FastAPI, Header, HTTPException from fastapi.middleware.cors import CORSMiddleware from supabase import create_client, Client from jose import jwt, JWTError from dotenv import load_dotenv # Load environment variables from .env file for local development load_dotenv() # --- Configuration --- # These will be set as "Secrets" in your Hugging Face Space settings SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_ROLE_KEY") JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY") ALGORITHM = "HS256" # --- Initialization --- app = FastAPI() @app.get("/") def read_root(): return {"status": "ok", "message": "Itinerary API is running"} # Initialize Supabase client with the service role key for admin access try: supabase: Client = create_client(SUPABASE_URL, SUPABASE_SERVICE_KEY) except Exception as e: print(f"Error initializing Supabase client: {e}") supabase = None # Configure CORS (Cross-Origin Resource Sharing) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) # --- API Endpoint (MODIFIED to fetch all itineraries) --- @app.get("/get-itinerary") async def get_itinerary_data(authorization: str = Header(...)): """ Verifies the user's custom JWT and fetches ALL their itineraries from Supabase. """ if not supabase: raise HTTPException(status_code=500, detail="Database connection not configured.") try: # 1. Extract token from "Bearer " header token_type, token = authorization.split() if token_type.lower() != "bearer": raise HTTPException(status_code=401, detail="Invalid token type") # 2. Verify and decode the JWT payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=[ALGORITHM]) # 3. Extract user ID user_id = payload.get("user_id") if user_id is None: raise HTTPException(status_code=401, detail="User ID not found in token payload") # 4. Query Supabase for all itineraries, newest first # We removed .single() and .limit(1) to get a list # We also added "id" and "created_at" to help the frontend response = supabase.table("itineraries") \ .select("id, created_at, itinerary_data") \ .eq("user_id", user_id) \ .order("created_at", desc=True) \ .execute() # 5. Return the list of data return response.data except JWTError: raise HTTPException(status_code=401, detail="Could not validate credentials") except HTTPException as e: # Re-raise HTTPExceptions to preserve status code and detail raise e except Exception as e: # Catch any other unexpected errors print(f"An unexpected error occurred: {e}") raise HTTPException(status_code=500, detail="Internal server error") @app.delete("/itinerary/{itinerary_id}") async def delete_itinerary(itinerary_id: str, authorization: str = Header(...)): # 🎯 MODIFIED: Changed int to str """ Deletes a specific itinerary, ensuring the user owns it. """ if not supabase: raise HTTPException(status_code=500, detail="Database connection not configured.") try: # First, verify the user's token to get their ID token_type, token = authorization.split() if token_type.lower() != "bearer": raise HTTPException(status_code=401, detail="Invalid token type") payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=[ALGORITHM]) user_id = payload.get("user_id") if user_id is None: raise HTTPException(status_code=401, detail="User ID not found in token payload") # Securely delete the itinerary by matching both its ID and the user's ID response = supabase.table("itineraries").delete().match({ "id": itinerary_id, "user_id": user_id }).execute() if not response.data: raise HTTPException(status_code=404, detail="Itinerary not found or you do not have permission to delete it.") return {"message": "Itinerary deleted successfully"} except JWTError: raise HTTPException(status_code=401, detail="Could not validate credentials") except Exception as e: print(f"An unexpected error occurred: {e}") raise HTTPException(status_code=500, detail="Internal server error")