mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-12 23:23:21 +00:00
updates Mar6
This commit is contained in:
150
app/routers/leads.py
Normal file
150
app/routers/leads.py
Normal file
@@ -0,0 +1,150 @@
|
||||
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])
|
||||
Reference in New Issue
Block a user