Spaces:
Sleeping
Sleeping
| 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() | |
| 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) --- | |
| 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 <token>" 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") | |
| 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") |