- Add findagram.co React frontend with product search, brands, categories - Add findadispo.com React frontend with dispensary locator - Wire findagram to backend /api/az/* endpoints - Update category/brand links to route to /products with filters - Add k8s manifests for both frontends - Add multi-domain user support migrations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
165 lines
4.2 KiB
Python
165 lines
4.2 KiB
Python
from typing import Optional
|
|
from fastapi import APIRouter, HTTPException
|
|
import httpx
|
|
from pydantic import BaseModel
|
|
|
|
from config import get_settings
|
|
|
|
router = APIRouter(prefix="/dispensaries", tags=["Dispensaries"])
|
|
settings = get_settings()
|
|
|
|
|
|
class DispensaryQuery(BaseModel):
|
|
lat: Optional[float] = None
|
|
lng: Optional[float] = None
|
|
city: Optional[str] = None
|
|
state: Optional[str] = None
|
|
radius: Optional[int] = 25
|
|
limit: Optional[int] = 20
|
|
offset: Optional[int] = 0
|
|
|
|
|
|
async def fetch_from_api(endpoint: str, params: dict = None):
|
|
"""Fetch data from the external dispensary API"""
|
|
headers = {}
|
|
if settings.dispensary_api_key:
|
|
headers["X-API-Key"] = settings.dispensary_api_key
|
|
|
|
async with httpx.AsyncClient() as client:
|
|
try:
|
|
response = await client.get(
|
|
f"{settings.dispensary_api_url}{endpoint}",
|
|
params=params,
|
|
headers=headers,
|
|
timeout=30.0
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except httpx.HTTPStatusError as e:
|
|
raise HTTPException(
|
|
status_code=e.response.status_code,
|
|
detail=f"API error: {e.response.text}"
|
|
)
|
|
except httpx.RequestError as e:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail=f"Failed to connect to dispensary API: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.get("/")
|
|
async def search_dispensaries(
|
|
lat: Optional[float] = None,
|
|
lng: Optional[float] = None,
|
|
city: Optional[str] = None,
|
|
state: Optional[str] = "AZ",
|
|
radius: int = 25,
|
|
limit: int = 20,
|
|
offset: int = 0,
|
|
open_now: bool = False,
|
|
min_rating: Optional[float] = None
|
|
):
|
|
"""Search for dispensaries by location"""
|
|
params = {
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"state": state
|
|
}
|
|
|
|
if lat and lng:
|
|
params["lat"] = lat
|
|
params["lng"] = lng
|
|
params["radius"] = radius
|
|
|
|
if city:
|
|
params["city"] = city
|
|
|
|
# Fetch from external API
|
|
data = await fetch_from_api("/api/az/stores", params)
|
|
|
|
# Apply client-side filters if needed
|
|
stores = data.get("stores", [])
|
|
|
|
if open_now:
|
|
# Filter stores that are currently open
|
|
# This would need actual business hours logic
|
|
pass
|
|
|
|
if min_rating:
|
|
stores = [s for s in stores if (s.get("rating") or 0) >= min_rating]
|
|
|
|
return {
|
|
"dispensaries": stores,
|
|
"total": len(stores),
|
|
"limit": limit,
|
|
"offset": offset
|
|
}
|
|
|
|
|
|
@router.get("/{dispensary_id}")
|
|
async def get_dispensary(dispensary_id: int):
|
|
"""Get details for a specific dispensary"""
|
|
data = await fetch_from_api(f"/api/az/stores/{dispensary_id}")
|
|
return data
|
|
|
|
|
|
@router.get("/{dispensary_id}/products")
|
|
async def get_dispensary_products(
|
|
dispensary_id: int,
|
|
category: Optional[str] = None,
|
|
search: Optional[str] = None,
|
|
limit: int = 50,
|
|
offset: int = 0
|
|
):
|
|
"""Get products for a specific dispensary"""
|
|
params = {
|
|
"limit": limit,
|
|
"offset": offset
|
|
}
|
|
|
|
if category:
|
|
params["category"] = category
|
|
|
|
if search:
|
|
params["search"] = search
|
|
|
|
data = await fetch_from_api(f"/api/az/stores/{dispensary_id}/products", params)
|
|
return data
|
|
|
|
|
|
@router.get("/{dispensary_id}/categories")
|
|
async def get_dispensary_categories(dispensary_id: int):
|
|
"""Get product categories for a dispensary"""
|
|
data = await fetch_from_api(f"/api/az/stores/{dispensary_id}/categories")
|
|
return data
|
|
|
|
|
|
@router.get("/nearby")
|
|
async def get_nearby_dispensaries(
|
|
lat: float,
|
|
lng: float,
|
|
radius: int = 10,
|
|
limit: int = 10
|
|
):
|
|
"""Get nearby dispensaries by coordinates"""
|
|
params = {
|
|
"lat": lat,
|
|
"lng": lng,
|
|
"radius": radius,
|
|
"limit": limit
|
|
}
|
|
data = await fetch_from_api("/api/az/stores", params)
|
|
return data.get("stores", [])
|
|
|
|
|
|
@router.get("/featured")
|
|
async def get_featured_dispensaries(limit: int = 6):
|
|
"""Get featured dispensaries for the homepage"""
|
|
# For now, return top-rated dispensaries
|
|
params = {
|
|
"limit": limit,
|
|
"sort": "rating"
|
|
}
|
|
data = await fetch_from_api("/api/az/stores", params)
|
|
return data.get("stores", [])
|