AIDA / app /routes /listing.py
destinyebuka's picture
fyp
6972bca
# 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"])
@router.get("/")
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"}
@router.get("/user/{user_id}")
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"}
@router.delete("/{listing_id}")
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))