MEDIUM: GET /recipes/saved/collections missing tier gate — free users can list paid feature #95

Closed
opened 2026-04-18 09:02:56 -07:00 by pyr0ball · 0 comments
Owner

Summary

The GET /api/v1/recipes/saved/collections endpoint is missing a tier check. Free-tier users can call it (they get an empty list, since they have no collections). All write operations (POST, PATCH, DELETE) are properly gated. The GET is an inconsistency.

Affected Code

app/api/endpoints/saved_recipes.py:

@router.get("/collections", response_model=list[CollectionSummary])
async def list_collections(
    session: CloudUser = Depends(get_session),
) -> list[CollectionSummary]:
    rows = await asyncio.to_thread(...)  # ← no tier check
    return [CollectionSummary(**r) for r in rows]

While this is low-risk (free users get an empty list), it violates feature-gating consistency and may confuse the frontend about whether the feature is available.

Fix

@router.get("/collections", response_model=list[CollectionSummary])
async def list_collections(
    session: CloudUser = Depends(get_session),
) -> list[CollectionSummary]:
    if not can_use("recipe_collections", session.tier):
        raise HTTPException(status_code=403, detail="Collections require Paid tier.")
    rows = await asyncio.to_thread(...)
    return [CollectionSummary(**r) for r in rows]
## Summary The `GET /api/v1/recipes/saved/collections` endpoint is missing a tier check. Free-tier users can call it (they get an empty list, since they have no collections). All write operations (POST, PATCH, DELETE) are properly gated. The GET is an inconsistency. ## Affected Code `app/api/endpoints/saved_recipes.py`: ```python @router.get("/collections", response_model=list[CollectionSummary]) async def list_collections( session: CloudUser = Depends(get_session), ) -> list[CollectionSummary]: rows = await asyncio.to_thread(...) # ← no tier check return [CollectionSummary(**r) for r in rows] ``` While this is low-risk (free users get an empty list), it violates feature-gating consistency and may confuse the frontend about whether the feature is available. ## Fix ```python @router.get("/collections", response_model=list[CollectionSummary]) async def list_collections( session: CloudUser = Depends(get_session), ) -> list[CollectionSummary]: if not can_use("recipe_collections", session.tier): raise HTTPException(status_code=403, detail="Collections require Paid tier.") rows = await asyncio.to_thread(...) return [CollectionSummary(**r) for r in rows] ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/kiwi#95
No description provided.