Spaces:
Running
Running
| # app/routes/listing.py | |
| from fastapi import APIRouter, Depends, Query, HTTPException | |
| from typing import Optional | |
| from bson import ObjectId | |
| from app.database import get_db | |
| from app.models.listing import Listing | |
| from app.guards.jwt_guard import get_current_user | |
| router = APIRouter(tags=["Listings"]) | |
| async def get_active_listings( | |
| listing_type: Optional[str] = Query(None, description="Filter by listing type: rent, sale, short-stay, roommate"), | |
| current_user: dict = Depends(get_current_user) | |
| ): | |
| """Fetch active listings, optionally filtered by type (auth required).""" | |
| db = await get_db() | |
| # Build query | |
| query = {"status": "active"} | |
| if listing_type: | |
| query["listing_type"] = listing_type | |
| try: | |
| cursor = db.listings.find(query).sort("created_at", -1) | |
| listings = [] | |
| async for doc in cursor: | |
| # Convert MongoDB ObjectId to string | |
| if "_id" in doc: | |
| doc["_id"] = str(doc["_id"]) | |
| try: | |
| # Pydantic will handle datetime conversion | |
| listing = Listing(**doc) | |
| listings.append(listing.model_dump(by_alias=True)) | |
| except Exception as e: | |
| print(f"[WARNING] Error parsing listing {doc.get('_id', 'unknown')}: {e}") | |
| continue | |
| print(f"[DEBUG] Query: {query} | Found: {len(listings)} listings") | |
| return {"success": True, "data": listings} | |
| except Exception as e: | |
| print(f"[ERROR] {str(e)}") | |
| return {"success": False, "message": str(e), "error_code": "FETCH_ERROR"} | |
| async def get_listings_by_user( | |
| user_id: str, | |
| listing_type: Optional[str] = Query(None, description="Filter by listing type: rent, sale, short-stay, roommate"), | |
| status: Optional[str] = Query(None, description="Filter by status: draft, active, archived"), | |
| ): | |
| """ | |
| Fetch all listings for a specific user by their user ID. | |
| No authentication required - public endpoint for viewing user profiles. | |
| """ | |
| db = await get_db() | |
| # Validate user_id format | |
| if not ObjectId.is_valid(user_id): | |
| raise HTTPException(status_code=400, detail="Invalid user ID format") | |
| # Build query | |
| query = {"user_id": user_id} | |
| if listing_type: | |
| query["listing_type"] = listing_type | |
| if status: | |
| query["status"] = status | |
| else: | |
| # Default to active listings if no status specified | |
| query["status"] = "active" | |
| try: | |
| cursor = db.listings.find(query).sort("created_at", -1) | |
| listings = [] | |
| async for doc in cursor: | |
| # Convert MongoDB ObjectId to string | |
| if "_id" in doc: | |
| doc["_id"] = str(doc["_id"]) | |
| try: | |
| listing = Listing(**doc) | |
| listings.append(listing.model_dump(by_alias=True)) | |
| except Exception as e: | |
| print(f"[WARNING] Error parsing listing {doc.get('_id', 'unknown')}: {e}") | |
| continue | |
| print(f"[DEBUG] User {user_id} listings query: {query} | Found: {len(listings)} listings") | |
| return { | |
| "success": True, | |
| "data": listings, | |
| "total": len(listings), | |
| "user_id": user_id | |
| } | |
| except Exception as e: | |
| print(f"[ERROR] {str(e)}") | |
| return {"success": False, "message": str(e), "error_code": "FETCH_ERROR"} | |
| async def delete_listing( | |
| listing_id: str, | |
| current_user: dict = Depends(get_current_user) | |
| ): | |
| """ | |
| Delete a listing by ID. | |
| Only the owner can delete their listing. | |
| Decrements user's totalListings counter. | |
| """ | |
| db = await get_db() | |
| user_id = current_user.get("user_id") or current_user.get("sub") | |
| try: | |
| # Validate ObjectId | |
| if not ObjectId.is_valid(listing_id): | |
| raise HTTPException(status_code=400, detail="Invalid listing ID format") | |
| # Find the listing first to verify ownership | |
| listing = await db.listings.find_one({"_id": ObjectId(listing_id)}) | |
| if not listing: | |
| raise HTTPException(status_code=404, detail="Listing not found") | |
| # Check ownership | |
| if listing.get("user_id") != user_id: | |
| raise HTTPException(status_code=403, detail="You can only delete your own listings") | |
| # Delete the listing | |
| result = await db.listings.delete_one({"_id": ObjectId(listing_id)}) | |
| if result.deleted_count == 0: | |
| raise HTTPException(status_code=500, detail="Failed to delete listing") | |
| # ✅ Sync to Qdrant - remove the vector | |
| from app.ai.services.vector_service import delete_listing_from_vector_db | |
| await delete_listing_from_vector_db(listing_id) | |
| # Decrement user's totalListings counter | |
| try: | |
| await db.users.update_one( | |
| {"_id": ObjectId(user_id)}, | |
| {"$inc": {"totalListings": -1}} | |
| ) | |
| print(f"[INFO] User {user_id} totalListings decremented") | |
| except Exception as counter_err: | |
| print(f"[WARNING] Failed to decrement totalListings: {counter_err}") | |
| return { | |
| "success": True, | |
| "message": "Listing deleted successfully", | |
| "listing_id": listing_id | |
| } | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| print(f"[ERROR] Delete listing error: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) |