Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions app/api/pricing_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from datetime import datetime, UTC

from app.db.database import get_db
from app.db.models import DBSystemSecret
from app.core.security import check_system_admin, get_role_min_team_admin
from app.schemas.models import PricingTableCreate, PricingTableResponse

router = APIRouter(
tags=["pricing-tables"]
)

@router.post("", response_model=PricingTableResponse, status_code=status.HTTP_201_CREATED, dependencies=[Depends(check_system_admin)])
@router.post("/", response_model=PricingTableResponse, status_code=status.HTTP_201_CREATED, dependencies=[Depends(check_system_admin)])
async def create_pricing_table(
pricing_table: PricingTableCreate,
db: Session = Depends(get_db)
):
"""
Create or update the current pricing table. Only accessible by system admin users.
There can only be one active pricing table at a time.
"""
# Check if a pricing table already exists
existing_table = db.query(DBSystemSecret).filter(DBSystemSecret.key == "CurrentPricingTable").first()

if existing_table:
# Update existing table
existing_table.value = pricing_table.pricing_table_id
existing_table.updated_at = datetime.now(UTC)
db.commit()
db.refresh(existing_table)
return PricingTableResponse(
pricing_table_id=existing_table.value,
updated_at=existing_table.updated_at
)
else:
# Create new table
db_table = DBSystemSecret(
key="CurrentPricingTable",
value=pricing_table.pricing_table_id,
description="Current Stripe pricing table ID",
created_at=datetime.now(UTC)
)
db.add(db_table)
db.commit()
db.refresh(db_table)
return PricingTableResponse(
pricing_table_id=db_table.value,
updated_at=db_table.created_at
)

@router.get("", response_model=PricingTableResponse, dependencies=[Depends(get_role_min_team_admin)])
@router.get("/", response_model=PricingTableResponse, dependencies=[Depends(get_role_min_team_admin)])
async def get_pricing_table(
db: Session = Depends(get_db)
):
"""
Get the current pricing table ID. Only accessible by team admin users or higher privileges.
"""
pricing_table = db.query(DBSystemSecret).filter(DBSystemSecret.key == "CurrentPricingTable").first()
if not pricing_table:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No pricing table found"
)
return PricingTableResponse(
pricing_table_id=pricing_table.value,
updated_at=pricing_table.updated_at or pricing_table.created_at
)

@router.delete("", dependencies=[Depends(check_system_admin)])
@router.delete("/", dependencies=[Depends(check_system_admin)])
async def delete_pricing_table(
db: Session = Depends(get_db)
):
"""
Delete the current pricing table. Only accessible by system admin users.
"""
pricing_table = db.query(DBSystemSecret).filter(DBSystemSecret.key == "CurrentPricingTable").first()
if not pricing_table:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No pricing table found"
)

db.delete(pricing_table)
db.commit()

return {"message": "Pricing table deleted successfully"}
3 changes: 2 additions & 1 deletion app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from prometheus_fastapi_instrumentator import Instrumentator, metrics
from app.api import auth, private_ai_keys, users, regions, audit, teams, billing, products
from app.api import auth, private_ai_keys, users, regions, audit, teams, billing, products, pricing_tables
from app.core.config import settings
from app.db.database import get_db
from app.middleware.audit import AuditLogMiddleware
Expand Down Expand Up @@ -134,6 +134,7 @@ async def health_check():
app.include_router(teams.router, prefix="/teams", tags=["teams"])
app.include_router(billing.router, prefix="/billing", tags=["billing"])
app.include_router(products.router, prefix="/products", tags=["products"])
app.include_router(pricing_tables.router, prefix="/pricing-tables", tags=["pricing-tables"])

@app.get("/", include_in_schema=False)
async def custom_swagger_ui_html():
Expand Down
9 changes: 9 additions & 0 deletions app/schemas/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,13 @@ class Product(ProductBase):

class PricingTableSession(BaseModel):
client_secret: str
model_config = ConfigDict(from_attributes=True)

class PricingTableCreate(BaseModel):
pricing_table_id: str
model_config = ConfigDict(from_attributes=True)

class PricingTableResponse(BaseModel):
pricing_table_id: str
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
4 changes: 3 additions & 1 deletion frontend/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
env: {
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.STRIPE_PUBLISHABLE_KEY,
},
};

export default nextConfig;
Loading