Co-authored-by: Michael Chihlas <michael@resolutionflow.com> Co-committed-by: Michael Chihlas <michael@resolutionflow.com>
59 lines
2.1 KiB
Python
59 lines
2.1 KiB
Python
"""Public plans endpoint — no auth required.
|
|
|
|
GET /api/v1/plans/public
|
|
Returns the public-safe view of `plan_billing` joined with
|
|
`plan_limits.max_users` (exposed as `max_seats`), filtered to
|
|
`is_public=True AND is_archived=False`, ordered by sort_order ASC, plan ASC.
|
|
|
|
Distinct from `/admin/plan-limits` (admin-only, returns ALL plans including
|
|
archived/internal). This endpoint exists to power the marketing /pricing page
|
|
without exposing the rest of the admin-only billing surface.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.admin_database import get_admin_db
|
|
from app.models.plan_billing import PlanBilling
|
|
from app.models.plan_limits import PlanLimits
|
|
from app.schemas.billing import PublicPlanResponse
|
|
|
|
router = APIRouter(prefix="/plans", tags=["plans"])
|
|
|
|
|
|
@router.get("/public", response_model=list[PublicPlanResponse])
|
|
async def list_public_plans(
|
|
db: Annotated[AsyncSession, Depends(get_admin_db)],
|
|
) -> list[PublicPlanResponse]:
|
|
"""List public, non-archived plans for the marketing /pricing page.
|
|
|
|
Public — no auth. Uses `get_admin_db` because this is a cross-tenant read
|
|
of the global plan catalog (same pattern as `/config/public`).
|
|
"""
|
|
stmt = (
|
|
select(PlanBilling, PlanLimits.max_users)
|
|
.outerjoin(PlanLimits, PlanBilling.plan == PlanLimits.plan)
|
|
.where(PlanBilling.is_public.is_(True))
|
|
.where(PlanBilling.is_archived.is_(False))
|
|
.order_by(PlanBilling.sort_order.asc(), PlanBilling.plan.asc())
|
|
)
|
|
rows = (await db.execute(stmt)).all()
|
|
return [
|
|
PublicPlanResponse(
|
|
plan=billing.plan,
|
|
display_name=billing.display_name,
|
|
description=billing.description,
|
|
monthly_price_cents=billing.monthly_price_cents,
|
|
annual_price_cents=billing.annual_price_cents,
|
|
max_seats=max_users,
|
|
sort_order=billing.sort_order,
|
|
is_public=billing.is_public,
|
|
)
|
|
for billing, max_users in rows
|
|
]
|