Files
contexta_be/app/routers/leads.py
belviskhoremk 9dccc83293 updates Mar6
2026-03-06 22:37:40 +00:00

151 lines
5.1 KiB
Python

from fastapi import APIRouter, HTTPException, Depends, Query
from fastapi.responses import StreamingResponse
from app.database import get_supabase
from app.dependencies import get_current_user
from app.models import LeadCreate, LeadResponse
from typing import List, Optional
import uuid
import csv
import io
import logging
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/leads", tags=["Leads"])
def _get_user_company_id(user_id: str, supabase) -> str:
result = supabase.table("companies").select("id").eq("owner_id", user_id).execute()
if not result.data:
raise HTTPException(status_code=404, detail="Company not found")
return result.data[0]["id"]
def _check_leads_access(user_id: str, supabase):
sub = supabase.table("subscriptions").select("plan").eq("user_id", user_id).eq("status", "active").execute()
plan = sub.data[0]["plan"] if sub.data else "free"
if plan not in ("starter", "business", "agency", "enterprise"):
raise HTTPException(status_code=402, detail="Lead capture requires Starter plan or higher")
return plan
@router.get("", response_model=List[LeadResponse])
async def list_leads(
chatbot_id: Optional[str] = Query(None),
page: int = Query(1, ge=1),
limit: int = Query(50, ge=1, le=200),
user=Depends(get_current_user),
):
"""List leads for the user's chatbots."""
supabase = get_supabase()
_check_leads_access(user.id, supabase)
company_id = _get_user_company_id(user.id, supabase)
# Get owned chatbot IDs
chatbots_q = supabase.table("chatbots").select("id").eq("company_id", company_id)
if chatbot_id:
chatbots_q = chatbots_q.eq("id", chatbot_id)
chatbots = chatbots_q.execute()
chatbot_ids = [c["id"] for c in (chatbots.data or [])]
if not chatbot_ids:
return []
offset = (page - 1) * limit
result = supabase.table("leads").select("*") \
.in_("chatbot_id", chatbot_ids) \
.order("created_at", desc=True) \
.range(offset, offset + limit - 1) \
.execute()
return [LeadResponse(**lead) for lead in (result.data or [])]
@router.get("/export")
async def export_leads_csv(
chatbot_id: Optional[str] = Query(None),
user=Depends(get_current_user),
):
"""Export leads as CSV file."""
supabase = get_supabase()
_check_leads_access(user.id, supabase)
company_id = _get_user_company_id(user.id, supabase)
chatbots_q = supabase.table("chatbots").select("id").eq("company_id", company_id)
if chatbot_id:
chatbots_q = chatbots_q.eq("id", chatbot_id)
chatbots = chatbots_q.execute()
chatbot_ids = [c["id"] for c in (chatbots.data or [])]
if not chatbot_ids:
leads_data = []
else:
result = supabase.table("leads").select("*") \
.in_("chatbot_id", chatbot_ids) \
.order("created_at", desc=True) \
.execute()
leads_data = result.data or []
# Build CSV
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=["id", "chatbot_id", "email", "name", "phone", "company", "created_at"])
writer.writeheader()
for lead in leads_data:
writer.writerow({
"id": lead.get("id", ""),
"chatbot_id": lead.get("chatbot_id", ""),
"email": lead.get("email", ""),
"name": lead.get("name", ""),
"phone": lead.get("phone", ""),
"company": lead.get("company", ""),
"created_at": lead.get("created_at", ""),
})
csv_bytes = output.getvalue().encode("utf-8")
return StreamingResponse(
iter([csv_bytes]),
media_type="text/csv",
headers={"Content-Disposition": "attachment; filename=leads.csv"},
)
# Public endpoint — no auth required
leads_public_router = APIRouter(tags=["Leads"])
@leads_public_router.post("/chatbots/{chatbot_id}/leads", response_model=LeadResponse, status_code=201)
async def submit_lead(chatbot_id: str, data: LeadCreate):
"""Submit a lead for a chatbot (public endpoint, no auth required)."""
supabase = get_supabase()
# Verify chatbot exists
chatbot = supabase.table("chatbots").select("id, lead_capture_enabled").eq("id", chatbot_id).execute()
if not chatbot.data:
raise HTTPException(status_code=404, detail="Chatbot not found")
if not chatbot.data[0].get("lead_capture_enabled", False):
raise HTTPException(status_code=400, detail="Lead capture not enabled for this chatbot")
# Deduplicate by email+chatbot_id
if data.email:
existing = supabase.table("leads").select("*") \
.eq("chatbot_id", chatbot_id) \
.eq("email", data.email) \
.execute()
if existing.data:
return LeadResponse(**existing.data[0])
lead_data = {
"id": str(uuid.uuid4()),
"chatbot_id": chatbot_id,
"conversation_id": data.conversation_id,
"email": data.email,
"name": data.name,
"phone": data.phone,
"company": data.company,
}
result = supabase.table("leads").insert(lead_data).execute()
if not result.data:
raise HTTPException(status_code=500, detail="Failed to save lead")
return LeadResponse(**result.data[0])